new file.
[bpt/guile.git] / emacs / guileint / guile.el
1 ;;; @(#) guile.el -- A GNU Emacs interface to Guile
2 ;;; @(#) $Keywords: guile, comint, scheme-mode $
3
4 ;; Copyright (C) 1995, 2002 Mikael Djurfeldt
5
6 ;; LCD Archive Entry:
7 ;; guile|djurfeldt@nada.kth.se|
8 ;; A GNU Emacs extension which |
9 ;; $Date: 2003-08-20 19:00:44 $|$Revision: 1.1 $|~/misc/guile.el.Z|
10
11 ;; Author: Mikael Djurfeldt <djurfeldt@nada.kth.se>
12 ;; Version: 1.5.2
13
14 ;; This program is free software; you can redistribute it and/or modify it
15 ;; under the terms of the GNU General Public License as published by the Free
16 ;; Software Foundation; either version 2 of the License, or (at your option)
17 ;; any later version.
18 ;;
19 ;; This program is distributed in the hope that it will be useful, but
20 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 ;; for more details.
23 ;;
24 ;; You should have received a copy of the GNU General Public License along
25 ;; with GNU Emacs. If you did not, write to the Free Software Foundation,
26 ;; Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
27
28 ;;; Commentary:
29 ;;
30 ;; Requirements:
31 ;;
32 ;; Usage:
33 ;;
34 ;; Bugs:
35 ;;
36 ;;
37 ;;; *************************************************************************
38 ;;; * This is code is currently under development *
39 ;;; * Mail any problems to djurfeldt@nada.kth.se *
40 ;;; *************************************************************************
41
42 (require 'cl)
43 (require 'fcreate)
44
45 (defvar guile-auto-attach nil)
46
47 (defvar guile-load-hook nil
48 "*Hook run when file is loaded")
49
50 ;;(require 'cmuscheme)
51 (load "comint") ; `comint' and `cmuscheme' are already loaded.
52 (load "cmuscheme") ; We need to replace them.
53
54 ;; Faces are set in the cond expression below.
55
56 (defvar guile-error-face nil
57 "Face used to highlight erroneous scheme forms.")
58
59 (defvar guile-backtrace-mouse-face nil
60 "Face used when the mouse is over a backtrace frame.")
61
62 (defvar guile-modified-face nil
63 "Face for modified top-level forms in scheme-mode buffers.")
64
65 (defvar guile-broken-face nil
66 "Face for broken top-level forms in scheme-mode buffers.")
67
68 ;; These faces are used during debugging of the list parsing code.
69
70 (defvar guile-unmodified-face-1 nil)
71 (defvar guile-unmodified-face-2 nil)
72 (defvar guile-modified-face-1 nil)
73 (defvar guile-modified-face-2 nil)
74 (defvar guile-broken-face-1 nil)
75 (defvar guile-broken-face-2 nil)
76
77 ;;; Customization
78 ;;;
79
80 (defvar guile-backtrace-in-source-window t
81 "*If non-nil, let backtrace windows appear in bottom of source window.
82 This only occurs if the erring expression can be located.")
83
84 (defvar guile-show-runlight-in-scheme-mode nil
85 "*If non-nil, show process status also in attached scheme-mode buffers.
86 Otherwise the mode-line shows if the buffer is attached or not.")
87
88 (defvar guile-default-enhanced-edit t
89 "If non-nil, automatically enter enhanced edit mode for scheme buffers.")
90
91 (defvar guile-popup-restart-on-death t)
92
93 (defvar guile-popup-restart-on-stop t)
94
95 (defvar guile-insert-reason t)
96
97 (defvar guile-kill-buffer-on-death nil)
98
99 (defvar guile-process-timeout 500
100 "Milliseconds")
101
102 (defconst guile-backtrace-buffer-name "*Scheme Backtrace*")
103
104 (defconst guile-error-buffer-name "*Scheme Error*")
105
106 (defconst guile-backtrace-min-height 10)
107 (defconst guile-backtrace-max-height 30)
108 (defconst guile-backtrace-min-width 30)
109 (defconst guile-backtrace-max-width 90)
110
111 (cond ((not window-system)
112 ;; Faces for text terminals
113 (setq guile-error-face 'modeline)
114 (setq guile-backtrace-mouse-face 'highlight)
115 (setq guile-modified-face nil) ; no special face
116 (setq guile-broken-face nil)
117 (setq guile-unmodified-face-1 nil)
118 (setq guile-unmodified-face-2 'modeline)
119 (setq guile-modified-face-1 'bold)
120 (setq guile-modified-face-2 guile-error-face)
121 (setq guile-broken-face-1 nil)
122 (setq guile-broken-face-2 nil))
123 ((x-display-color-p)
124 ;; Faces for color screens
125 (setq guile-error-face (lookup-face-create 'black/red-bold))
126 (setq guile-backtrace-mouse-face 'highlight)
127 (setq guile-modified-face nil) ; no special face
128 (setq guile-broken-face 'bold)
129 (setq guile-unmodified-face-1 (lookup-face-create 'black/lightblue))
130 (setq guile-unmodified-face-2 'secondary-selection)
131 (setq guile-modified-face-1 'highlight)
132 (setq guile-modified-face-2 (lookup-face-create 'black/pink))
133 (setq guile-broken-face-1
134 (let ((face (make-face 'broken-form-1)))
135 (copy-face guile-modified-face-1 face)
136 (set-face-underline-p face t)
137 face))
138 (setq guile-broken-face-2
139 (let ((face (make-face 'broken-form-2)))
140 (copy-face guile-modified-face-2 face)
141 (set-face-underline-p face t)
142 face)))
143 (t
144 ;; Faces for monochrome screens
145 (setq guile-error-face (lookup-face-create 'white/black-bold))
146 (setq guile-backtrace-mouse-face 'highlight)
147 (setq guile-modified-face nil) ; no special face
148 (setq guile-broken-face 'bold)
149 (setq guile-unmodified-face-1 nil)
150 (setq guile-unmodified-face-2 'modeline)
151 (setq guile-modified-face-1 'bold)
152 (setq guile-modified-face-2 guile-error-face)
153 (setq guile-broken-face-1
154 (let ((face (make-face 'broken-form-1)))
155 (copy-face guile-modified-face-1 face)
156 (set-face-underline-p face t)
157 face))
158 (setq guile-broken-face-2
159 (let ((face (make-face 'broken-form-2)))
160 (copy-face guile-modified-face-2 face)
161 (set-face-underline-p face t)
162 face))))
163
164 (if (not (fboundp 'lisp-mode-auto-fill))
165 (defun lisp-mode-auto-fill ()
166 (if (> (current-column) (current-fill-column))
167 (if (save-excursion
168 (nth 4 (parse-partial-sexp (save-excursion
169 (beginning-of-defun)
170 (point))
171 (point))))
172 (do-auto-fill)
173 (let ((comment-start nil) (comment-start-skip nil))
174 (do-auto-fill))))))
175
176 (defconst guile-symclash-obarray-size 521)
177
178 (defconst guile-big-integer 33333333)
179
180 ;;; Mode initializers
181 ;;;
182
183 (defvar guile-inferior-scheme-frame nil)
184
185 ;; Inferior Scheme Mode
186 ;;
187 (defun guile-inferior-initialize ()
188 ;; Buffer local variables
189 (make-local-variable 'guile-eval-result)
190 (make-local-variable 'guile-eval-output)
191 (make-local-variable 'guile-last-output-end)
192 (make-local-variable 'guile-last-prompt-end)
193 (make-local-variable 'guile-define-name-marker)
194 (make-local-variable 'guile-unallowed-output)
195 (make-local-variable 'guile-define-startcol)
196 (make-local-variable 'guile-define-filler)
197 (make-local-variable 'guile-define-fillcol)
198 (set-process-sentinel (scheme-proc) (function guile-sentinel))
199 (setq comint-dispatch-alist guile-dispatch-alist)
200 (add-hook 'comint-input-filter-functions
201 (function guile-sync-on-input) nil 'local)
202 (add-hook 'comint-unallowed-output-filter-functions
203 (function guile-remember-unallowed-output) nil 'local)
204 (setq comint-dynamic-complete-functions '(guile-complete-symbol))
205 (make-local-hook 'scheme-enter-input-wait-hook)
206 ;; Some initializations
207 (setq scheme-ready-p nil)
208 (setq scheme-load-p nil)
209 (setq guile-no-stack-p nil)
210 (setq guile-no-source-p nil)
211 (setq guile-last-output-end (make-marker))
212 (setq guile-last-prompt-end (make-marker))
213 (setq guile-input-sent-p t)
214 (setq guile-define-name-marker (make-marker))
215 (setq guile-error-p nil)
216 (setq guile-sexp-overlay nil)
217 (setq guile-frame-overlay nil)
218 (let ((enhanced (guile-get-enhanced-buffers)))
219 (and scheme-buffer (guile-detach-all))
220 (for-each (function guile-normal-edit) enhanced)
221 (guile-kill-overlays)
222 (for-each (function (lambda (buffer)
223 (save-excursion
224 (set-buffer buffer)
225 (guile-enhanced-edit
226 buffer
227 (not scheme-buffer-modified-p)))))
228 enhanced))
229 (setq guile-synchronizedp t)
230 (setq comint-allow-output-p t)
231 (setq guile-unallowed-output nil)
232 )
233
234 (defvar default-handle-switch-frame-binding
235 (lookup-key global-map [switch-frame]))
236 (define-key global-map [switch-frame] 'guile-handle-switch-frame)
237
238 (defun guile-handle-switch-frame (event)
239 (interactive "e")
240 (let ((frame (nth 1 event)))
241 (if (eq frame guile-inferior-scheme-frame)
242 (guile-sync-with-scheme))
243 (funcall default-handle-switch-frame-binding frame)))
244
245 (defun guile-sync-on-input (string)
246 (if scheme-load-p
247 (progn
248 nil))
249 (setq guile-error-p nil) ;; What is this??? *fixme*
250 (guile-sync-with-scheme)
251 (if guile-error-p
252 (progn
253 ;; The read-only-overlay extends during transfer of error and
254 ;; backtrace information. Check why! *fixme*
255 (let ((inhibit-read-only t))
256 (comint-kill-input))
257 ;; By generating an error we interrupt the execution
258 ;; of the comint-input-filter-functions hook.
259 (error "Bad expression! Please correct."))))
260
261 (defvar guile-unallowed-output nil)
262
263 (defun guile-remember-unallowed-output (string)
264 (if guile-unallowed-output
265 (setq guile-unallowed-output
266 (concat guile-unallowed-output string))))
267
268 (add-hook 'inferior-scheme-mode-hook (function guile-inferior-initialize))
269
270 ;; Scheme Mode
271 ;;
272 (defvar scheme-buffer-overlays ()
273 "The overlays containing top-level sexps when in enhanced edit mode.
274 A nil value indicates that the buffer is not in enhanced edit mode.")
275
276 (defvar scheme-buffer-last-overlay nil
277 "When in enhanced edit mode, this variable contains the lowermost
278 overlay.")
279
280 (defvar scheme-buffer-modified-p nil
281 "Non-nil if any overlay has been modified since last synchronization.")
282
283 (defvar scheme-buffer-overlays-modified-p nil)
284
285 (defvar scheme-associated-process-buffer nil
286 "The buffer of the scheme process to which this buffer is associated.
287 A value of nil means that this buffer is detached.")
288
289 (defvar scheme-overlay-repair-function nil)
290
291 (make-variable-buffer-local 'scheme-overlay-repair-function)
292
293 (defvar scheme-overlay-repair-idle-timer nil)
294
295 (defun guile-scheme-mode-initialize ()
296 "Initialize a scheme mode buffer."
297 (make-local-variable 'scheme-buffer-overlays)
298 (make-local-variable 'scheme-buffer-modified-p)
299 (make-local-variable 'scheme-buffer-last-overlay)
300 (make-local-variable 'scheme-buffer-overlays-modified-p)
301 (make-local-variable 'scheme-associated-process-buffer)
302 (make-local-variable 'guile-last-broken)
303 (make-local-variable 'guile-repair-limit)
304 (make-local-hook 'first-change-hook)
305 (add-hook 'first-change-hook (function guile-scheme-buffer-modified) nil t)
306 (make-local-hook 'kill-buffer-hook)
307 (add-hook 'kill-buffer-hook (function guile-scheme-mode-cleanup) nil t)
308 (if guile-default-enhanced-edit
309 (guile-enhanced-edit (current-buffer)
310 ;; If buffer not modified, take a chance...
311 (and (not scheme-buffer-modified-p)
312 (not (buffer-modified-p (current-buffer))))
313 ))
314 )
315
316 (add-hook 'scheme-mode-hook (function guile-scheme-mode-initialize))
317
318 (defun guile-scheme-buffer-modified ()
319 (setq scheme-buffer-modified-p t))
320
321 (defun guile-scheme-mode-cleanup ()
322 (if (guile-attachedp (current-buffer))
323 (progn
324 (guile-sync-buffer (current-buffer))
325 (guile-detach-buffer (current-buffer))))
326 (if (guile-enhancedp (current-buffer))
327 (guile-normal-edit (current-buffer))))
328
329 ;;; User interface support
330 ;;;
331
332 (defun guile-clear-transcript ()
333 "Delete all text before the last prompt in the scheme process buffer."
334 (interactive)
335 (if (or (not (buffer-name))
336 (not (string= (buffer-name) scheme-buffer)))
337 (error "This command must be issued in the scheme process buffer!"))
338 (save-excursion
339 (goto-char (or (marker-position guile-last-prompt-end)
340 (point-max)))
341 (if (re-search-backward comint-prompt-regexp nil t)
342 (goto-char (match-beginning 0))
343 (beginning-of-line))
344 (let ((inhibit-read-only t))
345 (delete-region (point-min) (point)))))
346
347 (defun guile-switch-to-scheme ()
348 "Switch to the scheme process buffer and places cursor at the end.
349 Also update the scheme process with all changes made in attached buffers."
350 (interactive)
351 (guile-sync-with-scheme)
352 ;(if (not guile-error-p)
353 ; (switch-to-scheme t))
354 (switch-to-scheme t))
355
356 ;;; Process control
357 ;;;
358 ;(defvar scheme-running-p nil
359 ; "This variable, if nil, indicates that the process is waiting for input.")
360
361 (defvar scheme-ready-p nil
362 "If non-nil, the process is waiting for input at the top-level repl.")
363
364 (defvar scheme-load-p nil)
365
366 (defvar guile-no-stack-p nil)
367
368 (defvar guile-no-source-p nil)
369
370 (defun guile-inferior-dialog (contents)
371 (let ((window (display-buffer "*scheme*")))
372 (x-popup-dialog window contents)))
373
374 (defun guile-sentinel (process reason)
375 (let ((status (process-status process)))
376 (if guile-insert-reason
377 (let ((old-buffer (current-buffer)))
378 (unwind-protect
379 (progn
380 (set-buffer (process-buffer process))
381 (goto-char (point-max))
382 (insert reason)
383 (goto-char (point-max))
384 (sit-for 0))
385 (set-buffer old-buffer))))
386 (cond ((eq status 'run)
387 (scheme-set-runlight scheme-last-runlight))
388 ((eq status 'stop)
389 (scheme-set-runlight 'stopped)
390 (if guile-popup-restart-on-stop
391 (if (guile-inferior-dialog '("The scheme process has been stopped.
392 Do you want to restart it?" ("Yes" . t) nil ("No" . nil)))
393 (continue-process process))))
394 (t
395 (guile-inferior-death-cleanup)
396 (if guile-popup-restart-on-death
397 (if (guile-inferior-dialog '("The scheme process has died.
398 Do you want to restart it?" ("Yes" . t) nil ("No" . nil)))
399 (run-scheme scheme-program-name)
400 (or guile-kill-buffer-on-death
401 (kill-buffer "*scheme*")))
402 (or guile-kill-buffer-on-death
403 (kill-buffer "*scheme*")))))))
404
405 (defun guile-inferior-death-cleanup ()
406 (scheme-set-runlight nil)
407 (setq scheme-ready-p nil)
408 (setq scheme-virtual-file-list nil)
409 (guile-detach-all))
410
411 ;; It would be too late to set this variable in the inferior-scheme-mode-hook:
412 ;;(setq comint-output-filter-function (function comint-dispatch-output-filter))
413 ;; *fixme* This should rather be done with advice.
414
415 (defun run-scheme (cmd)
416 "Run an inferior Scheme process, input and output via buffer *scheme*.
417 If there is a process already running in *scheme*, just switch to that buffer.
418 With argument, allows you to edit the command line (default is value
419 of scheme-program-name). Runs the hooks from inferior-scheme-mode-hook
420 \(after the comint-mode-hook is run).
421 \(Type \\[describe-mode] in the process buffer for a list of commands.)"
422
423 (interactive (list (if current-prefix-arg
424 (read-string "Run Scheme: " scheme-program-name)
425 scheme-program-name)))
426 (if (not (comint-check-proc "*scheme*"))
427 (let ((cmdlist (scheme-args-to-list cmd))
428 (comint-output-filter-function
429 (function comint-dispatch-output-filter)))
430 (set-buffer (apply 'make-comint "scheme" (car cmdlist)
431 nil (cdr cmdlist)))
432 (inferior-scheme-mode)))
433 (setq scheme-program-name cmd)
434 (setq scheme-buffer "*scheme*")
435 (pop-to-buffer "*scheme*")
436 ;; *fixme* Ugly to specialize `run-scheme' in this way...
437 (setq guile-inferior-scheme-frame (selected-frame)))
438
439 (defun guile-restart-scheme ()
440 (interactive)
441 (let ((old-buffer (current-buffer)))
442 (unwind-protect
443 (progn
444 (set-buffer scheme-buffer)
445 (let ((attached-buffers inferior-scheme-associated-buffers))
446 (guile-shutdown)
447 (let ((inhibit-read-only t))
448 (erase-buffer))
449 (setq comint-allow-output-p t)
450 (run-scheme scheme-program-name)
451 ;(sit-for 0 200)
452 (for-each (function (lambda (buffer)
453 (if (buffer-name buffer)
454 (guile-attach-buffer buffer))))
455 (reverse attached-buffers))))
456 (set-buffer old-buffer))))
457
458 (defun guile-shutdown ()
459 (interactive)
460 (let ((guile-popup-restart-on-death nil)
461 (old-buffer (current-buffer)))
462 (unwind-protect
463 (progn
464 (set-buffer scheme-buffer)
465 (setq comint-allow-output-p nil) ; Hide output
466 (setq guile-unallowed-output nil)
467 (if scheme-ready-p
468 (let ((inhibit-read-only t))
469 (comint-kill-input)
470 (comint-send-string (scheme-proc) "(quit)\n")
471 (let ((countdown 5))
472 (while (and scheme-ready-p (> countdown 0))
473 (sit-for 0 300)
474 (setq countdown (1- countdown))))))
475 (sit-for 0 100)
476 (if (comint-check-proc "*scheme*")
477 (progn
478 (kill-process (scheme-proc))
479 (while (comint-check-proc "*scheme*")
480 (sit-for 0 300))))
481 (sit-for 0 100))
482 (set-buffer old-buffer))))
483
484 (defun guile-exit-scheme ()
485 "Stop the running scheme process and kill the corresponding window"
486 (interactive)
487 (guile-shutdown)
488 (if (not (comint-check-proc "*scheme*"))
489 (kill-buffer "*scheme*")))
490
491 ;;; Basic process protocol
492
493 (setq guile-dispatch-alist
494 '((?f scheme-exit-input-wait scheme:simple-action)
495 (?l scheme-load-acknowledge scheme:simple-action)
496 (?r scheme-enter-read scheme:simple-action)
497 (?s scheme-enter-input-wait scheme:simple-action)
498 (?B guile-receive-backtrace scheme:buffer-action)
499 (?F guile-receive-error scheme:buffer-action)
500 (?x guile-receive-result scheme:string-action)
501 (?S guile-no-stack scheme:simple-action)
502 (?R guile-no-source scheme:simple-action)
503 ))
504
505 (defun scheme:simple-action (action)
506 (setq comint-dispatch-state 'idle)
507 (funcall action))
508
509 (defun scheme:string-action (action)
510 (setq comint-string-receiver action)
511 (setq comint-string-accumulator "")
512 (setq comint-dispatch-state 'reading-string))
513
514 (defun scheme:buffer-action (action)
515 (setq comint-buffer-receiver action)
516 (setq comint-receiving-buffer (generate-new-buffer "*receiving-buffer*"))
517 (setq comint-dispatch-state 'reading-to-buffer))
518
519 ;;; Guile protocol
520
521 (defun guile-no-stack ()
522 (setq guile-no-stack-p t))
523
524 (defun guile-no-source ()
525 (setq guile-no-source-p t))
526
527 (defvar guile-eval-result nil)
528 (defvar guile-eval-output nil)
529
530 (defun guile-receive-result (string)
531 (setq comint-allow-output-p nil)
532 (setq guile-eval-result string)
533 (setq guile-eval-output guile-unallowed-output)
534 (setq guile-unallowed-output nil))
535
536 (defun guile-eval (sexp &optional stringp)
537 (let ((process (scheme-proc)) ;*fixme*
538 (comint-input-filter-functions '())
539 (comint-output-filter-functions '()))
540 (if (not scheme-ready-p)
541 (error "Scheme process not ready to receive commands."))
542 (setq guile-eval-result nil)
543 (comint-send-string process
544 (format "(%%%%emacs-eval-request '%S)\n" sexp))
545 (while (not guile-eval-result)
546 (accept-process-output process))
547 (while (not scheme-ready-p)
548 (accept-process-output process))
549 (if stringp
550 guile-eval-result
551 (car (read-from-string guile-eval-result)))))
552
553 (defun scheme-set-runlight (runlight)
554 (setq inferior-scheme-mode-line-process
555 (or runlight "no process"))
556 (setq scheme-last-runlight runlight)
557 (if guile-show-runlight-in-scheme-mode
558 (let ((old-buffer (current-buffer))
559 (buffers inferior-scheme-associated-buffers))
560 (unwind-protect
561 (while buffers
562 (set-buffer (car buffers))
563 (setq scheme-mode-line-process runlight)
564 (setq buffers (cdr buffers)))
565 (set-buffer old-buffer))))
566 (force-mode-line-update t))
567
568 (defconst scheme-runlight:running "eval"
569 "The character displayed when the Scheme process is running.")
570
571 (defconst scheme-runlight:input "ready"
572 "The character displayed when the Scheme process is waiting for input.")
573
574 (defconst scheme-runlight:read "input"
575 "The character displayed when the Scheme process is waiting for input.")
576
577 (defconst scheme-runlight:load "loading"
578 "The character displayed when the Scheme process is loading forms.")
579
580 (defvar guile-last-output-end)
581
582 (setq count 0)
583 (defun scheme-enter-input-wait ()
584 (scheme-set-runlight scheme-runlight:input)
585 (setq scheme-running-p nil)
586 (setq scheme-ready-p t)
587 (setq count (1+ count))
588 ;(insert-before-markers (format "#%d\n" count))
589 ;(setq n (1+ n)
590 ; l (append l (list (list n 'enter-input-wait))))
591 (if comint-allow-output-p
592 (progn
593 (set-marker guile-last-output-end (point))
594 (if (and guile-input-sent-p
595 ;; This code can be invoked multiple times
596 (or (not (marker-position guile-last-prompt-end))
597 (/= (marker-position guile-last-prompt-end)
598 (point))))
599 (progn
600 (setq guile-input-sent-p nil)
601 (set-marker guile-last-prompt-end (point))))))
602 (setq comint-allow-output-p t)
603 (run-hooks 'scheme-enter-input-wait-hook))
604
605 (defun guile-on-error ()
606 (setq guile-input-sent-p t) ;*fixme*
607 (if comint-allow-output-p
608 (progn
609 (goto-char (point-max))
610 (if (not (zerop (current-column)))
611 (insert "\n"))
612 (set-marker (process-mark (get-buffer-process scheme-buffer))
613 (point)))))
614
615 (defun scheme-exit-input-wait ()
616 (scheme-set-runlight scheme-runlight:running)
617 (setq scheme-ready-p nil)
618 (setq scheme-running-p t))
619
620 (defun scheme-enter-read ()
621 (scheme-set-runlight scheme-runlight:read)
622 (setq scheme-ready-p nil)
623 (setq scheme-running-p nil))
624
625 (defun scheme-enter-load ()
626 (scheme-set-runlight scheme-runlight:load)
627 (setq scheme-ready-p nil)
628 (setq scheme-load-p t))
629
630 (defun scheme-load-acknowledge ()
631 (setq scheme-load-p nil))
632
633 ;;; Error reporting and backtrace
634 ;;;
635 (defvar guile-error-p nil)
636
637 (defvar guile-last-displayed-position nil)
638
639 (defvar guile-positional-reliability nil)
640
641 (defvar guile-last-erring-overlay nil)
642
643 (defvar guile-sexp-overlay nil)
644
645 (defvar guile-frame-overlay nil)
646
647 ;(defconst guile-position-regexp
648 ; " at line \\([0-9]+\\), column \\([0-9]+\\) in file \\(.+\\):$")
649 (defconst guile-position-regexp
650 "^\\(.+\\):\\([0-9]+\\):\\([0-9]+\\): ")
651
652 (defconst guile-position-regexp-line 2)
653 (defconst guile-position-regexp-column 3)
654 (defconst guile-position-regexp-filename 1)
655
656 (defvar guile-error-width 0)
657 (defvar guile-backtrace-length nil)
658 (defvar guile-backtrace-width 0)
659
660 (defvar guile-error-map nil)
661 (if guile-error-map
662 nil
663 (setq guile-error-map ;(copy-keymap global-map) copies menus too...
664 (cons 'keymap (copy-sequence (nth 1 global-map))))
665 (suppress-keymap guile-error-map)
666 (define-key guile-error-map "\e" 'guile-exit-debug)
667 (define-key guile-error-map "e" 'guile-frame-eval)
668 (define-key guile-error-map "q" 'guile-exit-debug)
669 ;; The following line is included since `local-map' doesn't seem to work.
670 (define-key guile-error-map [mouse-2] 'guile-select-stackframe)
671 (define-key guile-error-map [S-mouse-2] 'guile-frame-eval-at-click)
672 )
673
674 (defvar guile-stack-frame-map nil)
675 (if guile-stack-frame-map
676 nil
677 (setq guile-stack-frame-map (copy-list guile-error-map))
678 (fset 'guile-stack-frame-map guile-stack-frame-map) ;*fixme*
679 (define-key guile-stack-frame-map [mouse-2] 'guile-select-stackframe)
680 )
681
682 (setplist 'guile-backtrace-button
683 (list 'mouse-face guile-backtrace-mouse-face
684 'local-map 'guile-stack-frame-map))
685
686 (defun guile-exit-debug ()
687 (interactive)
688 (if (eq (selected-frame) guile-error-frame)
689 (iconify-frame)
690 (if guile-sexp-overlay
691 (delete-overlay guile-sexp-overlay))
692 (delete-other-windows (frame-first-window)))
693 (guile-unselect-stackframe))
694
695 (setq guile-backtrace-received-p nil) ;*fixme*
696
697 (defun guile-receive-backtrace (buffer)
698 (let ((backtrace (get-buffer-create guile-backtrace-buffer-name)))
699 (save-excursion
700 (set-buffer backtrace)
701 (toggle-read-only 0)
702 (erase-buffer)
703 (insert-buffer-substring buffer)
704 (kill-buffer buffer)
705 (use-local-map guile-error-map)
706 (toggle-read-only 1)
707 (setq truncate-lines t)
708 (setq guile-backtrace-received-p t)))) ;*fixme*
709
710 (defun guile-prep-backtrace ()
711 (guile-unselect-stackframe)
712 (let ((buffer (get-buffer-create guile-backtrace-buffer-name)))
713 (and guile-got-backtrace-p ;*fixme*
714 (save-excursion
715 (set-buffer buffer)
716 (set-syntax-table scheme-mode-syntax-table)
717 (toggle-read-only 0)
718 (goto-char (point-max))
719 (delete-backward-char 1)
720 (goto-char (point-min))
721 ;; Parse
722 (save-match-data
723 (if (not (looking-at "\\(.\\|\n\\)*Backtrace:\n"))
724 nil
725 (replace-match "")
726 (let ((beg (point))
727 (width 0)
728 (len 0))
729 (while (not (eobp))
730 (forward-line 1)
731 (let ((o (make-overlay beg (point)))) ;(1- (point))
732 (overlay-put o 'category 'guile-backtrace-button)
733 (overlay-put o 'frame-number-pos beg))
734 (setq width (- (point) beg 1))
735 (if (> width guile-backtrace-width)
736 (setq guile-backtrace-width width))
737 (setq beg (point))
738 (setq len (1+ len)))
739 (setq guile-backtrace-length len))))
740 (toggle-read-only 1)))
741 buffer))
742
743 (defvar guile-selected-frame nil)
744
745 (defun guile-select-stackframe (click)
746 (interactive "e")
747 (setq guile-no-stack-p nil)
748 (setq guile-no-source-p nil)
749 (let* ((frame (save-excursion
750 (mouse-set-point click)
751 (goto-char (get-char-property (point) 'frame-number-pos))
752 (guile-place-frame-overlay)
753 (let ((start (point)))
754 (skip-chars-forward " ")
755 (skip-chars-forward "0-9")
756 (if (= (char-after) ?:)
757 ;; new backtrace format
758 (progn
759 (forward-char)
760 (skip-chars-forward " ")
761 (setq start (point))
762 (skip-chars-forward "0-9")))
763 (string-to-number (buffer-substring-no-properties start (point))))))
764 (oldpos (save-excursion
765 (set-buffer scheme-buffer)
766 (guile-eval `(%%emacs-select-frame ,frame))))
767 (pos (and oldpos (list (nth 0 oldpos)
768 (1+ (nth 1 oldpos)) ;Increment line number
769 (nth 2 oldpos)))))
770 (setq guile-selected-frame frame)
771 (cond (pos (if guile-source-window ;This is just insane *fixme*
772 (apply 'guile-display-scheme-sexp
773 (append pos (list guile-source-window t)))
774 (guile-display-error (get-buffer guile-error-buffer-name)
775 (get-buffer guile-backtrace-buffer-name)
776 pos)))
777 (guile-no-stack-p (message "No stack."))
778 (guile-no-source-p (message "No source.")))))
779
780 (defun guile-unselect-stackframe ()
781 (guile-turn-off-frame-overlay)
782 (setq guile-selected-frame nil))
783
784 (defun guile-frame-eval (string)
785 (interactive "sEval: ")
786 (if (not guile-selected-frame)
787 (message "No frame selected.")
788 (setq guile-no-stack-p nil)
789 (setq guile-no-source-p nil)
790 (let ((res (save-excursion
791 (set-buffer scheme-buffer)
792 (guile-eval `(%%emacs-frame-eval ,guile-selected-frame
793 ,string)))))
794 (cond (guile-no-stack-p (message "No stack."))
795 (guile-no-source-p (message "No source."))
796 ((eq (car res) 'result) (message "%s = %s" string (cadr res)))
797 (t (message "%s" (cadr res)))))))
798
799 (defun guile-frame-eval-at-click (click)
800 (interactive "e")
801 (save-excursion
802 (mouse-set-point click)
803 (forward-sexp)
804 (let ((end (point)))
805 (backward-sexp)
806 (guile-frame-eval (buffer-substring-no-properties (point) end)))))
807
808 (defun guile-receive-error (buffer)
809 (guile-on-error)
810 (setq guile-got-backtrace-p guile-backtrace-received-p)
811 (setq guile-backtrace-received-p nil) ;*fixme*
812 (setq guile-error-p t)
813 (let ((errbuf (get-buffer-create guile-error-buffer-name)))
814 (save-excursion
815 (set-buffer errbuf)
816 (toggle-read-only 0)
817 (erase-buffer)
818 (insert-buffer-substring buffer)
819 (kill-buffer buffer)
820 (use-local-map guile-error-map)
821 (toggle-read-only 1)
822 (setq guile-error-width 0)
823 (goto-char (point-min))
824 (let ((beg (point))
825 (width 0))
826 (while (not (eobp))
827 (forward-line 1)
828 (setq width (- (point) beg 1))
829 (if (> width guile-error-width)
830 (setq guile-error-width width))
831 (setq beg (point))))
832 (setq guile-backtrace-width guile-error-width)
833 (guile-display-error errbuf (guile-prep-backtrace)))))
834
835 (defvar guile-source-window nil)
836
837 (defun guile-display-error (errbuf backbuf &optional pos)
838 (set-buffer errbuf)
839 (setq guile-source-window nil)
840 (let* ((errbuf-len (progn
841 (goto-char (point-max))
842 (1- (guile-current-line))))
843 (selected-window (selected-window))
844 (mini-window nil)
845 (window
846 (if pos
847 (apply 'guile-display-scheme-sexp pos)
848 (and (progn
849 (goto-char (point-min))
850 (re-search-forward guile-position-regexp nil t))
851 (save-match-data
852 (guile-display-scheme-sexp
853 (car (read-from-string
854 (concat "\""
855 (match-string guile-position-regexp-filename)
856 "\"")))
857 (string-to-number (match-string guile-position-regexp-line))
858 (1- (string-to-number (match-string guile-position-regexp-column))))))))
859 (errbuf-lines
860 (min (+ errbuf-len
861 (* 2 (/ guile-error-width
862 (if window
863 (window-width window)
864 guile-backtrace-max-width))))
865 ;;In case we get big error messages
866 (/ guile-backtrace-max-height 2)))
867 (total-height
868 (if guile-got-backtrace-p
869 (min (max (+ guile-backtrace-length errbuf-lines 2)
870 guile-backtrace-min-height)
871 guile-backtrace-max-height)
872 (+ errbuf-lines 1))))
873 (if (and window guile-backtrace-in-source-window)
874 (progn
875 (set-buffer errbuf) ;*fixme* This is awkward...
876 (or pos
877 (let ((inhibit-read-only t))
878 (replace-match "")
879 (re-search-forward guile-position-regexp nil t)
880 (replace-match "")))
881 (setq guile-source-window window) ;*fixme*
882 (and (frame-live-p guile-error-frame)
883 (make-frame-invisible guile-error-frame))
884 (let* ((window-min-height 2)
885 (size (max (- (window-height window) total-height)
886 (/ (window-height window) 2)))
887 (new-window (split-window window size)))
888 (set-buffer (window-buffer window))
889 (goto-char guile-last-displayed-position)
890 (guile-safe-forward-sexp)
891 (recenter (/ size 2))
892 (setq x errbuf-lines)
893 (guile-display-buffers errbuf (1+ errbuf-lines) backbuf new-window
894 pos)))
895 (setq guile-source-window nil)
896 (guile-display-buffers
897 errbuf (1+ errbuf-lines) backbuf
898 (setq mini-window
899 (guile-get-create-error-window
900 total-height
901 (+ (min (max guile-backtrace-width
902 guile-backtrace-min-width)
903 guile-backtrace-max-width)
904 2)))
905 pos))
906 (cond ((window-live-p selected-window)
907 (select-window selected-window))
908 ((window-live-p window)
909 (select-window window))
910 ((window-live-p mini-window)
911 (select-window mini-window)))
912 ;; Warn if unreliable position
913 (if (and window (not guile-positional-reliability))
914 (message "Warning: Couldn't reliably locate erring expression."))
915 ))
916
917 (defun guile-display-buffers (buffer1 split buffer2 window no-ding)
918 "Display BUFFER1 and BUFFER2 in WINDOW and raise the containing frame.
919 Display BUFFER1 and BUFFER2 in two windows obtained by splitting WINDOW
920 and ring the bell. Make sure that the whole contents of BUFFER1 and the
921 lower part of BUFFER2 will be visible. Also delete all other windows
922 displaying the buffers."
923 ;; Delete other windows displaying the buffers
924 (or (not window-system) (delete-windows-on buffer1)) ; *fixme*
925 (delete-windows-on buffer2)
926 ;; Split the window
927 (let ((lower-window
928 (and guile-got-backtrace-p
929 (let ((window-min-height 2) ;; Parameter to split-window
930 )
931 (split-window window split)))))
932 ;; Contents
933 (set-window-buffer window buffer1)
934 (and guile-got-backtrace-p
935 (set-window-buffer lower-window buffer2))
936 ;; Look
937 (set-window-start window 1)
938 (if guile-got-backtrace-p
939 (progn
940 (let ((pos (save-excursion
941 (set-buffer buffer2)
942 (goto-char (point-max))
943 (forward-line -1)
944 (point))))
945 (set-window-point lower-window pos))
946 (select-window lower-window)
947 (recenter -1)))
948 ;; Raise frame
949 (make-frame-visible (window-frame window))
950 (raise-frame (window-frame window))
951 ;; Beep
952 (or no-ding (ding))
953 ))
954
955 (defvar guile-error-frame nil)
956
957 (defun guile-get-create-error-window (height width)
958 (if window-system
959 (progn
960 (if (frame-live-p guile-error-frame)
961 (set-frame-size guile-error-frame width height)
962 (setq guile-error-frame (make-frame (list (cons 'height height)
963 (cons 'width width)
964 '(minibuffer . nil)
965 '(menu-bar-lines . 0)))))
966 (let ((window (frame-first-window guile-error-frame)))
967 (delete-other-windows window)
968 window))
969 (let ((window (get-buffer-window (pop-to-buffer guile-error-buffer-name))))
970 (sit-for 0) ; necessary because of an Emacs bug
971 window)))
972
973 (defun guile-display-scheme-sexp (filename line column &optional swindow no-error-p)
974 (let ((finfo (scheme-virtual-file-list-find filename)))
975 (if finfo
976 (guile-display-sexp finfo line column swindow no-error-p)
977 (if (stringp filename)
978 (let ((buffer (guile-get-file-buffer filename)))
979 (if buffer
980 (if (and (guile-attachedp buffer)
981 (not guile-known-by-scheme))
982 (progn
983 ;(ding) ; We shouldn't generate errors inside a filter.
984 ;(message "Internal data structures corrupt: guile-display-scheme-sexp")
985 (error "Internal data structures corrupt: guile-display-scheme-sexp"))
986 (if (and (not scheme-buffer-modified-p)
987 (not (buffer-modified-p buffer)))
988 ;; Take a chance and let's hope the file looks
989 ;; like it did when scheme saw it...
990 (progn
991 (if guile-auto-attach
992 (guile-attach-buffer buffer t)
993 ;*fixme*
994 (guile-dont-attach-buffer buffer t))
995 (guile-display-scheme-sexp
996 (guile-buffer-file-name buffer) line column swindow no-error-p))
997 nil ; Can't trust this one...
998 ))
999 (if (guile-file-readable-p filename)
1000 (let ((guile-known-by-scheme t))
1001 (let ((buffer (guile-find-file-noselect filename)))
1002 (if guile-auto-attach
1003 (guile-attach-buffer buffer t)
1004 ;*fixme*
1005 (guile-dont-attach-buffer buffer t))
1006 (guile-display-scheme-sexp
1007 (guile-buffer-file-name buffer)
1008 line column swindow no-error-p)))
1009 (ding)
1010 (message "Couldn't find the erring file.")
1011 nil)))))))
1012
1013 (defun guile-file-readable-p (filename)
1014 (save-excursion
1015 (set-buffer scheme-buffer)
1016 (file-readable-p filename)))
1017
1018 (defun guile-find-file-noselect (filename)
1019 (save-excursion
1020 (set-buffer scheme-buffer)
1021 (find-file-noselect filename)))
1022
1023 (defun guile-display-sexp (finfo line column &optional swindow no-error-p)
1024 ;; Returns the window containing the displayed sexp
1025 (let ((overlay-list (cdr finfo))
1026 (overlay nil))
1027 ;; Select an overlay candidate
1028 (while overlay-list
1029 (if (not (overlay-get (car overlay-list) 'original-line))
1030 (setq overlay-list (cdr overlay-list))
1031 (if (>= line (overlay-get (car overlay-list) 'original-line))
1032 (progn
1033 (setq overlay (car overlay-list))
1034 (setq overlay-list nil))
1035 (setq overlay-list (cdr overlay-list)))))
1036 (let ((buffer (and overlay (overlay-buffer overlay))))
1037 (if buffer
1038 (progn
1039 (set-buffer buffer)
1040 (guile-goto-position line column overlay)
1041 (if (< (point) (overlay-end overlay))
1042 (progn
1043 (setq guile-positional-reliability
1044 (not (overlay-get overlay 'modifiedp)))
1045 (if (not (eq (char-syntax (following-char)) ?\())
1046 (progn
1047 (setq guile-positional-reliability nil)
1048 (goto-char (overlay-start overlay))))
1049 (setq guile-last-erring-overlay overlay)
1050 (guile-display-sexp-at-point swindow no-error-p))))))))
1051
1052 (defun guile-display-sexp-at-point (&optional swindow no-error-p)
1053 "Move sexp overlay to sexp at point and display window.
1054 Returns the displayed window."
1055 (let ((start (point))
1056 (end nil))
1057 (save-excursion
1058 (setq end
1059 (if (guile-safe-forward-sexp)
1060 (point)
1061 (goto-char (1+ start))
1062 (if (re-search-forward "^\\((\\|$\\)" nil t)
1063 (1- (match-beginning 0))
1064 (point-max)))))
1065 (if (overlayp guile-sexp-overlay)
1066 (move-overlay guile-sexp-overlay start end (current-buffer))
1067 (setq guile-sexp-overlay (make-overlay start end))
1068 (overlay-put guile-sexp-overlay 'category 'guile-error-sexp))
1069 (if (window-live-p swindow)
1070 (set-window-buffer swindow (current-buffer)))
1071 (guile-display-position start nil swindow no-error-p)))
1072
1073 (setplist 'guile-error-sexp
1074 (list 'face guile-error-face
1075 'evaporate t
1076 'modification-hooks '(guile-turn-off-sexp-overlay)
1077 'insert-behind-hooks '(guile-turn-off-sexp-overlay)))
1078
1079 (setplist 'guile-stack-frame
1080 (list 'face guile-error-face
1081 'mouse-face guile-error-face
1082 'evaporate t
1083 'modification-hooks '(guile-turn-off-frame-overlay)
1084 'insert-behind-hooks '(guile-turn-off-frame-overlay)))
1085
1086 (defun guile-place-frame-overlay ()
1087 (let ((end (save-excursion (forward-line) (point))))
1088 (if (and guile-frame-overlay (overlayp guile-frame-overlay))
1089 (move-overlay guile-frame-overlay (point) end)
1090 (setq guile-frame-overlay (make-overlay (point) end)))
1091 (overlay-put guile-frame-overlay 'category 'guile-stack-frame)))
1092
1093 (defun guile-turn-off-sexp-overlay (&rest args)
1094 (cond (guile-sexp-overlay (delete-overlay guile-sexp-overlay))
1095 ;; For stability.
1096 ((overlayp (car args)) (delete-overlay (car args)))))
1097
1098 (defun guile-turn-off-frame-overlay (&rest args)
1099 (cond (guile-frame-overlay (delete-overlay guile-frame-overlay))
1100 ;; For stability.
1101 ((overlayp (car args)) (delete-overlay (car args)))))
1102
1103 (defun guile-display-position (pos &optional buffer swindow no-delete-p)
1104 "Display position POS in BUFFER.
1105 If BUFFER is omitted, the current buffer is used.
1106 Returns the displaying window."
1107 (let ((buffer (or buffer (current-buffer))))
1108 (set-buffer buffer)
1109 (let ((window (or (and (window-live-p swindow) swindow)
1110 (get-buffer-window buffer t)
1111 (if (frame-live-p guile-error-frame)
1112 (delete-frame guile-error-frame))
1113 (display-buffer buffer))))
1114 (or no-delete-p
1115 (delete-other-windows window))
1116 (select-window window)
1117 (goto-char pos)
1118 (setq guile-last-displayed-position pos)
1119 window)))
1120
1121 (defun guile-goto-position (line column overlay)
1122 (goto-char (overlay-start overlay))
1123 (forward-line (- line (overlay-get overlay 'original-line)))
1124 (move-to-column column))
1125
1126
1127 ;;; Scheme process associated buffers
1128 ;;;
1129
1130 ;; This function must be fixed to handle rel/absol filenames
1131 (defun guile-get-file-buffer (filename)
1132 (get-file-buffer filename))
1133
1134 (defun guile-attachedp (&optional buffer)
1135 (if buffer
1136 (save-excursion
1137 (set-buffer buffer)
1138 scheme-associated-process-buffer)
1139 scheme-associated-process-buffer))
1140
1141 (defun guile-attach-buffer (buffer &optional known-by-scheme)
1142 "Put the buffer in enhanced editing mode and attach it to the scheme
1143 process: load it into scheme, and make sure to send any changes to it
1144 hereafter to scheme at synchronization points."
1145 (interactive (list (current-buffer)))
1146 (if (memq buffer inferior-scheme-associated-buffers)
1147 (error "Scheme buffer already attached!"))
1148 (if (not (guile-enhancedp buffer))
1149 (guile-enhanced-edit buffer known-by-scheme))
1150 (save-excursion
1151 (set-buffer scheme-buffer)
1152 (setq inferior-scheme-associated-buffers
1153 (cons buffer
1154 inferior-scheme-associated-buffers))
1155 (set-buffer buffer)
1156 (setq scheme-associated-process-buffer scheme-buffer)
1157 (if (not guile-show-runlight-in-scheme-mode)
1158 (setq scheme-mode-line-process "attached"))
1159 ;; Now link it to the scheme process
1160 (if (and (guile-buffer-file-name)
1161 (not (guile-virtually-linked-p (guile-buffer-file-name))))
1162 (guile-virtual-link (guile-buffer-file-name) scheme-buffer-overlays))
1163 ;; And sync.
1164 (if (not known-by-scheme)
1165 (progn
1166 (for-each (function (lambda (overlay)
1167 (overlay-put overlay 'modifiedp t)))
1168 scheme-buffer-overlays)
1169 (setq scheme-buffer-modified-p t)
1170 (setq guile-synchronizedp nil)
1171 (guile-sync-with-scheme))))
1172 ;; Rebuild menus...
1173 (force-mode-line-update))
1174
1175 ;;*fixme*
1176 (defun guile-dont-attach-buffer (buffer &optional known-by-scheme)
1177 "Put the buffer in enhanced editing mode and attach it to the scheme
1178 process: load it into scheme, and make sure to send any changes to it
1179 hereafter to scheme at synchronization points."
1180 (interactive (list (current-buffer)))
1181 (if (memq buffer inferior-scheme-associated-buffers)
1182 (error "Scheme buffer already attached!"))
1183 (if (not (guile-enhancedp buffer))
1184 (guile-enhanced-edit buffer known-by-scheme))
1185 (save-excursion
1186 ; (set-buffer scheme-buffer)
1187 ; (setq inferior-scheme-associated-buffers
1188 ; (cons buffer
1189 ; inferior-scheme-associated-buffers))
1190 (set-buffer buffer)
1191 ; (setq scheme-associated-process-buffer scheme-buffer) == attach
1192 ; (if (not guile-show-runlight-in-scheme-mode)
1193 ; (setq scheme-mode-line-process "attached"))
1194 ;; Now link it to the scheme process
1195 (if (guile-buffer-file-name)
1196 (guile-virtual-link (guile-buffer-file-name) scheme-buffer-overlays))
1197 ;; And sync.
1198 (if (not known-by-scheme)
1199 (progn
1200 (for-each (function (lambda (overlay)
1201 (overlay-put overlay 'modifiedp t)))
1202 scheme-buffer-overlays)
1203 (setq scheme-buffer-modified-p t)
1204 (setq guile-synchronizedp nil)
1205 ;(guile-sync-with-scheme)
1206 )))
1207 ;; Rebuild menus...
1208 (force-mode-line-update))
1209
1210 (defun guile-detach-buffer (buffer)
1211 "Disconnect the buffer from the scheme process."
1212 (interactive (list (current-buffer)))
1213 (save-excursion
1214 (set-buffer buffer)
1215 ;; Unlink any virtual overlay files associated with the buffer...
1216 ;(let ((overlays scheme-buffer-overlays))
1217 ; (while overlays
1218 ; (if (guile-virtual-p (car overlays))
1219 ; (scheme-virtual-unlink (overlay-get (car overlays) 'id)))
1220 ; (setq overlays (cdr overlays))))
1221 (setq scheme-associated-process-buffer nil)
1222 (if (not guile-show-runlight-in-scheme-mode)
1223 (setq scheme-mode-line-process nil))
1224 (set-buffer scheme-buffer)
1225 (setq inferior-scheme-associated-buffers
1226 (delq buffer
1227 inferior-scheme-associated-buffers))
1228 ;(scheme-virtual-unlink (guile-buffer-file-name buffer))
1229 )
1230 (force-mode-line-update))
1231
1232 (defun guile-detach-all ()
1233 "Disconnect all buffers from the scheme process."
1234 (interactive)
1235 (save-excursion
1236 (set-buffer scheme-buffer)
1237 (while inferior-scheme-associated-buffers
1238 ;; Is it alive?
1239 (if (buffer-name (car inferior-scheme-associated-buffers))
1240 (save-excursion
1241 (set-buffer (car inferior-scheme-associated-buffers))
1242 (setq scheme-associated-process-buffer nil)
1243 (if (not guile-show-runlight-in-scheme-mode)
1244 (setq scheme-mode-line-process nil))))
1245 (setq inferior-scheme-associated-buffers
1246 (cdr inferior-scheme-associated-buffers)))))
1247
1248 ;;; Linkage of files to scheme space
1249 ;;;
1250 (defvar scheme-virtual-file-list '())
1251
1252 (defun scheme-virtual-file-list-find (name)
1253 (let ((name (file-truename name)))
1254 (assoc name scheme-virtual-file-list)))
1255
1256 (defun guile-buffer-file-name (&optional buffer)
1257 (let ((name (buffer-file-name buffer)))
1258 (and name
1259 (file-truename name))))
1260
1261 (defvar guile-synchronizedp t)
1262
1263 (defvar guile-last-virtual-id 0)
1264
1265 (defun guile-synchronizedp ()
1266 guile-synchronizedp)
1267
1268 ;;*fixme*
1269 (defun guile-alloc-virtual-id (overlay)
1270 (let ((n (setq guile-last-virtual-id (1+ guile-last-virtual-id))))
1271 (let* ((buffer (overlay-buffer overlay))
1272 (name (or (guile-buffer-file-name buffer)
1273 (buffer-name buffer))))
1274 (format "%s(%d)" name n))))
1275
1276 (defun guile-virtual-p (overlay)
1277 (overlay-get overlay 'virtualp))
1278
1279 (defun guile-virtually-linked-p (name)
1280 (scheme-virtual-file-list-find name))
1281
1282 (defun guile-virtual-link (name overlay-list)
1283 (let ((finfo (scheme-virtual-file-list-find name)))
1284 (if finfo
1285 (progn
1286 (guile-kill-overlays (cdr finfo))
1287 (setcdr finfo (copy-sequence overlay-list)))
1288 (setq scheme-virtual-file-list
1289 (cons (cons name
1290 (copy-sequence overlay-list))
1291 scheme-virtual-file-list)))))
1292
1293 (defun scheme-virtual-unlink (name)
1294 (let ((finfo (scheme-virtual-file-list-find name)))
1295 (if finfo
1296 (setq scheme-virtual-file-list
1297 (delq finfo scheme-virtual-file-list)))))
1298
1299 (defun guile-load-file (filename)
1300 "Load a Scheme file into the inferior Scheme process."
1301 (interactive (comint-get-source "Load Scheme file: " scheme-prev-l/c-dir/file
1302 scheme-source-modes t)) ; T because LOAD
1303 ; needs an exact name
1304 (if (not scheme-ready-p)
1305 (error "Scheme not ready."))
1306 (comint-check-source filename) ; Check to see if buffer needs to be saved.
1307 (setq scheme-prev-l/c-dir/file (cons (file-name-directory filename)
1308 (file-name-nondirectory filename)))
1309 (let ((old-buffer (current-buffer)))
1310 (set-buffer scheme-buffer)
1311 (setq comint-allow-output-p nil)
1312 (setq guile-unallowed-output nil)
1313 (set-buffer old-buffer))
1314 (scheme-set-runlight scheme-runlight:load)
1315 (setq scheme-ready-p nil)
1316 (comint-send-string (scheme-proc) (concat "(load \""
1317 filename
1318 "\"\)\n"))
1319 ;; Syncronize...
1320 (while (not scheme-ready-p)
1321 (accept-process-output (scheme-proc) 0 guile-process-timeout))
1322 )
1323
1324 (defun guile-reread-buffer (buffer)
1325 "Make the scheme interpreter read the buffer contents again."
1326 (interactive (list (current-buffer)))
1327 (if (not scheme-ready-p)
1328 (error "Scheme not ready."))
1329 (save-excursion
1330 (set-buffer buffer)
1331 (for-each (function (lambda (overlay)
1332 (overlay-put overlay 'modifiedp t)))
1333 scheme-buffer-overlays)
1334 (setq scheme-buffer-modified-p t))
1335 (setq guile-synchronizedp nil)
1336 (guile-sync-with-scheme))
1337
1338 (defun guile-get-associated-buffers ()
1339 (save-excursion
1340 (set-buffer scheme-buffer)
1341 inferior-scheme-associated-buffers))
1342
1343 (defvar guile-symclash-obarray (make-vector guile-symclash-obarray-size 0))
1344
1345 (defun guile-reset-symclash-obarray ()
1346 (mapatoms (function makunbound) guile-symclash-obarray))
1347
1348 (defvar guile-displayed-erring-buffers nil)
1349 (defvar guile-quiet t)
1350
1351 (defun guile-check-all ()
1352 (interactive)
1353 (setq guile-quiet t)
1354 (guile-check-all-1))
1355
1356 (defun guile-check-all-1 ()
1357 (guile-show-check-error
1358 (catch 'erroneous-overlay
1359 (guile-reset-symclash-obarray)
1360 (if (not (and guile-last-displayed-erring-overlay
1361 (eq (overlay-buffer guile-last-displayed-erring-overlay)
1362 (current-buffer))))
1363 (progn
1364 (setq guile-last-displayed-erring-overlay nil)
1365 (setq guile-displayed-erring-buffers nil)))
1366 (for-each (function (lambda (buffer)
1367 (guile-check-buffer-1 buffer)
1368 (setq guile-displayed-erring-buffers
1369 (cons buffer
1370 guile-displayed-erring-buffers))))
1371 (let ((ls (guile-get-enhanced-buffers))
1372 (rem guile-displayed-erring-buffers))
1373 (while rem
1374 (setq ls (delq (car rem) ls))
1375 (setq rem (cdr rem)))
1376 ls))
1377 nil)))
1378
1379 (defun guile-check-buffer (buffer)
1380 (interactive (list (current-buffer)))
1381 (guile-show-check-error
1382 (catch 'erroneous-overlay
1383 (save-excursion
1384 (guile-reset-symclash-obarray)
1385 (guile-check-buffer-1 buffer)
1386 ;(set-buffer old-buffer)
1387 nil))))
1388
1389 (defun guile-show-check-error (oinfo)
1390 (if (not oinfo)
1391 (progn
1392 (if guile-last-displayed-erring-overlay
1393 (message "No more errors found among buffers in enhanced editing mode!")
1394 (message "No errors found among buffers in enhanced editing mode!"))
1395 (setq guile-last-displayed-erring-overlay nil)
1396 (setq guile-displayed-erring-buffers nil)
1397 t)
1398 (setq guile-last-displayed-erring-overlay (car oinfo))
1399 (set-buffer (overlay-buffer (car oinfo)))
1400 (goto-char (overlay-start (car oinfo)))
1401 (if (not guile-quiet)
1402 (ding))
1403 (guile-display-sexp-at-point)
1404 (recenter)
1405 (message "%s" (cdr oinfo))
1406 nil))
1407
1408 (defvar guile-last-displayed-erring-overlay nil)
1409
1410 (defun guile-check-buffer-1 (buffer)
1411 (set-buffer buffer)
1412 (save-excursion
1413 (for-each (function guile-check-overlay)
1414 (let* ((ls (reverse scheme-buffer-overlays))
1415 (tail (memq guile-last-displayed-erring-overlay ls)))
1416 (if tail
1417 (cdr tail)
1418 ls)))))
1419
1420 (defconst guile-defexpr "(\\(define\\|defmacro\\)[^ \t\n()]*[ \t\n]+(*\\([^ \t\n()]+\\)")
1421 (defconst guile-defexpr-name 2)
1422
1423 (defun guile-check-overlay (overlay)
1424 (if (overlay-get overlay 'brokenp)
1425 (throw 'erroneous-overlay
1426 (cons overlay "Bad expression."))
1427 (goto-char (overlay-start overlay))
1428 (if (looking-at guile-defexpr)
1429 (let ((sym (intern (match-string guile-defexpr-name)
1430 guile-symclash-obarray)))
1431 (if (boundp sym)
1432 (let* ((overlay1 (symbol-value sym))
1433 (buffer (overlay-buffer overlay1))
1434 (line (save-excursion
1435 (set-buffer buffer)
1436 (save-excursion
1437 (goto-char (overlay-start overlay1))
1438 (guile-current-line)))))
1439 (throw 'erroneous-overlay
1440 (cons overlay
1441 (format "Symbol \"%s\" already defined in %s, line %d."
1442 sym
1443 (file-name-nondirectory
1444 (or (guile-buffer-file-name buffer)
1445 (buffer-name buffer)))
1446 line))))
1447 (set sym overlay))))))
1448
1449 (defun guile-sync-with-scheme ()
1450 (interactive)
1451 (if (and (not guile-synchronizedp)
1452 scheme-ready-p)
1453 (progn
1454 (setq guile-error-p nil)
1455 (setq guile-last-erring-overlay nil)
1456 (catch 'exit
1457 (for-each (function guile-sync-buffer-1)
1458 (guile-get-associated-buffers))
1459 (setq guile-synchronizedp t))
1460 (if guile-last-erring-overlay
1461 (progn
1462 (overlay-put guile-last-erring-overlay 'brokenp t)
1463 (overlay-put guile-last-erring-overlay
1464 'face guile-broken-face)
1465 (if guile-show-overlays-p
1466 (save-excursion
1467 (set-buffer (overlay-buffer guile-last-erring-overlay))
1468 (guile-show-overlays))))))))
1469
1470 (defun guile-sync-buffer (buffer)
1471 (interactive (list (current-buffer)))
1472 (catch 'exit
1473 (guile-sync-buffer-1 buffer)))
1474
1475 (defun guile-sync-buffer-1 (buffer)
1476 (save-excursion
1477 (set-buffer buffer)
1478 (if scheme-buffer-modified-p
1479 (progn
1480 ;; Can we do it by loading the file again?
1481 (if (and (not (buffer-modified-p buffer))
1482 (file-readable-p (guile-buffer-file-name))
1483 (not (let ((overlays scheme-buffer-overlays))
1484 (while (and overlays
1485 (not (overlay-get (car overlays) 'brokenp)))
1486 (goto-char (overlay-start (car overlays)))
1487 (overlay-put (car overlays) 'original-line
1488 (guile-current-line)) ; non-optimal *fixme*
1489 (setq overlays (cdr overlays)))
1490 overlays)))
1491 (progn
1492 (guile-load-file (guile-buffer-file-name))
1493 (if guile-error-p
1494 (progn
1495 (throw 'exit nil)))
1496 (let ((overlays scheme-buffer-overlays))
1497 (while overlays
1498 (overlay-put (car overlays) 'modifiedp nil)
1499 (setq overlays (cdr overlays)))))
1500 ;; No - we have to send the overlays separately from top to bottom
1501 (let ((overlays (reverse scheme-buffer-overlays)))
1502 (if (or (= (point-min) (point-max))
1503 (not (eq (char-syntax (char-after (point-min))) ?\()))
1504 (setq overlays (cdr overlays)))
1505 (while overlays
1506 (if (and (overlay-get (car overlays) 'modifiedp)
1507 (not (overlay-get (car overlays) 'brokenp)))
1508 (progn
1509 (guile-send-overlay (guile-alloc-finfo (car overlays)))
1510 (if guile-error-p (throw 'exit nil))))
1511 (setq overlays (cdr overlays)))))
1512 (setq scheme-buffer-modified-p nil)))
1513 (if guile-show-overlays-p
1514 (guile-show-overlays))))
1515
1516 (defun guile-alloc-finfo (overlay)
1517 (if (not (overlay-get overlay 'id))
1518 (progn
1519 (let ((finfo (scheme-virtual-file-list-find (guile-buffer-file-name))))
1520 (if finfo
1521 (setcdr finfo (delq overlay (cdr finfo)))))
1522 (guile-new-finfo overlay))
1523 (let ((finfo (assq (overlay-get overlay 'id)
1524 scheme-virtual-file-list)))
1525 (if finfo
1526 (let ((id (guile-alloc-virtual-id overlay)))
1527 (setcar finfo id)
1528 (overlay-put overlay 'id id)
1529 (overlay-put overlay 'virtualp t)
1530 finfo)
1531 (guile-new-finfo overlay)))))
1532
1533 (defun guile-new-finfo (overlay)
1534 (let* ((id (guile-alloc-virtual-id overlay))
1535 (finfo (cons id (list overlay))))
1536 (overlay-put overlay 'id id)
1537 (overlay-put overlay 'virtualp t)
1538 (goto-char (overlay-start overlay))
1539 (overlay-put overlay 'original-line (guile-current-line))
1540 (setq scheme-virtual-file-list
1541 (cons finfo scheme-virtual-file-list))
1542 finfo))
1543
1544 (defvar guile-last-prompt-end nil)
1545 (defvar guile-input-sent-p t)
1546
1547 (defun guile-send-input ()
1548 (interactive)
1549 (if (and (marker-position guile-last-prompt-end)
1550 scheme-ready-p)
1551 (let ((start (save-excursion
1552 (goto-char (point-max))
1553 (and (guile-real-safe-backward-sexp)
1554 (point)))))
1555 (if (not (and start
1556 (<= (marker-position guile-last-prompt-end) start)
1557 (guile-whitespace-between-p guile-last-prompt-end
1558 start)))
1559 (progn
1560 (insert "\n")
1561 (put-text-property (1- (point)) (point) 'face 'bold))
1562 (goto-char (point-max))
1563 (comint-send-input)
1564 (setq guile-input-sent-p t)))
1565 (comint-send-input)))
1566
1567 (defconst guile-whitespace-chars " \t\n\r\f")
1568
1569 (defun guile-whitespace-between-p (beg end)
1570 (let ((beg (if (markerp beg) (marker-position beg) beg))
1571 (end (if (markerp end) (marker-position end) end)))
1572 (if (> beg end)
1573 (let ((swap beg))
1574 (setq beg end end swap)))
1575 (save-excursion
1576 (goto-char beg)
1577 (skip-chars-forward guile-whitespace-chars end)
1578 (= (point) end))))
1579
1580 ;;*fixme* This is redundant code. Compare sync.
1581 (defun guile-send-changes ()
1582 (interactive)
1583 (setq guile-last-displayed-erring-overlay nil)
1584 (setq guile-displayed-erring-buffers nil)
1585 (setq guile-quiet nil)
1586 (if (guile-check-all-1)
1587 (progn
1588 (setq guile-error-p nil)
1589 (catch 'exit
1590 (let ((old-buffer (current-buffer)))
1591 (for-each (function
1592 (lambda (buffer)
1593 (set-buffer buffer)
1594 (save-excursion
1595 (goto-char (point-max))
1596 (let ((end (point)))
1597 (beginning-of-buffer)
1598 (guile-send-region (point) end nil t)))
1599 (if guile-show-overlays-p
1600 (guile-show-overlays))))
1601 (guile-get-enhanced-buffers))
1602 (set-buffer old-buffer))))))
1603
1604 (defun scheme-send-region (start end)
1605 "Send the current region to the inferior Scheme process."
1606 (interactive "r")
1607 (if (not (guile-enhancedp (current-buffer)))
1608 (progn
1609 (comint-send-region (scheme-proc) start end)
1610 (comint-send-string (scheme-proc) "\n"))
1611 (setq guile-error-p nil)
1612 (catch 'exit
1613 (guile-send-region start end t)
1614 (cond (guile-define-header-emitted-p
1615 (message "Defined."))
1616 (guile-last-result
1617 (guile-insert-before-prompt
1618 (concat "RESULT: " guile-last-result "\n"))
1619 (message "%s" (concat "Result: " guile-last-result)))))
1620 (if guile-show-overlays-p
1621 (guile-show-overlays))))
1622
1623 (defvar guile-define-name-marker)
1624
1625 (defun guile-insert-before-prompt (string)
1626 (save-excursion
1627 (set-buffer scheme-buffer)
1628 (save-excursion
1629 (goto-char guile-last-prompt-end)
1630 (forward-line 0) ;; ignore field boundary
1631 (let ((inhibit-read-only t)
1632 (before-prompt (point))
1633 (w (or (get-buffer-window scheme-buffer 'visible)
1634 (get-buffer-window scheme-buffer t))))
1635 (let ((w-start (and w (window-start w))))
1636 (insert-before-markers string)
1637 (if (and w (= before-prompt w-start))
1638 (let ((selected (selected-window)))
1639 (unwind-protect
1640 (progn
1641 (select-window w)
1642 (recenter))
1643 (select-window selected)
1644 (set-buffer scheme-buffer)))))))))
1645
1646 (defvar guile-define-header-emitted-p nil)
1647 (defvar guile-define-startcol 0)
1648 (defvar guile-define-filler "")
1649 (defvar guile-define-fillcol 0)
1650 (defvar guile-last-result nil)
1651
1652 (defun guile-send-region (start end send-all-p &optional multip)
1653 (if (not scheme-ready-p)
1654 (error "Scheme is not ready to receive expressions from Emacs."))
1655 (let ((overlays (reverse scheme-buffer-overlays)))
1656 (if (or (= (point-min) (point-max))
1657 (not (eq (char-syntax (char-after (point-min))) ?\()))
1658 (setq overlays (cdr overlays)))
1659 ;; First skip some overlays
1660 (while (and overlays (<= (overlay-end (car overlays)) start))
1661 (setq overlays (cdr overlays)))
1662 (setq guile-define-header-emitted-p nil)
1663 (setq guile-last-result nil)
1664 (let ((start (max start (overlay-start (car overlays)))))
1665 (if (/= start (overlay-start (car overlays)))
1666 (guile-send-overlay (save-excursion
1667 (guile-alloc-finfo (car overlays)))
1668 t
1669 multip
1670 start
1671 end)
1672 (while (and overlays
1673 (< (overlay-start (car overlays)) end))
1674 (if (and (not (overlay-get (car overlays) 'brokenp))
1675 (or send-all-p
1676 (overlay-get (car overlays) 'modifiedp)))
1677 (guile-send-overlay (save-excursion
1678 (guile-alloc-finfo (car overlays)))
1679 t
1680 multip))
1681 (setq overlays (cdr overlays)))))))
1682
1683 (defconst guile-end-of-chunk "\001\n")
1684
1685 ;; *fixme* Improve code.
1686 (defun guile-send-overlay (finfo &optional interactivep multip start end)
1687 (let* ((filename (car finfo))
1688 (overlay (car (cdr finfo)))
1689 (module-overlay (overlay-get overlay 'module-overlay))
1690 (module (or (and module-overlay
1691 (overlay-get module-overlay 'define-module))
1692 "#f"))
1693 (old-buffer (current-buffer))
1694 (old-pos (point)))
1695
1696 ;; Define the module of the overlay if not done before
1697 (if (and module-overlay
1698 (overlay-get module-overlay 'modifiedp))
1699 (guile-send-overlay (save-excursion
1700 (guile-alloc-finfo module-overlay))))
1701
1702 (set-buffer scheme-buffer)
1703 ;; Inhibit process output and hamster it
1704 (setq comint-allow-output-p nil)
1705 (setq guile-eval-output nil)
1706 (setq guile-unallowed-output "")
1707
1708 (set-buffer old-buffer)
1709 ;; Turn on runlight
1710 (scheme-enter-load)
1711 ;; Send load command
1712 (comint-send-string
1713 (scheme-proc)
1714 (if start
1715 (let ((column (save-excursion
1716 (goto-char start)
1717 (current-column))))
1718 (format "(%%%%emacs-load %S %d %d '%s #%c)\n"
1719 filename
1720 (+ (overlay-get overlay 'original-line)
1721 -1
1722 (count-lines (overlay-get overlay 'original-line)
1723 start)
1724 (if (zerop column) 0 -1))
1725 column
1726 module
1727 (if interactivep ?t ?f)))
1728 (format "(%%%%emacs-load %S %d %d '%s #%c)\n"
1729 filename
1730 (1- (overlay-get overlay 'original-line))
1731 0
1732 module
1733 (if interactivep ?t ?f))))
1734 ;; Send overlay contents
1735 (comint-send-string
1736 (scheme-proc)
1737 (buffer-substring-no-properties (or start (overlay-start overlay))
1738 (or end (overlay-end overlay))))
1739 ;; If this is the last overlay we may have to send a final newline
1740 ;;(if (and (eq overlay scheme-buffer-last-overlay)
1741 ;; (/= (overlay-start overlay)
1742 ;; (overlay-end overlay))
1743 ;; (not (eq (char-after (1- (overlay-end overlay))) ?\n)))
1744 (comint-send-string (scheme-proc) "\n")
1745 ;; Remove modified mark so that Emacs will trust its idea about positions.
1746 (or start (overlay-put overlay 'modifiedp nil))
1747 ;; Send end-of-text
1748 (comint-send-string (scheme-proc) guile-end-of-chunk)
1749 ;; Wait for acknowledge.
1750 (while (and scheme-load-p (not guile-error-p))
1751 (accept-process-output (scheme-proc) 0 guile-process-timeout))
1752
1753 ;; Have we received an error?
1754 (if guile-error-p
1755 (progn
1756 (if interactivep
1757 (save-excursion
1758 (set-buffer scheme-buffer)
1759 (let ((output guile-unallowed-output))
1760 (if (string-match "\\(^ABORT:.*\n\\)+" output)
1761 (guile-insert-before-prompt (match-string 1 output))))))
1762 (overlay-put overlay 'modifiedp t)
1763 (setq scheme-load-p nil)
1764 (throw 'exit nil))) ;Abort whatever we was doing.
1765
1766 ;; The transfer has been successful. Display defined symbol.
1767 (if interactivep
1768 (progn
1769 (goto-char (overlay-start overlay))
1770 (if (and (not (and start (/= start (overlay-start overlay))))
1771 (looking-at guile-defexpr))
1772 (progn
1773 (guile-display-name (match-string guile-defexpr-name)
1774 multip)
1775 (setq guile-last-result nil))
1776 (set-buffer scheme-buffer)
1777 (if guile-eval-output
1778 (guile-insert-before-prompt guile-eval-output))
1779 (setq guile-last-result guile-eval-result)
1780 (set-buffer old-buffer))
1781 (goto-char old-pos)
1782 (sit-for 0))
1783
1784 (goto-char old-pos))))
1785
1786 (defun guile-display-name (name multip)
1787 (save-excursion
1788 (let ((buffer-file (guile-buffer-file-name))
1789 (buffer-name (buffer-name)))
1790 (set-buffer scheme-buffer)
1791 (save-excursion
1792 (let ((inhibit-read-only t))
1793 (if (not guile-define-header-emitted-p)
1794 (let ((header
1795 (format "DEFINED:%s ()\n"
1796 (if multip
1797 (concat " "
1798 (or (and buffer-file
1799 (file-name-nondirectory
1800 buffer-file))
1801 buffer-name))
1802 ""))))
1803 (guile-insert-before-prompt header)
1804 (set-marker guile-define-name-marker
1805 (save-excursion
1806 (goto-char guile-last-prompt-end)
1807 (forward-line 0)
1808 (- (point) 2)))
1809 (setq guile-define-startcol (- (length header) 2))
1810 (setq guile-define-filler
1811 (concat "\n"
1812 (make-string guile-define-startcol ? )))
1813 (setq guile-define-fillcol
1814 (let ((window (get-buffer-window scheme-buffer t)))
1815 (if window
1816 (- (window-width window) 3)
1817 fill-column)))
1818 (setq guile-define-header-emitted-p t)))
1819 (goto-char guile-define-name-marker)
1820 (cond ((= (current-column) guile-define-startcol))
1821 ((> (+ (current-column) (length name)) guile-define-fillcol)
1822 (insert-before-markers guile-define-filler))
1823 (t (insert-before-markers " ")))
1824 (insert-before-markers name))))))
1825
1826 ;;; Enhanced editing
1827 ;;;
1828
1829 (defvar guile-n-enhanced-buffers 0
1830 "Number of buffers in enhanced edit mode.")
1831
1832 (defun guile-enhancedp (&optional buffer)
1833 (interactive)
1834 (if (not buffer)
1835 scheme-buffer-overlays
1836 (save-excursion
1837 (set-buffer buffer)
1838 scheme-buffer-overlays)))
1839
1840 (defun guile-get-enhanced-buffers ()
1841 (let ((ls (buffer-list))
1842 (ans '()))
1843 (while ls
1844 (if (guile-enhancedp (car ls))
1845 (setq ans (cons (car ls) ans)))
1846 (setq ls (cdr ls)))
1847 (reverse ans)))
1848
1849 (defun guile-enhanced-edit (buffer &optional known-by-scheme)
1850 "Put the current scheme buffer into enhanced editing mode."
1851 (interactive (list (current-buffer)))
1852 (if (guile-enhancedp buffer)
1853 (error "Already in enhanced editing mode!"))
1854 (save-excursion
1855 (set-buffer buffer)
1856 (guile-parse-buffer known-by-scheme)
1857 (setq scheme-overlay-repair-function 'guile-repair-overlays)
1858 (if (not (memq scheme-overlay-repair-idle-timer timer-idle-list))
1859 (setq scheme-overlay-repair-idle-timer
1860 (run-with-idle-timer 0.1 t 'run-hook-with-args
1861 'scheme-overlay-repair-function)))
1862 (setq guile-n-enhanced-buffers (1+ guile-n-enhanced-buffers)))
1863 (force-mode-line-update))
1864
1865 (defun guile-normal-edit (buffer)
1866 "Exit enhanced editing mode."
1867 (interactive (list (current-buffer)))
1868 (if (guile-attachedp)
1869 (error "Can't exit enhanced editing mode while attached to scheme. Detach first."))
1870 (save-excursion
1871 (set-buffer buffer)
1872 (for-each (function (lambda (overlay)
1873 (if (overlayp overlay) ; For stability's sake
1874 (progn
1875 (if (guile-virtual-p overlay)
1876 (scheme-virtual-unlink (overlay-get overlay 'id)))
1877 (delete-overlay overlay)))))
1878 scheme-buffer-overlays)
1879 (setq scheme-buffer-overlays ())
1880 (setq scheme-buffer-last-overlay nil)
1881 ;; Since we let go of the control, we have to mark the buffer...
1882 ;(setq scheme-buffer-modified-p t) Now using first-change-hook.
1883 (setq scheme-overlay-repair-function nil)
1884 (scheme-virtual-unlink (guile-buffer-file-name buffer))
1885 (setq guile-n-enhanced-buffers (1- guile-n-enhanced-buffers)))
1886 (force-mode-line-update))
1887
1888 ;;; Overlay lists
1889 ;;;
1890 ;;; Every non-broken overlay containing a sexp starts with a character
1891 ;;; with syntax ?\(.
1892 ;;; The first overlay in the overlay list is never broken.
1893
1894 (defun guile-current-line ()
1895 (+ (count-lines 1 (point))
1896 (if (= (current-column) 0) 1 0)))
1897
1898 (defun guile-safe-forward-sexp ()
1899 "Move point one sexp forwards.
1900 Returns non-nil if no error was encountered."
1901 (not (condition-case err
1902 (forward-sexp)
1903 (error err))))
1904
1905 (defun guile-safe-backward-sexp ()
1906 "Move point one sexp forwards.
1907 Returns non-nil if no error was encountered."
1908 (not (condition-case err
1909 (backward-sexp)
1910 (error err))))
1911
1912 (defun guile-real-safe-backward-sexp ()
1913 (and (guile-safe-backward-sexp)
1914 (progn
1915 (and (char-before)
1916 (char-before (1- (point)))
1917 (eq (char-before (1- (point))) ?#)
1918 (eq (char-syntax (char-before)) ?w)
1919 (forward-char -2))
1920 t)))
1921
1922 (defun guile-parse-buffer (&optional initialp)
1923 (interactive)
1924 (if (= (point-min) (point-max))
1925 ;; Apparently, the buffer is empty
1926 (progn
1927 (setq overlay (make-overlay (point-min) (point-max) nil nil t))
1928 (overlay-put overlay 'modification-hooks
1929 '(guile-handle-modification))
1930 (overlay-put overlay 'insert-behind-hooks
1931 '(rear-sticky-overlay-function guile-handle-modification))
1932 (setq scheme-buffer-overlays (list overlay))
1933 (setq scheme-buffer-last-overlay overlay))
1934 (setq scheme-buffer-last-overlay nil)
1935 (guile-reparse-buffer nil (point-min) initialp)
1936 (guile-modularize scheme-buffer-overlays)))
1937
1938 (defvar guile-tail-cons (cons nil nil))
1939
1940 (defun guile-cons-before-match (x ls)
1941 "Match X against successive elements of LS.
1942 Return cons before the one with car matching X."
1943 (if (or (null ls)
1944 (eq (car ls) x))
1945 nil
1946 (while (and (cdr ls) (not (eq (car (cdr ls)) x)))
1947 (setq ls (cdr ls)))
1948 (and (cdr ls)
1949 ls)))
1950
1951 ;; Here I've sacrificed readability for speed...
1952 ;; Geeh! What a monstrum!
1953 ;;
1954 (defun guile-reparse-buffer (start-overlay limit &optional initialp)
1955 "Reparse buffer backwards to build/update `scheme-buffer-overlays'.
1956 Start with overlay START-OVERLAY. Stop when we have passed LIMIT.
1957 If START-OVERLAY is nil parsing starts from (point-max).
1958 The optional third argument INITIALP should be non-nil if parsing
1959 for the first time. This will cause initialization of the
1960 original-line property."
1961 (let* ((tailp (and start-overlay
1962 (progn
1963 (goto-char (overlay-end start-overlay))
1964 (if (bolp)
1965 (guile-cons-before-match start-overlay
1966 scheme-buffer-overlays)
1967 (let ((after (guile-cons-before-match
1968 start-overlay
1969 scheme-buffer-overlays)))
1970 (if after
1971 (progn
1972 (overlay-put (car after) 'brokenp t)
1973 (guile-cons-before-match
1974 after
1975 scheme-buffer-overlays))))))))
1976 (tail (or tailp guile-tail-cons))
1977 (overlays (if tailp (cdr tail) scheme-buffer-overlays))
1978 (overlay nil)
1979 (first-broken nil)
1980 (last-broken nil)
1981 (last-end (if tailp
1982 (overlay-end (car (cdr tail)))
1983 (point-max))))
1984 (goto-char last-end)
1985 ;; Parse buffer backwards...
1986 (save-match-data
1987 (while (> (point) limit)
1988 ;; First try to move one sexp backwards...
1989 (if (and (guile-safe-backward-sexp)
1990 (bolp))
1991 (progn
1992 ;; Do we have it in the list?
1993 (while (and overlays
1994 (> (overlay-start (car overlays)) (point)))
1995 ;; First throw away some trash overlays...
1996 (let ((id (overlay-get (car overlays) 'id)))
1997 (delete-overlay (car overlays))
1998 (if id
1999 ;; It's a stand-alone sexp, remove it from the list
2000 (scheme-virtual-unlink id)))
2001 (setq overlays (cdr overlays)))
2002 (if (and overlays
2003 (= (overlay-start (car overlays)) (point)))
2004 ;; Yes!
2005 (progn ; Is it intact?
2006 (if (or (overlay-get (car overlays) 'brokenp)
2007 (/= (overlay-end (car overlays)) last-end))
2008 ;; No...
2009 (progn
2010 ;; Adjust it.
2011 (move-overlay (car overlays) (point) last-end)
2012 ;; Can we repair it?
2013 (if (if (bobp)
2014 (or (eolp)
2015 (eq (char-syntax (following-char)) ?\()
2016 (eq (char-syntax (following-char)) ?<)
2017 (eq (char-syntax (following-char)) ? ))
2018 (eq (char-syntax (following-char)) ?\())
2019 ;; Yes!
2020 (progn
2021 (overlay-put (car overlays) 'brokenp nil)
2022 (overlay-put (car overlays) 'face nil)
2023 (overlay-put (car overlays) 'modifiedp t)
2024 (overlay-put (car overlays)
2025 'define-module
2026 (and (looking-at "(define-module \\((.*)\\)")
2027 (condition-case err
2028 (save-excursion
2029 (goto-char (match-beginning 1))
2030 (read (current-buffer)))
2031 (error nil)))))
2032 ;; No...
2033 (overlay-put (car overlays) 'face guile-broken-face)
2034 (overlay-put (car overlays) 'modifiedp t))))
2035 ;; Link it in.
2036 (setcdr tail overlays)
2037 (setq tail (cdr tail))
2038 (setq overlays (cdr overlays)))
2039 ;; We probably have to make a new overlay...
2040 ;; First check if it's OK.
2041 (if (if (bobp)
2042 (or (eolp)
2043 (eq (char-syntax (following-char)) ?\()
2044 (eq (char-syntax (following-char)) ?<)
2045 (eq (char-syntax (following-char)) ? ))
2046 (eq (char-syntax (following-char)) ?\())
2047 ;; Everything seems OK with this one.
2048 (progn
2049 (setq overlay (make-overlay (point) last-end nil nil t))
2050 (if initialp
2051 (overlay-put overlay 'original-line
2052 (guile-current-line))
2053 (overlay-put overlay 'modifiedp t))
2054 (overlay-put overlay 'modification-hooks
2055 '(guile-handle-modification))
2056 (overlay-put overlay
2057 'define-module
2058 (and (looking-at "(define-module \\((.*)\\)")
2059 (condition-case err
2060 (save-excursion
2061 (goto-char (match-beginning 1))
2062 (read (current-buffer)))
2063 (error nil))))
2064 ;; And link it in...
2065 (setcdr tail (cons overlay overlays))
2066 (setq tail (cdr tail)))
2067 ;; But this one is broken!
2068 ;; Try to find some structure...
2069 (guile-backward-broken-sexp)
2070 (while (and overlays
2071 (> (overlay-start (car overlays)) (point)))
2072 (let ((id (overlay-get (car overlays) 'id)))
2073 (delete-overlay (car overlays))
2074 (if id
2075 (scheme-virtual-unlink id)))
2076 (setq overlays (cdr overlays)))
2077 ;; Is it possibly the first one in the overlay list?
2078 (if (and overlays
2079 (= (overlay-start (car overlays)) (point)))
2080 (progn
2081 ;; Adjust it.
2082 (move-overlay (car overlays) (point) last-end)
2083 (overlay-put (car overlays) 'face guile-broken-face)
2084 (overlay-put (car overlays) 'modifiedp t)
2085 ;; Link it in.
2086 (setcdr tail overlays)
2087 (setq tail (cdr tail))
2088 (setq overlays (cdr overlays)))
2089 ;; It wasn't - make a new overlay.
2090 (setq overlay (make-overlay (point) last-end nil nil t))
2091 (overlay-put overlay 'brokenp t)
2092 (overlay-put overlay 'face guile-broken-face)
2093 (overlay-put overlay 'modification-hooks
2094 '(guile-handle-modification))
2095 ;; And link it in...
2096 (setcdr tail (cons overlay overlays))
2097 (setq tail (cdr tail))))))
2098 ;; Broken overlay... Here we go again!
2099 (guile-backward-broken-sexp)
2100 (while (and overlays
2101 (> (overlay-start (car overlays)) (point)))
2102 (let ((id (overlay-get (car overlays) 'id)))
2103 (delete-overlay (car overlays))
2104 (if id
2105 (scheme-virtual-unlink id)))
2106 (setq overlays (cdr overlays)))
2107 (if (and overlays
2108 (= (overlay-start (car overlays)) (point)))
2109 (progn
2110 (setq overlay (car overlays))
2111 (move-overlay overlay (point) last-end)
2112 (setcdr tail overlays)
2113 (setq tail (cdr tail))
2114 (setq overlays (cdr overlays)))
2115 (setq overlay (make-overlay (point) last-end nil nil t))
2116 (overlay-put overlay 'modification-hooks
2117 '(guile-handle-modification))
2118 (setcdr tail (cons overlay overlays))
2119 (setq tail (cdr tail)))
2120 (overlay-put overlay 'brokenp t)
2121 (overlay-put overlay 'face guile-broken-face))
2122 (if (overlay-get (car tail) 'brokenp)
2123 (progn
2124 (setq first-broken (car tail))
2125 (if (not last-broken)
2126 (setq last-broken (car tail)))))
2127 (setq last-end (point))))
2128 (if (not tailp)
2129 (progn
2130 (setq scheme-buffer-overlays
2131 (cdr guile-tail-cons))
2132 ;; Don't let the rear-stickiness propagate upwards...
2133 (if scheme-buffer-last-overlay
2134 (if (not (eq (car scheme-buffer-overlays)
2135 scheme-buffer-last-overlay))
2136 (progn
2137 (overlay-put scheme-buffer-last-overlay
2138 'insert-behind-hooks
2139 nil)
2140 (overlay-put (car scheme-buffer-overlays)
2141 'insert-behind-hooks
2142 '(rear-sticky-overlay-function
2143 guile-handle-modification))))
2144 (overlay-put (car scheme-buffer-overlays)
2145 'insert-behind-hooks
2146 '(rear-sticky-overlay-function guile-handle-modification)))
2147 (setq scheme-buffer-last-overlay
2148 (car scheme-buffer-overlays))))
2149 (setq guile-last-broken last-broken)
2150 (setq guile-repair-limit
2151 (if first-broken
2152 ;(overlay-start
2153 ; (let ((ovls (memq first-broken scheme-buffer-overlays)))
2154 ; (or (and ovls (cdr ovls) (car (cdr ovls)))
2155 ; first-broken)
2156 (overlay-start first-broken)
2157 guile-big-integer)))
2158 (if guile-show-overlays-p
2159 (guile-show-overlays))
2160 )
2161
2162 (defvar guile-last-broken nil)
2163 (defvar guile-repair-limit guile-big-integer)
2164
2165 (defun guile-handle-modification (overlay after from to &optional length)
2166 (if after
2167 (progn
2168 (overlay-put overlay 'brokenp t)
2169 (setq scheme-buffer-overlays-modified-p t)
2170 (if guile-last-broken
2171 (if (< (overlay-start overlay) guile-repair-limit)
2172 (setq guile-repair-limit
2173 ;(overlay-start
2174 ; (let ((ovls (memq overlay scheme-buffer-overlays)))
2175 ; (or (and ovls (cdr ovls) (car (cdr ovls)))
2176 ; overlay)))
2177 (overlay-start overlay))
2178 (if (> (overlay-start overlay)
2179 (overlay-start guile-last-broken))
2180 (setq guile-last-broken overlay)))
2181 (setq guile-last-broken overlay)
2182 (setq guile-repair-limit
2183 ;(overlay-start
2184 ; (let ((ovls (memq overlay scheme-buffer-overlays)))
2185 ; (or (and ovls (cdr ovls) (car (cdr ovls)))
2186 ; overlay)))
2187 (overlay-start overlay))))))
2188
2189 (defun guile-repair-overlays ()
2190 (if (and (eq major-mode 'scheme-mode)
2191 scheme-buffer-overlays-modified-p)
2192 (save-excursion
2193 ;(ding)
2194 ;(message "Repair!")
2195 (setq scheme-buffer-modified-p t)
2196 (if scheme-associated-process-buffer
2197 (setq guile-synchronizedp nil))
2198 (guile-reparse-buffer guile-last-broken guile-repair-limit)
2199 (guile-modularize scheme-buffer-overlays)
2200 (setq scheme-buffer-overlays-modified-p nil))))
2201
2202 (defun guile-modularize (r-overlays)
2203 (let ((overlays (reverse r-overlays))
2204 (module nil))
2205 (while overlays
2206 (if (overlay-get (car overlays) 'define-module)
2207 (progn
2208 (overlay-put (car overlays) 'module-overlay nil)
2209 (setq module (car overlays)))
2210 (overlay-put (car overlays) 'module-overlay module))
2211 (setq overlays (cdr overlays)))))
2212
2213 (defun guile-backward-broken-sexp ()
2214 (interactive)
2215 (beginning-of-line)
2216 (let ((last (point)))
2217 (while (not (or (bobp)
2218 (and (eq (following-char) ?\()
2219 (guile-safe-backward-sexp)
2220 (bolp))))
2221 (forward-line -1)
2222 (beginning-of-line)
2223 (setq last (point)))
2224 (let ((end (point)))
2225 (goto-char (if (guile-safe-forward-sexp)
2226 last
2227 end)))))
2228
2229 ;; rear-sticky-overlay-function:
2230 ;; Put this function in the `insert-behind-hooks' of an overlay
2231 ;; in order to make the overlay rear-sticky.
2232
2233 (defun rear-sticky-overlay-function (overlay after from to &optional length)
2234 (if after
2235 (move-overlay overlay (overlay-start overlay) to)))
2236
2237 ;;; Some debugging utilities
2238 ;;;
2239
2240 (defvar guile-show-overlays-p nil)
2241
2242 (defun guile-show-overlays ()
2243 (interactive)
2244 (if (guile-enhancedp)
2245 (let ((n 1)
2246 (color nil)
2247 (previous nil)
2248 (overlays scheme-buffer-overlays))
2249 (if (null overlays)
2250 (progn
2251 (ding)
2252 (message "Empty overlay list!"))
2253 (if (not (memq 'rear-sticky-overlay-function
2254 (overlay-get (car overlays) 'insert-behind-hooks)))
2255 (progn
2256 (ding)
2257 (message "Last overlay not rear-sticky!")))
2258 (while overlays
2259 (overlay-put (car overlays)
2260 'face
2261 (if (setq color (not color))
2262 (if (overlay-get (car overlays) 'brokenp)
2263 guile-broken-face-1
2264 (if (overlay-get (car overlays) 'modifiedp)
2265 guile-modified-face-1
2266 guile-unmodified-face-1))
2267 (if (overlay-get (car overlays) 'brokenp)
2268 guile-broken-face-2
2269 (if (overlay-get (car overlays) 'modifiedp)
2270 guile-modified-face-2
2271 guile-unmodified-face-2))))
2272 (if previous
2273 (progn
2274 (if (/= (overlay-end (car overlays))
2275 (overlay-start previous))
2276 (progn (ding)
2277 (message "Bad end boundary at overlay no. %d" n)))
2278 (if (overlay-get (car overlays) 'insert-behind-hooks)
2279 (progn
2280 (ding)
2281 (message "Inner overlay no. %d rear-sticky!" n)))))
2282 (setq previous (car overlays))
2283 (setq n (1+ n))
2284 (setq overlays (cdr overlays)))
2285 (if (/= (overlay-start previous) (point-min))
2286 (progn
2287 (ding)
2288 (message "First overlay doesn't start at %d" (point-min)))))))
2289 (setq guile-show-overlays-p t))
2290
2291 (defun guile-hide-overlays ()
2292 (interactive)
2293 (let ((color nil)
2294 (overlays scheme-buffer-overlays))
2295 (while overlays
2296 (overlay-put (car overlays)
2297 'face
2298 (if (overlay-get (car overlays) 'brokenp)
2299 guile-broken-face
2300 nil))
2301 (setq overlays (cdr overlays))))
2302 (setq guile-show-overlays-p nil))
2303
2304 ;; *fixme* Consider removing this function
2305 (defun guile-kill-overlays (&optional ls)
2306 (interactive)
2307 (if (not ls)
2308 (progn
2309 (setq ls (apply (function append)
2310 (mapcar (function cdr)
2311 scheme-virtual-file-list)))
2312 (setq scheme-virtual-file-list ())))
2313 (while ls
2314 (delete-overlay (car ls))
2315 (setq ls (cdr ls))))
2316
2317 ;; *fixme* Consider removing this function
2318 (defun overlay-kill ()
2319 (interactive)
2320 (delete-overlay (car (overlays-at (point)))))
2321
2322 (defun for-each (func ls)
2323 (while ls
2324 (funcall func (car ls))
2325 (setq ls (cdr ls))))
2326
2327
2328 ;;; Completion
2329
2330 (defconst guile-symbol-chars "---A-ZÅÄÖa-zåäö0-9!$%&/=?@+*<>|-_:.")
2331
2332 (defun guile-match-symnames (word &optional exactp)
2333 (if (not word)
2334 '()
2335 (save-excursion
2336 (set-buffer scheme-buffer)
2337 (guile-eval `(map symbol->string
2338 (%%apropos-internal
2339 ,(concat "^"
2340 (regexp-quote word)
2341 (and exactp "$"))))))))
2342
2343 (defmacro guile-force-splittable (&rest forms)
2344 `(let ((f (selected-frame))
2345 (w (selected-window)))
2346 (let ((unsplittable (assq 'unsplittable (frame-parameters f)))
2347 (dedicatedp (window-dedicated-p w))
2348 (same-window-buffer-names
2349 (append same-window-buffer-names
2350 (list (buffer-name (window-buffer w))))))
2351 (unwind-protect
2352 (progn
2353 (modify-frame-parameters f '((unsplittable . nil)))
2354 (set-window-dedicated-p w nil)
2355 ,@forms)
2356 (modify-frame-parameters f (list unsplittable))
2357 (set-window-dedicated-p w dedicatedp)))))
2358
2359 (defvar guile-complete-function 'comint-dynamic-complete)
2360
2361 (defun guile-indent-or-complete ()
2362 (interactive)
2363 (let ((beg (save-excursion
2364 (beginning-of-line)
2365 (point))))
2366 (if (guile-whitespace-between-p beg (point))
2367 (funcall 'indent-for-tab-command)
2368 (funcall guile-complete-function))))
2369
2370 (defun guile-complete-symbol ()
2371 (interactive)
2372 (let ((word (comint-word guile-symbol-chars)))
2373 (if word
2374 (progn
2375 (guile-force-splittable
2376 (comint-dynamic-simple-complete word (guile-match-symnames word)))
2377 (if (string= (buffer-name) scheme-buffer)
2378 (put-text-property comint-last-output-start
2379 (point) 'face 'bold))))))
2380
2381 (defun guile-list-completions ()
2382 (interactive)
2383 (let* ((word (comint-word guile-symbol-chars))
2384 (candidates (mapcar (function (lambda (x) (list x)))
2385 (guile-match-symnames word)))
2386 (completions (all-completions word candidates)))
2387 (if (null completions)
2388 (message "No completions of %s" word)
2389 (guile-force-splittable
2390 (comint-dynamic-list-completions completions))
2391 (if (string= (buffer-name) scheme-buffer)
2392 (put-text-property comint-last-output-start (point) 'face 'bold)))))
2393
2394 ;;; Documentation
2395
2396 (defun guile-documentation-symbols ()
2397 (save-excursion
2398 (set-buffer scheme-buffer)
2399 (guile-eval '(map symbol->string (%%apropos-internal "")))))
2400
2401 (defun guile-variable-at-point (symnames)
2402 (condition-case ()
2403 (let ((stab (syntax-table)))
2404 (unwind-protect
2405 (save-excursion
2406 (set-syntax-table scheme-mode-syntax-table)
2407 (or (not (zerop (skip-syntax-backward "_w")))
2408 (eq (char-syntax (following-char)) ?w)
2409 (eq (char-syntax (following-char)) ?_)
2410 (forward-sexp -1))
2411 (skip-chars-forward "'")
2412 (let ((obj (read (current-buffer))))
2413 (and (symbolp obj) (member (symbol-name obj) symnames) obj)))
2414 (set-syntax-table stab)))
2415 (error nil)))
2416
2417 (defun guile-describe-variable (variable)
2418 "Display the full documentation of Guile variable VARIABLE."
2419 (interactive
2420 (let ((symnames (guile-documentation-symbols)))
2421 (let ((symbol (guile-variable-at-point symnames))
2422 (enable-recursive-minibuffers t)
2423 val)
2424 (setq val (completing-read (if symbol
2425 (format "Describe Guile variable (default %s): " symbol)
2426 "Describe Guile variable: ")
2427 (mapcar (lambda (s)
2428 (cons s '()))
2429 symnames)
2430 nil t))
2431 (list (if (equal val "")
2432 symbol
2433 (intern val))))))
2434 (guile-force-splittable
2435 (with-output-to-temp-buffer "*Help*"
2436 (prin1 variable)
2437 (princ ": ")
2438 (princ (save-excursion
2439 (set-buffer scheme-buffer)
2440 (guile-eval variable t)))
2441 (terpri)
2442 (terpri)
2443 (let ((doc (save-excursion
2444 (set-buffer scheme-buffer)
2445 (guile-eval `(%%emacs-symdoc ',variable)))))
2446 (if doc
2447 (princ doc)
2448 (princ "not documented")))
2449 (print-help-return-message)
2450 (save-excursion
2451 (set-buffer standard-output)
2452 (help-mode)
2453 ;; Return the text we displayed.
2454 (buffer-string)))))
2455
2456 (provide 'guile)
2457 (run-hooks 'guile-load-hook)