Fix autoconf underquoting warnings
[bpt/guile.git] / emacs / gds.el
CommitLineData
41a80feb 1;;; gds.el -- frontend for Guile development in Emacs
79b1c5b6
NJ
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)
41a80feb 26(require 'scheme)
79b1c5b6
NJ
27
28
79b1c5b6
NJ
29;;;; Customization group setup.
30
31(defgroup gds nil
41a80feb 32 "Customization options for Guile Emacs frontend."
79b1c5b6
NJ
33 :group 'scheme)
34
35
e707c78b 36;;;; Communication with the (emacs gds-server) subprocess.
79b1c5b6
NJ
37
38;; The subprocess object.
39(defvar gds-process nil)
40
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)
45
79b1c5b6 46(defun gds-start ()
41a80feb
NJ
47 "Start (or restart, if already running) the GDS subprocess."
48 (interactive)
79b1c5b6
NJ
49 (if gds-process (gds-shutdown))
50 (with-current-buffer (get-buffer-create "*GDS Process*")
51 (erase-buffer)
52 (setq gds-process
53 (let ((process-connection-type nil)) ; use a pipe
54 (start-process "gds"
55 (current-buffer)
56 "guile"
57 "-q"
58 "--debug"
e707c78b
NJ
59 "-c"
60 "(begin (use-modules (emacs gds-server)) (run-server))"))))
79b1c5b6
NJ
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))
65
66;; Shutdown the subprocess and cleanup all associated data.
67(defun gds-shutdown ()
41a80feb
NJ
68 "Shut down the GDS subprocess."
69 (interactive)
e707c78b
NJ
70 ;; Reset variables.
71 (setq gds-buffers nil
72 gds-focus-client nil
79b1c5b6 73 gds-waiting nil)
79b1c5b6
NJ
74 ;; Kill the subprocess.
75 (process-kill-without-query gds-process)
76 (condition-case nil
77 (progn
78 (kill-process gds-process)
79 (accept-process-output gds-process 0 200))
80 (error))
81 (setq gds-process nil))
82
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)
88 (save-excursion
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))
94 (error nil))))
95 (if form
96 (save-excursion
97 (gds-handle-input form)))
98 form)
99 (setq gds-read-cursor (point)))))
100
101;; Subprocess sentinel: do nothing. (Currently just here to avoid
102;; inserting un-`read'able process status messages into the process
103;; buffer.)
104(defun gds-sentinel (proc event)
105 )
106
107;; Send input to the subprocess.
ea73836c
NJ
108(defun gds-send (string client)
109 (process-send-string gds-process (format "(%S %s)\n" client string)))
79b1c5b6
NJ
110
111
112;;;; Multiple application scheduling.
113
e707c78b
NJ
114;; Here is how we schedule the display of multiple clients that are
115;; competing for user attention.
116;;
117;; - `gds-waiting' holds a list of clients that want attention but
d9d022a7
NJ
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.
e707c78b
NJ
121;;
122;; - `gds-focus-client' holds the client, if any, that currently has
d9d022a7
NJ
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'
129;; that client.
e707c78b 130(defvar gds-focus-client nil)
79b1c5b6
NJ
131(defvar gds-waiting nil)
132
e707c78b
NJ
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))
137 (gds-focus-client
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)))))
141 (t
142 ;; Give focus to CLIENT and display its buffer.
143 (setq gds-focus-client client)
144 (gds-display-buffers))))
145
146;; Explicitly give up focus.
147(defun gds-quit ()
79b1c5b6 148 (interactive)
e707c78b
NJ
149 (if (or (car gds-waiting)
150 (not (gds-client-blocked))
151 (y-or-n-p
152 "Client is blocked and no others are waiting. Still quit? "))
d9d022a7 153 (progn
e707c78b
NJ
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.
162 (if gds-focus-client
163 (gds-request-focus gds-focus-client)))))
164
165
d9d022a7
NJ
166;;;; GDS protocol dispatch.
167
168;; General dispatch function called by the subprocess filter.
169(defun gds-handle-input (form)
170 (let ((client (car form)))
171 (or (eq client '*)
172 (let* ((proc (cadr form))
173 (args (cddr form))
174 (buf (gds-client-buffer client proc args)))
175 (if buf (gds-handle-client-input buf client proc args))))))
176
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))
ea73836c 182 (insert (format "rx %S" (cons client (cons proc args))) "\n")))
d9d022a7
NJ
183 (cond (;; (name ...) - Client name.
184 (eq proc 'name)
185 (setq gds-pid (cadr args))
186 (gds-promote-view 'interaction)
187 (gds-request-focus client))
188
189 (;; (current-module ...) - Current module.
190 (eq proc 'current-module)
191 (setq gds-current-module (car args)))
192
193 (;; (stack ...) - Stack at an error or breakpoint.
194 (eq proc 'stack)
195 (setq gds-stack args)
196 (gds-promote-view 'stack))
197
198 (;; (modules ...) - Application's loaded modules.
199 (eq proc 'modules)
200 (while args
201 (or (assoc (car args) gds-modules)
202 (setq gds-modules (cons (list (car args)) gds-modules)))
203 (setq args (cdr args))))
204
205 (;; (output ...) - Last printed output.
206 (eq proc 'output)
207 (setq gds-output (car args))
208 (gds-add-view 'messages))
209
210 (;; (status ...) - Application status indication.
211 (eq proc 'status)
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)
217 (progn
218 (gds-promote-view 'stack)
219 (gds-update-buffers)
220 (gds-request-focus client))
221 (setq gds-stack nil)
222 (gds-delete-view 'stack)
223 (gds-update-buffers-in-a-while)))
224
225 (;; (module MODULE ...) - The specified module's bindings.
226 (eq proc 'module)
227 (let ((minfo (assoc (car args) gds-modules)))
228 (if minfo
229 (setcdr (cdr minfo) (cdr args)))))
230
231 (;; (closed) - Client has gone away.
232 (eq proc 'closed)
233 (setq gds-status 'closed)
234 (gds-update-buffers)
235 (setq gds-buffers
236 (delq (assq client gds-buffers) gds-buffers))
237 (if (eq client gds-focus-client)
238 (gds-quit)))
239
240 (;; (eval-results ...) - Results of evaluation.
241 (eq proc 'eval-results)
ea73836c 242 (gds-display-results client (car args) (cdr args)))
d9d022a7
NJ
243
244 ((eq proc 'completion-result)
245 (setq gds-completion-results (or (car args) t)))
246
7dd3f110
NJ
247 (;; (breakpoint-set FILE LINE COLUMN INFO) - Breakpoint set.
248 (eq proc 'breakpoint-set)
249 (let ((file (nth 0 args))
250 (line (nth 1 args))
251 (column (nth 2 args))
252 (info (nth 3 args)))
253 (with-current-buffer (find-file-noselect file)
254 (save-excursion
255 (goto-char (point-min))
256 (or (zerop line)
257 (forward-line line))
258 (move-to-column column)
259 (let ((os (overlays-at (point))) o)
260 (while os
261 (if (and (overlay-get (car os) 'gds-breakpoint-info)
262 (= (overlay-start (car os)) (point)))
263 (progn
264 (overlay-put (car os)
265 'gds-breakpoint-info
266 info)
267 (overlay-put (car os)
268 'before-string
269 gds-active-breakpoint-before-string)
270 (overlay-put (car os)
271 'after-string
272 gds-active-breakpoint-after-string)
273 (setq os nil))
274 (setq os (cdr os)))))))))
275
d9d022a7
NJ
276 )))
277
278
e707c78b 279;;;; Per-client buffer state.
79b1c5b6 280
d9d022a7
NJ
281;; This section contains code that is specific to each Guile client's
282;; buffer but independent of any particular `view'.
283
284;; Alist mapping each client port number to corresponding buffer.
285(defvar gds-buffers nil)
286
e707c78b
NJ
287(define-derived-mode gds-mode
288 scheme-mode
289 "Guile Interaction"
290 "Major mode for interacting with a Guile client application.")
79b1c5b6 291
e707c78b
NJ
292(defvar gds-client nil
293 "GDS client's port number.")
294(make-variable-buffer-local 'gds-client)
79b1c5b6 295
d9d022a7
NJ
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
300 from the frontend.
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)
79b1c5b6 304
d9d022a7
NJ
305(defvar gds-transcript nil
306 "Transcript buffer for this GDS client.")
307(make-variable-buffer-local 'gds-transcript)
79b1c5b6 308
d9d022a7
NJ
309;; Return client buffer for specified client and protocol input.
310(defun gds-client-buffer (client proc args)
311 (if (eq proc 'name)
312 ;; Introduction from client - create a new buffer.
313 (with-current-buffer (generate-new-buffer (car args))
314 (gds-mode)
315 (setq gds-client client)
316 (setq gds-transcript
317 (find-file-noselect
318 (expand-file-name (concat "~/.gds-transcript-" (car args)))))
319 (with-current-buffer gds-transcript
320 (goto-char (point-max))
321 (insert "\nTranscript:\n"))
322 (setq gds-buffers
323 (cons (cons client (current-buffer))
324 gds-buffers))
325 (current-buffer))
326 ;; Otherwise there should be an existing buffer that we can
327 ;; return.
328 (let ((existing (assq client gds-buffers)))
329 (if (buffer-live-p (cdr existing))
330 (cdr existing)
331 (setq gds-buffers (delq existing gds-buffers))
332 (gds-client-buffer client 'name '("(GDS buffer killed)"))))))
79b1c5b6 333
d9d022a7
NJ
334(defun gds-client-blocked ()
335 (eq gds-status 'waiting-for-input))
79b1c5b6 336
d9d022a7
NJ
337(defvar gds-delayed-update-timer nil)
338
339(defvar gds-delayed-update-buffers nil)
340
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))))
347
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)
355 (gds-insert-stack))
356 ((eq view 'interaction)
357 (gds-insert-interaction))
358 ((eq view 'browser)
359 (gds-insert-modules))
360 ((eq view 'messages)
361 (gds-insert-messages))
362 (t
363 (error "Bad GDS view %S" view)))
364 ;; Finish off.
365 (widget-setup)
366 (force-mode-line-update t)))
e707c78b 367
d9d022a7
NJ
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)
373 nil
374 (setq gds-delayed-update-timer
375 (run-at-time 0.5 nil (function gds-update-delayed-update-buffers)))))
e707c78b 376
d9d022a7
NJ
377(defun gds-display-buffers ()
378 (if gds-focus-client
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)))
382 (if window
383 (progn
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)))))))
e707c78b 397
e707c78b 398
d9d022a7 399;;;; Management of `views'.
e707c78b 400
d9d022a7
NJ
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.
405
406(defvar gds-views nil
407 "List of available views for a GDS client. Each element is one of
408the 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)
415
416(defun gds-promote-view (view)
417 (setq gds-views (cons view (delq view gds-views))))
418
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))
424
425(defun gds-add-view (view)
426 (or (memq view gds-views)
427 (setq gds-views (append gds-views (list view)))))
428
429(defun gds-delete-view (view)
430 (setq gds-views (delq view gds-views)))
431
432
433;;;; `Interaction' view.
434
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.
441
442(defvar gds-current-module "()"
443 "GDS client's current module.")
444(make-variable-buffer-local 'gds-current-module)
e707c78b
NJ
445
446(defvar gds-pid nil
447 "GDS client's process ID.")
448(make-variable-buffer-local 'gds-pid)
449
450(defvar gds-debug-exceptions nil
451 "Whether to debug exceptions.")
452(make-variable-buffer-local 'gds-debug-exceptions)
453
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)
457
d9d022a7
NJ
458(defun gds-insert-interaction ()
459 (erase-buffer)
460 ;; Insert stuff for interacting with a running (non-blocked) Guile
461 ;; client.
462 (widget-insert (buffer-name)
463 ", "
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"))))
469 ", in "
470 gds-current-module
471 "\n")
472 (widget-create 'push-button
473 :notify (function gds-sigint)
474 "SIGINT")
475 (widget-insert " ")
476 (widget-create 'push-button
477 :notify (function gds-async-break)
478 "Break")
479 (widget-insert "\n")
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)
486 gds-exception-keys)
487 (widget-insert "\n"))
e707c78b
NJ
488
489(defun gds-sigint (w &rest ignore)
490 (interactive)
491 (signal-process gds-pid 2))
492
493(defun gds-async-break (w &rest ignore)
494 (interactive)
ea73836c 495 (gds-send "async-break" gds-focus-client))
e707c78b
NJ
496
497(defun gds-toggle-debug-exceptions (w &rest ignore)
498 (interactive)
499 (setq gds-debug-exceptions (widget-value w))
500 (gds-eval-expression (concat "(use-modules (ice-9 debugger))"
501 "(debug-on-error '("
502 gds-exception-keys
503 "))")))
504
505(defun gds-set-exception-keys (w &rest ignore)
506 (interactive)
507 (setq gds-exception-keys (widget-value w)))
79b1c5b6 508
d9d022a7
NJ
509(defun gds-view-interaction ()
510 (interactive)
511 (gds-switch-to-view 'interaction))
512
513
514;;;; `Stack' view.
515
516;; This view shows the Guile call stack after the application has hit
517;; an error, or when it is stopped in the debugger.
518
519(defvar gds-stack nil
520 "GDS client's stack when last stopped.")
521(make-variable-buffer-local 'gds-stack)
522
523(defun gds-insert-stack ()
524 (erase-buffer)
525 (let ((frames (car gds-stack))
526 (index (cadr gds-stack))
527 (flags (caddr gds-stack))
79b1c5b6 528 frame items)
e707c78b
NJ
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))
536 "\n"))
537 (t
538 (widget-insert "Stack: " (prin1-to-string flags) "\n")))
79b1c5b6
NJ
539 (let ((i -1))
540 (gds-show-selected-frame (caddr (nth index frames)))
541 (while frames
542 (setq frame (car frames)
543 frames (cdr frames)
544 i (+ i 1)
545 items (cons (list 'item
546 (let ((s (cadr frame)))
547 (put-text-property 0 1 'index i s)
548 s))
549 items))))
550 (setq items (nreverse items))
551 (apply (function widget-create)
552 'radio-button-choice
553 :value (cadr (nth index items))
554 :notify (function gds-select-stack-frame)
555 items)
d9d022a7
NJ
556 (widget-insert "\n")
557 (goto-char (point-min))))
79b1c5b6
NJ
558
559(defun gds-select-stack-frame (widget &rest ignored)
560 (let* ((s (widget-value widget))
561 (ind (memq 'index (text-properties-at 0 s))))
ea73836c
NJ
562 (gds-send (format "debugger-command frame %d" (cadr ind))
563 gds-focus-client)))
79b1c5b6
NJ
564
565;; Overlay used to highlight the source expression corresponding to
566;; the selected frame.
d9d022a7 567(defvar gds-frame-source-overlay nil)
79b1c5b6
NJ
568
569(defun gds-show-selected-frame (source)
570 ;; Highlight the frame source, if possible.
571 (if (and source
572 (file-readable-p (car source)))
573 (with-current-buffer (find-file-noselect (car source))
d9d022a7 574 (if gds-frame-source-overlay
79b1c5b6 575 nil
d9d022a7
NJ
576 (setq gds-frame-source-overlay (make-overlay 0 0))
577 (overlay-put gds-frame-source-overlay 'face 'highlight))
79b1c5b6
NJ
578 ;; Move to source line. Note that Guile line numbering is
579 ;; 0-based, while Emacs numbering is 1-based.
580 (save-restriction
581 (widen)
582 (goto-line (+ (cadr source) 1))
583 (move-to-column (caddr source))
d9d022a7 584 (move-overlay gds-frame-source-overlay
79b1c5b6
NJ
585 (point)
586 (if (not (looking-at ")"))
587 (save-excursion (forward-sexp 1) (point))
588 ;; It seems that the source coordinates for
589 ;; backquoted expressions are at the end of
590 ;; the sexp rather than the beginning...
591 (save-excursion (forward-char 1)
592 (backward-sexp 1) (point)))
d9d022a7
NJ
593 (current-buffer))))
594 (if gds-frame-source-overlay
595 (move-overlay gds-frame-source-overlay 0 0))))
596
597(defun gds-view-stack ()
598 (interactive)
599 (gds-switch-to-view 'stack))
600
601
602;;;; `Breakpoints' view.
603
604;; This view shows a list of breakpoints.
605
606(defun gds-view-breakpoints ()
607 (interactive)
608 (gds-switch-to-view 'breakpoints))
609
610
611;;;; `Browser' view.
612
613;; This view shows a list of modules and module bindings.
79b1c5b6
NJ
614
615(defcustom gds-module-filter '(t (guile nil) (ice-9 nil) (oop nil))
616 "Specification of which Guile modules the debugger should display.
617This is a list with structure (DEFAULT EXCEPTION EXCEPTION...), where
618DEFAULT is `t' or `nil' and each EXCEPTION has the structure (SYMBOL
619DEFAULT EXCEPTION EXCEPTION...).
620
621A Guile module name `(x y z)' is matched against this filter as
622follows. If one of the top level EXCEPTIONs has SYMBOL `x', continue
623by matching the rest of the module name, in this case `(y z)', against
624that SYMBOL's DEFAULT and next level EXCEPTION list. Otherwise, if
625the current DEFAULT is `t' display the module, and if the current
626DEFAULT is `nil', don't display it.
627
628This variable is usually set to exclude Guile system modules that are
629not of primary interest when debugging application code."
630 :type 'sexp
631 :group 'gds)
632
633(defun gds-show-module-p (name)
634 ;; Determine whether to display the NAMEd module by matching NAME
635 ;; against `gds-module-filter'.
636 (let ((default (car gds-module-filter))
637 (exceptions (cdr gds-module-filter)))
638 (let ((exception (assq (car name) exceptions)))
639 (if exception
640 (let ((gds-module-filter (cdr exception)))
641 (gds-show-module-p (cdr name)))
642 default))))
643
d9d022a7
NJ
644(defvar gds-modules nil
645 "GDS client's module information.
646Alist mapping module names to their symbols and related information.
647This looks like:
648
649 (((guile) t sym1 sym2 ...)
650 ((guile-user))
651 ((ice-9 debug) nil sym3 sym4)
652 ...)
653
654The `t' or `nil' after the module name indicates whether the module is
655displayed in expanded form (that is, showing the bindings in that
656module). The syms are actually all strings because some Guile symbols
657are not readable by Emacs.")
658(make-variable-buffer-local 'gds-modules)
659
660(defun gds-insert-modules ()
661 (let ((p (if (eq (window-buffer (selected-window)) (current-buffer))
662 (point)
663 (point-min)))
664 (modules gds-modules))
665 (erase-buffer)
666 (insert "Modules:\n")
667 (while modules
668 (let ((minfo (car modules)))
669 (if (gds-show-module-p (car minfo))
670 (let ((w (widget-create 'push-button
671 :notify (function gds-module-notify)
672 (if (and (cdr minfo)
673 (cadr minfo))
674 "-" "+"))))
675 (widget-put w :module (cons gds-client (car minfo)))
676 (widget-insert " " (prin1-to-string (car minfo)) "\n")
677 (if (cadr minfo)
678 (let ((syms (cddr minfo)))
679 (while syms
680 (widget-insert " > " (car syms) "\n")
681 (setq syms (cdr syms))))))))
682 (setq modules (cdr modules)))
683 (insert "\n")
684 (goto-char p)))
79b1c5b6
NJ
685
686(defun gds-module-notify (w &rest ignore)
687 (let* ((module (widget-get w :module))
688 (client (car module))
689 (name (cdr module))
e707c78b 690 (minfo (assoc name gds-modules)))
79b1c5b6
NJ
691 (if (cdr minfo)
692 ;; Just toggle expansion state.
693 (progn
694 (setcar (cdr minfo) (not (cadr minfo)))
d9d022a7 695 (gds-update-buffers))
79b1c5b6
NJ
696 ;; Set flag to indicate module expanded.
697 (setcdr minfo (list t))
698 ;; Get symlist from Guile.
ea73836c 699 (gds-send (format "query-module %S" name) client))))
79b1c5b6 700
e707c78b
NJ
701(defun gds-query-modules ()
702 (interactive)
ea73836c 703 (gds-send "query-modules" gds-focus-client))
e707c78b 704
d9d022a7
NJ
705(defun gds-view-browser ()
706 (interactive)
707 (or gds-modules (gds-query-modules))
708 (gds-switch-to-view 'browser))
e707c78b 709
e707c78b 710
d9d022a7 711;;;; `Messages' view.
e707c78b 712
d9d022a7
NJ
713;; This view shows recent non-GDS-protocol messages output from the
714;; (ice-9 debugger) code.
e707c78b 715
d9d022a7
NJ
716(defvar gds-output nil
717 "GDS client's recent output (printed).")
718(make-variable-buffer-local 'gds-output)
e707c78b 719
d9d022a7
NJ
720(defun gds-insert-messages ()
721 (erase-buffer)
722 ;; Insert recent non-protocol output from (ice-9 debugger).
723 (insert gds-output)
724 (goto-char (point-min)))
e707c78b 725
d9d022a7
NJ
726(defun gds-view-messages ()
727 (interactive)
728 (gds-switch-to-view 'messages))
79b1c5b6 729
79b1c5b6 730
d9d022a7 731;;;; Debugger commands.
79b1c5b6 732
d9d022a7 733;; Typically but not necessarily used from the `stack' view.
79b1c5b6
NJ
734
735(defun gds-go ()
736 (interactive)
ea73836c 737 (gds-send "debugger-command continue" gds-focus-client))
79b1c5b6
NJ
738
739(defun gds-next ()
740 (interactive)
ea73836c 741 (gds-send "debugger-command next 1" gds-focus-client))
79b1c5b6
NJ
742
743(defun gds-evaluate (expr)
744 (interactive "sEvaluate (in this stack frame): ")
ea73836c
NJ
745 (gds-send (format "debugger-command evaluate %s" (prin1-to-string expr))
746 gds-focus-client))
79b1c5b6
NJ
747
748(defun gds-step-in ()
749 (interactive)
ea73836c 750 (gds-send "debugger-command step 1" gds-focus-client))
79b1c5b6
NJ
751
752(defun gds-step-out ()
753 (interactive)
ea73836c 754 (gds-send "debugger-command finish" gds-focus-client))
79b1c5b6
NJ
755
756(defun gds-trace-finish ()
757 (interactive)
ea73836c 758 (gds-send "debugger-command trace-finish" gds-focus-client))
e707c78b
NJ
759
760(defun gds-frame-info ()
761 (interactive)
ea73836c 762 (gds-send "debugger-command info-frame" gds-focus-client))
e707c78b
NJ
763
764(defun gds-frame-args ()
765 (interactive)
ea73836c 766 (gds-send "debugger-command info-args" gds-focus-client))
79b1c5b6 767
d9d022a7
NJ
768
769;;;; Setting breakpoints.
770
79b1c5b6
NJ
771(defun gds-set-breakpoint ()
772 (interactive)
773 (cond ((gds-in-source-buffer)
774 (gds-set-source-breakpoint))
775 ((gds-in-stack)
776 (gds-set-stack-breakpoint))
777 ((gds-in-modules)
778 (gds-set-module-breakpoint))
779 (t
780 (error "No way to set a breakpoint from here"))))
781
782(defun gds-in-source-buffer ()
783 ;; Not yet worked out what will be available in Scheme source
784 ;; buffers.
785 nil)
786
787(defun gds-in-stack ()
e707c78b
NJ
788 (save-excursion
789 (and (re-search-backward "^\\(Stack\\|Modules\\):" nil t)
790 (looking-at "Stack"))))
79b1c5b6
NJ
791
792(defun gds-in-modules ()
e707c78b
NJ
793 (save-excursion
794 (and (re-search-backward "^\\(Stack\\|Modules\\):" nil t)
795 (looking-at "Modules"))))
79b1c5b6
NJ
796
797(defun gds-set-module-breakpoint ()
798 (let ((sym (save-excursion
799 (beginning-of-line)
800 (and (looking-at " > \\([^ \n\t]+\\)")
801 (match-string 1))))
802 (module (save-excursion
803 (and (re-search-backward "^\\[[+---]\\] \\(([^)]+)\\)" nil t)
804 (match-string 1)))))
805 (or sym
806 (error "Couldn't find procedure name on current line"))
807 (or module
808 (error "Couldn't find module name for current line"))
809 (let ((behaviour
810 (completing-read
811 (format "Behaviour for breakpoint at %s:%s (default debug-here): "
812 module sym)
813 '(("debug-here")
814 ("trace-here")
815 ("trace-subtree"))
816 nil
817 t
818 nil
819 nil
820 "debug-here")))
ea73836c 821 (gds-send (format "set-breakpoint %s %s %s"
79b1c5b6
NJ
822 module
823 sym
ea73836c
NJ
824 behaviour)
825 gds-focus-client))))
02b0c692
NJ
826
827
7dd3f110
NJ
828;;;; Scheme source breakpoints.
829
830(defcustom gds-breakpoint-face 'default
831 "*Face used to highlight the location of a source breakpoint.
832Specifically, this face highlights the opening parenthesis of the
833form where the breakpoint is set."
834 :type 'face
835 :group 'gds)
836
837(defcustom gds-new-breakpoint-before-string ""
838 "*String used to show the presence of a new source breakpoint.
839`New' means that the breakpoint has been set but isn't yet known to
840Guile because the containing code hasn't been reevaluated yet.
841This string appears before the opening parenthesis of the form where
842the breakpoint is set. If you prefer a marker to appear after the
843opening parenthesis, make this string empty and use
844`gds-new-breakpoint-after-string'."
845 :type 'string
846 :group 'gds)
847
848(defcustom gds-new-breakpoint-after-string "=?= "
849 "*String used to show the presence of a new source breakpoint.
850`New' means that the breakpoint has been set but isn't yet known to
851Guile because the containing code hasn't been reevaluated yet.
852This string appears after the opening parenthesis of the form where
853the breakpoint is set. If you prefer a marker to appear before the
854opening parenthesis, make this string empty and use
855`gds-new-breakpoint-before-string'."
856 :type 'string
857 :group 'gds)
858
859(defcustom gds-active-breakpoint-before-string ""
860 "*String used to show the presence of a source breakpoint.
861`Active' means that the breakpoint is known to Guile.
862This string appears before the opening parenthesis of the form where
863the breakpoint is set. If you prefer a marker to appear after the
864opening parenthesis, make this string empty and use
865`gds-active-breakpoint-after-string'."
866 :type 'string
867 :group 'gds)
868
869(defcustom gds-active-breakpoint-after-string "=|= "
870 "*String used to show the presence of a source breakpoint.
871`Active' means that the breakpoint is known to Guile.
872This string appears after the opening parenthesis of the form where
873the breakpoint is set. If you prefer a marker to appear before the
874opening parenthesis, make this string empty and use
875`gds-active-breakpoint-before-string'."
876 :type 'string
877 :group 'gds)
878
879(defun gds-source-breakpoint-pos ()
880 "Return the position of the starting parenthesis of the innermost
881Scheme pair around point."
882 (if (eq (char-syntax (char-after)) ?\()
883 (point)
884 (save-excursion
885 (condition-case nil
886 (while t (forward-sexp -1))
887 (error))
888 (forward-char -1)
889 (while (not (eq (char-syntax (char-after)) ?\())
890 (forward-char -1))
891 (point))))
892
893(defun gds-source-breakpoint-overlay-at (pos)
894 "Return the source breakpoint overlay at POS, if any."
895 (let* (o (os (overlays-at pos)))
896 (while os
897 (if (and (overlay-get (car os) 'gds-breakpoint-info)
898 (= (overlay-start (car os)) pos))
899 (setq o (car os)
900 os nil))
901 (setq os (cdr os)))
902 o))
903
904(defun gds-set-source-breakpoint ()
905 (interactive)
906 (let* ((pos (gds-source-breakpoint-pos))
907 (o (gds-source-breakpoint-overlay-at pos)))
908 (if o
909 (error "There is already a breakpoint here!")
910 (setq o (make-overlay pos (+ pos 1)))
911 (overlay-put o 'evaporate t)
912 (overlay-put o 'face gds-breakpoint-face)
913 (overlay-put o 'gds-breakpoint-info 0)
914 (overlay-put o 'before-string gds-new-breakpoint-before-string)
915 (overlay-put o 'after-string gds-new-breakpoint-after-string))))
916
917(defun gds-delete-source-breakpoint ()
918 (interactive)
919 (let* ((pos (gds-source-breakpoint-pos))
920 (o (gds-source-breakpoint-overlay-at pos)))
921 (or o
922 (error "There is no breakpoint here to delete!"))
923 (delete-overlay o)))
924
925(defun gds-region-breakpoint-info (beg end)
926 "Return an alist of breakpoints in REGION.
927The car of each alist element is a cons (LINE . COLUMN) giving the
928source location of the breakpoint. The cdr is information describing
929breakpoint properties. Currently `information' is just the breakpoint
930index, for an existing Guile breakpoint, or 0 for a breakpoint that
931isn't yet known to Guile."
932 (interactive "r")
933 (let ((os (overlays-in beg end))
934 info o)
935 (while os
936 (setq o (car os)
937 os (cdr os))
938 (if (overlay-get o 'gds-breakpoint-info)
939 (progn
940 (setq info
941 (cons (cons (save-excursion
942 (goto-char (overlay-start o))
943 (cons (save-excursion
944 (beginning-of-line)
945 (count-lines (point-min) (point)))
946 (current-column)))
947 (overlay-get o 'gds-breakpoint-info))
948 info))
949 ;; Also now mark the breakpoint as `new'. It will become
950 ;; `active' (again) when we receive a notification from
951 ;; Guile that the breakpoint has been set.
952 (overlay-put o 'gds-breakpoint-info 0)
953 (overlay-put o 'before-string gds-new-breakpoint-before-string)
954 (overlay-put o 'after-string gds-new-breakpoint-after-string))))
955 (nreverse info)))
956
957
02b0c692
NJ
958;;;; Evaluating code.
959
41a80feb
NJ
960;; The following commands send code for evaluation through the GDS TCP
961;; connection, receive the result and any output generated through the
962;; same connection, and display the result and output to the user.
963;;
964;; Where there are multiple Guile applications known to GDS, GDS by
965;; default sends code to the one that holds the debugging focus,
e707c78b 966;; i.e. `gds-focus-client'. Where no application has the focus,
9f1af5d9 967;; or the command is invoked with `C-u', GDS asks the user which
41a80feb
NJ
968;; application is intended.
969
970(defun gds-read-client ()
e707c78b
NJ
971 (let* ((def (if gds-focus-client
972 (cdr (assq gds-focus-client gds-names))))
41a80feb
NJ
973 (prompt (if def
974 (concat "Application for eval (default "
975 def
976 "): ")
977 "Application for eval: "))
978 (name
979 (completing-read prompt
9f1af5d9
NJ
980 (mapcar (function list)
981 (mapcar (function cdr) gds-names))
41a80feb
NJ
982 nil t nil nil
983 def)))
984 (let (client (names gds-names))
985 (while (and names (not client))
9f1af5d9 986 (if (string-equal (cdar names) name)
41a80feb 987 (setq client (caar names)))
9f1af5d9
NJ
988 (setq names (cdr names)))
989 client)))
41a80feb
NJ
990
991(defun gds-choose-client (client)
992 (or ;; If client is an integer, it is the port number of the
993 ;; intended client.
994 (if (integerp client) client)
995 ;; Any other non-nil value indicates invocation with a prefix
996 ;; arg, which forces asking the user which application is
997 ;; intended.
998 (if client (gds-read-client))
999 ;; If ask not forced, and there is a client with the focus,
1000 ;; default to that one.
e707c78b 1001 gds-focus-client
9f1af5d9
NJ
1002 ;; If there are no clients at this point, and we are allowed to
1003 ;; autostart a captive Guile, do so.
e707c78b 1004 (and (null gds-buffers)
9f1af5d9
NJ
1005 gds-autostart-captive
1006 (progn
1007 (gds-start-captive t)
e707c78b 1008 (while (null gds-buffers)
9f1af5d9
NJ
1009 (accept-process-output (get-buffer-process gds-captive)
1010 0 100000))
e707c78b 1011 (caar gds-buffers)))
9f1af5d9 1012 ;; If there is only one known client, use that one.
e707c78b
NJ
1013 (if (and (car gds-buffers)
1014 (null (cdr gds-buffers)))
1015 (caar gds-buffers))
41a80feb
NJ
1016 ;; Last resort - ask the user.
1017 (gds-read-client)
1018 ;; Signal an error.
1019 (error "No application chosen.")))
1020
41a80feb
NJ
1021(defun gds-module-name (start end)
1022 "Determine and return the name of the module that governs the
1023specified region. The module name is returned as a list of symbols."
1024 (interactive "r") ; why not?
1025 (save-excursion
1026 (goto-char start)
1027 (let (module-name)
1028 (while (and (not module-name)
1029 (beginning-of-defun-raw 1))
1030 (if (looking-at "(define-module ")
1031 (setq module-name
1032 (progn
1033 (goto-char (match-end 0))
1034 (read (current-buffer))))))
1035 module-name)))
1036
1037(defun gds-port-name (start end)
1038 "Return port name for the specified region of the current buffer.
1039The name will be used by Guile as the port name when evaluating that
1040region's code."
1041 (or (buffer-file-name)
1042 (concat "Emacs buffer: " (buffer-name))))
1043
1044(defun gds-eval-region (start end &optional client)
1045 "Evaluate the current region."
1046 (interactive "r\nP")
1047 (setq client (gds-choose-client client))
1048 (let ((module (gds-module-name start end))
1049 (port-name (gds-port-name start end))
1050 line column)
1051 (save-excursion
1052 (goto-char start)
1053 (setq column (current-column)) ; 0-based
1054 (beginning-of-line)
1055 (setq line (count-lines (point-min) (point)))) ; 0-based
ea73836c 1056 (gds-send (format "eval region %s %S %d %d %s %S"
41a80feb
NJ
1057 (if module (prin1-to-string module) "#f")
1058 port-name line column
ea73836c
NJ
1059 (let ((bpinfo (gds-region-breakpoint-info start end)))
1060 ;; Make sure that "no bpinfo" is represented
1061 ;; as "()", not "nil", as Scheme doesn't
1062 ;; understand "nil".
1063 (if bpinfo (format "%S" bpinfo) "()"))
1064 (buffer-substring-no-properties start end))
1065 client)))
1066
1067(defun gds-eval-expression (expr &optional client correlator)
41a80feb
NJ
1068 "Evaluate the supplied EXPR (a string)."
1069 (interactive "sEvaluate expression: \nP")
1070 (setq client (gds-choose-client client))
ea73836c
NJ
1071 (gds-send (format "eval %S #f \"Emacs expression\" 0 0 () %S"
1072 (or correlator 'expression)
1073 expr)
1074 client))
41a80feb
NJ
1075
1076(defun gds-eval-defun (&optional client)
1077 "Evaluate the defun (top-level form) at point."
1078 (interactive "P")
1079 (save-excursion
1080 (end-of-defun)
1081 (let ((end (point)))
1082 (beginning-of-defun)
1083 (gds-eval-region (point) end client))))
1084
1085(defun gds-eval-last-sexp (&optional client)
1086 "Evaluate the sexp before point."
1087 (interactive "P")
1088 (gds-eval-region (save-excursion (backward-sexp) (point)) (point) client))
1089
9f1af5d9
NJ
1090
1091;;;; Help.
1092
ea73836c
NJ
1093;; Help is implemented as a special case of evaluation, identified by
1094;; the evaluation correlator 'help.
9f1af5d9
NJ
1095
1096(defun gds-help-symbol (sym &optional client)
1097 "Get help for SYM (a Scheme symbol)."
e707c78b
NJ
1098 (interactive
1099 (let ((sym (thing-at-point 'symbol))
1100 (enable-recursive-minibuffers t)
1101 val)
1102 (setq val (read-from-minibuffer
1103 (if sym
1104 (format "Describe Guile symbol (default %s): " sym)
1105 "Describe Guile symbol: ")))
1106 (list (if (zerop (length val)) sym val)
1107 current-prefix-arg)))
ea73836c 1108 (gds-eval-expression (format "(help %s)" sym) client 'help))
9f1af5d9 1109
9f1af5d9
NJ
1110(defun gds-apropos (regex &optional client)
1111 "List Guile symbols matching REGEX."
e707c78b
NJ
1112 (interactive
1113 (let ((sym (thing-at-point 'symbol))
1114 (enable-recursive-minibuffers t)
1115 val)
1116 (setq val (read-from-minibuffer
1117 (if sym
1118 (format "Guile apropos (regexp, default \"%s\"): " sym)
1119 "Guile apropos (regexp): ")))
1120 (list (if (zerop (length val)) sym val)
1121 current-prefix-arg)))
ea73836c 1122 (gds-eval-expression (format "(apropos %S)" regex) client 'help))
9f1af5d9 1123
e707c78b
NJ
1124(defvar gds-completion-results nil)
1125
1126(defun gds-complete-symbol (&optional client)
1127 "Complete the Guile symbol before point. Returns `t' if anything
1128interesting happened, `nil' if not."
1129 (interactive "P")
1130 (let* ((chars (- (point) (save-excursion
1131 (while (let ((syntax (char-syntax (char-before (point)))))
1132 (or (eq syntax ?w) (eq syntax ?_)))
1133 (forward-char -1))
1134 (point)))))
1135 (if (zerop chars)
1136 nil
1137 (setq client (gds-choose-client client))
1138 (setq gds-completion-results nil)
ea73836c 1139 (gds-send (format "complete %s"
e707c78b
NJ
1140 (prin1-to-string
1141 (buffer-substring-no-properties (- (point) chars)
ea73836c
NJ
1142 (point))))
1143 client)
e707c78b
NJ
1144 (while (null gds-completion-results)
1145 (accept-process-output gds-process 0 200))
1146 (cond ((eq gds-completion-results t)
1147 nil)
1148 ((stringp gds-completion-results)
1149 (if (<= (length gds-completion-results) chars)
1150 nil
1151 (insert (substring gds-completion-results chars))
1152 (message "Sole completion")
1153 t))
1154 ((= (length gds-completion-results) 1)
1155 (if (<= (length (car gds-completion-results)) chars)
1156 nil
1157 (insert (substring (car gds-completion-results) chars))
1158 t))
1159 (t
1160 (with-output-to-temp-buffer "*Completions*"
1161 (display-completion-list gds-completion-results))
1162 t)))))
1163
9f1af5d9
NJ
1164
1165;;;; Display of evaluation and help results.
1166
ea73836c
NJ
1167(defun gds-display-results (client correlator results)
1168 (let ((helpp (eq correlator 'help)))
9f1af5d9
NJ
1169 (let ((buf (get-buffer-create (if helpp
1170 "*Guile Help*"
1171 "*Guile Results*"))))
1172 (save-excursion
1173 (set-buffer buf)
1174 (erase-buffer)
e707c78b 1175 (scheme-mode)
9f1af5d9
NJ
1176 (while results
1177 (insert (car results))
1178 (if helpp
1179 nil
1180 (mapcar (function (lambda (value)
1181 (insert " => " value "\n")))
1182 (cadr results))
1183 (insert "\n"))
1184 (setq results (cddr results)))
1185 (goto-char (point-min))
1186 (if (and helpp (looking-at "Evaluating in "))
1187 (delete-region (point) (progn (forward-line 1) (point)))))
1188 (pop-to-buffer buf)
1189 (run-hooks 'temp-buffer-show-hook)
1190 (other-window 1))))
1191
1192
1193;;;; Loading (evaluating) a whole Scheme file.
1194
41a80feb
NJ
1195(defcustom gds-source-modes '(scheme-mode)
1196 "*Used to determine if a buffer contains Scheme source code.
1197If it's loaded into a buffer that is in one of these major modes, it's
1198considered a scheme source file by `gds-load-file'."
1199 :type '(repeat function)
1200 :group 'gds)
1201
1202(defvar gds-prev-load-dir/file nil
1203 "Holds the last (directory . file) pair passed to `gds-load-file'.
1204Used for determining the default for the next `gds-load-file'.")
1205
1206(defun gds-load-file (file-name &optional client)
1207 "Load a Scheme file into the inferior Scheme process."
1208 (interactive (list (car (comint-get-source "Load Scheme file: "
1209 gds-prev-load-dir/file
1210 gds-source-modes t))
1211 ; T because LOAD needs an
1212 ; exact name
1213 current-prefix-arg))
1214 (comint-check-source file-name) ; Check to see if buffer needs saved.
1215 (setq gds-prev-load-dir/file (cons (file-name-directory file-name)
1216 (file-name-nondirectory file-name)))
1217 (setq client (gds-choose-client client))
ea73836c 1218 (gds-send (format "load %S" file-name) client))
41a80feb 1219
d9d022a7
NJ
1220
1221;;;; Scheme mode keymap items.
1222
41a80feb
NJ
1223(define-key scheme-mode-map "\M-\C-x" 'gds-eval-defun);gnu convention
1224(define-key scheme-mode-map "\C-x\C-e" 'gds-eval-last-sexp);gnu convention
e707c78b 1225(define-key scheme-mode-map "\C-c\C-e" 'gds-eval-expression)
41a80feb
NJ
1226(define-key scheme-mode-map "\C-c\C-r" 'gds-eval-region)
1227(define-key scheme-mode-map "\C-c\C-l" 'gds-load-file)
e707c78b
NJ
1228(define-key scheme-mode-map "\C-hg" 'gds-help-symbol)
1229(define-key scheme-mode-map "\C-h\C-g" 'gds-apropos)
1230(define-key scheme-mode-map "\e\t" 'gds-complete-symbol)
7dd3f110
NJ
1231(define-key scheme-mode-map "\C-x " 'gds-set-source-breakpoint)
1232(define-key scheme-mode-map "\C-x\e " 'gds-delete-source-breakpoint)
41a80feb
NJ
1233
1234
d9d022a7
NJ
1235;;;; GDS (Guile Interaction) mode keymap and menu items.
1236
1237(set-keymap-parent gds-mode-map widget-keymap)
1238
1239(define-key gds-mode-map "M" (function gds-query-modules))
1240
1241(define-key gds-mode-map "g" (function gds-go))
1242(define-key gds-mode-map "q" (function gds-quit))
1243(define-key gds-mode-map " " (function gds-next))
1244(define-key gds-mode-map "e" (function gds-evaluate))
1245(define-key gds-mode-map "i" (function gds-step-in))
1246(define-key gds-mode-map "o" (function gds-step-out))
1247(define-key gds-mode-map "t" (function gds-trace-finish))
1248(define-key gds-mode-map "I" (function gds-frame-info))
1249(define-key gds-mode-map "A" (function gds-frame-args))
1250
1251(define-key gds-mode-map "b" (function gds-set-breakpoint))
1252
1253(define-key gds-mode-map "vi" (function gds-view-interaction))
1254(define-key gds-mode-map "vs" (function gds-view-stack))
1255(define-key gds-mode-map "vb" (function gds-view-breakpoints))
1256(define-key gds-mode-map "vB" (function gds-view-browser))
1257(define-key gds-mode-map "vm" (function gds-view-messages))
1258
1259(defvar gds-view-menu nil
1260 "GDS view menu.")
1261(if gds-view-menu
1262 nil
1263 (setq gds-view-menu (make-sparse-keymap "View"))
1264 (define-key gds-view-menu [messages]
1265 '(menu-item "Messages" gds-view-messages
1266 :enable (memq 'messages gds-views)))
1267 (define-key gds-view-menu [browser]
1268 '(menu-item "Browser" gds-view-browser
1269 :enable (memq 'browser gds-views)))
1270 (define-key gds-view-menu [breakpoints]
1271 '(menu-item "Breakpoints" gds-view-breakpoints
1272 :enable (memq 'breakpoints gds-views)))
1273 (define-key gds-view-menu [stack]
1274 '(menu-item "Stack" gds-view-stack
1275 :enable (memq 'stack gds-views)))
1276 (define-key gds-view-menu [interaction]
1277 '(menu-item "Interaction" gds-view-interaction
1278 :enable (memq 'interaction gds-views))))
41a80feb
NJ
1279
1280(defvar gds-debug-menu nil
1281 "GDS debugging menu.")
1282(if gds-debug-menu
1283 nil
1284 (setq gds-debug-menu (make-sparse-keymap "Debug"))
1285 (define-key gds-debug-menu [go]
1286 '(menu-item "Go" gds-go))
1287 (define-key gds-debug-menu [trace-finish]
1288 '(menu-item "Trace This Frame" gds-trace-finish))
1289 (define-key gds-debug-menu [step-out]
1290 '(menu-item "Finish This Frame" gds-step-out))
1291 (define-key gds-debug-menu [next]
1292 '(menu-item "Next" gds-next))
1293 (define-key gds-debug-menu [step-in]
1294 '(menu-item "Single Step" gds-step-in))
1295 (define-key gds-debug-menu [eval]
1296 '(menu-item "Eval In This Frame..." gds-evaluate)))
1297
7dd3f110
NJ
1298(defvar gds-breakpoint-menu nil
1299 "GDS breakpoint menu.")
1300(if gds-breakpoint-menu
1301 nil
1302 (setq gds-breakpoint-menu (make-sparse-keymap "Breakpoint"))
1303 (define-key gds-breakpoint-menu [last-sexp]
1304 '(menu-item "Delete Breakpoint" gds-delete-source-breakpoint))
1305 (define-key gds-breakpoint-menu [set]
1306 '(menu-item "Set Breakpoint" gds-set-source-breakpoint)))
1307
41a80feb
NJ
1308(defvar gds-eval-menu nil
1309 "GDS evaluation menu.")
1310(if gds-eval-menu
1311 nil
1312 (setq gds-eval-menu (make-sparse-keymap "Evaluate"))
1313 (define-key gds-eval-menu [load-file]
1314 '(menu-item "Load Scheme File" gds-load-file))
1315 (define-key gds-eval-menu [defun]
1316 '(menu-item "Defun At Point" gds-eval-defun))
1317 (define-key gds-eval-menu [region]
1318 '(menu-item "Region" gds-eval-region))
1319 (define-key gds-eval-menu [last-sexp]
1320 '(menu-item "Sexp Before Point" gds-eval-last-sexp))
1321 (define-key gds-eval-menu [expr]
1322 '(menu-item "Expression..." gds-eval-expression)))
1323
1324(defvar gds-help-menu nil
1325 "GDS help menu.")
1326(if gds-help-menu
1327 nil
1328 (setq gds-help-menu (make-sparse-keymap "Help"))
1329 (define-key gds-help-menu [apropos]
1330 '(menu-item "Apropos..." gds-apropos))
41a80feb
NJ
1331 (define-key gds-help-menu [sym]
1332 '(menu-item "Symbol..." gds-help-symbol)))
1333
1334(defvar gds-advanced-menu nil
1335 "Menu of rarely needed GDS operations.")
1336(if gds-advanced-menu
1337 nil
1338 (setq gds-advanced-menu (make-sparse-keymap "Advanced"))
9f1af5d9
NJ
1339 (define-key gds-advanced-menu [run-captive]
1340 '(menu-item "Run Captive Guile" gds-start-captive
1341 :enable (not (comint-check-proc gds-captive))))
41a80feb
NJ
1342 (define-key gds-advanced-menu [restart-gds]
1343 '(menu-item "Restart IDE" gds-start :enable gds-process))
1344 (define-key gds-advanced-menu [kill-gds]
1345 '(menu-item "Shutdown IDE" gds-shutdown :enable gds-process))
1346 (define-key gds-advanced-menu [start-gds]
1347 '(menu-item "Start IDE" gds-start :enable (not gds-process))))
1348
1349(defvar gds-menu nil
1350 "Global menu for GDS commands.")
1351(if gds-menu
1352 nil
1353 (setq gds-menu (make-sparse-keymap "Guile"))
1354 (define-key gds-menu [advanced]
1355 (cons "Advanced" gds-advanced-menu))
1356 (define-key gds-menu [separator-1]
1357 '("--"))
d9d022a7
NJ
1358 (define-key gds-menu [view]
1359 `(menu-item "View" ,gds-view-menu :enable gds-views))
41a80feb 1360 (define-key gds-menu [debug]
e707c78b
NJ
1361 `(menu-item "Debug" ,gds-debug-menu :enable (and gds-focus-client
1362 (gds-client-blocked))))
7dd3f110
NJ
1363 (define-key gds-menu [breakpoint]
1364 `(menu-item "Breakpoints" ,gds-breakpoint-menu :enable t))
9f1af5d9 1365 (define-key gds-menu [eval]
e707c78b 1366 `(menu-item "Evaluate" ,gds-eval-menu :enable (or gds-buffers
9f1af5d9
NJ
1367 gds-autostart-captive)))
1368 (define-key gds-menu [help]
e707c78b 1369 `(menu-item "Help" ,gds-help-menu :enable (or gds-buffers
9f1af5d9 1370 gds-autostart-captive)))
41a80feb
NJ
1371 (setq menu-bar-final-items
1372 (cons 'guile menu-bar-final-items))
e707c78b 1373 (define-key scheme-mode-map [menu-bar guile]
41a80feb
NJ
1374 (cons "Guile" gds-menu)))
1375
9f1af5d9 1376
41a80feb
NJ
1377;;;; Autostarting the GDS server.
1378
1379(defcustom gds-autostart-server t
1380 "Whether to automatically start the GDS server when `gds.el' is loaded."
1381 :type 'boolean
1382 :group 'gds)
1383
1384(if (and gds-autostart-server
1385 (not gds-process))
1386 (gds-start))
1387
9f1af5d9
NJ
1388
1389;;;; `Captive' Guile - a Guile process that is started when needed to
1390;;;; provide help, completion, evaluations etc.
1391
1392(defcustom gds-autostart-captive t
1393 "Whether to automatically start a `captive' Guile process when needed."
1394 :type 'boolean
1395 :group 'gds)
1396
1397(defvar gds-captive nil
1398 "Buffer of captive Guile.")
1399
1400(defun gds-start-captive (&optional restart)
1401 (interactive)
1402 (if (and restart
1403 (comint-check-proc gds-captive))
1404 (gds-kill-captive))
1405 (if (comint-check-proc gds-captive)
1406 nil
1407 (let ((process-connection-type nil))
1408 (setq gds-captive (make-comint "captive-guile"
1409 "guile"
1410 nil
1411 "-q")))
1412 (let ((proc (get-buffer-process gds-captive)))
1413 (comint-send-string proc "(set! %load-path (cons \"/home/neil/Guile/cvs/guile-core\" %load-path))\n")
1414 (comint-send-string proc "(debug-enable 'backtrace)\n")
e707c78b
NJ
1415 (comint-send-string proc "(use-modules (emacs gds-client))\n")
1416 (comint-send-string proc "(gds-connect \"Captive Guile\" #f)\n"))))
9f1af5d9
NJ
1417
1418(defun gds-kill-captive ()
1419 (if gds-captive
1420 (let ((proc (get-buffer-process gds-captive)))
1421 (process-kill-without-query proc)
1422 (condition-case nil
1423 (progn
e707c78b 1424 (kill-process proc)
9f1af5d9
NJ
1425 (accept-process-output gds-process 0 200))
1426 (error)))))
1427
1428
1429;;;; The end!
1430
41a80feb
NJ
1431(provide 'gds)
1432
1433;;; gds.el ends here.