Mercurial > emacs
comparison lisp/ielm.el @ 7267:4aa70d4d981c
Initial revision
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Mon, 02 May 1994 05:16:59 +0000 |
parents | |
children | cd1541f4c87c |
comparison
equal
deleted
inserted
replaced
7266:f87808bd90e9 | 7267:4aa70d4d981c |
---|---|
1 ;;; ielm.el --- interaction mode for Emacs Lisp | |
2 ;; Copyright (C) 1994 Free Software Foundation, Inc. | |
3 | |
4 ;; Author: David Smith <maa036@lancaster.ac.uk> | |
5 ;; Created: 25 Feb 1994 | |
6 ;; Keywords: lisp | |
7 | |
8 ;; This file is part of GNU Emacs. | |
9 | |
10 ;; GNU Emacs is free software; you can redistribute it and/or modify | |
11 ;; it under the terms of the GNU General Public License as published by | |
12 ;; the Free Software Foundation; either version 2, or (at your option) | |
13 ;; any later version. | |
14 | |
15 ;; GNU Emacs is distributed in the hope that it will be useful, | |
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 ;; GNU General Public License for more details. | |
19 | |
20 ;; You should have received a copy of the GNU General Public License | |
21 ;; along with GNU Emacs; see the file COPYING. If not, write to | |
22 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
23 | |
24 ;;; Commentary: | |
25 | |
26 ;; Provides a nice interface to evaluating Emacs-Lisp expressions. | |
27 ;; Input is handled by the comint package, and output is passed | |
28 ;; through the pretty-printer. | |
29 | |
30 ;; To install: copy this file to a directory in your load-path, and | |
31 ;; add the line | |
32 ;; | |
33 ;; (autoload 'ielm "ielm" "Start an inferior emacs-lisp session" t) | |
34 ;; | |
35 ;; For completion to work, the comint.el from FSF Emacs 19.23 is | |
36 ;; required. If you do not have it, or if you are running Lemacs, | |
37 ;; also add the following code to your .emacs: | |
38 ;; | |
39 ;; (setq ielm-mode-hook | |
40 ;; '(lambda nil | |
41 ;; (define-key ielm-map "\t" | |
42 ;; '(lambda nil (interactive) (or (ielm-tab) | |
43 ;; (lisp-complete-symbol)))))) | |
44 | |
45 ;; To start: M-x ielm. Type C-h m in the *ielm* buffer for more info. | |
46 | |
47 ;; The latest version is available by WWW from | |
48 ;; http://mathssun5.lancs.ac.uk:2080/~maa036/elisp/dir.html | |
49 ;; or by anonymous FTP from | |
50 ;; /anonymous@wingra.stat.wisc.edu:pub/src/emacs-lisp/ielm.el.gz | |
51 ;; or from the author: David M. Smith <maa036@lancaster.ac.uk> | |
52 | |
53 ;;; Code: | |
54 | |
55 (require 'comint) | |
56 (require 'pp) | |
57 | |
58 ;;; User variables | |
59 | |
60 (defvar ielm-noisy t | |
61 "*If non-nil, beep on error") | |
62 | |
63 (defvar ielm-prompt "ELISP> ") | |
64 | |
65 (defvar ielm-dynamic-return t | |
66 "*If non-nil, RET either evaluates input or inserts a newline, | |
67 depending on context") | |
68 | |
69 (defvar ielm-mode-hook nil | |
70 "*Hooks to be run when the inferior-emacs-lisp-mode is started") | |
71 | |
72 ;;; System variables | |
73 | |
74 (defvar ielm-working-buffer nil | |
75 "Buffer, if any, to use in ielm. Usually buffer-local") | |
76 | |
77 (defvar ielm-header | |
78 (concat | |
79 "*** Welcome to IELM mode version " | |
80 (substring "$Revision: 1.15 $" 11 -2) | |
81 " *** Type (describe-mode) for help.\n" | |
82 "IELM has ABSOLUTELY NO WARRANTY; type (describe-no-warranty) for details\n")) | |
83 | |
84 (defvar ielm-map nil) | |
85 (if ielm-map nil | |
86 (if (string-match "Lucid" emacs-version) | |
87 ;; Lemacs | |
88 (progn | |
89 (setq ielm-map (make-sparse-keymap)) | |
90 (set-keymap-parent ielm-map comint-mode-map)) | |
91 ;; FSF | |
92 (setq ielm-map (cons 'keymap comint-mode-map))) | |
93 (define-key ielm-map "\t" 'comint-dynamic-complete) | |
94 (define-key ielm-map "\C-m" 'ielm-return) | |
95 (define-key ielm-map "\C-j" 'ielm-send-input)) | |
96 | |
97 ;;; Completion stuff | |
98 | |
99 (defun ielm-tab nil | |
100 "Possibly indent the current line as lisp code" | |
101 (interactive) | |
102 (if (or (eq (preceding-char) ?\n) | |
103 (eq (char-syntax (preceding-char)) ? )) | |
104 (progn | |
105 (ielm-indent-line) | |
106 t))) | |
107 | |
108 (defun ielm-complete-symbol nil | |
109 "Just like lisp-complete-symbol" | |
110 ;; except that it returns non-nil if completion has occurred | |
111 (let* ((btick (buffer-modified-tick)) | |
112 (cbuffer (get-buffer " *Completions*")) | |
113 (ctick (and cbuffer (buffer-modified-tick cbuffer)))) | |
114 (lisp-complete-symbol) | |
115 ;; completion has occurred if: | |
116 (or | |
117 ;; the buffer has been modified | |
118 (not (= btick (buffer-modified-tick))) | |
119 ;; a completions buffer has been modifed or created | |
120 (if cbuffer | |
121 (not (= ctick (buffer-modified-tick cbuffer))) | |
122 (get-buffer " *Completions*"))))) | |
123 | |
124 (defun ielm-complete-filename nil | |
125 ;; Completes filenames if in a string | |
126 (if (nth 3 (parse-partial-sexp comint-last-input-start (point))) | |
127 (comint-dynamic-complete-filename))) | |
128 | |
129 (defun ielm-indent-line nil | |
130 "Indent the current line as lisp code if it is not a prompt line" | |
131 (if (save-excursion | |
132 (beginning-of-line) | |
133 (looking-at comint-prompt-regexp)) nil | |
134 (lisp-indent-line))) | |
135 | |
136 ;;; Other bindings | |
137 | |
138 (defun ielm-return nil | |
139 "Evaluate the sexp at the prompt if it is complete, otherwise newline | |
140 and indent. If ielm-dynamic-return is nil, just insert a newline." | |
141 (interactive) | |
142 (if ielm-dynamic-return | |
143 (let ((state | |
144 (save-excursion | |
145 (end-of-line) | |
146 (parse-partial-sexp (ielm-pm) | |
147 (point))))) | |
148 (if (and (< (car state) 1) (not (nth 3 state))) | |
149 (ielm-send-input) | |
150 (newline-and-indent))) | |
151 (newline))) | |
152 | |
153 (defun ielm-input-sender (proc input) | |
154 (setq ielm-input input)) | |
155 | |
156 (defun ielm-send-input nil | |
157 "Evaluate the Emacs Lisp expression after the prompt" | |
158 (interactive) | |
159 (let ((buf (current-buffer)) | |
160 ielm-input) ; set by ielm-input-sender | |
161 (comint-send-input) ; update history, markers etc. | |
162 (ielm-eval-input ielm-input))) | |
163 | |
164 ;;; Utility functions | |
165 | |
166 (defun ielm-is-whitespace (string) | |
167 "Return non-nil if STRING is all whitespace" | |
168 (or (string= string "") (string-match "\\`[ \t\n]+\\'" string))) | |
169 | |
170 (defun ielm-format-errors (errlist) | |
171 (let ((result "")) | |
172 (while errlist | |
173 (setq result (concat result (prin1-to-string (car errlist)) ", ")) | |
174 (setq errlist (cdr errlist))) | |
175 (substring result 0 -2))) | |
176 | |
177 | |
178 (defun ielm-format-error (err) | |
179 "Return a string form of the error ERR" | |
180 (format "%s%s" | |
181 (or (get (car err) 'error-message) "Peculiar error") | |
182 (if (cdr err) | |
183 (format ": %s" (ielm-format-errors (cdr err))) | |
184 ""))) | |
185 | |
186 ;;; Evaluation | |
187 | |
188 (defun ielm-eval-input (string) | |
189 "Evaluate the lisp expression STRING, and pretty-print the result" | |
190 ;; This is the function that actually `sends' the input to the | |
191 ;; `inferior lisp process'. All comint-send-input does is works out | |
192 ;; what that input is. What this function does is evaluates that | |
193 ;; input and produces `output' which gets inserted into the buffer, | |
194 ;; along with a new prompt. A better way of doing this might have | |
195 ;; been to actually send the output to the `cat' process, and write | |
196 ;; this as in output filter that converted sexps in the output | |
197 ;; stream to their evaluated value. But that would have involved | |
198 ;; more process coordination than I was happy to deal with. | |
199 (let (form ; form to evaluate | |
200 pos ; End posn of parse in string | |
201 result ; Result, or error message | |
202 error-type ; string, nil if no error | |
203 (output "") ; result to display | |
204 (wbuf ielm-working-buffer) ; current buffer after evaluation | |
205 (pmark (ielm-pm))) | |
206 (if (not (ielm-is-whitespace string)) | |
207 (progn | |
208 (condition-case err | |
209 (let (rout) | |
210 (setq rout (read-from-string string)) | |
211 (setq form (car rout)) | |
212 (setq pos (cdr rout))) | |
213 (error (setq result (ielm-format-error err)) | |
214 (setq error-type "Read error"))) | |
215 (if error-type nil | |
216 (if (ielm-is-whitespace (substring string pos)) | |
217 ;; need this awful let convolution to work around | |
218 ;; an Emacs bug involving local vbls and let binding | |
219 (let ((:save :) | |
220 (::save ::) | |
221 (:::save :::)) | |
222 (save-excursion | |
223 (set-buffer ielm-working-buffer) | |
224 (condition-case err | |
225 (let ((: :save) | |
226 (:: ::save) | |
227 (::: :::save)) | |
228 (save-excursion | |
229 (setq result (eval form)) | |
230 (setq wbuf (current-buffer)))) | |
231 (error (setq result (ielm-format-error err)) | |
232 (setq error-type "Eval error")) | |
233 (quit (setq result "Quit during evaluation") | |
234 (setq error-type "Eval error"))))) | |
235 (setq error-type "IELM error") | |
236 (setq result "More than one sexp in input"))) | |
237 | |
238 ;; If the eval changed the current buffer, mention it here | |
239 (if (eq wbuf ielm-working-buffer) nil | |
240 (message "current buffer is now: %s" wbuf) | |
241 (setq ielm-working-buffer wbuf)) | |
242 | |
243 (goto-char pmark) | |
244 (if (not error-type) | |
245 (condition-case err | |
246 ;; Self-referential objects cause loops in the printer, so | |
247 ;; trap quits here. May as well do errors, too | |
248 (setq output (concat output (pp-to-string result))) | |
249 (error (setq error-type "IELM Error") | |
250 (setq result "Error during pretty-printing (bug in pp)")) | |
251 (quit (setq error-type "IELM Error") | |
252 (setq result "Quit during pretty-printing")))) | |
253 (if error-type | |
254 (progn | |
255 (if ielm-noisy (ding)) | |
256 (setq output (concat output "*** " error-type " *** ")) | |
257 (setq output (concat output result))) | |
258 ;; There was no error, so shift the ::: values | |
259 (setq ::: ::) | |
260 (setq :: :) | |
261 (setq : result)) | |
262 (setq output (concat output "\n")))) | |
263 (setq output (concat output ielm-prompt)) | |
264 (comint-output-filter (ielm-process) output))) | |
265 | |
266 ;;; Process and marker utilities | |
267 | |
268 (defun ielm-process nil | |
269 "Return the current buffer's process" | |
270 (get-buffer-process (current-buffer))) | |
271 | |
272 (defun ielm-pm nil | |
273 "Return the process mark of the current buffer" | |
274 (process-mark (get-buffer-process (current-buffer)))) | |
275 | |
276 (defun ielm-set-pm (pos) | |
277 "Set the process mark in the current buffer to POS" | |
278 (set-marker (process-mark (get-buffer-process (current-buffer))) pos)) | |
279 | |
280 ;;; Major mode | |
281 | |
282 (defun inferior-emacs-lisp-mode nil | |
283 "Major mode for interactively evaluating Emacs-Lisp expressions | |
284 Uses the interface provided by `comint-mode' (q.v.) | |
285 | |
286 \\[ielm-send-input] evaluates the sexp following the prompt. There must be at most | |
287 one top-level sexp per prompt. | |
288 \\[ielm-return] inserts a newline and indents. However, if the variable | |
289 ielm-dynamic-return is non-nil (the default) then it will also evaluate | |
290 a complete expression. | |
291 \\[comint-dynamic-complete] completes lisp symbols (or filenames, within strings), | |
292 or indents the line if there is nothing to complete. | |
293 | |
294 During evaluations, the values of the variables `:', `::', and `:::' | |
295 are the results of the previous, second previous and third previous | |
296 evaluations respectively. | |
297 | |
298 The current buffer may be changed, and its value is preserved between | |
299 successive evaluations. In this way, expressions may be evaluated in | |
300 a different buffer than the *ielm* buffer. | |
301 | |
302 Expressions evaluated by IELM are not subject to debug-on-quit or | |
303 debug-on-error. | |
304 | |
305 The behaviour of IELM may be customised with the following variables: | |
306 * To stop beeping on error, set `ielm-noisy' to nil | |
307 * If you don't like the prompt, you can change it by setting `ielm-prompt'. | |
308 * Set `ielm-dynamic-return' to nil for bindings like `lisp-interaction-mode' | |
309 * Entry to this mode runs `comint-mode-hook' and `ielm-mode-hook' | |
310 (in that order). | |
311 | |
312 Customised bindings may be defined in `ielm-map', which currently contains: | |
313 \\{ielm-map}" | |
314 (interactive) | |
315 (comint-mode) | |
316 (setq comint-prompt-regexp (concat "^" (regexp-quote ielm-prompt))) | |
317 (make-local-variable 'paragraph-start) | |
318 (setq paragraph-start comint-prompt-regexp) | |
319 (setq comint-input-sender 'ielm-input-sender) | |
320 (setq comint-process-echoes nil) | |
321 (setq comint-dynamic-complete-functions | |
322 '(ielm-tab comint-replace-by-expanded-history ielm-complete-filename ielm-complete-symbol)) | |
323 | |
324 (setq major-mode 'inferior-emacs-lisp-mode) | |
325 (setq mode-name "IELM") | |
326 (use-local-map ielm-map) | |
327 (set-syntax-table emacs-lisp-mode-syntax-table) | |
328 | |
329 (make-local-variable 'indent-line-function) | |
330 (make-local-variable 'ielm-working-buffer) | |
331 (setq ielm-working-buffer (current-buffer)) | |
332 (setq indent-line-function 'ielm-indent-line) | |
333 | |
334 ;;; Value holders | |
335 (setq : nil) | |
336 (make-local-variable ':) | |
337 (setq :: nil) | |
338 (make-local-variable '::) | |
339 (setq ::: nil) | |
340 (make-local-variable ':::) | |
341 | |
342 ;; A dummy process to keep comint happy. It will never get any input | |
343 (if (comint-check-proc (current-buffer)) nil | |
344 (start-process "ielm" (current-buffer) "cat") | |
345 (process-kill-without-query (ielm-process)) | |
346 (goto-char (point-max)) | |
347 ;; Add a silly header | |
348 (insert ielm-header) | |
349 (ielm-set-pm (point-max)) | |
350 (comint-output-filter (ielm-process) ielm-prompt) | |
351 (set-marker comint-last-input-start (ielm-pm)) | |
352 (set-process-filter (get-buffer-process (current-buffer)) 'comint-output-filter)) | |
353 (run-hooks 'ielm-mode-hook)) | |
354 | |
355 ;;; User command | |
356 | |
357 (defun ielm nil | |
358 "Switch to or create the buffer *ielm* for evaluating emacs-lisp expressions" | |
359 (interactive) | |
360 (if (comint-check-proc "*ielm*") nil | |
361 (progn | |
362 (set-buffer (get-buffer-create "*ielm*")) | |
363 (inferior-emacs-lisp-mode))) | |
364 (switch-to-buffer "*ielm*")) | |
365 | |
366 ;; ielm.el ends here |