Document removal of guileint.
[bpt/guile.git] / emacs / gds.el
... / ...
CommitLineData
1;;; gds.el -- frontend for Guile development in Emacs
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(require 'scheme)
27(require 'cl)
28(require 'comint)
29(require 'info)
30
31
32;;;; Customization group setup.
33
34(defgroup gds nil
35 "Customization options for Guile Emacs frontend."
36 :group 'scheme)
37
38
39;;;; Communication with the (emacs gds-server) subprocess.
40
41;; The subprocess object.
42(defvar gds-process nil)
43
44;; Subprocess output goes into the `*GDS Process*' buffer, and
45;; is then read from there one form at a time. `gds-read-cursor' is
46;; the buffer position of the start of the next unread form.
47(defvar gds-read-cursor nil)
48
49;; The guile executable used by the GDS server and captive client
50;; processes.
51(defcustom gds-guile-program "guile"
52 "*The guile executable used by GDS, specifically by its server and
53captive client processes."
54 :type 'string
55 :group 'gds)
56
57(defun gds-start ()
58 "Start (or restart, if already running) the GDS subprocess."
59 (interactive)
60 (gds-kill-captive)
61 (if gds-process (gds-shutdown))
62 (with-current-buffer (get-buffer-create "*GDS Process*")
63 (erase-buffer)
64 (setq gds-process
65 (let ((process-connection-type nil)) ; use a pipe
66 (start-process "gds"
67 (current-buffer)
68 gds-guile-program
69 "-q"
70 "--debug"
71 "-c"
72 "(begin (use-modules (emacs gds-server)) (run-server))"))))
73 (setq gds-read-cursor (point-min))
74 (set-process-filter gds-process (function gds-filter))
75 (set-process-sentinel gds-process (function gds-sentinel))
76 (set-process-coding-system gds-process 'latin-1-unix)
77 (process-kill-without-query gds-process))
78
79;; Shutdown the subprocess and cleanup all associated data.
80(defun gds-shutdown ()
81 "Shut down the GDS subprocess."
82 (interactive)
83 ;; Reset variables.
84 (setq gds-buffers nil)
85 ;; Kill the subprocess.
86 (condition-case nil
87 (progn
88 (kill-process gds-process)
89 (accept-process-output gds-process 0 200))
90 (error))
91 (setq gds-process nil))
92
93;; Subprocess output filter: inserts normally into the process buffer,
94;; then tries to reread the output one form at a time and delegates
95;; processing of each form to `gds-handle-input'.
96(defun gds-filter (proc string)
97 (with-current-buffer (process-buffer proc)
98 (save-excursion
99 (goto-char (process-mark proc))
100 (insert-before-markers string))
101 (goto-char gds-read-cursor)
102 (while (let ((form (condition-case nil
103 (read (current-buffer))
104 (error nil))))
105 (if form
106 (save-excursion
107 (gds-handle-input form)))
108 form)
109 (setq gds-read-cursor (point)))))
110
111;; Subprocess sentinel: do nothing. (Currently just here to avoid
112;; inserting un-`read'able process status messages into the process
113;; buffer.)
114(defun gds-sentinel (proc event)
115 )
116
117;; Send input to the subprocess.
118(defun gds-send (string client)
119 (process-send-string gds-process (format "(%S %s)\n" client string))
120 (let ((buf (gds-client-ref 'gds-transcript)))
121 (if buf
122 (with-current-buffer buf
123 (goto-char (point-max))
124 (let ((inhibit-read-only t))
125 (insert (format "tx (%S %s)\n" client string)))))))
126
127
128;;;; Focussing in and out on interaction with a particular client.
129
130;;;; The slight possible problems here are that popping up a client's
131;;;; interaction windows when that client wants attention might
132;;;; interrupt something else that the Emacs user was working on at
133;;;; the time, and that if multiple clients are being debugged at the
134;;;; same time, their popping up of interaction windows might become
135;;;; confusing. For this reason, we allow GDS's behavior to be
136;;;; customized via the variables `gds-focus-in-function' and
137;;;; `gds-focus-out-function'.
138;;;;
139;;;; That said, the default policy, which is probably OK for most
140;;;; users most of the time, is very simple: when a client wants
141;;;; attention, its interaction windows are popped up immediately.
142
143(defun gds-request-focus (client)
144 (funcall gds-focus-in-function client))
145
146(defcustom gds-focus-in-function (function gds-focus-in)
147 "Function to call when a GDS client program wants user attention.
148The function is called with one argument, the CLIENT in question."
149 :type 'function
150 :group 'gds)
151
152(defun gds-focus-in (client)
153 (gds-display-buffers client))
154
155(defun gds-quit ()
156 (interactive)
157 (funcall gds-focus-out-function))
158
159(defcustom gds-focus-out-function (function gds-focus-out)
160 "Function to call when user quits interacting with a GDS client."
161 :type 'function
162 :group 'gds)
163
164(defun gds-focus-out ()
165 (if (if (gds-client-blocked)
166 (y-or-n-p "Client is waiting for input. Quit anyway? ")
167 t)
168 (bury-buffer (current-buffer))))
169
170
171;;;; Multiple client focus -- an alternative implementation.
172
173;;;; The following code is provided as an alternative example of how a
174;;;; customized GDS could schedule the display of multiple clients
175;;;; that are competing for user attention.
176
177;; - `gds-waiting' holds a list of clients that want attention but
178;; haven't yet got it. A client is added to this list for two
179;; reasons. (1) When it is blocked waiting for user input.
180;; (2) When it first connects to GDS, even if not blocked.
181;;
182;; - `gds-focus-client' holds the client, if any, that currently has
183;; the user's attention. A client can be given the focus if
184;; `gds-focus-client' is nil at the time that the client wants
185;; attention, or if another client relinquishes it. A client can
186;; relinquish the focus in two ways. (1) If the client application
187;; says that it is no longer blocked, and a small time passes without
188;; it becoming blocked again. (2) If the user explicitly `quits'
189;; that client.
190;;
191;; (defvar gds-focus-client nil)
192;; (defvar gds-waiting nil)
193;;
194;; (defun gds-focus-in-alternative (client)
195;; (cond ((eq client gds-focus-client)
196;; ;; CLIENT already has the focus. Display its buffer.
197;; (gds-display-buffers client))
198;; (gds-focus-client
199;; ;; Another client has the focus. Add CLIENT to `gds-waiting'.
200;; (or (memq client gds-waiting)
201;; (setq gds-waiting (append gds-waiting (list client)))))
202;; (t
203;; ;; Give focus to CLIENT and display its buffer.
204;; (setq gds-focus-client client)
205;; (gds-display-buffers client))))
206;;
207;; (defun gds-focus-out-alternative ()
208;; (if (or (car gds-waiting)
209;; (not (gds-client-blocked))
210;; (y-or-n-p
211;; "Client is blocked and no others are waiting. Still quit? "))
212;; (progn
213;; (bury-buffer (current-buffer))
214;; ;; Pass on the focus.
215;; (setq gds-focus-client (car gds-waiting)
216;; gds-waiting (cdr gds-waiting))
217;; ;; If this client is blocked, add it back into the waiting list.
218;; (if (gds-client-blocked)
219;; (gds-request-focus gds-client))
220;; ;; If there is a new focus client, request display for it.
221;; (if gds-focus-client
222;; (gds-request-focus gds-focus-client)))))
223
224
225;;;; GDS protocol dispatch.
226
227;; General dispatch function called by the subprocess filter.
228(defun gds-handle-input (form)
229 (let ((client (car form)))
230 (or (eq client '*)
231 (let* ((proc (cadr form))
232 (args (cddr form))
233 (buf (gds-client-buffer client proc args)))
234 (if buf (gds-handle-client-input buf client proc args))))))
235
236(defun gds-handle-client-input (buf client proc args)
237 (with-current-buffer buf
238 (with-current-buffer gds-transcript
239 (goto-char (point-max))
240 (let ((inhibit-read-only t))
241 (insert (format "rx %S" (cons client (cons proc args))) "\n")))
242
243 (cond (;; (name ...) - Client name.
244 (eq proc 'name)
245 (setq gds-pid (cadr args))
246 (gds-promote-view 'interaction)
247 (gds-request-focus client))
248
249 (;; (current-module ...) - Current module.
250 (eq proc 'current-module)
251 (setq gds-current-module (car args)))
252
253 (;; (stack ...) - Stack at an error or breakpoint.
254 (eq proc 'stack)
255 (setq gds-stack args)
256 (gds-promote-view 'stack))
257
258 (;; (modules ...) - Application's loaded modules.
259 (eq proc 'modules)
260 (while args
261 (or (assoc (car args) gds-modules)
262 (setq gds-modules (cons (list (car args)) gds-modules)))
263 (setq args (cdr args))))
264
265 (;; (output ...) - Last printed output.
266 (eq proc 'output)
267 (setq gds-output (car args))
268 (gds-add-view 'messages))
269
270 (;; (status ...) - Application status indication.
271 (eq proc 'status)
272 (setq gds-status (car args))
273 (if (eq gds-status 'running)
274 (gds-delete-view 'browser)
275 (gds-add-view 'browser))
276 (if (eq gds-status 'waiting-for-input)
277 (progn
278 (gds-promote-view 'stack)
279 (gds-update-buffers)
280 (gds-request-focus client))
281 (setq gds-stack nil)
282 (gds-delete-view 'stack)
283 (gds-update-buffers-in-a-while)))
284
285 (;; (module MODULE ...) - The specified module's bindings.
286 (eq proc 'module)
287 (let ((minfo (assoc (car args) gds-modules)))
288 (if minfo
289 (setcdr (cdr minfo) (cdr args)))))
290
291 (;; (closed) - Client has gone away.
292 (eq proc 'closed)
293 (setq gds-status 'closed)
294 (gds-update-buffers)
295 (setq gds-buffers
296 (delq (assq client gds-buffers) gds-buffers)))
297
298 (;; (eval-results ...) - Results of evaluation.
299 (eq proc 'eval-results)
300 (gds-display-results client (car args) (cdr args)))
301
302 (;; (completion-result ...) - Available completions.
303 (eq proc 'completion-result)
304 (setq gds-completion-results (or (car args) t)))
305
306 (;; (breakpoint-set FILE LINE COLUMN INFO) - Breakpoint set.
307 (eq proc 'breakpoint-set)
308 (let ((file (nth 0 args))
309 (line (nth 1 args))
310 (column (nth 2 args))
311 (info (nth 3 args)))
312 (with-current-buffer (find-file-noselect file)
313 (save-excursion
314 (goto-char (point-min))
315 (or (zerop line)
316 (forward-line line))
317 (move-to-column column)
318 (let ((os (overlays-at (point))) o)
319 (while os
320 (if (and (overlay-get (car os) 'gds-breakpoint-info)
321 (= (overlay-start (car os)) (point)))
322 (progn
323 (overlay-put (car os)
324 'gds-breakpoint-info
325 info)
326 (overlay-put (car os)
327 'before-string
328 gds-active-breakpoint-before-string)
329 (overlay-put (car os)
330 'after-string
331 gds-active-breakpoint-after-string)
332 (setq os nil))
333 (setq os (cdr os)))))))))
334
335 (;; (thread-status THREAD-TYPE THREAD-NUMBER STATUS [CORRELATOR])
336 (eq proc 'thread-status)
337 (if (eq (car args) 'eval)
338 (let ((number (nth 1 args))
339 (status (nth 2 args))
340 (correlator (nth 3 args)))
341 (if (eq status 'busy)
342 (progn
343 (setq gds-evals-in-progress
344 (append gds-evals-in-progress
345 (list (cons number correlator))))
346 (run-at-time 0.5 nil
347 (function gds-display-slow-eval)
348 buf number correlator)
349 (gds-promote-view 'interaction))
350 (let ((existing (assq number gds-evals-in-progress)))
351 (if existing
352 (setq gds-evals-in-progress
353 (delq existing gds-evals-in-progress)))))
354 (gds-update-buffers))))
355
356 )))
357
358(defun gds-display-slow-eval (buf number correlator)
359 (with-current-buffer buf
360 (let ((entry (assq number gds-evals-in-progress)))
361 (if (and entry
362 (eq (cdr entry) correlator))
363 (progn
364 (gds-promote-view 'interaction)
365 (gds-request-focus gds-client))))))
366
367
368;;;; Per-client buffer state.
369
370;; This section contains code that is specific to each Guile client's
371;; buffer but independent of any particular `view'.
372
373;; Alist mapping each client port number to corresponding buffer.
374(defvar gds-buffers nil)
375
376(define-derived-mode gds-mode
377 scheme-mode
378 "Guile Interaction"
379 "Major mode for interacting with a Guile client application."
380 (widget-minor-mode 1))
381
382(defvar gds-client nil
383 "GDS client's port number.")
384(make-variable-buffer-local 'gds-client)
385
386(defvar gds-status nil
387 "GDS client's latest status, one of the following symbols.
388`running' - Application is running.
389`waiting-for-input' - Application is blocked waiting for instruction
390 from the frontend.
391`ready-for-input' - Application is not blocked but can also accept
392 asynchronous instructions from the frontend.")
393(make-variable-buffer-local 'gds-status)
394
395(defvar gds-transcript nil
396 "Transcript buffer for this GDS client.")
397(make-variable-buffer-local 'gds-transcript)
398
399;; Return client buffer for specified client and protocol input.
400(defun gds-client-buffer (client proc args)
401 (if (eq proc 'name)
402 ;; Introduction from client - create a new buffer.
403 (with-current-buffer (generate-new-buffer (car args))
404 (gds-mode)
405 (setq gds-client client)
406 (setq gds-transcript
407 (find-file-noselect
408 (expand-file-name (concat "~/.gds-transcript-" (car args)))))
409 (with-current-buffer gds-transcript
410 (goto-char (point-max))
411 (insert "\nTranscript:\n"))
412 (setq gds-buffers
413 (cons (cons client (current-buffer))
414 gds-buffers))
415 (current-buffer))
416 ;; Otherwise there should be an existing buffer that we can
417 ;; return.
418 (let ((existing (assq client gds-buffers)))
419 (if (buffer-live-p (cdr existing))
420 (cdr existing)
421 (setq gds-buffers (delq existing gds-buffers))
422 (gds-client-buffer client 'name '("(GDS buffer killed)"))))))
423
424;; Get the current buffer's associated client's value of SYM.
425(defun gds-client-ref (sym &optional client)
426 (and (or client gds-client)
427 (let ((buf (assq (or client gds-client) gds-buffers)))
428 (and buf
429 (cdr buf)
430 (buffer-live-p (cdr buf))
431 (with-current-buffer (cdr buf)
432 (symbol-value sym))))))
433
434(defun gds-client-blocked ()
435 (eq (gds-client-ref 'gds-status) 'waiting-for-input))
436
437(defvar gds-delayed-update-timer nil)
438
439(defvar gds-delayed-update-buffers nil)
440
441(defun gds-update-delayed-update-buffers ()
442 (while gds-delayed-update-buffers
443 (with-current-buffer (car gds-delayed-update-buffers)
444 (setq gds-delayed-update-buffers
445 (cdr gds-delayed-update-buffers))
446 (gds-update-buffers))))
447
448(defun gds-update-buffers ()
449 (if (timerp gds-delayed-update-timer)
450 (cancel-timer gds-delayed-update-timer))
451 (setq gds-delayed-update-timer nil)
452 (let ((view (car gds-views))
453 (inhibit-read-only t))
454 (cond ((eq view 'stack)
455 (gds-insert-stack))
456 ((eq view 'interaction)
457 (gds-insert-interaction))
458 ((eq view 'browser)
459 (gds-insert-modules))
460 ((eq view 'messages)
461 (gds-insert-messages))
462 (t
463 (error "Bad GDS view %S" view)))
464 ;; Finish off.
465 (force-mode-line-update t)))
466
467(defun gds-update-buffers-in-a-while ()
468 (or (memq (current-buffer) gds-delayed-update-buffers)
469 (setq gds-delayed-update-buffers
470 (cons (current-buffer) gds-delayed-update-buffers)))
471 (if (timerp gds-delayed-update-timer)
472 nil
473 (setq gds-delayed-update-timer
474 (run-at-time 0.5 nil (function gds-update-delayed-update-buffers)))))
475
476(defun gds-display-buffers (client)
477 (let ((buf (cdr (assq client gds-buffers))))
478 ;; If there's already a window showing the buffer, use it.
479 (let ((window (get-buffer-window buf t)))
480 (if window
481 (make-frame-visible (window-frame window))
482 (display-buffer buf)))
483 ;; If there is an associated source buffer, display it as well.
484 (if (and (eq (car gds-views) 'stack)
485 gds-frame-source-overlay
486 (> (overlay-end gds-frame-source-overlay) 1))
487 (let ((window (display-buffer
488 (overlay-buffer gds-frame-source-overlay))))
489 (set-window-point window
490 (overlay-start gds-frame-source-overlay))))))
491
492
493;;;; Management of `views'.
494
495;; The idea here is to keep the buffer describing a Guile client
496;; relatively uncluttered by only showing one kind of information
497;; about that client at a time. Menu items and key sequences are
498;; provided to switch easily between the available views.
499
500(defvar gds-views nil
501 "List of available views for a GDS client. Each element is one of
502the following symbols.
503`interaction' - Interaction with running client.
504`stack' - Call stack view.
505`browser' - Modules and bindings browser view.
506`breakpoints' - List of set breakpoints.
507`messages' - Non-GDS-protocol output from the debugger.")
508(make-variable-buffer-local 'gds-views)
509
510(defun gds-promote-view (view)
511 (setq gds-views (cons view (delq view gds-views))))
512
513(defun gds-switch-to-view (view)
514 (or (memq view gds-views)
515 (error "View %S is not available" view))
516 (gds-promote-view view)
517 (gds-update-buffers))
518
519(defun gds-add-view (view)
520 (or (memq view gds-views)
521 (setq gds-views (append gds-views (list view)))))
522
523(defun gds-delete-view (view)
524 (setq gds-views (delq view gds-views)))
525
526
527;;;; `Interaction' view.
528
529;; This view provides interaction with a normally running Guile
530;; client, in other words one that is not stopped in the debugger but
531;; is still available to take input from GDS (usually via a thread for
532;; that purpose). The view supports evaluation, help requests,
533;; control of `debug-on-exception' function, and methods for breaking
534;; into the running code.
535
536(defvar gds-current-module "()"
537 "GDS client's current module.")
538(make-variable-buffer-local 'gds-current-module)
539
540(defvar gds-pid nil
541 "GDS client's process ID.")
542(make-variable-buffer-local 'gds-pid)
543
544(defvar gds-debug-exceptions nil
545 "Whether to debug exceptions.")
546(make-variable-buffer-local 'gds-debug-exceptions)
547
548(defvar gds-exception-keys "signal misc-error"
549 "The exception keys for which to debug a GDS client.")
550(make-variable-buffer-local 'gds-exception-keys)
551
552(defvar gds-evals-in-progress nil
553 "Alist describing evaluations in progress.")
554(make-variable-buffer-local 'gds-evals-in-progress)
555
556(defvar gds-results nil
557 "Last help or evaluation results.")
558(make-variable-buffer-local 'gds-results)
559
560(defcustom gds-heading-face 'info-menu-header
561 "*Face used for headings in Guile Interaction buffers."
562 :type 'face
563 :group 'gds)
564
565(defun gds-insert-interaction ()
566 (erase-buffer)
567 ;; Insert stuff for interacting with a running (non-blocked) Guile
568 ;; client.
569 (gds-heading-insert (buffer-name))
570 (widget-insert " "
571 (cdr (assq gds-status
572 '((running . "running (cannot accept input)")
573 (waiting-for-input . "waiting for input")
574 (ready-for-input . "running")
575 (closed . "closed"))))
576 ", in "
577 gds-current-module
578 "\n\n")
579 (widget-create 'push-button
580 :notify (function gds-sigint)
581 "SIGINT")
582 (widget-insert " ")
583 (widget-create 'push-button
584 :notify (function gds-async-break)
585 "Break")
586 (widget-insert "\n")
587 (widget-create 'checkbox
588 :notify (function gds-toggle-debug-exceptions)
589 gds-debug-exceptions)
590 (widget-insert " Debug exception keys: ")
591 (widget-create 'editable-field
592 :notify (function gds-set-exception-keys)
593 gds-exception-keys)
594 ;; Evaluation report area.
595 (widget-insert "\n")
596 (gds-heading-insert "Recent Evaluations")
597 (widget-insert " To run an evaluation, see the Guile->Evaluate menu.\n")
598 (if gds-results
599 (widget-insert "\n" (cdr gds-results)))
600 (let ((evals gds-evals-in-progress))
601 (while evals
602 (widget-insert "\n" (cddar evals) " - running ")
603 (let ((w (widget-create 'push-button
604 :notify (function gds-interrupt-eval)
605 "Interrupt")))
606 (widget-put w :thread-number (caar evals)))
607 (widget-insert "\n")
608 (setq evals (cdr evals)))))
609
610(defun gds-heading-insert (text)
611 (let ((start (point)))
612 (widget-insert text)
613 (let ((o (make-overlay start (point))))
614 (overlay-put o 'face gds-heading-face)
615 (overlay-put o 'evaporate t))))
616
617(defun gds-sigint (w &rest ignore)
618 (interactive)
619 (signal-process gds-pid 2))
620
621(defun gds-async-break (w &rest ignore)
622 (interactive)
623 (gds-send "async-break" gds-client))
624
625(defun gds-interrupt-eval (w &rest ignore)
626 (interactive)
627 (gds-send (format "interrupt-eval %S" (widget-get w :thread-number))
628 gds-client))
629
630(defun gds-toggle-debug-exceptions (w &rest ignore)
631 (interactive)
632 (setq gds-debug-exceptions (widget-value w))
633 (gds-eval-expression (concat "(use-modules (ice-9 debugger))"
634 "(debug-on-error '("
635 gds-exception-keys
636 "))")))
637
638(defun gds-set-exception-keys (w &rest ignore)
639 (interactive)
640 (setq gds-exception-keys (widget-value w)))
641
642(defun gds-view-interaction ()
643 (interactive)
644 (gds-switch-to-view 'interaction))
645
646
647;;;; `Stack' view.
648
649;; This view shows the Guile call stack after the application has hit
650;; an error, or when it is stopped in the debugger.
651
652(defvar gds-stack nil
653 "GDS client's stack when last stopped.")
654(make-variable-buffer-local 'gds-stack)
655
656(defun gds-insert-stack ()
657 (erase-buffer)
658 (let ((frames (car gds-stack))
659 (index (cadr gds-stack))
660 (flags (caddr gds-stack))
661 frame items)
662 (cond ((memq 'application flags)
663 (widget-insert "Calling procedure:\n"))
664 ((memq 'evaluation flags)
665 (widget-insert "Evaluating expression:\n"))
666 ((memq 'return flags)
667 (widget-insert "Return value: "
668 (cadr (memq 'return flags))
669 "\n"))
670 (t
671 (widget-insert "Stack: " (prin1-to-string flags) "\n")))
672 (let ((i -1))
673 (gds-show-selected-frame (caddr (nth index frames)))
674 (while frames
675 (setq frame (car frames)
676 frames (cdr frames)
677 i (+ i 1)
678 items (cons (list 'item
679 (let ((s (cadr frame)))
680 (put-text-property 0 1 'index i s)
681 s))
682 items))))
683 (setq items (nreverse items))
684 (apply (function widget-create)
685 'radio-button-choice
686 :value (cadr (nth index items))
687 :notify (function gds-select-stack-frame)
688 items)
689 (widget-insert "\n")
690 (goto-char (point-min))))
691
692(defun gds-select-stack-frame (widget &rest ignored)
693 (let* ((s (widget-value widget))
694 (ind (memq 'index (text-properties-at 0 s))))
695 (gds-send (format "debugger-command frame %d" (cadr ind))
696 gds-client)))
697
698;; Overlay used to highlight the source expression corresponding to
699;; the selected frame.
700(defvar gds-frame-source-overlay nil)
701
702(defun gds-show-selected-frame (source)
703 ;; Highlight the frame source, if possible.
704 (if (and source
705 (file-readable-p (car source)))
706 (with-current-buffer (find-file-noselect (car source))
707 (if gds-frame-source-overlay
708 nil
709 (setq gds-frame-source-overlay (make-overlay 0 0))
710 (overlay-put gds-frame-source-overlay 'face 'highlight))
711 ;; Move to source line. Note that Guile line numbering is
712 ;; 0-based, while Emacs numbering is 1-based.
713 (save-restriction
714 (widen)
715 (goto-line (+ (cadr source) 1))
716 (move-to-column (caddr source))
717 (move-overlay gds-frame-source-overlay
718 (point)
719 (if (not (looking-at ")"))
720 (save-excursion (forward-sexp 1) (point))
721 ;; It seems that the source coordinates for
722 ;; backquoted expressions are at the end of
723 ;; the sexp rather than the beginning...
724 (save-excursion (forward-char 1)
725 (backward-sexp 1) (point)))
726 (current-buffer))))
727 (if gds-frame-source-overlay
728 (move-overlay gds-frame-source-overlay 0 0))))
729
730(defun gds-view-stack ()
731 (interactive)
732 (gds-switch-to-view 'stack))
733
734
735;;;; `Breakpoints' view.
736
737;; This view shows a list of breakpoints.
738
739(defun gds-view-breakpoints ()
740 (interactive)
741 (gds-switch-to-view 'breakpoints))
742
743
744;;;; `Browser' view.
745
746;; This view shows a list of modules and module bindings.
747
748(defcustom gds-module-filter '(t (guile nil) (ice-9 nil) (oop nil))
749 "Specification of which Guile modules the debugger should display.
750This is a list with structure (DEFAULT EXCEPTION EXCEPTION...), where
751DEFAULT is `t' or `nil' and each EXCEPTION has the structure (SYMBOL
752DEFAULT EXCEPTION EXCEPTION...).
753
754A Guile module name `(x y z)' is matched against this filter as
755follows. If one of the top level EXCEPTIONs has SYMBOL `x', continue
756by matching the rest of the module name, in this case `(y z)', against
757that SYMBOL's DEFAULT and next level EXCEPTION list. Otherwise, if
758the current DEFAULT is `t' display the module, and if the current
759DEFAULT is `nil', don't display it.
760
761This variable is usually set to exclude Guile system modules that are
762not of primary interest when debugging application code."
763 :type 'sexp
764 :group 'gds)
765
766(defun gds-show-module-p (name)
767 ;; Determine whether to display the NAMEd module by matching NAME
768 ;; against `gds-module-filter'.
769 (let ((default (car gds-module-filter))
770 (exceptions (cdr gds-module-filter)))
771 (let ((exception (assq (car name) exceptions)))
772 (if exception
773 (let ((gds-module-filter (cdr exception)))
774 (gds-show-module-p (cdr name)))
775 default))))
776
777(defvar gds-modules nil
778 "GDS client's module information.
779Alist mapping module names to their symbols and related information.
780This looks like:
781
782 (((guile) t sym1 sym2 ...)
783 ((guile-user))
784 ((ice-9 debug) nil sym3 sym4)
785 ...)
786
787The `t' or `nil' after the module name indicates whether the module is
788displayed in expanded form (that is, showing the bindings in that
789module). The syms are actually all strings because some Guile symbols
790are not readable by Emacs.")
791(make-variable-buffer-local 'gds-modules)
792
793(defun gds-insert-modules ()
794 (let ((p (if (eq (window-buffer (selected-window)) (current-buffer))
795 (point)
796 (point-min)))
797 (modules gds-modules))
798 (erase-buffer)
799 (insert "Modules:\n")
800 (while modules
801 (let ((minfo (car modules)))
802 (if (gds-show-module-p (car minfo))
803 (let ((w (widget-create 'push-button
804 :notify (function gds-module-notify)
805 (if (and (cdr minfo)
806 (cadr minfo))
807 "-" "+"))))
808 (widget-put w :module (cons gds-client (car minfo)))
809 (widget-insert " " (prin1-to-string (car minfo)) "\n")
810 (if (cadr minfo)
811 (let ((syms (cddr minfo)))
812 (while syms
813 (widget-insert " > " (car syms) "\n")
814 (setq syms (cdr syms))))))))
815 (setq modules (cdr modules)))
816 (insert "\n")
817 (goto-char p)))
818
819(defun gds-module-notify (w &rest ignore)
820 (let* ((module (widget-get w :module))
821 (client (car module))
822 (name (cdr module))
823 (minfo (assoc name gds-modules)))
824 (if (cdr minfo)
825 ;; Just toggle expansion state.
826 (progn
827 (setcar (cdr minfo) (not (cadr minfo)))
828 (gds-update-buffers))
829 ;; Set flag to indicate module expanded.
830 (setcdr minfo (list t))
831 ;; Get symlist from Guile.
832 (gds-send (format "query-module %S" name) client))))
833
834(defun gds-query-modules ()
835 (interactive)
836 (gds-send "query-modules" gds-client))
837
838(defun gds-view-browser ()
839 (interactive)
840 (or gds-modules (gds-query-modules))
841 (gds-switch-to-view 'browser))
842
843
844;;;; `Messages' view.
845
846;; This view shows recent non-GDS-protocol messages output from the
847;; (ice-9 debugger) code.
848
849(defvar gds-output nil
850 "GDS client's recent output (printed).")
851(make-variable-buffer-local 'gds-output)
852
853(defun gds-insert-messages ()
854 (erase-buffer)
855 ;; Insert recent non-protocol output from (ice-9 debugger).
856 (insert gds-output)
857 (goto-char (point-min)))
858
859(defun gds-view-messages ()
860 (interactive)
861 (gds-switch-to-view 'messages))
862
863
864;;;; Debugger commands.
865
866;; Typically but not necessarily used from the `stack' view.
867
868(defun gds-go ()
869 (interactive)
870 (gds-send "debugger-command continue" gds-client))
871
872(defun gds-next ()
873 (interactive)
874 (gds-send "debugger-command next 1" gds-client))
875
876(defun gds-evaluate (expr)
877 (interactive "sEvaluate (in this stack frame): ")
878 (gds-send (format "debugger-command evaluate %s" (prin1-to-string expr))
879 gds-client))
880
881(defun gds-step-in ()
882 (interactive)
883 (gds-send "debugger-command step 1" gds-client))
884
885(defun gds-step-out ()
886 (interactive)
887 (gds-send "debugger-command finish" gds-client))
888
889(defun gds-trace-finish ()
890 (interactive)
891 (gds-send "debugger-command trace-finish" gds-client))
892
893(defun gds-frame-info ()
894 (interactive)
895 (gds-send "debugger-command info-frame" gds-client))
896
897(defun gds-frame-args ()
898 (interactive)
899 (gds-send "debugger-command info-args" gds-client))
900
901(defun gds-debug-trap-hooks ()
902 (interactive)
903 (gds-send "debugger-command debug-trap-hooks" gds-client))
904
905(defun gds-up ()
906 (interactive)
907 (gds-send "debugger-command up 1" gds-client))
908
909(defun gds-down ()
910 (interactive)
911 (gds-send "debugger-command down 1" gds-client))
912
913
914;;;; Setting breakpoints.
915
916(defun gds-set-breakpoint ()
917 (interactive)
918 (cond ((gds-in-source-buffer)
919 (gds-set-source-breakpoint))
920 ((gds-in-stack)
921 (gds-set-stack-breakpoint))
922 ((gds-in-modules)
923 (gds-set-module-breakpoint))
924 (t
925 (error "No way to set a breakpoint from here"))))
926
927(defun gds-in-source-buffer ()
928 ;; Not yet worked out what will be available in Scheme source
929 ;; buffers.
930 nil)
931
932(defun gds-in-stack ()
933 (save-excursion
934 (and (re-search-backward "^\\(Stack\\|Modules\\):" nil t)
935 (looking-at "Stack"))))
936
937(defun gds-in-modules ()
938 (save-excursion
939 (and (re-search-backward "^\\(Stack\\|Modules\\):" nil t)
940 (looking-at "Modules"))))
941
942(defun gds-set-module-breakpoint ()
943 (let ((sym (save-excursion
944 (beginning-of-line)
945 (and (looking-at " > \\([^ \n\t]+\\)")
946 (match-string 1))))
947 (module (save-excursion
948 (and (re-search-backward "^\\[[+---]\\] \\(([^)]+)\\)" nil t)
949 (match-string 1)))))
950 (or sym
951 (error "Couldn't find procedure name on current line"))
952 (or module
953 (error "Couldn't find module name for current line"))
954 (let ((behaviour
955 (completing-read
956 (format "Behaviour for breakpoint at %s:%s (default debug-here): "
957 module sym)
958 '(("debug-here")
959 ("trace-here")
960 ("trace-subtree"))
961 nil
962 t
963 nil
964 nil
965 "debug-here")))
966 (gds-send (format "set-breakpoint %s %s %s"
967 module
968 sym
969 behaviour)
970 gds-client))))
971
972
973;;;; Scheme source breakpoints.
974
975(defcustom gds-breakpoint-face 'default
976 "*Face used to highlight the location of a source breakpoint.
977Specifically, this face highlights the opening parenthesis of the
978form where the breakpoint is set."
979 :type 'face
980 :group 'gds)
981
982(defcustom gds-new-breakpoint-before-string ""
983 "*String used to show the presence of a new source breakpoint.
984`New' means that the breakpoint has been set but isn't yet known to
985Guile because the containing code hasn't been reevaluated yet.
986This string appears before the opening parenthesis of the form where
987the breakpoint is set. If you prefer a marker to appear after the
988opening parenthesis, make this string empty and use
989`gds-new-breakpoint-after-string'."
990 :type 'string
991 :group 'gds)
992
993(defcustom gds-new-breakpoint-after-string "=?= "
994 "*String used to show the presence of a new source breakpoint.
995`New' means that the breakpoint has been set but isn't yet known to
996Guile because the containing code hasn't been reevaluated yet.
997This string appears after the opening parenthesis of the form where
998the breakpoint is set. If you prefer a marker to appear before the
999opening parenthesis, make this string empty and use
1000`gds-new-breakpoint-before-string'."
1001 :type 'string
1002 :group 'gds)
1003
1004(defcustom gds-active-breakpoint-before-string ""
1005 "*String used to show the presence of a source breakpoint.
1006`Active' means that the breakpoint is known to Guile.
1007This string appears before the opening parenthesis of the form where
1008the breakpoint is set. If you prefer a marker to appear after the
1009opening parenthesis, make this string empty and use
1010`gds-active-breakpoint-after-string'."
1011 :type 'string
1012 :group 'gds)
1013
1014(defcustom gds-active-breakpoint-after-string "=|= "
1015 "*String used to show the presence of a source breakpoint.
1016`Active' means that the breakpoint is known to Guile.
1017This string appears after the opening parenthesis of the form where
1018the breakpoint is set. If you prefer a marker to appear before the
1019opening parenthesis, make this string empty and use
1020`gds-active-breakpoint-before-string'."
1021 :type 'string
1022 :group 'gds)
1023
1024(defun gds-source-breakpoint-pos ()
1025 "Return the position of the starting parenthesis of the innermost
1026Scheme pair around point."
1027 (if (eq (char-syntax (char-after)) ?\()
1028 (point)
1029 (save-excursion
1030 (condition-case nil
1031 (while t (forward-sexp -1))
1032 (error))
1033 (forward-char -1)
1034 (while (not (eq (char-syntax (char-after)) ?\())
1035 (forward-char -1))
1036 (point))))
1037
1038(defun gds-source-breakpoint-overlay-at (pos)
1039 "Return the source breakpoint overlay at POS, if any."
1040 (let* (o (os (overlays-at pos)))
1041 (while os
1042 (if (and (overlay-get (car os) 'gds-breakpoint-info)
1043 (= (overlay-start (car os)) pos))
1044 (setq o (car os)
1045 os nil))
1046 (setq os (cdr os)))
1047 o))
1048
1049(defun gds-set-source-breakpoint ()
1050 (interactive)
1051 (let* ((pos (gds-source-breakpoint-pos))
1052 (o (gds-source-breakpoint-overlay-at pos)))
1053 (if o
1054 (error "There is already a breakpoint here!")
1055 (setq o (make-overlay pos (+ pos 1)))
1056 (overlay-put o 'evaporate t)
1057 (overlay-put o 'face gds-breakpoint-face)
1058 (overlay-put o 'gds-breakpoint-info 0)
1059 (overlay-put o 'before-string gds-new-breakpoint-before-string)
1060 (overlay-put o 'after-string gds-new-breakpoint-after-string))))
1061
1062(defun gds-delete-source-breakpoint ()
1063 (interactive)
1064 (let* ((pos (gds-source-breakpoint-pos))
1065 (o (gds-source-breakpoint-overlay-at pos)))
1066 (or o
1067 (error "There is no breakpoint here to delete!"))
1068 (delete-overlay o)))
1069
1070(defun gds-region-breakpoint-info (beg end)
1071 "Return an alist of breakpoints in REGION.
1072The car of each alist element is a cons (LINE . COLUMN) giving the
1073source location of the breakpoint. The cdr is information describing
1074breakpoint properties. Currently `information' is just the breakpoint
1075index, for an existing Guile breakpoint, or 0 for a breakpoint that
1076isn't yet known to Guile."
1077 (interactive "r")
1078 (let ((os (overlays-in beg end))
1079 info o)
1080 (while os
1081 (setq o (car os)
1082 os (cdr os))
1083 (if (overlay-get o 'gds-breakpoint-info)
1084 (progn
1085 (setq info
1086 (cons (cons (save-excursion
1087 (goto-char (overlay-start o))
1088 (cons (save-excursion
1089 (beginning-of-line)
1090 (count-lines (point-min) (point)))
1091 (current-column)))
1092 (overlay-get o 'gds-breakpoint-info))
1093 info))
1094 ;; Also now mark the breakpoint as `new'. It will become
1095 ;; `active' (again) when we receive a notification from
1096 ;; Guile that the breakpoint has been set.
1097 (overlay-put o 'gds-breakpoint-info 0)
1098 (overlay-put o 'before-string gds-new-breakpoint-before-string)
1099 (overlay-put o 'after-string gds-new-breakpoint-after-string))))
1100 (nreverse info)))
1101
1102
1103;;;; Evaluating code.
1104
1105;; The following commands send code for evaluation through the GDS TCP
1106;; connection, receive the result and any output generated through the
1107;; same connection, and display the result and output to the user.
1108;;
1109;; For each buffer where evaluations can be requested, GDS uses the
1110;; buffer-local variable `gds-client' to track which GDS client
1111;; program should receive and handle that buffer's evaluations. In
1112;; the common case where GDS is only managing one client program, a
1113;; buffer's value of `gds-client' is set automatically to point to
1114;; that program the first time that an evaluation (or help or
1115;; completion) is requested. If there are multiple GDS clients
1116;; running at that time, GDS asks the user which one is intended.
1117
1118(defun gds-read-client ()
1119 (let* ((def (and gds-client (cdr (assq gds-client gds-names))))
1120 (prompt (if def
1121 (concat "Application for eval (default "
1122 def
1123 "): ")
1124 "Application for eval: "))
1125 (name
1126 (completing-read prompt
1127 (mapcar (function list)
1128 (mapcar (function cdr) gds-names))
1129 nil t nil nil
1130 def)))
1131 (let (client (names gds-names))
1132 (while (and names (not client))
1133 (if (string-equal (cdar names) name)
1134 (setq client (caar names)))
1135 (setq names (cdr names)))
1136 client)))
1137
1138(defun gds-choose-client (client)
1139 ;; Only keep the supplied client number if it is still valid.
1140 (if (integerp client)
1141 (setq client (gds-client-ref 'gds-client client)))
1142 ;; Only keep the current buffer's setting of `gds-client' if it is
1143 ;; still valid.
1144 (if gds-client
1145 (setq gds-client (gds-client-ref 'gds-client)))
1146
1147 (or ;; If client is an integer, it is the port number of the
1148 ;; intended client.
1149 (if (integerp client)
1150 client)
1151 ;; Any other non-nil value indicates invocation with a prefix
1152 ;; arg, which forces asking the user which application is
1153 ;; intended.
1154 (if client
1155 (setq gds-client (gds-read-client)))
1156 ;; If ask not forced, and current buffer is associated with a
1157 ;; client, use that client.
1158 gds-client
1159 ;; If there are no clients at this point, and we are
1160 ;; allowed to autostart a captive Guile, do so.
1161 (and (null gds-buffers)
1162 gds-autostart-captive
1163 (progn
1164 (gds-start-captive t)
1165 (while (null gds-buffers)
1166 (accept-process-output (get-buffer-process gds-captive)
1167 0 100000))
1168 (setq gds-client (caar gds-buffers))))
1169 ;; If there is only one known client, use that one.
1170 (if (and (car gds-buffers)
1171 (null (cdr gds-buffers)))
1172 (setq gds-client (caar gds-buffers)))
1173 ;; Last resort - ask the user.
1174 (setq gds-client (gds-read-client))
1175 ;; Signal an error.
1176 (error "No application chosen.")))
1177
1178(defun gds-module-name (start end)
1179 "Determine and return the name of the module that governs the
1180specified region. The module name is returned as a list of symbols."
1181 (interactive "r") ; why not?
1182 (save-excursion
1183 (goto-char start)
1184 (let (module-name)
1185 (while (and (not module-name)
1186 (beginning-of-defun-raw 1))
1187 (if (looking-at "(define-module ")
1188 (setq module-name
1189 (progn
1190 (goto-char (match-end 0))
1191 (read (current-buffer))))))
1192 module-name)))
1193
1194(defun gds-port-name (start end)
1195 "Return port name for the specified region of the current buffer.
1196The name will be used by Guile as the port name when evaluating that
1197region's code."
1198 (or (buffer-file-name)
1199 (concat "Emacs buffer: " (buffer-name))))
1200
1201(defun gds-eval-region (start end &optional client)
1202 "Evaluate the current region."
1203 (interactive "r\nP")
1204 (setq client (gds-choose-client client))
1205 (let ((module (gds-module-name start end))
1206 (port-name (gds-port-name start end))
1207 line column)
1208 (save-excursion
1209 (goto-char start)
1210 (setq column (current-column)) ; 0-based
1211 (beginning-of-line)
1212 (setq line (count-lines (point-min) (point)))) ; 0-based
1213 (let ((code (buffer-substring-no-properties start end)))
1214 (gds-send (format "eval (region . %S) %s %S %d %d %s %S"
1215 (gds-abbreviated code)
1216 (if module (prin1-to-string module) "#f")
1217 port-name line column
1218 (let ((bpinfo (gds-region-breakpoint-info start end)))
1219 ;; Make sure that "no bpinfo" is represented
1220 ;; as "()", not "nil", as Scheme doesn't
1221 ;; understand "nil".
1222 (if bpinfo (format "%S" bpinfo) "()"))
1223 code)
1224 client))))
1225
1226(defun gds-eval-expression (expr &optional client correlator)
1227 "Evaluate the supplied EXPR (a string)."
1228 (interactive "sEvaluate expression: \nP")
1229 (setq client (gds-choose-client client))
1230 (set-text-properties 0 (length expr) nil expr)
1231 (gds-send (format "eval (%S . %S) #f \"Emacs expression\" 0 0 () %S"
1232 (or correlator 'expression)
1233 (gds-abbreviated expr)
1234 expr)
1235 client))
1236
1237(defconst gds-abbreviated-length 35)
1238
1239(defun gds-abbreviated (code)
1240 (let ((nlpos (string-match (regexp-quote "\n") code)))
1241 (while nlpos
1242 (setq code
1243 (if (= nlpos (- (length code) 1))
1244 (substring code 0 nlpos)
1245 (concat (substring code 0 nlpos)
1246 "\\n"
1247 (substring code (+ nlpos 1)))))
1248 (setq nlpos (string-match (regexp-quote "\n") code))))
1249 (if (> (length code) gds-abbreviated-length)
1250 (concat (substring code 0 (- gds-abbreviated-length 3)) "...")
1251 code))
1252
1253(defun gds-eval-defun (&optional client)
1254 "Evaluate the defun (top-level form) at point."
1255 (interactive "P")
1256 (save-excursion
1257 (end-of-defun)
1258 (let ((end (point)))
1259 (beginning-of-defun)
1260 (gds-eval-region (point) end client))))
1261
1262(defun gds-eval-last-sexp (&optional client)
1263 "Evaluate the sexp before point."
1264 (interactive "P")
1265 (gds-eval-region (save-excursion (backward-sexp) (point)) (point) client))
1266
1267
1268;;;; Help.
1269
1270;; Help is implemented as a special case of evaluation, identified by
1271;; the evaluation correlator 'help.
1272
1273(defun gds-help-symbol (sym &optional client)
1274 "Get help for SYM (a Scheme symbol)."
1275 (interactive
1276 (let ((sym (thing-at-point 'symbol))
1277 (enable-recursive-minibuffers t)
1278 val)
1279 (setq val (read-from-minibuffer
1280 (if sym
1281 (format "Describe Guile symbol (default %s): " sym)
1282 "Describe Guile symbol: ")))
1283 (list (if (zerop (length val)) sym val)
1284 current-prefix-arg)))
1285 (gds-eval-expression (format "(help %s)" sym) client 'help))
1286
1287(defun gds-apropos (regex &optional client)
1288 "List Guile symbols matching REGEX."
1289 (interactive
1290 (let ((sym (thing-at-point 'symbol))
1291 (enable-recursive-minibuffers t)
1292 val)
1293 (setq val (read-from-minibuffer
1294 (if sym
1295 (format "Guile apropos (regexp, default \"%s\"): " sym)
1296 "Guile apropos (regexp): ")))
1297 (list (if (zerop (length val)) sym val)
1298 current-prefix-arg)))
1299 (set-text-properties 0 (length regex) nil regex)
1300 (gds-eval-expression (format "(apropos %S)" regex) client 'help))
1301
1302(defvar gds-completion-results nil)
1303
1304(defun gds-complete-symbol (&optional client)
1305 "Complete the Guile symbol before point. Returns `t' if anything
1306interesting happened, `nil' if not."
1307 (interactive "P")
1308 (let* ((chars (- (point) (save-excursion
1309 (while (let ((syntax (char-syntax (char-before (point)))))
1310 (or (eq syntax ?w) (eq syntax ?_)))
1311 (forward-char -1))
1312 (point)))))
1313 (if (zerop chars)
1314 nil
1315 (setq client (gds-choose-client client))
1316 (setq gds-completion-results nil)
1317 (gds-send (format "complete %s"
1318 (prin1-to-string
1319 (buffer-substring-no-properties (- (point) chars)
1320 (point))))
1321 client)
1322 (while (null gds-completion-results)
1323 (accept-process-output gds-process 0 200))
1324 (cond ((eq gds-completion-results t)
1325 nil)
1326 ((stringp gds-completion-results)
1327 (if (<= (length gds-completion-results) chars)
1328 nil
1329 (insert (substring gds-completion-results chars))
1330 (message "Sole completion")
1331 t))
1332 ((= (length gds-completion-results) 1)
1333 (if (<= (length (car gds-completion-results)) chars)
1334 nil
1335 (insert (substring (car gds-completion-results) chars))
1336 t))
1337 (t
1338 (with-output-to-temp-buffer "*Completions*"
1339 (display-completion-list gds-completion-results))
1340 t)))))
1341
1342
1343;;;; Display of evaluation and help results.
1344
1345(defun gds-display-results (client correlator results)
1346 (let ((helpp (eq (car correlator) 'help)))
1347 (let ((buf (get-buffer-create (if helpp
1348 "*Guile Help*"
1349 "*Guile Results*"))))
1350 (setq gds-results
1351 (save-excursion
1352 (set-buffer buf)
1353 (erase-buffer)
1354 (scheme-mode)
1355 (insert (cdr correlator) "\n\n")
1356 (while results
1357 (insert (car results))
1358 (or (bolp) (insert "\\\n"))
1359 (if helpp
1360 nil
1361 (if (cadr results)
1362 (mapcar (function (lambda (value)
1363 (insert " => " value "\n")))
1364 (cadr results))
1365 (insert " => no (or unspecified) value\n"))
1366 (insert "\n"))
1367 (setq results (cddr results)))
1368 (goto-char (point-min))
1369 (if (and helpp (looking-at "Evaluating in "))
1370 (delete-region (point) (progn (forward-line 1) (point))))
1371 (cons correlator (buffer-string))))
1372 ;;(pop-to-buffer buf)
1373 ;;(run-hooks 'temp-buffer-show-hook)
1374 ;;(other-window 1)
1375 ))
1376 (gds-promote-view 'interaction)
1377 (gds-request-focus client))
1378
1379
1380;;;; Loading (evaluating) a whole Scheme file.
1381
1382(defcustom gds-source-modes '(scheme-mode)
1383 "*Used to determine if a buffer contains Scheme source code.
1384If it's loaded into a buffer that is in one of these major modes, it's
1385considered a scheme source file by `gds-load-file'."
1386 :type '(repeat function)
1387 :group 'gds)
1388
1389(defvar gds-prev-load-dir/file nil
1390 "Holds the last (directory . file) pair passed to `gds-load-file'.
1391Used for determining the default for the next `gds-load-file'.")
1392
1393(defun gds-load-file (file-name &optional client)
1394 "Load a Scheme file into the inferior Scheme process."
1395 (interactive (list (car (comint-get-source "Load Scheme file: "
1396 gds-prev-load-dir/file
1397 gds-source-modes t))
1398 ; T because LOAD needs an
1399 ; exact name
1400 current-prefix-arg))
1401 (comint-check-source file-name) ; Check to see if buffer needs saved.
1402 (setq gds-prev-load-dir/file (cons (file-name-directory file-name)
1403 (file-name-nondirectory file-name)))
1404 (setq client (gds-choose-client client))
1405 (gds-send (format "load %S" file-name) client))
1406
1407
1408;;;; Scheme mode keymap items.
1409
1410(define-key scheme-mode-map "\M-\C-x" 'gds-eval-defun);gnu convention
1411(define-key scheme-mode-map "\C-x\C-e" 'gds-eval-last-sexp);gnu convention
1412(define-key scheme-mode-map "\C-c\C-e" 'gds-eval-expression)
1413(define-key scheme-mode-map "\C-c\C-r" 'gds-eval-region)
1414(define-key scheme-mode-map "\C-c\C-l" 'gds-load-file)
1415(define-key scheme-mode-map "\C-hg" 'gds-help-symbol)
1416(define-key scheme-mode-map "\C-h\C-g" 'gds-apropos)
1417(define-key scheme-mode-map "\e\t" 'gds-complete-symbol)
1418(define-key scheme-mode-map "\C-x " 'gds-set-source-breakpoint)
1419(define-key scheme-mode-map "\C-x\e " 'gds-delete-source-breakpoint)
1420
1421
1422;;;; Guile Interaction mode keymap and menu items.
1423
1424(define-key gds-mode-map "M" (function gds-query-modules))
1425
1426(define-key gds-mode-map "g" (function gds-go))
1427(define-key gds-mode-map "q" (function gds-quit))
1428(define-key gds-mode-map " " (function gds-next))
1429(define-key gds-mode-map "e" (function gds-evaluate))
1430(define-key gds-mode-map "i" (function gds-step-in))
1431(define-key gds-mode-map "o" (function gds-step-out))
1432(define-key gds-mode-map "t" (function gds-trace-finish))
1433(define-key gds-mode-map "I" (function gds-frame-info))
1434(define-key gds-mode-map "A" (function gds-frame-args))
1435(define-key gds-mode-map "H" (function gds-debug-trap-hooks))
1436(define-key gds-mode-map "u" (function gds-up))
1437(define-key gds-mode-map "d" (function gds-down))
1438(define-key gds-mode-map "b" (function gds-set-breakpoint))
1439
1440(define-key gds-mode-map "vi" (function gds-view-interaction))
1441(define-key gds-mode-map "vs" (function gds-view-stack))
1442(define-key gds-mode-map "vb" (function gds-view-breakpoints))
1443(define-key gds-mode-map "vB" (function gds-view-browser))
1444(define-key gds-mode-map "vm" (function gds-view-messages))
1445
1446(defvar gds-view-menu nil
1447 "GDS view menu.")
1448(if gds-view-menu
1449 nil
1450 (setq gds-view-menu (make-sparse-keymap "View"))
1451 (define-key gds-view-menu [messages]
1452 '(menu-item "Messages" gds-view-messages
1453 :enable (memq 'messages gds-views)))
1454 (define-key gds-view-menu [browser]
1455 '(menu-item "Browser" gds-view-browser
1456 :enable (memq 'browser gds-views)))
1457 (define-key gds-view-menu [breakpoints]
1458 '(menu-item "Breakpoints" gds-view-breakpoints
1459 :enable (memq 'breakpoints gds-views)))
1460 (define-key gds-view-menu [stack]
1461 '(menu-item "Stack" gds-view-stack
1462 :enable (memq 'stack gds-views)))
1463 (define-key gds-view-menu [interaction]
1464 '(menu-item "Interaction" gds-view-interaction
1465 :enable (memq 'interaction gds-views))))
1466
1467(defvar gds-debug-menu nil
1468 "GDS debugging menu.")
1469(if gds-debug-menu
1470 nil
1471 (setq gds-debug-menu (make-sparse-keymap "Debug"))
1472 (define-key gds-debug-menu [go]
1473 '(menu-item "Go" gds-go))
1474 (define-key gds-debug-menu [down]
1475 '(menu-item "Move Down 1 Frame" gds-down))
1476 (define-key gds-debug-menu [up]
1477 '(menu-item "Move Up 1 Frame" gds-up))
1478 (define-key gds-debug-menu [trace-finish]
1479 '(menu-item "Trace This Frame" gds-trace-finish))
1480 (define-key gds-debug-menu [step-out]
1481 '(menu-item "Finish This Frame" gds-step-out))
1482 (define-key gds-debug-menu [next]
1483 '(menu-item "Next" gds-next))
1484 (define-key gds-debug-menu [step-in]
1485 '(menu-item "Single Step" gds-step-in))
1486 (define-key gds-debug-menu [eval]
1487 '(menu-item "Eval In This Frame..." gds-evaluate)))
1488
1489(defvar gds-breakpoint-menu nil
1490 "GDS breakpoint menu.")
1491(if gds-breakpoint-menu
1492 nil
1493 (setq gds-breakpoint-menu (make-sparse-keymap "Breakpoint"))
1494 (define-key gds-breakpoint-menu [last-sexp]
1495 '(menu-item "Delete Breakpoint" gds-delete-source-breakpoint))
1496 (define-key gds-breakpoint-menu [set]
1497 '(menu-item "Set Breakpoint" gds-set-source-breakpoint)))
1498
1499(defvar gds-eval-menu nil
1500 "GDS evaluation menu.")
1501(if gds-eval-menu
1502 nil
1503 (setq gds-eval-menu (make-sparse-keymap "Evaluate"))
1504 (define-key gds-eval-menu [load-file]
1505 '(menu-item "Load Scheme File" gds-load-file))
1506 (define-key gds-eval-menu [defun]
1507 '(menu-item "Defun At Point" gds-eval-defun))
1508 (define-key gds-eval-menu [region]
1509 '(menu-item "Region" gds-eval-region))
1510 (define-key gds-eval-menu [last-sexp]
1511 '(menu-item "Sexp Before Point" gds-eval-last-sexp))
1512 (define-key gds-eval-menu [expr]
1513 '(menu-item "Expression..." gds-eval-expression)))
1514
1515(defvar gds-help-menu nil
1516 "GDS help menu.")
1517(if gds-help-menu
1518 nil
1519 (setq gds-help-menu (make-sparse-keymap "Help"))
1520 (define-key gds-help-menu [apropos]
1521 '(menu-item "Apropos..." gds-apropos))
1522 (define-key gds-help-menu [sym]
1523 '(menu-item "Symbol..." gds-help-symbol)))
1524
1525(defvar gds-advanced-menu nil
1526 "Menu of rarely needed GDS operations.")
1527(if gds-advanced-menu
1528 nil
1529 (setq gds-advanced-menu (make-sparse-keymap "Advanced"))
1530 (define-key gds-advanced-menu [run-captive]
1531 '(menu-item "Run Captive Guile" gds-start-captive
1532 :enable (not (comint-check-proc gds-captive))))
1533 (define-key gds-advanced-menu [restart-gds]
1534 '(menu-item "Restart IDE" gds-start :enable gds-process))
1535 (define-key gds-advanced-menu [kill-gds]
1536 '(menu-item "Shutdown IDE" gds-shutdown :enable gds-process))
1537 (define-key gds-advanced-menu [start-gds]
1538 '(menu-item "Start IDE" gds-start :enable (not gds-process))))
1539
1540(defvar gds-menu nil
1541 "Global menu for GDS commands.")
1542(if gds-menu
1543 nil
1544 (setq gds-menu (make-sparse-keymap "Guile"))
1545 (define-key gds-menu [advanced]
1546 (cons "Advanced" gds-advanced-menu))
1547 (define-key gds-menu [separator-1]
1548 '("--"))
1549 (define-key gds-menu [view]
1550 `(menu-item "View" ,gds-view-menu :enable gds-views))
1551 (define-key gds-menu [debug]
1552 `(menu-item "Debug" ,gds-debug-menu :enable (and gds-client
1553 (gds-client-blocked))))
1554 (define-key gds-menu [breakpoint]
1555 `(menu-item "Breakpoints" ,gds-breakpoint-menu :enable t))
1556 (define-key gds-menu [eval]
1557 `(menu-item "Evaluate" ,gds-eval-menu :enable (or gds-buffers
1558 gds-autostart-captive)))
1559 (define-key gds-menu [help]
1560 `(menu-item "Help" ,gds-help-menu :enable (or gds-buffers
1561 gds-autostart-captive)))
1562 (setq menu-bar-final-items
1563 (cons 'guile menu-bar-final-items))
1564 (define-key scheme-mode-map [menu-bar guile]
1565 (cons "Guile" gds-menu)))
1566
1567
1568;;;; Autostarting the GDS server.
1569
1570(defcustom gds-autostart-server t
1571 "Whether to automatically start the GDS server when `gds.el' is loaded."
1572 :type 'boolean
1573 :group 'gds)
1574
1575
1576;;;; `Captive' Guile - a Guile process that is started when needed to
1577;;;; provide help, completion, evaluations etc.
1578
1579(defcustom gds-autostart-captive t
1580 "Whether to automatically start a `captive' Guile process when needed."
1581 :type 'boolean
1582 :group 'gds)
1583
1584(defvar gds-captive nil
1585 "Buffer of captive Guile.")
1586
1587(defun gds-start-captive (&optional restart)
1588 (interactive)
1589 (if (and restart
1590 (comint-check-proc gds-captive))
1591 (gds-kill-captive))
1592 (if (comint-check-proc gds-captive)
1593 nil
1594 (let ((process-connection-type nil))
1595 (setq gds-captive (make-comint "captive-guile"
1596 gds-guile-program
1597 nil
1598 "-q")))
1599 (let ((proc (get-buffer-process gds-captive)))
1600 (process-kill-without-query proc)
1601 (comint-send-string proc "(set! %load-path (cons \"/home/neil/Guile/cvs/guile-core\" %load-path))\n")
1602 (comint-send-string proc "(debug-enable 'backtrace)\n")
1603 (comint-send-string proc "(use-modules (emacs gds-client))\n")
1604 (comint-send-string proc "(gds-connect \"Captive Guile\" #f)\n"))))
1605
1606(defun gds-kill-captive ()
1607 (if gds-captive
1608 (condition-case nil
1609 (progn
1610 (kill-process (get-buffer-process gds-captive))
1611 (accept-process-output gds-process 0 200))
1612 (error))))
1613
1614
1615;;;; If requested, autostart the server after loading.
1616
1617(if (and gds-autostart-server
1618 (not gds-process))
1619 (gds-start))
1620
1621
1622;;;; The end!
1623
1624(provide 'gds)
1625
1626;;; gds.el ends here.