*** empty log message ***
[bpt/guile.git] / emacs / gds.el
CommitLineData
79b1c5b6
NJ
1;;; gds.el -- Guile debugging frontend
2
3;;;; Copyright (C) 2003 Free Software Foundation, Inc.
4;;;;
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
9;;;; version.
10;;;;
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.
15;;;;
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
19;;;; 02111-1307 USA
20
21
22;;;; Prerequisites.
23
24(require 'widget)
25(require 'wid-edit)
26
27
28;;;; Debugging (of this code!).
29
30(defsubst dmessage (msg &rest args)
31 ;;(apply (function message) msg args)
32 )
33
34
35;;;; Customization group setup.
36
37(defgroup gds nil
38 "Customization options for Guile Debugging."
39 :group 'scheme)
40
41
42;;;; Communication with the (ice-9 debugger ui-server) subprocess.
43
44;; The subprocess object.
45(defvar gds-process nil)
46
47;; Subprocess output goes into the `*GDS Process*' buffer, and
48;; is then read from there one form at a time. `gds-read-cursor' is
49;; the buffer position of the start of the next unread form.
50(defvar gds-read-cursor nil)
51
52;; Start (or restart) the subprocess.
53(defun gds-start ()
54 (if gds-process (gds-shutdown))
55 (with-current-buffer (get-buffer-create "*GDS Process*")
56 (erase-buffer)
57 (setq gds-process
58 (let ((process-connection-type nil)) ; use a pipe
59 (start-process "gds"
60 (current-buffer)
61 "guile"
62 "-q"
63 "--debug"
64 "-e"
65 "run"
66 "-s"
67 "/home/neil/Guile/cvs/guile-core/ice-9/debugger/ui-server.scm"))))
68 (setq gds-read-cursor (point-min))
69 (set-process-filter gds-process (function gds-filter))
70 (set-process-sentinel gds-process (function gds-sentinel))
71 (set-process-coding-system gds-process 'latin-1-unix))
72
73;; Shutdown the subprocess and cleanup all associated data.
74(defun gds-shutdown ()
75 ;; Do cleanup for all clients.
76 (while gds-names
77 (gds-client-cleanup (caar gds-names)))
78 ;; Reset any remaining variables.
79 (setq gds-displayed-client nil
80 gds-waiting nil)
81 ;; If the timer is running, cancel it.
82 (if gds-timer
83 (cancel-timer gds-timer))
84 (setq gds-timer nil)
85 ;; Kill the subprocess.
86 (process-kill-without-query gds-process)
87 (condition-case nil
88 (progn
89 (kill-process gds-process)
90 (accept-process-output gds-process 0 200))
91 (error))
92 (setq gds-process nil))
93
94;; Subprocess output filter: inserts normally into the process buffer,
95;; then tries to reread the output one form at a time and delegates
96;; processing of each form to `gds-handle-input'.
97(defun gds-filter (proc string)
98 (with-current-buffer (process-buffer proc)
99 (save-excursion
100 (goto-char (process-mark proc))
101 (insert-before-markers string))
102 (goto-char gds-read-cursor)
103 (while (let ((form (condition-case nil
104 (read (current-buffer))
105 (error nil))))
106 (if form
107 (save-excursion
108 (gds-handle-input form)))
109 form)
110 (setq gds-read-cursor (point)))))
111
112;; Subprocess sentinel: do nothing. (Currently just here to avoid
113;; inserting un-`read'able process status messages into the process
114;; buffer.)
115(defun gds-sentinel (proc event)
116 )
117
118;; Send input to the subprocess.
119(defun gds-send (string)
120 (process-send-string gds-process string))
121
122
123;;;; Multiple application scheduling.
124
125;; At any moment one Guile application has the focus of the frontend
126;; code. `gds-displayed-client' holds the port number of that client.
127;; If there are no Guile applications wanting the focus - that is,
128;; ready for debugging instructions - `gds-displayed-client' is nil.
129(defvar gds-displayed-client nil)
130
131;; The list of other Guile applications waiting for focus, referenced
132;; by their port numbers.
133(defvar gds-waiting nil)
134
135;; An idle timer that we use to avoid confusing any user work when
136;; popping up debug buffers. `gds-timer' is non-nil whenever the
137;; timer is running and nil whenever it is not running.
138(defvar gds-timer nil)
139
140;; Debug the specified client. If it already has the focus, do so
141;; immediately, but using the idle timer to ensure that it doesn't
142;; confuse any work the user may be doing. Non-structural work is
143;; delegated to `gds-display-state'.
144(defun gds-debug (&optional client)
145 (dmessage "gds-debug")
146 ;; If `client' is specified, add it to the end of `gds-waiting',
147 ;; unless that client is already the current client or it is already
148 ;; in the waiting list.
149 (if (and client
150 (not (eq client gds-displayed-client))
151 (not (memq client gds-waiting)))
152 (setq gds-waiting (append gds-waiting (list client))))
153 ;; Now update `client' to be the next client in the list.
154 (setq client (or gds-displayed-client (car gds-waiting)))
155 ;; If conditions are right, start the idle timer.
156 (if (and client
157 (or (null gds-displayed-client)
158 (eq gds-displayed-client client)))
159 (gds-display-state (or gds-displayed-client
160 (prog1 (car gds-waiting)
161 (setq gds-waiting
162 (cdr gds-waiting)))))))
163
164;; Give up focus because debugging is done for now. Display detail in
165;; case of no waiting clients is delegated to `gds-clear-display'.
166(defun gds-focus-done ()
167 (gds-clear-display)
168 (gds-debug))
169
170;; Although debugging of this client isn't done, yield focus to the
171;; next waiting client.
172(defun gds-focus-yield ()
173 (interactive)
174 (if (and (null gds-waiting)
175 (y-or-n-p "No other clients waiting - bury *Guile Debug* buffer? "))
176 (bury-buffer)
177 (or (memq gds-displayed-client gds-waiting)
178 (setq gds-waiting (append gds-waiting (list gds-displayed-client))))
179 (gds-focus-done)))
180
181
182;;;; Per-client state information.
183
184;; Alist mapping client port numbers to application names. The names
185;; in this list have been uniquified by `gds-uniquify'.
186(defvar gds-names nil)
187
188;; Return unique form of NAME.
189(defun gds-uniquify (name)
190 (let ((count 1)
191 (maybe-unique name))
192 (while (member maybe-unique (mapcar (function cdr) gds-names))
193 (setq count (1+ count)
194 maybe-unique (concat name "<" (number-to-string count) ">")))
195 maybe-unique))
196
197;; Alist mapping client port numbers to last known status.
198;;
199;; Status is one of the following symbols.
200;;
201;; `running' - application is running.
202;;
203;; `waiting-for-input' - application is blocked waiting for
204;; instruction from the frontend.
205;;
206;; `ready-for-input' - application is not blocked but can also
207;; accept asynchronous instructions from the frontend.
208;;
209(defvar gds-statuses nil)
210
211;; Alist mapping client port numbers to last printed outputs.
212(defvar gds-outputs nil)
213
214;; Alist mapping client port numbers to last known stacks.
215(defvar gds-stacks nil)
216
217;; Alist mapping client port numbers to module information. This
218;; looks like:
219;;
220;; ((4 ((guile) t sym1 sym2 ...) ((guile-user)) ((ice-9 debug) nil sym3 sym4) ...) ...)
221;;
222;; So, for example:
223;;
224;; (assq client gds-modules)
225;; =>
226;; (4 ((guile) t sym1 sym2 ...) ((guile-user)) ((ice-9 debug) nil sym3 sym4) ...)
227;;
228;; The t or nil after the module name indicates whether the module is
229;; displayed in expanded form (that is, showing the bindings in that
230;; module).
231;;
232;; The syms are actually all strings, because some Guile symbols are
233;; not readable by Emacs.
234(defvar gds-modules nil)
235
236
237;;;; Handling debugging instructions.
238
239;; General dispatch function called by the subprocess filter.
240(defun gds-handle-input (form)
241 (dmessage "Form: %S" form)
242 (let ((client (car form)))
243 (cond ((eq client '*))
244 (t
245 (let ((proc (cadr form)))
246
247 (cond ((eq proc 'name)
248 ;; (name ...) - Application's name.
249 (setq gds-names
250 (cons (cons client (gds-uniquify (caddr form)))
251 gds-names)))
252
253 ((eq proc 'stack)
254 ;; (stack ...) - Stack at an error or breakpoint.
255 (gds-set gds-stacks client (cddr form)))
256
257 ((eq proc 'modules)
258 ;; (modules ...) - Application's loaded modules.
259 (gds-set gds-modules client
260 (mapcar (function list) (cddr form))))
261
262 ((eq proc 'output)
263 ;; (output ...) - Last printed output.
264 (gds-set gds-outputs client (caddr form)))
265
266 ((eq proc 'status)
267 ;; (status ...) - Application status indication.
268 (let ((status (caddr form)))
269 (gds-set gds-statuses client status)
270 (cond ((eq status 'waiting-for-input)
271 (gds-debug client))
272 ((eq status 'running)
273 (if (eq client gds-displayed-client)
274 (gds-display-state client)))
275 (t
276 (error "Unexpected status: %S" status)))))
277
278 ((eq proc 'module)
279 ;; (module MODULE ...) - The specified module's bindings.
280 (let* ((modules (assq client gds-modules))
281 (minfo (assoc (caddr form) modules)))
282 (if minfo
283 (setcdr (cdr minfo) (cdddr form)))))
284
285 ((eq proc 'closed)
286 ;; (closed) - Client has gone away.
287 (gds-client-cleanup client))
288
289 ))))))
290
291;; Store latest status, stack or module list for the specified client.
292(defmacro gds-set (alist client val)
293 `(let ((existing (assq ,client ,alist)))
294 (if existing
295 (setcdr existing ,val)
296 (setq ,alist
297 (cons (cons client ,val) ,alist)))))
298
299;; Cleanup processing when CLIENT goes away.
300(defun gds-client-cleanup (client)
301 (if (eq client gds-displayed-client)
302 (gds-focus-done))
303 (setq gds-names
304 (delq (assq client gds-names) gds-names))
305 (setq gds-stacks
306 (delq (assq client gds-stacks) gds-stacks))
307 (setq gds-modules
308 (delq (assq client gds-modules) gds-modules)))
309
310
311;;;; Displaying debugging information.
312
313(defvar gds-client-buffer nil)
314
315(define-derived-mode gds-mode
316 fundamental-mode
317 "Guile Debugging"
318 "Major mode for Guile debugging information buffers.")
319
320(defun gds-set-client-buffer (&optional client)
321 (if (and gds-client-buffer
322 (buffer-live-p gds-client-buffer))
323 (set-buffer gds-client-buffer)
324 (setq gds-client-buffer (get-buffer-create "*Guile Debug*"))
325 (set-buffer gds-client-buffer)
326 (gds-mode))
327 ;; Rename to something we don't want first. Otherwise, if the
328 ;; buffer is already correctly named, we get a confusing change
329 ;; from, say, `*Guile Debug: REPL*' to `*Guile Debug: REPL*<2>'.
330 (rename-buffer "*Guile Debug Fake Buffer Name*" t)
331 (rename-buffer (if client
332 (concat "*Guile Debug: "
333 (cdr (assq client gds-names))
334 "*")
335 "*Guile Debug*")
336 t) ; Rename uniquely if needed,
337 ; although it shouldn't be.
338 (force-mode-line-update t))
339
340(defun gds-clear-display ()
341 ;; Clear the client buffer.
342 (gds-set-client-buffer)
343 (let ((inhibit-read-only t))
344 (erase-buffer)
345 (insert "Stack:\nNo clients ready for debugging.\n")
346 (goto-char (point-min)))
347 (setq gds-displayed-stack 'no-clients)
348 (setq gds-displayed-modules nil)
349 (setq gds-displayed-client nil)
350 (bury-buffer))
351
352;; Determine whether the client display buffer is visible in the
353;; currently selected frame (i.e. where the user is editing).
354(defun gds-buffer-visible-in-selected-frame-p ()
355 (let ((visible-p nil))
356 (walk-windows (lambda (w)
357 (if (eq (window-buffer w) gds-client-buffer)
358 (setq visible-p t))))
359 visible-p))
360
361;; Cached display variables for `gds-display-state'.
362(defvar gds-displayed-stack nil)
363(defvar gds-displayed-modules nil)
364
365;; Types of display areas in the *Guile Debug* buffer.
366(defvar gds-display-types '("Status" "Stack" "Modules"))
367(defvar gds-display-type-regexp
368 (concat "^\\("
369 (substring (apply (function concat)
370 (mapcar (lambda (type)
371 (concat "\\|" type))
372 gds-display-types))
373 2)
374 "\\):"))
375
376(defun gds-maybe-delete-region (type)
377 (let ((beg (save-excursion
378 (goto-char (point-min))
379 (and (re-search-forward (concat "^"
380 (regexp-quote type)
381 ":")
382 nil t)
383 (match-beginning 0)))))
384 (if beg
385 (delete-region beg
386 (save-excursion
387 (goto-char beg)
388 (end-of-line)
389 (or (and (re-search-forward gds-display-type-regexp
390 nil t)
391 (match-beginning 0))
392 (point-max)))))))
393
394(defun gds-maybe-skip-region (type)
395 (if (looking-at (regexp-quote type))
396 (if (re-search-forward gds-display-type-regexp nil t 2)
397 (beginning-of-line)
398 (goto-char (point-max)))))
399
400(defun gds-display-state (client)
401 (dmessage "gds-display-state")
402 ;; Avoid continually popping up the last associated source buffer
403 ;; unless it really is still current.
404 (setq gds-selected-frame-source-buffer nil)
405 (gds-set-client-buffer client)
406 (let ((stack (cdr (assq client gds-stacks)))
407 (modules (cdr (assq client gds-modules)))
408 (inhibit-read-only t)
409 (p (if (eq client gds-displayed-client)
410 (point)
411 (point-min)))
412 stack-changed)
413 ;; Start at top of buffer.
414 (goto-char (point-min))
415 ;; Display status; too simple to be worth caching.
416 (gds-maybe-delete-region "Status")
417 (widget-insert "Status: "
418 (cdr (assq (cdr (assq client gds-statuses))
419 '((running . "running")
420 (waiting-for-input . "waiting for input")
421 (ready-for-input . "ready for input"))))
422 "\n\n")
423 (let ((output (cdr (assq client gds-outputs))))
424 (if (> (length output) 0)
425 (widget-insert output "\n\n")))
426 ;; Display stack.
427 (dmessage "insert stack")
428 (if (equal stack gds-displayed-stack)
429 (gds-maybe-skip-region "Stack")
430 ;; Note that stack has changed.
431 (if stack (setq stack-changed t))
432 ;; Delete existing stack.
433 (gds-maybe-delete-region "Stack")
434 ;; Insert new stack.
435 (if stack (gds-insert-stack stack))
436 ;; Record displayed stack.
437 (setq gds-displayed-stack stack))
438 ;; Display module list.
439 (dmessage "insert modules")
440 (if (equal modules gds-displayed-modules)
441 (gds-maybe-skip-region "Modules")
442 ;; Delete existing module list.
443 (gds-maybe-delete-region "Modules")
444 ;; Insert new list.
445 (if modules (gds-insert-modules modules))
446 ;; Record displayed list.
447 (setq gds-displayed-modules (copy-tree modules)))
448 ;; Finish off.
449 (dmessage "widget-setup")
450 (widget-setup)
451 (if stack-changed
452 ;; Stack is being seen for the first time, so make sure top of
453 ;; buffer is visible.
454 (progn
455 (goto-char (point-min))
456 (re-search-forward "^Stack:")
457 (forward-line (+ 1 (cadr stack))))
458 ;; Restore point from before buffer was redrawn.
459 (goto-char p)))
460 (setq gds-displayed-client client)
461 (dmessage "consider display")
462 (if (eq (window-buffer (selected-window)) gds-client-buffer)
463 ;; *Guile Debug* buffer already selected.
464 (gds-display-buffers)
465 (dmessage "Running GDS timer")
466 (setq gds-timer
467 (run-with-idle-timer 0.5
468 nil
469 (lambda ()
470 (setq gds-timer nil)
471 (gds-display-buffers))))))
472
473(defun gds-display-buffers ()
474 ;; If there's already a window showing the *Guile Debug* buffer, use
475 ;; it.
476 (let ((window (get-buffer-window gds-client-buffer t)))
477 (if window
478 (progn
479 (make-frame-visible (window-frame window))
480 (raise-frame (window-frame window))
481 (select-frame (window-frame window))
482 (select-window window))
483 (switch-to-buffer gds-client-buffer)))
484 ;; If there is an associated source buffer, display it as well.
485 (if gds-selected-frame-source-buffer
486 (let ((window (display-buffer gds-selected-frame-source-buffer)))
487 (set-window-point window
488 (overlay-start gds-selected-frame-source-overlay))))
489 ;; Force redisplay.
490 (sit-for 0))
491
492(defun old-stuff ()
493 (if (gds-buffer-visible-in-selected-frame-p)
494 ;; Buffer already visible enough.
495 nil
496 ;; Delete any views of the buffer in other frames - we don't want
497 ;; views all over the place.
498 (delete-windows-on gds-client-buffer)
499 ;; Run idle timer to display the buffer as soon as user isn't in
500 ;; the middle of something else.
501 ))
502
503(defun gds-insert-stack (stack)
504 (let ((frames (car stack))
505 (index (cadr stack))
506 (flags (caddr stack))
507 frame items)
508 (widget-insert "Stack: " (prin1-to-string flags) "\n")
509 (let ((i -1))
510 (gds-show-selected-frame (caddr (nth index frames)))
511 (while frames
512 (setq frame (car frames)
513 frames (cdr frames)
514 i (+ i 1)
515 items (cons (list 'item
516 (let ((s (cadr frame)))
517 (put-text-property 0 1 'index i s)
518 s))
519 items))))
520 (setq items (nreverse items))
521 (apply (function widget-create)
522 'radio-button-choice
523 :value (cadr (nth index items))
524 :notify (function gds-select-stack-frame)
525 items)
526 (widget-insert "\n")))
527
528(defun gds-select-stack-frame (widget &rest ignored)
529 (let* ((s (widget-value widget))
530 (ind (memq 'index (text-properties-at 0 s))))
531 (gds-send (format "(%S debugger-command frame %d)\n"
532 gds-displayed-client
533 (cadr ind)))))
534
535;; Overlay used to highlight the source expression corresponding to
536;; the selected frame.
537(defvar gds-selected-frame-source-overlay nil)
538
539;; Buffer containing source for the selected frame.
540(defvar gds-selected-frame-source-buffer nil)
541
542(defun gds-show-selected-frame (source)
543 ;; Highlight the frame source, if possible.
544 (if (and source
545 (file-readable-p (car source)))
546 (with-current-buffer (find-file-noselect (car source))
547 (if gds-selected-frame-source-overlay
548 nil
549 (setq gds-selected-frame-source-overlay (make-overlay 0 0))
550 (overlay-put gds-selected-frame-source-overlay 'face 'highlight))
551 ;; Move to source line. Note that Guile line numbering is
552 ;; 0-based, while Emacs numbering is 1-based.
553 (save-restriction
554 (widen)
555 (goto-line (+ (cadr source) 1))
556 (move-to-column (caddr source))
557 (move-overlay gds-selected-frame-source-overlay
558 (point)
559 (if (not (looking-at ")"))
560 (save-excursion (forward-sexp 1) (point))
561 ;; It seems that the source coordinates for
562 ;; backquoted expressions are at the end of
563 ;; the sexp rather than the beginning...
564 (save-excursion (forward-char 1)
565 (backward-sexp 1) (point)))
566 (current-buffer)))
567 (setq gds-selected-frame-source-buffer (current-buffer)))
568 (if gds-selected-frame-source-overlay
569 (move-overlay gds-selected-frame-source-overlay 0 0))))
570
571(defcustom gds-module-filter '(t (guile nil) (ice-9 nil) (oop nil))
572 "Specification of which Guile modules the debugger should display.
573This is a list with structure (DEFAULT EXCEPTION EXCEPTION...), where
574DEFAULT is `t' or `nil' and each EXCEPTION has the structure (SYMBOL
575DEFAULT EXCEPTION EXCEPTION...).
576
577A Guile module name `(x y z)' is matched against this filter as
578follows. If one of the top level EXCEPTIONs has SYMBOL `x', continue
579by matching the rest of the module name, in this case `(y z)', against
580that SYMBOL's DEFAULT and next level EXCEPTION list. Otherwise, if
581the current DEFAULT is `t' display the module, and if the current
582DEFAULT is `nil', don't display it.
583
584This variable is usually set to exclude Guile system modules that are
585not of primary interest when debugging application code."
586 :type 'sexp
587 :group 'gds)
588
589(defun gds-show-module-p (name)
590 ;; Determine whether to display the NAMEd module by matching NAME
591 ;; against `gds-module-filter'.
592 (let ((default (car gds-module-filter))
593 (exceptions (cdr gds-module-filter)))
594 (let ((exception (assq (car name) exceptions)))
595 (if exception
596 (let ((gds-module-filter (cdr exception)))
597 (gds-show-module-p (cdr name)))
598 default))))
599
600(defun gds-insert-modules (modules)
601 (insert "Modules:\n")
602 (while modules
603 (let ((minfo (car modules)))
604 (if (gds-show-module-p (car minfo))
605 (let ((w (widget-create 'push-button
606 :notify (function gds-module-notify)
607 (if (and (cdr minfo)
608 (cadr minfo))
609 "-" "+"))))
610 (widget-put w :module (cons client (car minfo)))
611 (widget-insert " " (prin1-to-string (car minfo)) "\n")
612 (if (cadr minfo)
613 (let ((syms (cddr minfo)))
614 (while syms
615 (widget-insert " > " (car syms) "\n")
616 (setq syms (cdr syms))))))))
617 (setq modules (cdr modules))))
618
619(defun gds-module-notify (w &rest ignore)
620 (let* ((module (widget-get w :module))
621 (client (car module))
622 (name (cdr module))
623 (modules (assq client gds-modules))
624 (minfo (assoc name modules)))
625 (if (cdr minfo)
626 ;; Just toggle expansion state.
627 (progn
628 (setcar (cdr minfo) (not (cadr minfo)))
629 (gds-display-state client))
630 ;; Set flag to indicate module expanded.
631 (setcdr minfo (list t))
632 ;; Get symlist from Guile.
633 (gds-send (format "(%S query-module %S)\n" client name)))))
634
635
636;;;; Guile Debugging keymap.
637
638(set-keymap-parent gds-mode-map widget-keymap)
639(define-key gds-mode-map "g" (function gds-go))
640(define-key gds-mode-map "b" (function gds-set-breakpoint))
641(define-key gds-mode-map "q" (function gds-quit))
642(define-key gds-mode-map "y" (function gds-yield))
643(define-key gds-mode-map " " (function gds-next))
644(define-key gds-mode-map "e" (function gds-evaluate))
645(define-key gds-mode-map "i" (function gds-step-in))
646(define-key gds-mode-map "o" (function gds-step-out))
647(define-key gds-mode-map "t" (function gds-trace-finish))
648
649(defun gds-client-waiting ()
650 (eq (cdr (assq gds-displayed-client gds-statuses)) 'waiting-for-input))
651
652(defun gds-go ()
653 (interactive)
654 (gds-send (format "(%S debugger-command continue)\n" gds-displayed-client)))
655
656(defun gds-quit ()
657 (interactive)
658 (if (gds-client-waiting)
659 (if (y-or-n-p "Client is waiting for instruction - tell it to continue? ")
660 (gds-go)))
661 (gds-yield))
662
663(defun gds-yield ()
664 (interactive)
665 (if (gds-client-waiting)
666 (gds-focus-yield)
667 (gds-focus-done)))
668
669(defun gds-next ()
670 (interactive)
671 (gds-send (format "(%S debugger-command next 1)\n" gds-displayed-client)))
672
673(defun gds-evaluate (expr)
674 (interactive "sEvaluate (in this stack frame): ")
675 (gds-send (format "(%S debugger-command evaluate %s)\n"
676 gds-displayed-client
677 (prin1-to-string expr))))
678
679(defun gds-step-in ()
680 (interactive)
681 (gds-send (format "(%S debugger-command step 1)\n" gds-displayed-client)))
682
683(defun gds-step-out ()
684 (interactive)
685 (gds-send (format "(%S debugger-command finish)\n" gds-displayed-client)))
686
687(defun gds-trace-finish ()
688 (interactive)
689 (gds-send (format "(%S debugger-command trace-finish)\n"
690 gds-displayed-client)))
691
692(defun gds-set-breakpoint ()
693 (interactive)
694 (cond ((gds-in-source-buffer)
695 (gds-set-source-breakpoint))
696 ((gds-in-stack)
697 (gds-set-stack-breakpoint))
698 ((gds-in-modules)
699 (gds-set-module-breakpoint))
700 (t
701 (error "No way to set a breakpoint from here"))))
702
703(defun gds-in-source-buffer ()
704 ;; Not yet worked out what will be available in Scheme source
705 ;; buffers.
706 nil)
707
708(defun gds-in-stack ()
709 (and (eq (current-buffer) gds-client-buffer)
710 (save-excursion
711 (and (re-search-backward "^\\(Stack\\|Modules\\):" nil t)
712 (looking-at "Stack")))))
713
714(defun gds-in-modules ()
715 (and (eq (current-buffer) gds-client-buffer)
716 (save-excursion
717 (and (re-search-backward "^\\(Stack\\|Modules\\):" nil t)
718 (looking-at "Modules")))))
719
720(defun gds-set-module-breakpoint ()
721 (let ((sym (save-excursion
722 (beginning-of-line)
723 (and (looking-at " > \\([^ \n\t]+\\)")
724 (match-string 1))))
725 (module (save-excursion
726 (and (re-search-backward "^\\[[+---]\\] \\(([^)]+)\\)" nil t)
727 (match-string 1)))))
728 (or sym
729 (error "Couldn't find procedure name on current line"))
730 (or module
731 (error "Couldn't find module name for current line"))
732 (let ((behaviour
733 (completing-read
734 (format "Behaviour for breakpoint at %s:%s (default debug-here): "
735 module sym)
736 '(("debug-here")
737 ("trace-here")
738 ("trace-subtree"))
739 nil
740 t
741 nil
742 nil
743 "debug-here")))
744 (gds-send (format "(%S set-breakpoint %s %s %s)\n"
745 gds-displayed-client
746 module
747 sym
748 behaviour)))))