To enable the use of GDS in your own Emacs sessions, simply add
(require 'gds)
somewhere in your .emacs file. This will cause Emacs to load the GDS Emacs Lisp code when starting up, and to start the inferior GDS server process so that it is ready and waiting for any Guile programs that want to use GDS.
The Scheme side of GDS is installed automatically by Guile. The Emacs Lisp side, however, is not. You will have to grab gds-server.el, gds-scheme.el, and gds.el from Guile's source distribution, and make sure they end up in Emacs' load path.
(If GDS's Scheme code is not installed in one of the locations in Guile's load path, you may find that the server process fails to start. When this happens you will see an error message from Emacs:
error in process filter: Wrong type argument: listp, Backtrace:
and the gds-debug
buffer will contain a Scheme backtrace ending
with the message:
no code for module (ice-9 gds-server)
The solution for this is to customize the Emacs variable
gds-scheme-directory
so that it specifies where the GDS Scheme
code is installed. Then either restart Emacs or type M-x
gds-run-debug-server to try starting the GDS server process again.)
For evaluations, help and completion from Scheme code buffers that you
are working on, this is all you need. The first time you do any of
these things, GDS will automatically start a new Guile client program as
an Emacs subprocess. This Guile program does nothing but wait for and
act on instructions from GDS, and we refer to it as a utility
Guile client. Over time this utility client will accumulate the code
that you ask it to evaluate, and you can also tell it to load complete
files or modules by sending it load
or use-modules
expressions.
When you want to use GDS to work on an independent Guile application, you need to add something to that application's Scheme code to cause it to connect to and interact with GDS at the right times. The following subsections describe the ways of doing this.
One option is to use GDS to catch and display any exceptions that
are thrown by the application's code. If you already have a
lazy-catch
or with-throw-handler
around the area of code
that you want to monitor, you just need to add the following to the
handler code:
(gds-debug-trap (throw->trap-context key args))
where key
and args
are the first and rest arguments that
Guile passes to the handler. (In other words, they assume the handler
signature (lambda (key . args) ...)
.) With Guile 1.8 or
later, you can also do this with a catch
, by adding this same
code to the catch's pre-unwind handler.
If you don't already have any of these, insert a whole
with-throw-handler
expression (or lazy-catch
if your Guile
is pre-1.8) around the code of interest like this:
(with-throw-handler #t (lambda () ;; Protected code here. ) (lambda (key . args) (gds-debug-trap (throw->trap-context key args))))
Either way, you will need to use the (ice-9 gds-client)
and
(ice-9 debugging traps)
modules.
Two special cases of this are the lazy-catch that the Guile REPL code
uses to catch exceptions in user code, and the lazy-catch inside the
stack-catch
utility procedure that is provided by the
(ice-9 stack-catch)
module. Both of these use a handler called
lazy-handler-dispatch
(defined in boot-9.scm), which you
can hook into such that it calls GDS to display the stack when an
exception occurs. To do this, use the on-lazy-handler-dispatch
procedure as follows.
(use-modules (ice-9 gds-client) (ice-9 debugging traps)) (on-lazy-handler-dispatch gds-debug-trap)
After this the program will use GDS to display the stack whenever it
hits an exception that is protected by a lazy-catch
using
lazy-handler-dispatch
.
In addition to setting an exception handler as described above, a Guile program can in principle set itself up to accept new instructions from GDS at any time, not just when it has stopped at an exception. This would allow the GDS user to evaluate code in the context of the running program, without having to wait for the program to stop first.
(use-modules (ice-9 gds-client)) (gds-accept-input #t)
gds-accept-input
causes the calling program to loop processing
instructions from GDS, until GDS sends the continue
instruction.
This blocks the thread that calls it, however, so it will normally be
more practical for the program to set up a dedicated GDS thread and call
gds-accept-input
from that thread.
For select
-driven applications, an alternative approach would be
for the GDS client code to provide an API which allowed the application
to
select
call
select
indicated data
available for reading on those descriptors/ports.
This approach is not yet implemented, though.
The “utility” Guile client mentioned above is a simple combination of the mechanisms that we have just described. In fact the code for the utility Guile client is essentially just this:
(use-modules (ice-9 gds-client)) (named-module-use! '(guile-user) '(ice-9 session)) (gds-accept-input #f))
The named-module-use!
line ensures that the client can process
help
and apropos
expressions, to implement lookups in
Guile's online help. The #f
parameter to
gds-accept-input
means that the continue
instruction
will not cause the instruction loop to exit, which makes sense here
because the utility client has nothing to do except to process GDS
instructions.
The utility client does not use on-lazy-handler-dispatch
at its
top level, because it has its own mechanism for catching and reporting
exceptions in the code that it is asked to evaluate. This mechanism
summarizes the exception and gives the user a button they can click to
see the full stack, so the end result is very similar to what
on-lazy-handler-dispatch
provides. Deep inside
gds-accept-input
, in the part that handles evaluating
expressions from Emacs, the GDS client code uses
throw->trap-context
and gds-debug-trap
to implement
this.