summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Jerram <neil@ossau.uklinux.net>2009-08-27 22:52:20 +0100
committerNeil Jerram <neil@ossau.uklinux.net>2009-08-27 22:52:20 +0100
commit639bf3e507959ca53fef713306eb33f1074f1588 (patch)
treef29a7fdd8e296976b041388892f68c97bc7469b2
parentcdc4f3db09727cc1613f7a2fd2335efa10a9d53f (diff)
parent8a40db3714628f88b138017835d448231257d13b (diff)
downloadguile-639bf3e507959ca53fef713306eb33f1074f1588.tar.gz
Merge branch 'ossau-gds-dev'
Conflicts: THANKS
-rw-r--r--.gitignore2
-rw-r--r--AUTHORS29
-rw-r--r--THANKS3
-rwxr-xr-xemacs/gds-faq.txt225
-rw-r--r--emacs/gds-server.el19
-rw-r--r--emacs/gds-test.el166
-rwxr-xr-xemacs/gds-test.sh2
-rw-r--r--emacs/gds-test.stdin1
-rwxr-xr-xemacs/gds-tutorial.txt223
-rw-r--r--emacs/gds.el27
-rwxr-xr-xmodule/ice-9/gds-client.scm29
-rw-r--r--module/ice-9/gds-server.scm53
12 files changed, 715 insertions, 64 deletions
diff --git a/.gitignore b/.gitignore
index 9d507e11b..8754b488a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,6 +72,8 @@ gdb-pre-inst-guile
cscope.out
cscope.files
*.log
+gds-test.debug
+gds-test.transcript
INSTALL
*.aux
*.cp
diff --git a/AUTHORS b/AUTHORS
index ed2adbab7..b8f605efa 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -206,8 +206,34 @@ In the subdirectory doc, changes to:
Many changes throughout.
Neil Jerram:
+In the subdirectory emacs, wrote:
+ gds.el gds-scheme.el gds-server.el
+ gds-test.el gds-test.sh gds-test.stdin
+ gds-tutorial.txt gds-faq.txt
In the subdirectory ice-9, wrote:
- buffered-input.scm
+ buffered-input.scm gds-client.scm gds-server.scm
+In the subdirectory ice-9/debugging, wrote:
+ example-fns.scm ice-9-debugger-extensions.scm
+ steps.scm trace.scm traps.scm
+ trc.scm
+In the subdirectory lang/elisp, wrote:
+ base.scm example.el interface.scm
+ transform.scm variables.scm
+In the subdirectory lang/elisp/internals, wrote:
+ evaluation.scm format.scm fset.scm
+ lambda.scm load.scm null.scm
+ set.scm signal.scm time.scm
+ trace.scm
+In the subdirectory lang/elisp/primitives, wrote:
+ buffers.scm char-table.scm features.scm
+ fns.scm format.scm guile.scm
+ keymaps.scm lists.scm load.scm
+ match.scm numbers.scm pure.scm
+ read.scm signal.scm strings.scm
+ symprop.scm syntax.scm system.scm
+ time.scm
+In the subdirectory srfi, wrote:
+ srfi-34.scm
In the subdirectory doc, wrote:
deprecated.texi goops.texi scheme-ideas.texi
scheme-reading.texi
@@ -227,6 +253,7 @@ In the subdirectory doc, changes to:
scm.texi scripts.texi script-getopt.texi
In the subdirectory doc/maint, wrote:
docstring.el
+Many other changes throughout.
Thien-Thi Nguyen:
In the top-level directory, wrote:
diff --git a/THANKS b/THANKS
index 17816c51f..90121094b 100644
--- a/THANKS
+++ b/THANKS
@@ -30,6 +30,7 @@ For fixes or providing information which led to a fix:
Rob Browning
Adrian Bunk
Michael Carmack
+ R Clayton
Stephen Compall
Brian Crowder
Christopher Cramer
@@ -95,6 +96,7 @@ For fixes or providing information which led to a fix:
Werner Scheinast
Bill Schottstaedt
Frank Schwidom
+ John Steele Scott
Thiemo Seufer
Scott Shedden
Alex Shinn
@@ -115,6 +117,7 @@ For fixes or providing information which led to a fix:
Andreas Vögele
Michael Talbot-Wilson
Michael Tuexen
+ Thomas Wawrzinek
Mark H. Weaver
Jon Wilson
Andy Wingo
diff --git a/emacs/gds-faq.txt b/emacs/gds-faq.txt
new file mode 100755
index 000000000..b60a2c9ae
--- /dev/null
+++ b/emacs/gds-faq.txt
@@ -0,0 +1,225 @@
+
+* Installation
+
+** How do I install guile-debugging?
+
+After unpacking the .tar.gz file, run the usual sequence of commands:
+
+$ ./configure
+$ make
+$ sudo make install
+
+Then you need to make sure that the directory where guile-debugging's
+Scheme files were installed is included in your Guile's load path.
+(The sequence above will usually install guile-debugging under
+/usr/local, and /usr/local is not in Guile's load path by default,
+unless Guile itself was installed under /usr/local.) You can discover
+your Guile's default load path by typing
+
+$ guile -q -c '(begin (write %load-path) (newline))'
+
+There are two ways to add guile-debugging's installation directory to
+Guile's load path, if it isn't already there.
+
+1. Edit or create the `init.scm' file, which Guile reads on startup,
+ so that it includes a line like this:
+
+ (set! %load-path (cons "/usr/local/share/guile" %load-path))
+
+ but with "/usr/local" replaced by the prefix that you installed
+ guile-debugging under, if not /usr/local.
+
+ The init.scm file must be installed (if it does not already exist
+ there) in one of the directories in Guile's default load-path.
+
+2. Add this line to your .emacs file:
+
+ (setq gds-scheme-directory "/usr/local/share/guile")
+
+ before the `require' or `load' line that loads GDS, but with
+ "/usr/local" replaced by the prefix that you installed
+ guile-debugging under, if not /usr/local.
+
+Finally, if you want guile-debugging's GDS interface to be loaded
+automatically whenever you run Emacs, add this line to your .emacs:
+
+(require 'gds)
+
+* Troubleshooting
+
+** "error in process filter" when starting Emacs (or loading GDS)
+
+This is caused by an internal error in GDS's Scheme code, for which a
+backtrace will have appeared in the gds-debug buffer, so please switch
+to the gds-debug buffer and see what it says there.
+
+The most common cause is a load path problem: Guile cannot find GDS's
+Scheme code because it is not in the known load path. In this case
+you should see the error message "no code for module" somewhere in the
+backtrace. If you see this, please try the remedies described in `How
+do I install guile-debugging?' above, then restart Emacs and see if
+the problem has been cured.
+
+If you don't see "no code for module", or if the described remedies
+don't fix the problem, please send the contents of the gds-debug
+buffer to me at <neil@ossau.uklinux.net>, so I can debug the problem.
+
+If you don't see a backtrace at all in the gds-debug buffer, try the
+next item ...
+
+** "error in process filter" at some other time
+
+This is caused by an internal error somewhere in GDS's Emacs Lisp
+code. If possible, please
+
+- switch on the `debug-on-error' option (M-x set-variable RET
+ debug-on-error RET t RET)
+
+- do whatever you were doing so that the same error happens again
+
+- send the Emacs Lisp stack trace which pops up to me at
+ <neil@ossau.uklinux.net>.
+
+If that doesn't work, please just mail me with as much detail as
+possible of what you were doing when the error occurred.
+
+* GDS Features
+
+** How do I inspect variable values?
+
+Type `e' followed by the name of the variable, then <RET>. This
+works whenever GDS is displaying a stack for an error at at a
+breakpoint. (You can actually `e' to evaluate any expression in the
+local environment of the selected stack frame; inspecting variables is
+the special case of this where the expression is only a variable name.)
+
+If GDS is displaying the associated source code in the window above or
+below the stack, you can see the values of any variables in the
+highlighted code just by hovering your mouse over them.
+
+** How do I change a variable's value?
+
+Type `e' and then `(set! VARNAME NEWVAL)', where VARNAME is the name
+of the variable you want to set and NEWVAL is an expression which
+Guile can evaluate to get the new value. This works whenever GDS is
+displaying a stack for an error at at a breakpoint. The setting will
+take effect in the local environment of the selected stack frame.
+
+** How do I change the expression that Guile is about to evaluate?
+
+Type `t' followed by the expression that you want Guile to evaluate
+instead, then <RET>.
+
+Then type one of the commands that tells Guile to continue execution.
+
+(Tweaking expressions, as described here, is only supported by the
+latest CVS version of Guile. The GDS stack display tells you when
+tweaking is possible by adding "(tweakable)" to the first line of the
+stack window.)
+
+** How do I return a value from the current stack frame different to what the evaluator has calculated?
+
+You have to be at the normal exit of the relevant frame first, so if
+GDS is not already showing you the normally calculated return value,
+type `o' to finish the evaluation of the selected frame.
+
+Then type `t' followed by the value you want to return, and <RET>.
+The value that you type can be any expression, but note that it will
+not be evaluated before being returned; for example if you type `(+ 2
+3)', the return value will be a three-element list, not 5.
+
+Finally type one of the commands that tells Guile to continue
+execution.
+
+(Tweaking return values, as described here, is only supported by the
+latest CVS version of Guile. The GDS stack display tells you when
+tweaking is possible by adding "(tweakable)" to the first line of the
+stack window.)
+
+** How do I step over a line of code?
+
+Scheme isn't organized by lines, so it doesn't really make sense to
+think of stepping over lines. Instead please see the next entry on
+stepping over expressions.
+
+** How do I step over an expression?
+
+It depends what you mean by "step over". If you mean that you want
+Guile to evaluate that expression normally, but then show you its
+return value, type `o', which does exactly that.
+
+If you mean that you want to skip the evaluation of that expression
+(for example because it has side effects that you don't want to
+happen), use `t' to change the expression to something else which
+Guile will evaluate instead.
+
+There has to be a substitute expression so Guile can calculate a value
+to return to the calling frame. If you know at a particular point
+that the return value is not important, you can type `t #f <RET>' or
+`t 0 <RET>'.
+
+See `How do I change the expression that Guile is about to evaluate?'
+above for more on using `t'.
+
+** How do I move up and down the call stack?
+
+Type `u' to move up and `d' to move down. "Up" in GDS means to a more
+"inner" frame, and "down" means to a more "outer" frame.
+
+** How do I run until the next breakpoint?
+
+Type `g' (for "go").
+
+** How do I run until the end of the selected stack frame?
+
+Type `o'.
+
+** How do I set a breakpoint?
+
+First identify the code that you want to set the breakpoint in, and
+what kind of breakpoint you want. To set a breakpoint on entry to a
+top level procedure, move the cursor to anywhere in the procedure
+definition, and make sure that the region/mark is inactive. To set a
+breakpoint on a particular expression (or sequence of expressions) set
+point and mark so that the region covers the opening parentheses of
+all the target expressions.
+
+Then type ...
+
+ `C-c C-b d' for a `debug' breakpoint, which means that GDS will
+ display the stack when the breakpoint is hit
+
+ `C-c C-b t' for a `trace' breakpoint, which means that the start and
+ end of the relevant procedure or expression(s) will be traced to the
+ *GDS Trace* buffer
+
+ `C-c C-b T' for a `trace-subtree' breakpoint, which means that every
+ evaluation step involved in the evaluation of the relevant procedure
+ or expression(s) will be traced to the *GDS Trace* buffer.
+
+You can also type `C-x <SPC>', which does the same as one of the
+above, depending on the value of `gds-default-breakpoint-type'.
+
+** How do I clear a breakpoint?
+
+Select a region containing the breakpoints that you want to clear, and
+type `C-c C-b <DEL>'.
+
+** How do I trace calls to a particular procedure or evaluations of a particular expression?
+
+In GDS this means setting a breakpoint whose type is `trace' or
+`trace-subtree'. See `How do I set a breakpoint?' above.
+
+* Development
+
+** How can I follow or contribute to guile-debugging's development?
+
+guile-debugging is hosted at http://gna.org, so please see the project
+page there. Feel free to raise bugs, tasks containing patches or
+feature requests, and so on. You can also write directly to me by
+email: <neil@ossau.uklinux.net>.
+
+
+Local Variables:
+mode: outline
+End:
diff --git a/emacs/gds-server.el b/emacs/gds-server.el
index d4fe997c2..9cfcd3aab 100644
--- a/emacs/gds-server.el
+++ b/emacs/gds-server.el
@@ -43,25 +43,24 @@
:group 'gds
:type '(choice (const :tag "nil" nil) directory))
-(defun gds-start-server (procname port-or-path protocol-handler &optional bufname)
- "Start a GDS server process called PROCNAME, listening on TCP port
-or Unix domain socket PORT-OR-PATH. PROTOCOL-HANDLER should be a
-function that accepts and processes one protocol form. Optional arg
-BUFNAME specifies the name of the buffer that is used for process
-output; if not specified the buffer name is the same as the process
-name."
- (with-current-buffer (get-buffer-create (or bufname procname))
+(defun gds-start-server (procname unix-socket-name tcp-port protocol-handler)
+ "Start a GDS server process called PROCNAME, listening on Unix
+domain socket UNIX-SOCKET-NAME and TCP port number TCP-PORT.
+PROTOCOL-HANDLER should be a function that accepts and processes
+one protocol form."
+ (with-current-buffer (get-buffer-create procname)
(erase-buffer)
(let* ((code (format "(begin
%s
(use-modules (ice-9 gds-server))
- (run-server %S))"
+ (run-server %S %S))"
(if gds-scheme-directory
(concat "(set! %load-path (cons "
(format "%S" gds-scheme-directory)
" %load-path))")
"")
- port-or-path))
+ unix-socket-name
+ tcp-port))
(process-connection-type nil) ; use a pipe
(proc (start-process procname
(current-buffer)
diff --git a/emacs/gds-test.el b/emacs/gds-test.el
new file mode 100644
index 000000000..dfd4f6c7b
--- /dev/null
+++ b/emacs/gds-test.el
@@ -0,0 +1,166 @@
+
+;; Test utility code.
+(defun gds-test-execute-keys (keys &optional keys2)
+ (execute-kbd-macro (apply 'vector (listify-key-sequence keys))))
+
+(defvar gds-test-expecting nil)
+
+(defun gds-test-protocol-hook (form)
+ (message "[protocol: %s]" (car form))
+ (if (eq (car form) gds-test-expecting)
+ (setq gds-test-expecting nil)))
+
+(defun gds-test-expect-protocol (proc &optional timeout)
+ (message "[expect: %s]" proc)
+ (setq gds-test-expecting proc)
+ (while gds-test-expecting
+ (or (accept-process-output gds-debug-server (or timeout 5))
+ (error "Timed out after %ds waiting for %s" (or timeout 5) proc))))
+
+(defun gds-test-check-buffer (name &rest strings)
+ (let ((buf (or (get-buffer name) (error "No %s buffer" name))))
+ (save-excursion
+ (set-buffer buf)
+ (goto-char (point-min))
+ (while strings
+ (search-forward (car strings))
+ (setq strings (cdr strings))))))
+
+(defun TEST (desc)
+ (message "TEST: %s" desc))
+
+;; Make sure we take GDS elisp code from this code tree.
+(setq load-path (cons (concat default-directory "emacs/") load-path))
+
+;; Protect the tests so we can do some cleanups in case of error.
+(unwind-protect
+ (progn
+
+ ;; Visit the tutorial.
+ (find-file "gds-tutorial.txt")
+
+ (TEST "Load up GDS.")
+ (search-forward "(require 'gds)")
+ (setq load-path (cons (concat default-directory "emacs/") load-path))
+ (gds-test-execute-keys "\C-x\C-e")
+
+ ;; Install our testing hook.
+ (add-hook 'gds-protocol-hook 'gds-test-protocol-hook)
+
+ (TEST "Help.")
+ (search-forward "(list-ref")
+ (backward-char 2)
+ (gds-test-execute-keys "\C-hg\C-m")
+ (gds-test-expect-protocol 'eval-results 10)
+ (gds-test-check-buffer "*Guile Help*"
+ "help list-ref"
+ "is a primitive procedure in the (guile) module")
+
+ (TEST "Completion.")
+ (re-search-forward "^with-output-to-s")
+ (gds-test-execute-keys "\e\C-i")
+ (beginning-of-line)
+ (or (looking-at "with-output-to-string")
+ (error "Expected completion `with-output-to-string' failed"))
+
+ (TEST "Eval defun.")
+ (search-forward "(display z)")
+ (gds-test-execute-keys "\e\C-x")
+ (gds-test-expect-protocol 'eval-results)
+ (gds-test-check-buffer "*Guile Evaluation*"
+ "(let ((x 1) (y 2))"
+ "Arctangent is: 0.46"
+ "=> 0.46")
+
+ (TEST "Multiple values.")
+ (search-forward "(values 'a ")
+ (gds-test-execute-keys "\e\C-x")
+ (gds-test-expect-protocol 'eval-results)
+ (gds-test-check-buffer "*Guile Evaluation*"
+ "(values 'a"
+ "hello world"
+ "=> a"
+ "=> b"
+ "=> c")
+
+ (TEST "Eval region with multiple expressions.")
+ (search-forward "(display \"Arctangent is: \")")
+ (beginning-of-line)
+ (push-mark nil nil t)
+ (forward-line 3)
+ (gds-test-execute-keys "\C-c\C-r")
+ (gds-test-expect-protocol 'eval-results)
+ (gds-test-check-buffer "*Guile Evaluation*"
+ "(display \"Arctangent is"
+ "Arctangent is:"
+ "=> no (or unspecified) value"
+ "ERROR: Unbound variable: z"
+ "=> error-in-evaluation"
+ "Evaluating expression 3"
+ "=> no (or unspecified) value")
+
+ (TEST "Eval syntactically unbalanced region.")
+ (search-forward "(let ((z (atan x y)))")
+ (beginning-of-line)
+ (push-mark nil nil t)
+ (forward-line 4)
+ (gds-test-execute-keys "\C-c\C-r")
+ (gds-test-expect-protocol 'eval-results)
+ (gds-test-check-buffer "*Guile Evaluation*"
+ "(let ((z (atan"
+ "Reading expressions to evaluate"
+ "ERROR"
+ "end of file"
+ "=> error-in-read")
+
+ (TEST "Stepping through an evaluation.")
+ (search-forward "(for-each (lambda (x)")
+ (forward-line 1)
+ (push-mark nil nil t)
+ (forward-line 1)
+ (gds-test-execute-keys "\C-u\e\C-x")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys " ")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys "o")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys "o")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys "o")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys "o")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys "o")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys "o")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys "o")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys "o")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys "o")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys "o")
+ (gds-test-expect-protocol 'stack)
+ (gds-test-execute-keys "g")
+ (gds-test-expect-protocol 'eval-results)
+ (gds-test-check-buffer "*Guile Evaluation*"
+ "(for-each (lambda"
+ "Evaluating in current module"
+ "3 cubed is 27"
+ "=> no (or unspecified) value")
+
+ ;; Done.
+ (message "====================================")
+ (message "gds-test.el completed without errors")
+ (message "====================================")
+
+ )
+
+ (switch-to-buffer "gds-debug")
+ (write-region (point-min) (point-max) "gds-test.debug")
+
+ (switch-to-buffer "*GDS Transcript*")
+ (write-region (point-min) (point-max) "gds-test.transcript")
+
+ )
diff --git a/emacs/gds-test.sh b/emacs/gds-test.sh
new file mode 100755
index 000000000..2f8ddff9f
--- /dev/null
+++ b/emacs/gds-test.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+GUILE_LOAD_PATH=$(pwd) emacs --batch --no-site-file -q -l gds-test.el < gds-test.stdin
diff --git a/emacs/gds-test.stdin b/emacs/gds-test.stdin
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/emacs/gds-test.stdin
@@ -0,0 +1 @@
+
diff --git a/emacs/gds-tutorial.txt b/emacs/gds-tutorial.txt
new file mode 100755
index 000000000..4254803ec
--- /dev/null
+++ b/emacs/gds-tutorial.txt
@@ -0,0 +1,223 @@
+
+;; Welcome to the GDS tutorial!
+
+;; This tutorial teaches the use of GDS by leading you through a set
+;; of examples where you actually use GDS, in Emacs, along the way.
+;; To get maximum benefit, therefore, you should be reading this
+;; tutorial in Emacs.
+
+;; ** GDS setup
+
+;; The first thing to do, if you haven't already, is to load the GDS
+;; library into Emacs. The Emacs Lisp expression for this is:
+
+(require 'gds)
+
+;; So, if you don't already have this in your .emacs, either add it
+;; and then restart Emacs, or evaluate it just for this Emacs session
+;; by moving the cursor to just after the closing parenthesis and
+;; typing `C-x C-e'.
+
+;; (Note that if you _have_ already loaded GDS, and you type `C-x C-e'
+;; after this expression, you will see a *Guile Evaluation* window
+;; telling you that the evaluation failed because `require' is
+;; unbound. Don't worry; this is not a problem, and the rest of the
+;; tutorial should still work just fine.)
+
+;; ** Help
+
+;; GDS makes it easy to access the Guile help system when working on a
+;; Scheme program in Emacs. For example, suppose that you are writing
+;; code that uses list-ref, and need to remind yourself about
+;; list-ref's arguments ...
+
+(define (penultimate l)
+ (list-ref
+
+;; Just place the cursor on the word "list-ref" and type `C-h g RET'.
+;; Try it now!
+
+;; If GDS is working correctly, a window should have popped up above
+;; or below showing the Guile help for list-ref.
+
+;; You can also do an "apropos" search through Guile's help. If you
+;; couldn't remember the name list-ref, for example, you could search
+;; for anything matching "list" by typing `C-h C-g' and entering
+;; "list" at the minibuffer prompt. Try doing this now: you should
+;; see a longish list of Guile definitions whose names include "list".
+;; As usual in Emacs, you can use `M-PageUp' and `M-PageDown' to
+;; conveniently scroll the other window without having to select it.
+
+;; The functions called by `C-h g' and `C-h C-g' are gds-help-symbol
+;; and gds-apropos. They both look up the symbol or word at point by
+;; default, but that default can be overidden by typing something else
+;; at the minibuffer prompt.
+
+;; ** Completion
+
+;; As you are typing Scheme code, you can ask GDS to complete the
+;; symbol before point for you, by typing `ESC TAB'. GDS selects
+;; possible completions by matching the text so far against all
+;; definitions in the Guile environment. (This may be contrasted with
+;; the "dabbrev" completion performed by `M-/', which selects possible
+;; completions from the contents of Emacs buffers. So, if you are
+;; trying to complete "with-ou", to get "with-output-to-string", for
+;; example, `ESC TAB' will always work, because with-output-to-string
+;; is always defined in Guile's default environment, whereas `M-/'
+;; will only work if one of Emacs's buffers happens to contain the
+;; full name "with-output-to-string".)
+
+;; To illustrate the idea, here are some partial names that you can
+;; try completing. For each one, move the cursor to the end of the
+;; line and type `ESC TAB' to try to complete it.
+
+list-
+with-ou
+with-output-to-s
+mkst
+
+;; (If you are not familiar with any of the completed definitions,
+;; feel free to use `C-h g' to find out about them!)
+
+;; ** Evaluation
+
+;; GDS provides several ways for you to evaluate Scheme code from
+;; within Emacs.
+
+;; Just like in Emacs Lisp, a single expression in a buffer can be
+;; evaluated using `C-x C-e' or `C-M-x'. For `C-x C-e', the
+;; expression is that which ends immediately before point (so that it
+;; is useful for evaluating something just after you have typed it).
+;; For `C-M-x', the expression is the "top level defun" around point;
+;; this means the balanced chunk of code around point whose opening
+;; parenthesis is in column 0.
+
+;; Take this code fragment as an example:
+
+(let ((x 1) (y 2))
+ (let ((z (atan x y)))
+ (display "Arctangent is: ")
+ (display z)
+ (newline)
+ z))
+
+;; If you move the cursor to the end of the (display z) line and type
+;; `C-x C-e', the code evaluated is just "(display z)", which normally
+;; produces an error, because z is not defined in the usual Guile
+;; environment. If, however, you type `C-M-x' with the cursor in the
+;; same place, the code evaluated is the whole "(let ((x 1) (y 2))
+;; ...)" kaboodle, because that is the most recent expression before
+;; point that starts in column 0.
+
+;; Try these now. The Guile Evaluation window should pop up again,
+;; and show you:
+;; - the expression that was evaluated (probably abbreviated)
+;; - the module that it was evaluated in
+;; - anything that the code wrote to its standard output
+;; - the return value(s) of the evaluation.
+;; Following the convention of the Emacs Lisp and Guile manuals,
+;; return values are indicated by the symbol "=>".
+
+;; To see what happens when an expression has multiple return values,
+;; try evaluating this one:
+
+(values 'a (begin (display "hello world\n") 'b) 'c)
+
+;; You can also evaluate a region of a buffer using `C-c C-r'. If the
+;; code in the region consists of multiple expressions, GDS evaluates
+;; them sequentially. For example, try selecting the following three
+;; lines and typing `C-c C-r'.
+
+ (display "Arctangent is: ")
+ (display z)
+ (newline)
+
+;; If the code in the region evaluated isn't syntactically balanced,
+;; GDS will indicate a read error, for example for this code:
+
+ (let ((z (atan x y)))
+ (display "Arctangent is: ")
+ (display z)
+ (newline)
+
+;; Finally, if you want to evaluate something quickly that is not in a
+;; buffer, you can use `C-c C-e' and type the code to evaluate at the
+;; minibuffer prompt. The results are popped up in the same way as
+;; for code from a buffer.
+
+;; ** Breakpoints
+
+;; Before evaluating Scheme code from an Emacs buffer, you may want to
+;; set some breakpoints in it. With GDS you can set breakpoints in
+;; Scheme code by typing `C-x SPC'.
+;;
+;; To see how this works, select the second line of the following code
+;; (the `(format ...)' line) and type `C-x SPC'.
+
+(for-each (lambda (x)
+ (format #t "~A cubed is ~A\n" x (* x x x)))
+ (iota 6))
+
+;; The two opening parentheses in that line should now be highlighted
+;; in red, to show that breakpoints have been set at the start of the
+;; `(format ...)' and `(* x x x)' expressions. Then evaluate the
+;; whole for-each expression by typing `C-M-x' ...
+;;
+;; In the upper half of your Emacs, a buffer appears showing you the
+;; Scheme stack.
+;;
+;; In the lower half, the `(format ...)' expression is highlighted.
+;;
+;; What has happened is that Guile started evaluating the for-each
+;; code, but then hit the breakpoint that you set on the start of the
+;; format expression. Guile therefore pauses the evaluation at that
+;; point and passes the stack (which encapsulates everything that is
+;; interesting about the state of Guile at that point) to GDS. You
+;; can then explore the stack and decide how to tell Guile to
+;; continue.
+;;
+;; - If you move your mouse over any of the identifiers in the
+;; highlighted code, a help echo (or tooltip) will appear to tell
+;; you that identifier's current value. (Note though that this only
+;; works when the stack buffer is selected. So if you have switched
+;; to this buffer in order to scroll down and read these lines, you
+;; will need to switch back to the stack buffer before trying this
+;; out.)
+;;
+;; - In the stack buffer, the "=>" on the left shows you that the top
+;; frame is currently selected. You can move up and down the stack
+;; by pressing the up and down arrows (or `u' and `d'). As you do
+;; this, GDS will change the highlight in the lower window to show
+;; the code that corresponds to the selected stack frame.
+;;
+;; - You can evaluate an arbitrary expression in the local environment
+;; of the selected stack frame by typing `e' followed by the
+;; expression.
+;;
+;; - You can show various bits of information about the selected frame
+;; by typing `I', `A' and `S'. Feel free to try these now, to see
+;; what they do.
+;;
+;; You also have control over the continuing evaluation of this code.
+;; Here are some of the things you can do - please try them as you
+;; read.
+;;
+;; - `g' tells Guile to continue execution normally. In this case
+;; that means that evaluation will continue until it hits the next
+;; breakpoint, which is on the `(* x x x)' expression.
+;;
+;; - `SPC' tells Guile to continue until the next significant event in
+;; the same source file as the selected frame. A "significant
+;; event" means either beginning to evaluate an expression in the
+;; relevant file, or completing such an evaluation, in which case
+;; GDS tells you the value that it is returning. Pressing `SPC'
+;; repeatedly is a nice way to step through all the details of the
+;; code in a given file, but stepping over calls that involve code
+;; from other files.
+;;
+;; - `o' tells Guile to continue execution until the selected stack
+;; frame completes, and then to show its return value.
+
+;; Local Variables:
+;; mode: scheme
+;; End:
diff --git a/emacs/gds.el b/emacs/gds.el
index a9450d065..991ba7504 100644
--- a/emacs/gds.el
+++ b/emacs/gds.el
@@ -36,10 +36,11 @@
;; The subprocess object for the debug server.
(defvar gds-debug-server nil)
-(defvar gds-socket-type-alist '((tcp . 8333)
- (unix . "/tmp/.gds_socket"))
- "Maps each of the possible socket types that the GDS server can
-listen on to the path that it should bind to for each one.")
+(defvar gds-unix-socket-name (format "/tmp/.gds-socket-%d" (emacs-pid))
+ "Name of the Unix domain socket that GDS will listen on.")
+
+(defvar gds-tcp-port 8333
+ "The TCP port number that GDS will listen on.")
(defun gds-run-debug-server ()
"Start (or restart, if already running) the GDS debug server process."
@@ -47,10 +48,14 @@ listen on to the path that it should bind to for each one.")
(if gds-debug-server (gds-kill-debug-server))
(setq gds-debug-server
(gds-start-server "gds-debug"
- (cdr (assq gds-server-socket-type
- gds-socket-type-alist))
+ gds-unix-socket-name
+ gds-tcp-port
'gds-debug-protocol))
- (process-kill-without-query gds-debug-server))
+ (process-kill-without-query gds-debug-server)
+ ;; Add the Unix socket name to the environment, so that Guile
+ ;; clients started from within this Emacs will be able to use it,
+ ;; and thereby ensure that they connect to the GDS in this Emacs.
+ (setenv "GDS_UNIX_SOCKET_NAME" gds-unix-socket-name))
(defun gds-kill-debug-server ()
"Kill the GDS debug server process."
@@ -137,7 +142,13 @@ listen on to the path that it should bind to for each one.")
;;;; Debugger protocol
+(defcustom gds-protocol-hook nil
+ "Hook called on receipt of a protocol form from the GDS client."
+ :type 'hook
+ :group 'gds)
+
(defun gds-debug-protocol (client form)
+ (run-hook-with-args 'gds-protocol-hook form)
(or (eq client '*)
(let ((proc (car form)))
(cond ((eq proc 'name)
@@ -610,7 +621,7 @@ you would add an element to this alist to transform
:group 'gds)
(defcustom gds-server-socket-type 'tcp
- "What kind of socket the GDS server should listen on."
+ "This option is now obsolete and has no effect."
:group 'gds
:type '(choice (const :tag "TCP" tcp)
(const :tag "Unix" unix)))
diff --git a/module/ice-9/gds-client.scm b/module/ice-9/gds-client.scm
index cdca7b652..03e292737 100755
--- a/module/ice-9/gds-client.scm
+++ b/module/ice-9/gds-client.scm
@@ -163,23 +163,20 @@
(define (connect-to-gds . application-name)
(or gds-port
- (begin
+ (let ((gds-unix-socket-name (getenv "GDS_UNIX_SOCKET_NAME")))
(set! gds-port
- (or (let ((s (socket PF_INET SOCK_STREAM 0))
- (SOL_TCP 6)
- (TCP_NODELAY 1))
- (setsockopt s SOL_TCP TCP_NODELAY 1)
- (catch #t
- (lambda ()
- (connect s AF_INET (inet-aton "127.0.0.1") 8333)
- s)
- (lambda _ #f)))
- (let ((s (socket PF_UNIX SOCK_STREAM 0)))
- (catch #t
- (lambda ()
- (connect s AF_UNIX "/tmp/.gds_socket")
- s)
- (lambda _ #f)))
+ (or (and gds-unix-socket-name
+ (false-if-exception
+ (let ((s (socket PF_UNIX SOCK_STREAM 0)))
+ (connect s AF_UNIX gds-unix-socket-name)
+ s)))
+ (false-if-exception
+ (let ((s (socket PF_INET SOCK_STREAM 0))
+ (SOL_TCP 6)
+ (TCP_NODELAY 1))
+ (setsockopt s SOL_TCP TCP_NODELAY 1)
+ (connect s AF_INET (inet-aton "127.0.0.1") 8333)
+ s))
(error "Couldn't connect to GDS by TCP or Unix domain socket")))
(write-form (list 'name (getpid) (apply client-name application-name))))))
diff --git a/module/ice-9/gds-server.scm b/module/ice-9/gds-server.scm
index b64e41161..5ec867535 100644
--- a/module/ice-9/gds-server.scm
+++ b/module/ice-9/gds-server.scm
@@ -36,38 +36,31 @@
(define connection->id (make-object-property))
-(define (run-server port-or-path)
-
- (or (integer? port-or-path)
- (string? port-or-path)
- (error "port-or-path should be an integer (port number) or a string (file name)"
- port-or-path))
-
- (let ((server (socket (if (integer? port-or-path) PF_INET PF_UNIX)
- SOCK_STREAM
- 0)))
-
- ;; Initialize server socket.
- (if (integer? port-or-path)
- (begin
- (setsockopt server SOL_SOCKET SO_REUSEADDR 1)
- (bind server AF_INET INADDR_ANY port-or-path))
- (begin
- (catch #t
- (lambda () (delete-file port-or-path))
- (lambda _ #f))
- (bind server AF_UNIX port-or-path)))
-
- ;; Start listening.
- (listen server 5)
+(define (run-server unix-socket-name tcp-port)
+ (let ((unix-server (socket PF_UNIX SOCK_STREAM 0))
+ (tcp-server (socket PF_INET SOCK_STREAM 0)))
+
+ ;; Bind and start listening on the Unix domain socket.
+ (false-if-exception (delete-file unix-socket-name))
+ (bind unix-server AF_UNIX unix-socket-name)
+ (listen unix-server 5)
+
+ ;; Bind and start listening on the TCP socket.
+ (setsockopt tcp-server SOL_SOCKET SO_REUSEADDR 1)
+ (false-if-exception (bind tcp-server AF_INET INADDR_ANY tcp-port))
+ (listen tcp-server 5)
+
+ ;; Main loop.
(let loop ((clients '()) (readable-sockets '()))
(define (do-read port)
(cond ((eq? port (current-input-port))
(do-read-from-ui))
- ((eq? port server)
- (accept-new-client))
+ ((eq? port unix-server)
+ (accept-new-client unix-server))
+ ((eq? port tcp-server)
+ (accept-new-client tcp-server))
(else
(do-read-from-client port))))
@@ -86,7 +79,7 @@
(trc "client not found")))
clients)
- (define (accept-new-client)
+ (define (accept-new-client server)
(let ((new-port (car (accept server))))
;; Read the client's ID.
(let ((name-form (read new-port)))
@@ -122,8 +115,10 @@
;;(trc 'readable-sockets readable-sockets)
(if (null? readable-sockets)
- (loop clients (car (select (cons (current-input-port)
- (cons server clients))
+ (loop clients (car (select (cons* (current-input-port)
+ unix-server
+ tcp-server
+ clients)
'()
'())))
(loop (do-read (car readable-sockets)) (cdr readable-sockets))))))