1 ;;; gds.el -- frontend for Guile development in Emacs
3 ;;;; Copyright (C) 2003 Free Software Foundation, Inc.
5 ;;;; This library is free software; you can redistribute it and/or
6 ;;;; modify it under the terms of the GNU Lesser General Public
7 ;;;; License as published by the Free Software Foundation; either
8 ;;;; version 2.1 of the License, or (at your option) any later
11 ;;;; This library is distributed in the hope that it will be useful,
12 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;;;; Lesser General Public License for more details.
16 ;;;; You should have received a copy of the GNU Lesser General Public
17 ;;;; License along with this library; if not, write to the Free
18 ;;;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
29 ;;;; Customization group setup.
32 "Customization options for Guile Emacs frontend."
36 ;;;; Communication with the (emacs gds-server) subprocess.
38 ;; The subprocess object.
39 (defvar gds-process nil
)
41 ;; Subprocess output goes into the `*GDS Process*' buffer, and
42 ;; is then read from there one form at a time. `gds-read-cursor' is
43 ;; the buffer position of the start of the next unread form.
44 (defvar gds-read-cursor nil
)
47 "Start (or restart, if already running) the GDS subprocess."
49 (if gds-process
(gds-shutdown))
50 (with-current-buffer (get-buffer-create "*GDS Process*")
53 (let ((process-connection-type nil
)) ; use a pipe
60 "(begin (use-modules (emacs gds-server)) (run-server))"))))
61 (setq gds-read-cursor
(point-min))
62 (set-process-filter gds-process
(function gds-filter
))
63 (set-process-sentinel gds-process
(function gds-sentinel
))
64 (set-process-coding-system gds-process
'latin-1-unix
))
66 ;; Shutdown the subprocess and cleanup all associated data.
67 (defun gds-shutdown ()
68 "Shut down the GDS subprocess."
74 ;; Kill the subprocess.
75 (process-kill-without-query gds-process
)
78 (kill-process gds-process
)
79 (accept-process-output gds-process
0 200))
81 (setq gds-process nil
))
83 ;; Subprocess output filter: inserts normally into the process buffer,
84 ;; then tries to reread the output one form at a time and delegates
85 ;; processing of each form to `gds-handle-input'.
86 (defun gds-filter (proc string
)
87 (with-current-buffer (process-buffer proc
)
89 (goto-char (process-mark proc
))
90 (insert-before-markers string
))
91 (goto-char gds-read-cursor
)
92 (while (let ((form (condition-case nil
93 (read (current-buffer))
97 (gds-handle-input form
)))
99 (setq gds-read-cursor
(point)))))
101 ;; Subprocess sentinel: do nothing. (Currently just here to avoid
102 ;; inserting un-`read'able process status messages into the process
104 (defun gds-sentinel (proc event
)
107 ;; Send input to the subprocess.
108 (defun gds-send (string)
109 (process-send-string gds-process string
))
112 ;;;; Multiple application scheduling.
114 ;; Here is how we schedule the display of multiple clients that are
115 ;; competing for user attention.
117 ;; - `gds-waiting' holds a list of clients that want attention but
118 ;; haven't yet got it. A client is added to this list for two
119 ;; reasons. (1) When it is blocked waiting for user input.
120 ;; (2) When it first connects to GDS, even if not blocked.
122 ;; - `gds-focus-client' holds the client, if any, that currently has
123 ;; the user's attention. A client can be given the focus if
124 ;; `gds-focus-client' is nil at the time that the client wants
125 ;; attention, or if another client relinquishes it. A client can
126 ;; relinquish the focus in two ways. (1) If the client application
127 ;; says that it is no longer blocked, and a small time passes without
128 ;; it becoming blocked again. (2) If the user explicitly `quits'
130 (defvar gds-focus-client nil
)
131 (defvar gds-waiting nil
)
133 (defun gds-request-focus (client)
134 (cond ((eq client gds-focus-client
)
135 ;; CLIENT already has the focus. Display its buffer.
136 (gds-display-buffers))
138 ;; Another client has the focus. Add CLIENT to `gds-waiting'.
139 (or (memq client gds-waiting
)
140 (setq gds-waiting
(append gds-waiting
(list client
)))))
142 ;; Give focus to CLIENT and display its buffer.
143 (setq gds-focus-client client
)
144 (gds-display-buffers))))
146 ;; Explicitly give up focus.
149 (if (or (car gds-waiting
)
150 (not (gds-client-blocked))
152 "Client is blocked and no others are waiting. Still quit? "))
154 (bury-buffer (current-buffer))
155 ;; Pass on the focus.
156 (setq gds-focus-client
(car gds-waiting
)
157 gds-waiting
(cdr gds-waiting
))
158 ;; If this client is blocked, add it back into the waiting list.
159 (if (gds-client-blocked)
160 (gds-request-focus gds-client
))
161 ;; If there is a new focus client, request display for it.
163 (gds-request-focus gds-focus-client
)))))
166 ;;;; GDS protocol dispatch.
168 ;; General dispatch function called by the subprocess filter.
169 (defun gds-handle-input (form)
170 (let ((client (car form
)))
172 (let* ((proc (cadr form
))
174 (buf (gds-client-buffer client proc args
)))
175 (if buf
(gds-handle-client-input buf client proc args
))))))
177 (defun gds-handle-client-input (buf client proc args
)
178 (with-current-buffer buf
179 (with-current-buffer gds-transcript
180 (goto-char (point-max))
181 (let ((inhibit-read-only t
))
182 (insert (format "<%S %S %S>" client proc args
) "\n")))
183 (cond (;; (name ...) - Client name.
185 (setq gds-pid
(cadr args
))
186 (gds-promote-view 'interaction
)
187 (gds-request-focus client
))
189 (;; (current-module ...) - Current module.
190 (eq proc
'current-module
)
191 (setq gds-current-module
(car args
)))
193 (;; (stack ...) - Stack at an error or breakpoint.
195 (setq gds-stack args
)
196 (gds-promote-view 'stack
))
198 (;; (modules ...) - Application's loaded modules.
201 (or (assoc (car args
) gds-modules
)
202 (setq gds-modules
(cons (list (car args
)) gds-modules
)))
203 (setq args
(cdr args
))))
205 (;; (output ...) - Last printed output.
207 (setq gds-output
(car args
))
208 (gds-add-view 'messages
))
210 (;; (status ...) - Application status indication.
212 (setq gds-status
(car args
))
213 (if (eq gds-status
'running
)
214 (gds-delete-view 'browser
)
215 (gds-add-view 'browser
))
216 (if (eq gds-status
'waiting-for-input
)
218 (gds-promote-view 'stack
)
220 (gds-request-focus client
))
222 (gds-delete-view 'stack
)
223 (gds-update-buffers-in-a-while)))
225 (;; (module MODULE ...) - The specified module's bindings.
227 (let ((minfo (assoc (car args
) gds-modules
)))
229 (setcdr (cdr minfo
) (cdr args
)))))
231 (;; (closed) - Client has gone away.
233 (setq gds-status
'closed
)
236 (delq (assq client gds-buffers
) gds-buffers
))
237 (if (eq client gds-focus-client
)
240 (;; (eval-results ...) - Results of evaluation.
241 (eq proc
'eval-results
)
242 (gds-display-results client args
))
244 ((eq proc
'completion-result
)
245 (setq gds-completion-results
(or (car args
) t
)))
247 (;; (breakpoint-set FILE LINE COLUMN INFO) - Breakpoint set.
248 (eq proc
'breakpoint-set
)
249 (let ((file (nth 0 args
))
251 (column (nth 2 args
))
253 (with-current-buffer (find-file-noselect file
)
255 (goto-char (point-min))
258 (move-to-column column
)
259 (let ((os (overlays-at (point))) o
)
261 (if (and (overlay-get (car os
) 'gds-breakpoint-info
)
262 (= (overlay-start (car os
)) (point)))
264 (overlay-put (car os
)
267 (overlay-put (car os
)
269 gds-active-breakpoint-before-string
)
270 (overlay-put (car os
)
272 gds-active-breakpoint-after-string
)
274 (setq os
(cdr os
)))))))))
279 ;;;; Per-client buffer state.
281 ;; This section contains code that is specific to each Guile client's
282 ;; buffer but independent of any particular `view'.
284 ;; Alist mapping each client port number to corresponding buffer.
285 (defvar gds-buffers nil
)
287 (define-derived-mode gds-mode
290 "Major mode for interacting with a Guile client application.")
292 (defvar gds-client nil
293 "GDS client's port number.")
294 (make-variable-buffer-local 'gds-client
)
296 (defvar gds-status nil
297 "GDS client's latest status, one of the following symbols.
298 `running' - Application is running.
299 `waiting-for-input' - Application is blocked waiting for instruction
301 `ready-for-input' - Application is not blocked but can also accept
302 asynchronous instructions from the frontend.")
303 (make-variable-buffer-local 'gds-status
)
305 (defvar gds-transcript nil
306 "Transcript buffer for this GDS client.")
307 (make-variable-buffer-local 'gds-transcript
)
309 ;; Return client buffer for specified client and protocol input.
310 (defun gds-client-buffer (client proc args
)
312 ;; Introduction from client - create a new buffer.
313 (with-current-buffer (generate-new-buffer (car args
))
315 (setq gds-client client
)
318 (expand-file-name (concat "~/.gds-transcript-" (car args
)))))
319 (with-current-buffer gds-transcript
320 (goto-char (point-max))
321 (insert "\nTranscript:\n"))
323 (cons (cons client
(current-buffer))
326 ;; Otherwise there should be an existing buffer that we can
328 (let ((existing (assq client gds-buffers
)))
329 (if (buffer-live-p (cdr existing
))
331 (setq gds-buffers
(delq existing gds-buffers
))
332 (gds-client-buffer client
'name
'("(GDS buffer killed)"))))))
334 (defun gds-client-blocked ()
335 (eq gds-status
'waiting-for-input
))
337 (defvar gds-delayed-update-timer nil
)
339 (defvar gds-delayed-update-buffers nil
)
341 (defun gds-update-delayed-update-buffers ()
342 (while gds-delayed-update-buffers
343 (with-current-buffer (car gds-delayed-update-buffers
)
344 (setq gds-delayed-update-buffers
345 (cdr gds-delayed-update-buffers
))
346 (gds-update-buffers))))
348 (defun gds-update-buffers ()
349 (if (timerp gds-delayed-update-timer
)
350 (cancel-timer gds-delayed-update-timer
))
351 (setq gds-delayed-update-timer nil
)
352 (let ((view (car gds-views
))
353 (inhibit-read-only t
))
354 (cond ((eq view
'stack
)
356 ((eq view
'interaction
)
357 (gds-insert-interaction))
359 (gds-insert-modules))
361 (gds-insert-messages))
363 (error "Bad GDS view %S" view
)))
366 (force-mode-line-update t
)))
368 (defun gds-update-buffers-in-a-while ()
369 (or (memq (current-buffer) gds-delayed-update-buffers
)
370 (setq gds-delayed-update-buffers
371 (cons (current-buffer) gds-delayed-update-buffers
)))
372 (if (timerp gds-delayed-update-timer
)
374 (setq gds-delayed-update-timer
375 (run-at-time 0.5 nil
(function gds-update-delayed-update-buffers
)))))
377 (defun gds-display-buffers ()
379 (let ((gds-focus-buffer (cdr (assq gds-focus-client gds-buffers
))))
380 ;; If there's already a window showing the buffer, use it.
381 (let ((window (get-buffer-window gds-focus-buffer t
)))
384 (make-frame-visible (window-frame window
))
385 (select-frame (window-frame window
))
386 (select-window window
))
387 ;(select-window (display-buffer gds-focus-buffer))
388 (display-buffer gds-focus-buffer
)))
389 ;; If there is an associated source buffer, display it as well.
390 (if (and (eq (car gds-views
) 'stack
)
391 gds-frame-source-overlay
392 (> (overlay-end gds-frame-source-overlay
) 0))
393 (let ((window (display-buffer
394 (overlay-buffer gds-frame-source-overlay
))))
395 (set-window-point window
396 (overlay-start gds-frame-source-overlay
)))))))
399 ;;;; Management of `views'.
401 ;; The idea here is to keep the buffer describing a Guile client
402 ;; relatively uncluttered by only showing one kind of information
403 ;; about that client at a time. Menu items and key sequences are
404 ;; provided to switch easily between the available views.
406 (defvar gds-views nil
407 "List of available views for a GDS client. Each element is one of
408 the following symbols.
409 `interaction' - Interaction with running client.
410 `stack' - Call stack view.
411 `browser' - Modules and bindings browser view.
412 `breakpoints' - List of set breakpoints.
413 `messages' - Non-GDS-protocol output from the debugger.")
414 (make-variable-buffer-local 'gds-views
)
416 (defun gds-promote-view (view)
417 (setq gds-views
(cons view
(delq view gds-views
))))
419 (defun gds-switch-to-view (view)
420 (or (memq view gds-views
)
421 (error "View %S is not available" view
))
422 (gds-promote-view view
)
423 (gds-update-buffers))
425 (defun gds-add-view (view)
426 (or (memq view gds-views
)
427 (setq gds-views
(append gds-views
(list view
)))))
429 (defun gds-delete-view (view)
430 (setq gds-views
(delq view gds-views
)))
433 ;;;; `Interaction' view.
435 ;; This view provides interaction with a normally running Guile
436 ;; client, in other words one that is not stopped in the debugger but
437 ;; is still available to take input from GDS (usually via a thread for
438 ;; that purpose). The view supports evaluation, help requests,
439 ;; control of `debug-on-exception' function, and methods for breaking
440 ;; into the running code.
442 (defvar gds-current-module
"()"
443 "GDS client's current module.")
444 (make-variable-buffer-local 'gds-current-module
)
447 "GDS client's process ID.")
448 (make-variable-buffer-local 'gds-pid
)
450 (defvar gds-debug-exceptions nil
451 "Whether to debug exceptions.")
452 (make-variable-buffer-local 'gds-debug-exceptions
)
454 (defvar gds-exception-keys
"signal misc-error"
455 "The exception keys for which to debug a GDS client.")
456 (make-variable-buffer-local 'gds-exception-keys
)
458 (defun gds-insert-interaction ()
460 ;; Insert stuff for interacting with a running (non-blocked) Guile
462 (widget-insert (buffer-name)
464 (cdr (assq gds-status
465 '((running .
"running (cannot accept input)")
466 (waiting-for-input .
"waiting for input")
467 (ready-for-input .
"running")
468 (closed .
"closed"))))
472 (widget-create 'push-button
473 :notify
(function gds-sigint
)
476 (widget-create 'push-button
477 :notify
(function gds-async-break
)
480 (widget-create 'checkbox
481 :notify
(function gds-toggle-debug-exceptions
)
482 gds-debug-exceptions
)
483 (widget-insert " Debug exception keys: ")
484 (widget-create 'editable-field
485 :notify
(function gds-set-exception-keys
)
487 (widget-insert "\n"))
489 (defun gds-sigint (w &rest ignore
)
491 (signal-process gds-pid
2))
493 (defun gds-async-break (w &rest ignore
)
495 (gds-send (format "(%S async-break)\n" gds-focus-client
)))
497 (defun gds-toggle-debug-exceptions (w &rest ignore
)
499 (setq gds-debug-exceptions
(widget-value w
))
500 (gds-eval-expression (concat "(use-modules (ice-9 debugger))"
505 (defun gds-set-exception-keys (w &rest ignore
)
507 (setq gds-exception-keys
(widget-value w
)))
509 (defun gds-view-interaction ()
511 (gds-switch-to-view 'interaction
))
516 ;; This view shows the Guile call stack after the application has hit
517 ;; an error, or when it is stopped in the debugger.
519 (defvar gds-stack nil
520 "GDS client's stack when last stopped.")
521 (make-variable-buffer-local 'gds-stack
)
523 (defun gds-insert-stack ()
525 (let ((frames (car gds-stack
))
526 (index (cadr gds-stack
))
527 (flags (caddr gds-stack
))
529 (cond ((memq 'application flags
)
530 (widget-insert "Calling procedure:\n"))
531 ((memq 'evaluation flags
)
532 (widget-insert "Evaluating expression:\n"))
533 ((memq 'return flags
)
534 (widget-insert "Return value: "
535 (cadr (memq 'return flags
))
538 (widget-insert "Stack: " (prin1-to-string flags
) "\n")))
540 (gds-show-selected-frame (caddr (nth index frames
)))
542 (setq frame
(car frames
)
545 items
(cons (list 'item
546 (let ((s (cadr frame
)))
547 (put-text-property 0 1 'index i s
)
550 (setq items
(nreverse items
))
551 (apply (function widget-create
)
553 :value
(cadr (nth index items
))
554 :notify
(function gds-select-stack-frame
)
557 (goto-char (point-min))))
559 (defun gds-select-stack-frame (widget &rest ignored
)
560 (let* ((s (widget-value widget
))
561 (ind (memq 'index
(text-properties-at 0 s
))))
562 (gds-send (format "(%S debugger-command frame %d)\n"
566 ;; Overlay used to highlight the source expression corresponding to
567 ;; the selected frame.
568 (defvar gds-frame-source-overlay nil
)
570 (defun gds-show-selected-frame (source)
571 ;; Highlight the frame source, if possible.
573 (file-readable-p (car source
)))
574 (with-current-buffer (find-file-noselect (car source
))
575 (if gds-frame-source-overlay
577 (setq gds-frame-source-overlay
(make-overlay 0 0))
578 (overlay-put gds-frame-source-overlay
'face
'highlight
))
579 ;; Move to source line. Note that Guile line numbering is
580 ;; 0-based, while Emacs numbering is 1-based.
583 (goto-line (+ (cadr source
) 1))
584 (move-to-column (caddr source
))
585 (move-overlay gds-frame-source-overlay
587 (if (not (looking-at ")"))
588 (save-excursion (forward-sexp 1) (point))
589 ;; It seems that the source coordinates for
590 ;; backquoted expressions are at the end of
591 ;; the sexp rather than the beginning...
592 (save-excursion (forward-char 1)
593 (backward-sexp 1) (point)))
595 (if gds-frame-source-overlay
596 (move-overlay gds-frame-source-overlay
0 0))))
598 (defun gds-view-stack ()
600 (gds-switch-to-view 'stack
))
603 ;;;; `Breakpoints' view.
605 ;; This view shows a list of breakpoints.
607 (defun gds-view-breakpoints ()
609 (gds-switch-to-view 'breakpoints
))
614 ;; This view shows a list of modules and module bindings.
616 (defcustom gds-module-filter
'(t (guile nil
) (ice-9 nil
) (oop nil
))
617 "Specification of which Guile modules the debugger should display.
618 This is a list with structure (DEFAULT EXCEPTION EXCEPTION...), where
619 DEFAULT is `t' or `nil' and each EXCEPTION has the structure (SYMBOL
620 DEFAULT EXCEPTION EXCEPTION...).
622 A Guile module name `(x y z)' is matched against this filter as
623 follows. If one of the top level EXCEPTIONs has SYMBOL `x', continue
624 by matching the rest of the module name, in this case `(y z)', against
625 that SYMBOL's DEFAULT and next level EXCEPTION list. Otherwise, if
626 the current DEFAULT is `t' display the module, and if the current
627 DEFAULT is `nil', don't display it.
629 This variable is usually set to exclude Guile system modules that are
630 not of primary interest when debugging application code."
634 (defun gds-show-module-p (name)
635 ;; Determine whether to display the NAMEd module by matching NAME
636 ;; against `gds-module-filter'.
637 (let ((default (car gds-module-filter
))
638 (exceptions (cdr gds-module-filter
)))
639 (let ((exception (assq (car name
) exceptions
)))
641 (let ((gds-module-filter (cdr exception
)))
642 (gds-show-module-p (cdr name
)))
645 (defvar gds-modules nil
646 "GDS client's module information.
647 Alist mapping module names to their symbols and related information.
650 (((guile) t sym1 sym2 ...)
652 ((ice-9 debug) nil sym3 sym4)
655 The `t' or `nil' after the module name indicates whether the module is
656 displayed in expanded form (that is, showing the bindings in that
657 module). The syms are actually all strings because some Guile symbols
658 are not readable by Emacs.")
659 (make-variable-buffer-local 'gds-modules
)
661 (defun gds-insert-modules ()
662 (let ((p (if (eq (window-buffer (selected-window)) (current-buffer))
665 (modules gds-modules
))
667 (insert "Modules:\n")
669 (let ((minfo (car modules
)))
670 (if (gds-show-module-p (car minfo
))
671 (let ((w (widget-create 'push-button
672 :notify
(function gds-module-notify
)
676 (widget-put w
:module
(cons gds-client
(car minfo
)))
677 (widget-insert " " (prin1-to-string (car minfo
)) "\n")
679 (let ((syms (cddr minfo
)))
681 (widget-insert " > " (car syms
) "\n")
682 (setq syms
(cdr syms
))))))))
683 (setq modules
(cdr modules
)))
687 (defun gds-module-notify (w &rest ignore
)
688 (let* ((module (widget-get w
:module
))
689 (client (car module
))
691 (minfo (assoc name gds-modules
)))
693 ;; Just toggle expansion state.
695 (setcar (cdr minfo
) (not (cadr minfo
)))
696 (gds-update-buffers))
697 ;; Set flag to indicate module expanded.
698 (setcdr minfo
(list t
))
699 ;; Get symlist from Guile.
700 (gds-send (format "(%S query-module %S)\n" client name
)))))
702 (defun gds-query-modules ()
704 (gds-send (format "(%S query-modules)\n" gds-focus-client
)))
706 (defun gds-view-browser ()
708 (or gds-modules
(gds-query-modules))
709 (gds-switch-to-view 'browser
))
712 ;;;; `Messages' view.
714 ;; This view shows recent non-GDS-protocol messages output from the
715 ;; (ice-9 debugger) code.
717 (defvar gds-output nil
718 "GDS client's recent output (printed).")
719 (make-variable-buffer-local 'gds-output
)
721 (defun gds-insert-messages ()
723 ;; Insert recent non-protocol output from (ice-9 debugger).
725 (goto-char (point-min)))
727 (defun gds-view-messages ()
729 (gds-switch-to-view 'messages
))
732 ;;;; Debugger commands.
734 ;; Typically but not necessarily used from the `stack' view.
738 (gds-send (format "(%S debugger-command continue)\n" gds-focus-client
)))
742 (gds-send (format "(%S debugger-command next 1)\n" gds-focus-client
)))
744 (defun gds-evaluate (expr)
745 (interactive "sEvaluate (in this stack frame): ")
746 (gds-send (format "(%S debugger-command evaluate %s)\n"
748 (prin1-to-string expr
))))
750 (defun gds-step-in ()
752 (gds-send (format "(%S debugger-command step 1)\n" gds-focus-client
)))
754 (defun gds-step-out ()
756 (gds-send (format "(%S debugger-command finish)\n" gds-focus-client
)))
758 (defun gds-trace-finish ()
760 (gds-send (format "(%S debugger-command trace-finish)\n"
763 (defun gds-frame-info ()
765 (gds-send (format "(%S debugger-command info-frame)\n" gds-focus-client
)))
767 (defun gds-frame-args ()
769 (gds-send (format "(%S debugger-command info-args)\n" gds-focus-client
)))
772 ;;;; Setting breakpoints.
774 (defun gds-set-breakpoint ()
776 (cond ((gds-in-source-buffer)
777 (gds-set-source-breakpoint))
779 (gds-set-stack-breakpoint))
781 (gds-set-module-breakpoint))
783 (error "No way to set a breakpoint from here"))))
785 (defun gds-in-source-buffer ()
786 ;; Not yet worked out what will be available in Scheme source
790 (defun gds-in-stack ()
792 (and (re-search-backward "^\\(Stack\\|Modules\\):" nil t
)
793 (looking-at "Stack"))))
795 (defun gds-in-modules ()
797 (and (re-search-backward "^\\(Stack\\|Modules\\):" nil t
)
798 (looking-at "Modules"))))
800 (defun gds-set-module-breakpoint ()
801 (let ((sym (save-excursion
803 (and (looking-at " > \\([^ \n\t]+\\)")
805 (module (save-excursion
806 (and (re-search-backward "^\\[[+---]\\] \\(([^)]+)\\)" nil t
)
809 (error "Couldn't find procedure name on current line"))
811 (error "Couldn't find module name for current line"))
814 (format "Behaviour for breakpoint at %s:%s (default debug-here): "
824 (gds-send (format "(%S set-breakpoint %s %s %s)\n"
831 ;;;; Scheme source breakpoints.
833 (defcustom gds-breakpoint-face
'default
834 "*Face used to highlight the location of a source breakpoint.
835 Specifically, this face highlights the opening parenthesis of the
836 form where the breakpoint is set."
840 (defcustom gds-new-breakpoint-before-string
""
841 "*String used to show the presence of a new source breakpoint.
842 `New' means that the breakpoint has been set but isn't yet known to
843 Guile because the containing code hasn't been reevaluated yet.
844 This string appears before the opening parenthesis of the form where
845 the breakpoint is set. If you prefer a marker to appear after the
846 opening parenthesis, make this string empty and use
847 `gds-new-breakpoint-after-string'."
851 (defcustom gds-new-breakpoint-after-string
"=?= "
852 "*String used to show the presence of a new source breakpoint.
853 `New' means that the breakpoint has been set but isn't yet known to
854 Guile because the containing code hasn't been reevaluated yet.
855 This string appears after the opening parenthesis of the form where
856 the breakpoint is set. If you prefer a marker to appear before the
857 opening parenthesis, make this string empty and use
858 `gds-new-breakpoint-before-string'."
862 (defcustom gds-active-breakpoint-before-string
""
863 "*String used to show the presence of a source breakpoint.
864 `Active' means that the breakpoint is known to Guile.
865 This string appears before the opening parenthesis of the form where
866 the breakpoint is set. If you prefer a marker to appear after the
867 opening parenthesis, make this string empty and use
868 `gds-active-breakpoint-after-string'."
872 (defcustom gds-active-breakpoint-after-string
"=|= "
873 "*String used to show the presence of a source breakpoint.
874 `Active' means that the breakpoint is known to Guile.
875 This string appears after the opening parenthesis of the form where
876 the breakpoint is set. If you prefer a marker to appear before the
877 opening parenthesis, make this string empty and use
878 `gds-active-breakpoint-before-string'."
882 (defun gds-source-breakpoint-pos ()
883 "Return the position of the starting parenthesis of the innermost
884 Scheme pair around point."
885 (if (eq (char-syntax (char-after)) ?\
()
889 (while t
(forward-sexp -
1))
892 (while (not (eq (char-syntax (char-after)) ?\
())
896 (defun gds-source-breakpoint-overlay-at (pos)
897 "Return the source breakpoint overlay at POS, if any."
898 (let* (o (os (overlays-at pos
)))
900 (if (and (overlay-get (car os
) 'gds-breakpoint-info
)
901 (= (overlay-start (car os
)) pos
))
907 (defun gds-set-source-breakpoint ()
909 (let* ((pos (gds-source-breakpoint-pos))
910 (o (gds-source-breakpoint-overlay-at pos
)))
912 (error "There is already a breakpoint here!")
913 (setq o
(make-overlay pos
(+ pos
1)))
914 (overlay-put o
'evaporate t
)
915 (overlay-put o
'face gds-breakpoint-face
)
916 (overlay-put o
'gds-breakpoint-info
0)
917 (overlay-put o
'before-string gds-new-breakpoint-before-string
)
918 (overlay-put o
'after-string gds-new-breakpoint-after-string
))))
920 (defun gds-delete-source-breakpoint ()
922 (let* ((pos (gds-source-breakpoint-pos))
923 (o (gds-source-breakpoint-overlay-at pos
)))
925 (error "There is no breakpoint here to delete!"))
928 (defun gds-region-breakpoint-info (beg end
)
929 "Return an alist of breakpoints in REGION.
930 The car of each alist element is a cons (LINE . COLUMN) giving the
931 source location of the breakpoint. The cdr is information describing
932 breakpoint properties. Currently `information' is just the breakpoint
933 index, for an existing Guile breakpoint, or 0 for a breakpoint that
934 isn't yet known to Guile."
936 (let ((os (overlays-in beg end
))
941 (if (overlay-get o
'gds-breakpoint-info
)
944 (cons (cons (save-excursion
945 (goto-char (overlay-start o
))
946 (cons (save-excursion
948 (count-lines (point-min) (point)))
950 (overlay-get o
'gds-breakpoint-info
))
952 ;; Also now mark the breakpoint as `new'. It will become
953 ;; `active' (again) when we receive a notification from
954 ;; Guile that the breakpoint has been set.
955 (overlay-put o
'gds-breakpoint-info
0)
956 (overlay-put o
'before-string gds-new-breakpoint-before-string
)
957 (overlay-put o
'after-string gds-new-breakpoint-after-string
))))
961 ;;;; Evaluating code.
963 ;; The following commands send code for evaluation through the GDS TCP
964 ;; connection, receive the result and any output generated through the
965 ;; same connection, and display the result and output to the user.
967 ;; Where there are multiple Guile applications known to GDS, GDS by
968 ;; default sends code to the one that holds the debugging focus,
969 ;; i.e. `gds-focus-client'. Where no application has the focus,
970 ;; or the command is invoked with `C-u', GDS asks the user which
971 ;; application is intended.
973 (defun gds-read-client ()
974 (let* ((def (if gds-focus-client
975 (cdr (assq gds-focus-client gds-names
))))
977 (concat "Application for eval (default "
980 "Application for eval: "))
982 (completing-read prompt
983 (mapcar (function list
)
984 (mapcar (function cdr
) gds-names
))
987 (let (client (names gds-names
))
988 (while (and names
(not client
))
989 (if (string-equal (cdar names
) name
)
990 (setq client
(caar names
)))
991 (setq names
(cdr names
)))
994 (defun gds-choose-client (client)
995 (or ;; If client is an integer, it is the port number of the
997 (if (integerp client
) client
)
998 ;; Any other non-nil value indicates invocation with a prefix
999 ;; arg, which forces asking the user which application is
1001 (if client
(gds-read-client))
1002 ;; If ask not forced, and there is a client with the focus,
1003 ;; default to that one.
1005 ;; If there are no clients at this point, and we are allowed to
1006 ;; autostart a captive Guile, do so.
1007 (and (null gds-buffers
)
1008 gds-autostart-captive
1010 (gds-start-captive t
)
1011 (while (null gds-buffers
)
1012 (accept-process-output (get-buffer-process gds-captive
)
1014 (caar gds-buffers
)))
1015 ;; If there is only one known client, use that one.
1016 (if (and (car gds-buffers
)
1017 (null (cdr gds-buffers
)))
1019 ;; Last resort - ask the user.
1022 (error "No application chosen.")))
1024 (defun gds-module-name (start end
)
1025 "Determine and return the name of the module that governs the
1026 specified region. The module name is returned as a list of symbols."
1027 (interactive "r") ; why not?
1031 (while (and (not module-name
)
1032 (beginning-of-defun-raw 1))
1033 (if (looking-at "(define-module ")
1036 (goto-char (match-end 0))
1037 (read (current-buffer))))))
1040 (defun gds-port-name (start end
)
1041 "Return port name for the specified region of the current buffer.
1042 The name will be used by Guile as the port name when evaluating that
1044 (or (buffer-file-name)
1045 (concat "Emacs buffer: " (buffer-name))))
1047 (defun gds-eval-region (start end
&optional client
)
1048 "Evaluate the current region."
1049 (interactive "r\nP")
1050 (setq client
(gds-choose-client client
))
1051 (let ((module (gds-module-name start end
))
1052 (port-name (gds-port-name start end
))
1056 (setq column
(current-column)) ; 0-based
1058 (setq line
(count-lines (point-min) (point)))) ; 0-based
1059 (gds-send (format "(%S eval %s %S %d %d %S %S)\n"
1061 (if module
(prin1-to-string module
) "#f")
1062 port-name line column
1063 (gds-region-breakpoint-info start end
)
1064 (buffer-substring-no-properties start end
)))))
1066 (defun gds-eval-expression (expr &optional client
)
1067 "Evaluate the supplied EXPR (a string)."
1068 (interactive "sEvaluate expression: \nP")
1069 (setq client
(gds-choose-client client
))
1070 (gds-send (format "(%S eval #f \"Emacs expression\" 0 0 %S)\n"
1073 (defun gds-eval-defun (&optional client
)
1074 "Evaluate the defun (top-level form) at point."
1078 (let ((end (point)))
1079 (beginning-of-defun)
1080 (gds-eval-region (point) end client
))))
1082 (defun gds-eval-last-sexp (&optional client
)
1083 "Evaluate the sexp before point."
1085 (gds-eval-region (save-excursion (backward-sexp) (point)) (point) client
))
1090 ;; Help is implemented as a special case of evaluation, where we
1091 ;; arrange for the evaluation result to be a known symbol that is
1092 ;; unlikely to crop up otherwise. When the evaluation result is this
1093 ;; symbol, we only display the output from the evaluation.
1095 (defvar gds-help-symbol
'%-gds-help-%
1096 "Symbol used by GDS to identify an evaluation response as help.")
1098 (defun gds-help-symbol (sym &optional client
)
1099 "Get help for SYM (a Scheme symbol)."
1101 (let ((sym (thing-at-point 'symbol
))
1102 (enable-recursive-minibuffers t
)
1104 (setq val
(read-from-minibuffer
1106 (format "Describe Guile symbol (default %s): " sym
)
1107 "Describe Guile symbol: ")))
1108 (list (if (zerop (length val
)) sym val
)
1109 current-prefix-arg
)))
1110 (gds-eval-expression (format "(begin (help %s) '%S)" sym gds-help-symbol
)
1113 (defun gds-apropos (regex &optional client
)
1114 "List Guile symbols matching REGEX."
1116 (let ((sym (thing-at-point 'symbol
))
1117 (enable-recursive-minibuffers t
)
1119 (setq val
(read-from-minibuffer
1121 (format "Guile apropos (regexp, default \"%s\"): " sym
)
1122 "Guile apropos (regexp): ")))
1123 (list (if (zerop (length val
)) sym val
)
1124 current-prefix-arg
)))
1125 (gds-eval-expression (format "(begin (apropos %S) '%S)" regex gds-help-symbol
)
1128 (defvar gds-completion-results nil
)
1130 (defun gds-complete-symbol (&optional client
)
1131 "Complete the Guile symbol before point. Returns `t' if anything
1132 interesting happened, `nil' if not."
1134 (let* ((chars (- (point) (save-excursion
1135 (while (let ((syntax (char-syntax (char-before (point)))))
1136 (or (eq syntax ?w
) (eq syntax ?_
)))
1141 (setq client
(gds-choose-client client
))
1142 (setq gds-completion-results nil
)
1143 (gds-send (format "(%S complete %s)\n" client
1145 (buffer-substring-no-properties (- (point) chars
)
1147 (while (null gds-completion-results
)
1148 (accept-process-output gds-process
0 200))
1149 (cond ((eq gds-completion-results t
)
1151 ((stringp gds-completion-results
)
1152 (if (<= (length gds-completion-results
) chars
)
1154 (insert (substring gds-completion-results chars
))
1155 (message "Sole completion")
1157 ((= (length gds-completion-results
) 1)
1158 (if (<= (length (car gds-completion-results
)) chars
)
1160 (insert (substring (car gds-completion-results
) chars
))
1163 (with-output-to-temp-buffer "*Completions*"
1164 (display-completion-list gds-completion-results
))
1168 ;;;; Display of evaluation and help results.
1170 (defun gds-display-results (client results
)
1171 (let ((helpp (and (= (length results
) 2)
1172 (= (length (cadr results
)) 1)
1173 (string-equal (caadr results
)
1174 (prin1-to-string gds-help-symbol
)))))
1175 (let ((buf (get-buffer-create (if helpp
1177 "*Guile Results*"))))
1183 (insert (car results
))
1186 (mapcar (function (lambda (value)
1187 (insert " => " value
"\n")))
1190 (setq results
(cddr results
)))
1191 (goto-char (point-min))
1192 (if (and helpp
(looking-at "Evaluating in "))
1193 (delete-region (point) (progn (forward-line 1) (point)))))
1195 (run-hooks 'temp-buffer-show-hook
)
1199 ;;;; Loading (evaluating) a whole Scheme file.
1201 (defcustom gds-source-modes
'(scheme-mode)
1202 "*Used to determine if a buffer contains Scheme source code.
1203 If it's loaded into a buffer that is in one of these major modes, it's
1204 considered a scheme source file by `gds-load-file'."
1205 :type
'(repeat function
)
1208 (defvar gds-prev-load-dir
/file nil
1209 "Holds the last (directory . file) pair passed to `gds-load-file'.
1210 Used for determining the default for the next `gds-load-file'.")
1212 (defun gds-load-file (file-name &optional client
)
1213 "Load a Scheme file into the inferior Scheme process."
1214 (interactive (list (car (comint-get-source "Load Scheme file: "
1215 gds-prev-load-dir
/file
1216 gds-source-modes t
))
1217 ; T because LOAD needs an
1219 current-prefix-arg
))
1220 (comint-check-source file-name
) ; Check to see if buffer needs saved.
1221 (setq gds-prev-load-dir
/file
(cons (file-name-directory file-name
)
1222 (file-name-nondirectory file-name
)))
1223 (setq client
(gds-choose-client client
))
1224 (gds-send (format "(%S load %S)\n" client file-name
)))
1227 ;;;; Scheme mode keymap items.
1229 (define-key scheme-mode-map
"\M-\C-x" 'gds-eval-defun
);gnu convention
1230 (define-key scheme-mode-map
"\C-x\C-e" 'gds-eval-last-sexp
);gnu convention
1231 (define-key scheme-mode-map
"\C-c\C-e" 'gds-eval-expression
)
1232 (define-key scheme-mode-map
"\C-c\C-r" 'gds-eval-region
)
1233 (define-key scheme-mode-map
"\C-c\C-l" 'gds-load-file
)
1234 (define-key scheme-mode-map
"\C-hg" 'gds-help-symbol
)
1235 (define-key scheme-mode-map
"\C-h\C-g" 'gds-apropos
)
1236 (define-key scheme-mode-map
"\e\t" 'gds-complete-symbol
)
1237 (define-key scheme-mode-map
"\C-x " 'gds-set-source-breakpoint
)
1238 (define-key scheme-mode-map
"\C-x\e " 'gds-delete-source-breakpoint
)
1241 ;;;; GDS (Guile Interaction) mode keymap and menu items.
1243 (set-keymap-parent gds-mode-map widget-keymap
)
1245 (define-key gds-mode-map
"M" (function gds-query-modules
))
1247 (define-key gds-mode-map
"g" (function gds-go
))
1248 (define-key gds-mode-map
"q" (function gds-quit
))
1249 (define-key gds-mode-map
" " (function gds-next
))
1250 (define-key gds-mode-map
"e" (function gds-evaluate
))
1251 (define-key gds-mode-map
"i" (function gds-step-in
))
1252 (define-key gds-mode-map
"o" (function gds-step-out
))
1253 (define-key gds-mode-map
"t" (function gds-trace-finish
))
1254 (define-key gds-mode-map
"I" (function gds-frame-info
))
1255 (define-key gds-mode-map
"A" (function gds-frame-args
))
1257 (define-key gds-mode-map
"b" (function gds-set-breakpoint
))
1259 (define-key gds-mode-map
"vi" (function gds-view-interaction
))
1260 (define-key gds-mode-map
"vs" (function gds-view-stack
))
1261 (define-key gds-mode-map
"vb" (function gds-view-breakpoints
))
1262 (define-key gds-mode-map
"vB" (function gds-view-browser
))
1263 (define-key gds-mode-map
"vm" (function gds-view-messages
))
1265 (defvar gds-view-menu nil
1269 (setq gds-view-menu
(make-sparse-keymap "View"))
1270 (define-key gds-view-menu
[messages]
1271 '(menu-item "Messages" gds-view-messages
1272 :enable (memq 'messages gds-views)))
1273 (define-key gds-view-menu [browser]
1274 '(menu-item "Browser" gds-view-browser
1275 :enable (memq 'browser gds-views)))
1276 (define-key gds-view-menu [breakpoints]
1277 '(menu-item "Breakpoints" gds-view-breakpoints
1278 :enable (memq 'breakpoints gds-views)))
1279 (define-key gds-view-menu [stack]
1280 '(menu-item "Stack" gds-view-stack
1281 :enable (memq 'stack gds-views)))
1282 (define-key gds-view-menu [interaction]
1283 '(menu-item "Interaction" gds-view-interaction
1284 :enable (memq 'interaction gds-views))))
1286 (defvar gds-debug-menu nil
1287 "GDS debugging menu.")
1290 (setq gds-debug-menu (make-sparse-keymap "Debug"))
1291 (define-key gds-debug-menu [go]
1292 '(menu-item "Go" gds-go))
1293 (define-key gds-debug-menu [trace-finish]
1294 '(menu-item "Trace This Frame" gds-trace-finish))
1295 (define-key gds-debug-menu [step-out]
1296 '(menu-item "Finish This Frame" gds-step-out))
1297 (define-key gds-debug-menu [next]
1298 '(menu-item "Next" gds-next))
1299 (define-key gds-debug-menu [step-in]
1300 '(menu-item "Single Step" gds-step-in))
1301 (define-key gds-debug-menu [eval]
1302 '(menu-item "Eval In This Frame..." gds-evaluate)))
1304 (defvar gds-breakpoint-menu nil
1305 "GDS breakpoint menu.")
1306 (if gds-breakpoint-menu
1308 (setq gds-breakpoint-menu (make-sparse-keymap "Breakpoint"))
1309 (define-key gds-breakpoint-menu [last-sexp]
1310 '(menu-item "Delete Breakpoint" gds-delete-source-breakpoint))
1311 (define-key gds-breakpoint-menu [set]
1312 '(menu-item "Set Breakpoint" gds-set-source-breakpoint)))
1314 (defvar gds-eval-menu nil
1315 "GDS evaluation menu.")
1318 (setq gds-eval-menu (make-sparse-keymap "Evaluate"))
1319 (define-key gds-eval-menu [load-file]
1320 '(menu-item "Load Scheme File" gds-load-file))
1321 (define-key gds-eval-menu [defun]
1322 '(menu-item "Defun At Point" gds-eval-defun))
1323 (define-key gds-eval-menu [region]
1324 '(menu-item "Region" gds-eval-region))
1325 (define-key gds-eval-menu [last-sexp]
1326 '(menu-item "Sexp Before Point" gds-eval-last-sexp))
1327 (define-key gds-eval-menu [expr]
1328 '(menu-item "Expression..." gds-eval-expression)))
1330 (defvar gds-help-menu nil
1334 (setq gds-help-menu (make-sparse-keymap "Help"))
1335 (define-key gds-help-menu [apropos]
1336 '(menu-item "Apropos..." gds-apropos))
1337 (define-key gds-help-menu [sym]
1338 '(menu-item "Symbol..." gds-help-symbol)))
1340 (defvar gds-advanced-menu nil
1341 "Menu of rarely needed GDS operations.")
1342 (if gds-advanced-menu
1344 (setq gds-advanced-menu (make-sparse-keymap "Advanced"))
1345 (define-key gds-advanced-menu [run-captive]
1346 '(menu-item "Run Captive Guile" gds-start-captive
1347 :enable (not (comint-check-proc gds-captive))))
1348 (define-key gds-advanced-menu [restart-gds]
1349 '(menu-item "Restart IDE" gds-start :enable gds-process))
1350 (define-key gds-advanced-menu [kill-gds]
1351 '(menu-item "Shutdown IDE" gds-shutdown :enable gds-process))
1352 (define-key gds-advanced-menu [start-gds]
1353 '(menu-item "Start IDE" gds-start :enable (not gds-process))))
1355 (defvar gds-menu nil
1356 "Global menu for GDS commands.")
1359 (setq gds-menu (make-sparse-keymap "Guile"))
1360 (define-key gds-menu [advanced]
1361 (cons "Advanced" gds-advanced-menu))
1362 (define-key gds-menu [separator-1]
1364 (define-key gds-menu [view]
1365 `(menu-item "View" ,gds-view-menu :enable gds-views))
1366 (define-key gds-menu [debug]
1367 `(menu-item "Debug" ,gds-debug-menu :enable (and gds-focus-client
1368 (gds-client-blocked))))
1369 (define-key gds-menu [breakpoint]
1370 `(menu-item "Breakpoints" ,gds-breakpoint-menu :enable t))
1371 (define-key gds-menu [eval]
1372 `(menu-item "Evaluate" ,gds-eval-menu :enable (or gds-buffers
1373 gds-autostart-captive)))
1374 (define-key gds-menu [help]
1375 `(menu-item "Help" ,gds-help-menu :enable (or gds-buffers
1376 gds-autostart-captive)))
1377 (setq menu-bar-final-items
1378 (cons 'guile menu-bar-final-items))
1379 (define-key scheme-mode-map [menu-bar guile]
1380 (cons "Guile" gds-menu)))
1383 ;;;; Autostarting the GDS server.
1385 (defcustom gds-autostart-server t
1386 "Whether to automatically start the GDS server when `gds.el' is loaded."
1390 (if (and gds-autostart-server
1395 ;;;; `Captive' Guile - a Guile process that is started when needed to
1396 ;;;; provide help, completion, evaluations etc.
1398 (defcustom gds-autostart-captive t
1399 "Whether to automatically start a `captive' Guile process when needed."
1403 (defvar gds-captive nil
1404 "Buffer of captive Guile.")
1406 (defun gds-start-captive (&optional restart)
1409 (comint-check-proc gds-captive))
1411 (if (comint-check-proc gds-captive)
1413 (let ((process-connection-type nil))
1414 (setq gds-captive (make-comint "captive-guile"
1418 (let ((proc (get-buffer-process gds-captive)))
1419 (comint-send-string proc "(set! %load-path (cons \"/home/neil/Guile/cvs/guile-core\" %load-path))\n")
1420 (comint-send-string proc "(debug-enable 'backtrace)\n")
1421 (comint-send-string proc "(use-modules (emacs gds-client))\n")
1422 (comint-send-string proc "(gds-connect \"Captive Guile\" #f)\n"))))
1424 (defun gds-kill-captive ()
1426 (let ((proc (get-buffer-process gds-captive)))
1427 (process-kill-without-query proc)
1431 (accept-process-output gds-process 0 200))
1439 ;;; gds.el ends here.