Commit | Line | Data |
---|---|---|
c81f296a | 1 | ;;;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. |
7dd98e0b MD |
2 | ;;;; |
3 | ;;;; This program is free software; you can redistribute it and/or modify | |
4 | ;;;; it under the terms of the GNU General Public License as published by | |
5 | ;;;; the Free Software Foundation; either version 2, or (at your option) | |
6 | ;;;; any later version. | |
7 | ;;;; | |
8 | ;;;; This program is distributed in the hope that it will be useful, | |
9 | ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | ;;;; GNU General Public License for more details. | |
12 | ;;;; | |
13 | ;;;; You should have received a copy of the GNU General Public License | |
14 | ;;;; along with this software; see the file COPYING. If not, write to | |
15328041 JB |
15 | ;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
16 | ;;;; Boston, MA 02111-1307 USA | |
7dd98e0b | 17 | ;;;; |
a482f2cc MV |
18 | ;;;; As a special exception, the Free Software Foundation gives permission |
19 | ;;;; for additional uses of the text contained in its release of GUILE. | |
20 | ;;;; | |
21 | ;;;; The exception is that, if you link the GUILE library with other files | |
22 | ;;;; to produce an executable, this does not by itself cause the | |
23 | ;;;; resulting executable to be covered by the GNU General Public License. | |
24 | ;;;; Your use of that executable is in no way restricted on account of | |
25 | ;;;; linking the GUILE library code into it. | |
26 | ;;;; | |
27 | ;;;; This exception does not however invalidate any other reasons why | |
28 | ;;;; the executable file might be covered by the GNU General Public License. | |
29 | ;;;; | |
30 | ;;;; This exception applies only to the code released by the | |
31 | ;;;; Free Software Foundation under the name GUILE. If you copy | |
32 | ;;;; code from other Free Software Foundation releases into a copy of | |
33 | ;;;; GUILE, as the General Public License permits, the exception does | |
34 | ;;;; not apply to the code that you add in this way. To avoid misleading | |
35 | ;;;; anyone as to the status of such modified files, you must delete | |
36 | ;;;; this exception notice from them. | |
37 | ;;;; | |
38 | ;;;; If you write modifications of your own for GUILE, it is your choice | |
39 | ;;;; whether to permit this exception to apply to your modifications. | |
40 | ;;;; If you do not wish that, delete this exception notice. | |
41 | ;;;; | |
7dd98e0b MD |
42 | ;;;; The author can be reached at djurfeldt@nada.kth.se |
43 | ;;;; Mikael Djurfeldt, SANS/NADA KTH, 10044 STOCKHOLM, SWEDEN | |
67da6c57 | 44 | ;;;; (I didn't write this!) |
7dd98e0b MD |
45 | ;;;; |
46 | \f | |
47 | ||
67da6c57 MD |
48 | ;;; ********************************************************************* |
49 | ;;; * This is the Guile side of the Emacs interface * | |
50 | ;;; * Experimental hACK---the real version will be coming soon (almost) * | |
51 | ;;; ********************************************************************* | |
7dd98e0b MD |
52 | |
53 | ;;; {Session support for Emacs} | |
54 | ;;; | |
55 | ||
56 | (define-module (ice-9 emacs) | |
1c215029 | 57 | :use-module (ice-9 debug) |
7dd98e0b | 58 | :use-module (ice-9 threads) |
3267d4a1 MD |
59 | :use-module (ice-9 session) |
60 | :no-backtrace) | |
7dd98e0b MD |
61 | |
62 | (define emacs-escape-character #\sub) | |
63 | ||
64 | (define emacs-output-port (current-output-port)) | |
65 | ||
66 | (define (make-emacs-command char) | |
67 | (let ((cmd (list->string (list emacs-escape-character char)))) | |
68 | (lambda () | |
69 | (display cmd emacs-output-port)))) | |
70 | ||
1c215029 MD |
71 | (define enter-input-wait (make-emacs-command #\s)) |
72 | (define exit-input-wait (make-emacs-command #\f)) | |
7dd98e0b | 73 | (define enter-read-character #\r) |
1c215029 | 74 | (define sending-error (make-emacs-command #\F)) |
7dd98e0b MD |
75 | (define sending-backtrace (make-emacs-command #\B)) |
76 | (define sending-result (make-emacs-command #\x)) | |
1c215029 MD |
77 | (define end-of-text (make-emacs-command #\.)) |
78 | (define no-stack (make-emacs-command #\S)) | |
79 | (define no-source (make-emacs-command #\R)) | |
7dd98e0b MD |
80 | |
81 | ;; {Error handling} | |
82 | ;; | |
83 | ||
84 | (add-hook! before-backtrace-hook sending-backtrace) | |
85 | (add-hook! after-backtrace-hook end-of-text) | |
86 | (add-hook! before-error-hook sending-error) | |
87 | (add-hook! after-error-hook end-of-text) | |
88 | ||
89 | ;; {Repl} | |
90 | ;; | |
91 | ||
92 | (set-current-error-port emacs-output-port) | |
93 | ||
94 | (add-hook! before-read-hook | |
95 | (lambda () | |
96 | (enter-input-wait) | |
97 | (force-output emacs-output-port))) | |
98 | ||
99 | (add-hook! after-read-hook | |
100 | (lambda () | |
101 | (exit-input-wait) | |
102 | (force-output emacs-output-port))) | |
103 | ||
104 | ;;; {Misc.} | |
105 | ||
106 | (define (make-emacs-load-port orig-port) | |
107 | (letrec ((read-char-fn (lambda args | |
108 | (let ((c (read-char orig-port))) | |
109 | (if (eq? c #\soh) | |
110 | (throw 'end-of-chunk) | |
111 | c))))) | |
112 | ||
113 | (make-soft-port | |
114 | (vector #f #f #f | |
115 | read-char-fn | |
116 | (lambda () (close-port orig-port))) | |
117 | "r"))) | |
118 | ||
3ede541d | 119 | (set-current-input-port (make-emacs-load-port (current-input-port))) |
7dd98e0b | 120 | |
8c5a8bed MD |
121 | (define (result-to-emacs exp) |
122 | (sending-result) | |
123 | (write exp emacs-output-port) | |
124 | (end-of-text) | |
125 | (force-output emacs-output-port)) | |
126 | ||
7dd98e0b MD |
127 | (define load-acknowledge (make-emacs-command #\l)) |
128 | ||
129 | (define load-port (current-input-port)) | |
130 | ||
131 | (define (flush-line port) | |
132 | (let loop ((c (read-char port))) | |
133 | (if (not (eq? c #\nl)) | |
134 | (loop (read-char port))))) | |
135 | ||
8c3420ad | 136 | (define whitespace-chars (list #\space #\tab #\nl #\np)) |
7dd98e0b MD |
137 | |
138 | (define (flush-whitespace port) | |
139 | (catch 'end-of-chunk | |
140 | (lambda () | |
141 | (let loop ((c (read-char port))) | |
142 | (cond ((eq? c the-eof-object) | |
40fa5c3f | 143 | (error "End of file while receiving Emacs data")) |
7dd98e0b MD |
144 | ((memq c whitespace-chars) (loop (read-char port))) |
145 | ((eq? c #\;) (flush-line port) (loop (read-char port))) | |
146 | (else (unread-char c port)))) | |
147 | #f) | |
148 | (lambda args | |
149 | (read-char port) ; Read final newline | |
150 | #t))) | |
151 | ||
d43f8c97 | 152 | (define (emacs-load filename linum colnum module interactivep) |
7dd98e0b | 153 | (set-port-filename! %%load-port filename) |
db75135d | 154 | (set-port-line! %%load-port linum) |
1c215029 | 155 | (set-port-column! %%load-port colnum) |
7dd98e0b MD |
156 | (lazy-catch #t |
157 | (lambda () | |
158 | (let loop ((endp (flush-whitespace %%load-port))) | |
159 | (if (not endp) | |
ef0d04e5 MD |
160 | (begin |
161 | (save-module-excursion | |
162 | (lambda () | |
163 | (if module | |
164 | (set-current-module (resolve-module module #f))) | |
165 | (let ((result | |
166 | (start-stack read-and-eval! | |
167 | (read-and-eval! %%load-port)))) | |
168 | (if interactivep | |
169 | (result-to-emacs result))))) | |
7dd98e0b MD |
170 | (loop (flush-whitespace %%load-port))) |
171 | (begin | |
31264e81 MD |
172 | (load-acknowledge))) |
173 | (set-port-filename! %%load-port #f))) ;reset port filename | |
7dd98e0b | 174 | (lambda (key . args) |
31264e81 | 175 | (set-port-filename! %%load-port #f) |
7dd98e0b | 176 | (cond ((eq? key 'end-of-chunk) |
a66c53a8 | 177 | (fluid-set! the-last-stack #f) |
7dd98e0b MD |
178 | (set! stack-saved? #t) |
179 | (scm-error 'misc-error | |
8c3420ad | 180 | #f |
7dd98e0b MD |
181 | "Incomplete expression" |
182 | '() | |
183 | '())) | |
184 | ((eq? key 'exit)) | |
185 | (else | |
eb7ec1e8 | 186 | (save-stack 2) |
7dd98e0b MD |
187 | (catch 'end-of-chunk |
188 | (lambda () | |
189 | (let loop () | |
190 | (read-char %%load-port) | |
191 | (loop))) | |
192 | (lambda args | |
193 | #f)) | |
194 | (apply throw key args)))))) | |
195 | ||
1c215029 | 196 | (define (emacs-eval-request form) |
a71aca7a | 197 | (result-to-emacs (eval form (interaction-environment)))) |
1c215029 | 198 | |
52f1b046 | 199 | ;;*fixme* Not necessary to use flags no-stack and no-source |
1c215029 | 200 | (define (get-frame-source frame) |
a66c53a8 MD |
201 | (if (or (not (fluid-ref the-last-stack)) |
202 | (>= frame (stack-length (fluid-ref the-last-stack)))) | |
1c215029 MD |
203 | (begin |
204 | (no-stack) | |
205 | #f) | |
a66c53a8 MD |
206 | (let* ((frame (stack-ref (fluid-ref the-last-stack) |
207 | (frame-number->index frame))) | |
1c215029 MD |
208 | (source (frame-source frame))) |
209 | (or source | |
210 | (begin (no-source) | |
211 | #f))))) | |
212 | ||
213 | (define (emacs-select-frame frame) | |
214 | (let ((source (get-frame-source frame))) | |
215 | (if source | |
216 | (let ((fname (source-property source 'filename)) | |
217 | (line (source-property source 'line)) | |
218 | (column (source-property source 'column))) | |
219 | (if (and fname line column) | |
220 | (list fname line column) | |
221 | (begin (no-source) | |
222 | '()))) | |
223 | '()))) | |
224 | ||
52f1b046 MD |
225 | (define (object->string x . method) |
226 | (with-output-to-string | |
227 | (lambda () | |
228 | ((if (null? method) | |
229 | write | |
230 | (car method)) | |
231 | x)))) | |
232 | ||
233 | (define (format template . rest) | |
234 | (let loop ((chars (string->list template)) | |
0b6925fe MD |
235 | (result '()) |
236 | (rest rest)) | |
52f1b046 MD |
237 | (cond ((null? chars) (list->string (reverse result))) |
238 | ((char=? (car chars) #\%) | |
239 | (loop (cddr chars) | |
240 | (append (reverse | |
241 | (string->list | |
242 | (case (cadr chars) | |
243 | ((#\S) (object->string (car rest))) | |
244 | ((#\s) (object->string (car rest) display))))) | |
0b6925fe MD |
245 | result) |
246 | (cdr rest))) | |
247 | (else (loop (cdr chars) (cons (car chars) result) rest))))) | |
52f1b046 MD |
248 | |
249 | (define (error-args->string args) | |
250 | (let ((msg (apply format (caddr args) (cadddr args)))) | |
251 | (if (symbol? (cadr args)) | |
252 | (string-append (symbol->string (cadr args)) | |
253 | ": " | |
254 | msg) | |
255 | msg))) | |
256 | ||
1c215029 MD |
257 | (define (emacs-frame-eval frame form) |
258 | (let ((source (get-frame-source frame))) | |
52f1b046 MD |
259 | (if source |
260 | (catch #t | |
261 | (lambda () | |
262 | (list 'result | |
263 | (object->string | |
264 | (local-eval (with-input-from-string form read) | |
265 | (memoized-environment source))))) | |
266 | (lambda args | |
267 | (list (car args) | |
268 | (error-args->string args)))) | |
269 | (begin | |
270 | (no-source) | |
271 | '())))) | |
1c215029 MD |
272 | |
273 | (define (emacs-symdoc symbol) | |
274 | (if (or (not (module-bound? (current-module) symbol)) | |
544468de | 275 | (not (procedure? (eval symbol (interaction-environment))))) |
1c215029 | 276 | 'nil |
a71aca7a | 277 | (procedure-documentation (eval symbol (interaction-environment))))) |
1c215029 | 278 | |
7dd98e0b MD |
279 | ;;; A fix to get the emacs interface to work together with the module system. |
280 | ;;; | |
c81f296a MD |
281 | (for-each (lambda (name value) |
282 | (module-define! the-root-module name value)) | |
283 | '(%%load-port | |
284 | %%emacs-load | |
285 | %%emacs-eval-request | |
286 | %%emacs-select-frame | |
287 | %%emacs-frame-eval | |
288 | %%emacs-symdoc | |
289 | %%apropos-internal) | |
290 | (list load-port | |
291 | emacs-load | |
292 | emacs-eval-request | |
293 | emacs-select-frame | |
294 | emacs-frame-eval | |
295 | emacs-symdoc | |
296 | apropos-internal)) |