(replace_buffer_in_all_windows):
[bpt/emacs.git] / lisp / ielm.el
CommitLineData
813f532d 1;;; ielm.el --- interaction mode for Emacs Lisp
b578f267 2
813f532d
RS
3;; Copyright (C) 1994 Free Software Foundation, Inc.
4
5;; Author: David Smith <maa036@lancaster.ac.uk>
4228277d 6;; Maintainer: FSF
813f532d
RS
7;; Created: 25 Feb 1994
8;; Keywords: lisp
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
b578f267
EN
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
813f532d
RS
26
27;;; Commentary:
28
e848854a 29;; Provides a nice interface to evaluating Emacs Lisp expressions.
813f532d
RS
30;; Input is handled by the comint package, and output is passed
31;; through the pretty-printer.
32
33;; To install: copy this file to a directory in your load-path, and
e848854a 34;; add the following line to your .emacs file:
813f532d 35;;
e848854a 36;; (autoload 'ielm "ielm" "Start an inferior Emacs Lisp session" t)
813f532d 37;;
326e87d2 38;; For completion to work, the comint.el from Emacs 19.23 is
813f532d
RS
39;; required. If you do not have it, or if you are running Lemacs,
40;; also add the following code to your .emacs:
41;;
42;; (setq ielm-mode-hook
43;; '(lambda nil
44;; (define-key ielm-map "\t"
45;; '(lambda nil (interactive) (or (ielm-tab)
46;; (lisp-complete-symbol))))))
47
48;; To start: M-x ielm. Type C-h m in the *ielm* buffer for more info.
49
50;; The latest version is available by WWW from
51;; http://mathssun5.lancs.ac.uk:2080/~maa036/elisp/dir.html
52;; or by anonymous FTP from
53;; /anonymous@wingra.stat.wisc.edu:pub/src/emacs-lisp/ielm.el.gz
54;; or from the author: David M. Smith <maa036@lancaster.ac.uk>
55
56;;; Code:
57
58(require 'comint)
59(require 'pp)
60
61;;; User variables
62
94114394
RS
63(defgroup ielm nil
64 "Interaction mode for Emacs Lisp."
65 :group 'lisp)
66
67
68(defcustom ielm-noisy t
69 "*If non-nil, IELM will beep on error."
70 :type 'boolean
71 :group 'ielm)
813f532d 72
e848854a
RS
73(defvar ielm-prompt "ELISP> "
74 "Prompt used in IELM.")
813f532d 75
94114394 76(defcustom ielm-dynamic-return t
e848854a
RS
77 "*Controls whether \\<ielm-map>\\[ielm-return] has intelligent behaviour in IELM.
78If non-nil, \\[ielm-return] evaluates input for complete sexps, or inserts a newline
94114394
RS
79and indents for incomplete sexps. If nil, always inserts newlines."
80 :type 'boolean
81 :group 'ielm)
e848854a 82
94114394 83(defcustom ielm-dynamic-multiline-inputs t
e848854a
RS
84 "*Force multiline inputs to start from column zero?
85If non-nil, after entering the first line of an incomplete sexp, a newline
86will be inserted after the prompt, moving the input to the next line.
87This gives more frame width for large indented sexps, and allows functions
94114394
RS
88such as `edebug-defun' to work with such inputs."
89 :type 'boolean
90 :group 'ielm)
813f532d 91
94114394
RS
92(defcustom ielm-mode-hook nil
93 "*Hooks to be run when IELM (`inferior-emacs-lisp-mode') is started."
94 :type 'hook
95 :group 'ielm)
813f532d
RS
96
97;;; System variables
98
99(defvar ielm-working-buffer nil
e848854a
RS
100 "Buffer in which IELM sexps will be evaluated.
101This variable is buffer-local.")
813f532d
RS
102
103(defvar ielm-header
0a13146e 104 "*** Welcome to IELM *** Type (describe-mode) for help.\n"
e848854a 105 "Message to display when IELM is started.")
813f532d
RS
106
107(defvar ielm-map nil)
108(if ielm-map nil
109 (if (string-match "Lucid" emacs-version)
110 ;; Lemacs
111 (progn
112 (setq ielm-map (make-sparse-keymap))
113 (set-keymap-parent ielm-map comint-mode-map))
114 ;; FSF
115 (setq ielm-map (cons 'keymap comint-mode-map)))
116 (define-key ielm-map "\t" 'comint-dynamic-complete)
117 (define-key ielm-map "\C-m" 'ielm-return)
e848854a
RS
118 (define-key ielm-map "\C-j" 'ielm-send-input)
119 (define-key ielm-map "\e\C-x" 'eval-defun) ; for consistency with
120 (define-key ielm-map "\e\t" 'lisp-complete-symbol) ; lisp-interaction-mode
121 ;; These bindings are from shared-lisp-mode-map -- can you inherit
122 ;; from more than one keymap??
123 (define-key ielm-map "\e\C-q" 'indent-sexp)
e848854a
RS
124 (define-key ielm-map "\177" 'backward-delete-char-untabify)
125 ;; Some convenience bindings for setting the working buffer
126 (define-key ielm-map "\C-c\C-b" 'ielm-change-working-buffer)
127 (define-key ielm-map "\C-c\C-f" 'ielm-display-working-buffer)
128 (define-key ielm-map "\C-c\C-v" 'ielm-print-working-buffer))
813f532d 129
9d42eea3
RS
130(defvar ielm-font-lock-keywords
131 (list
132 (cons (concat "^" (regexp-quote ielm-prompt)) 'font-lock-keyword-face)
133 '("\\(^\\*\\*\\*[^*]+\\*\\*\\*\\)\\(.*$\\)" (1 font-lock-comment-face) (2 font-lock-reference-face)))
134 "Additional expressions to highlight in ielm buffers.")
135
813f532d
RS
136;;; Completion stuff
137
138(defun ielm-tab nil
e848854a 139 "Possibly indent the current line as lisp code."
813f532d
RS
140 (interactive)
141 (if (or (eq (preceding-char) ?\n)
142 (eq (char-syntax (preceding-char)) ? ))
143 (progn
144 (ielm-indent-line)
145 t)))
146
147(defun ielm-complete-symbol nil
e848854a
RS
148 "Complete the lisp symbol before point."
149 ;; A wrapper for lisp-complete symbol that returns non-nil if
150 ;; completion has occurred
813f532d 151 (let* ((btick (buffer-modified-tick))
e58df0dc 152 (cbuffer (get-buffer "*Completions*"))
813f532d
RS
153 (ctick (and cbuffer (buffer-modified-tick cbuffer))))
154 (lisp-complete-symbol)
155 ;; completion has occurred if:
156 (or
157 ;; the buffer has been modified
158 (not (= btick (buffer-modified-tick)))
a7acbbe4 159 ;; a completions buffer has been modified or created
813f532d
RS
160 (if cbuffer
161 (not (= ctick (buffer-modified-tick cbuffer)))
e58df0dc 162 (get-buffer "*Completions*")))))
813f532d
RS
163
164(defun ielm-complete-filename nil
e848854a 165 "Dynamically complete filename before point, if in a string."
813f532d
RS
166 (if (nth 3 (parse-partial-sexp comint-last-input-start (point)))
167 (comint-dynamic-complete-filename)))
168
169(defun ielm-indent-line nil
e848854a 170 "Indent the current line as Lisp code if it is not a prompt line."
813f532d
RS
171 (if (save-excursion
172 (beginning-of-line)
173 (looking-at comint-prompt-regexp)) nil
174 (lisp-indent-line)))
175
e848854a
RS
176;;; Working buffer manipulation
177
178(defun ielm-print-working-buffer nil
179 "Print the current IELM working buffer's name in the echo area."
180 (interactive)
181 (message "The current working buffer is: %s" (buffer-name ielm-working-buffer)))
182
183(defun ielm-display-working-buffer nil
184 "Display the current IELM working buffer.
185Don't forget that selecting that buffer will change its value of `point'
186to its value of `window-point'!"
187 (interactive)
188 (display-buffer ielm-working-buffer)
189 (ielm-print-working-buffer))
190
191(defun ielm-change-working-buffer (buf)
192 "Change the current IELM working buffer to BUF.
193This is the buffer in which all sexps entered at the IELM prompt are
194evaluated. You can achieve the same effect with a call to
195`set-buffer' at the IELM prompt."
196 (interactive "bSet working buffer to: ")
197 (setq ielm-working-buffer (or (get-buffer buf) (error "No such buffer")))
198 (ielm-print-working-buffer))
199
813f532d
RS
200;;; Other bindings
201
202(defun ielm-return nil
e848854a
RS
203 "Newline and indent, or evaluate the sexp before the prompt.
204Complete sexps are evaluated; for incomplete sexps inserts a newline
205and indents. If however `ielm-dynamic-return' is nil, this always
206simply inserts a newline."
813f532d
RS
207 (interactive)
208 (if ielm-dynamic-return
209 (let ((state
210 (save-excursion
211 (end-of-line)
212 (parse-partial-sexp (ielm-pm)
213 (point)))))
214 (if (and (< (car state) 1) (not (nth 3 state)))
215 (ielm-send-input)
e848854a
RS
216 (if (and ielm-dynamic-multiline-inputs
217 (save-excursion
218 (beginning-of-line)
219 (looking-at comint-prompt-regexp)))
220 (save-excursion
221 (goto-char (ielm-pm))
222 (newline 1)))
813f532d
RS
223 (newline-and-indent)))
224 (newline)))
225
94aba130
RS
226(defvar ielm-input)
227
813f532d 228(defun ielm-input-sender (proc input)
e848854a
RS
229 ;; Just sets the variable ielm-input, which is in the scope of
230 ;; `ielm-send-input's call.
813f532d
RS
231 (setq ielm-input input))
232
233(defun ielm-send-input nil
e848854a 234 "Evaluate the Emacs Lisp expression after the prompt."
813f532d
RS
235 (interactive)
236 (let ((buf (current-buffer))
237 ielm-input) ; set by ielm-input-sender
238 (comint-send-input) ; update history, markers etc.
239 (ielm-eval-input ielm-input)))
240
241;;; Utility functions
242
243(defun ielm-is-whitespace (string)
e848854a 244 "Return non-nil if STRING is all whitespace."
813f532d
RS
245 (or (string= string "") (string-match "\\`[ \t\n]+\\'" string)))
246
247(defun ielm-format-errors (errlist)
248 (let ((result ""))
249 (while errlist
250 (setq result (concat result (prin1-to-string (car errlist)) ", "))
251 (setq errlist (cdr errlist)))
252 (substring result 0 -2)))
253
254
255(defun ielm-format-error (err)
e848854a 256 ;; Return a string form of the error ERR.
813f532d
RS
257 (format "%s%s"
258 (or (get (car err) 'error-message) "Peculiar error")
259 (if (cdr err)
260 (format ": %s" (ielm-format-errors (cdr err)))
261 "")))
262
263;;; Evaluation
264
e848854a
RS
265(defun ielm-eval-input (ielm-string)
266 "Evaluate the Lisp expression IELM-STRING, and pretty-print the result."
813f532d 267 ;; This is the function that actually `sends' the input to the
e848854a 268 ;; `inferior Lisp process'. All comint-send-input does is works out
813f532d
RS
269 ;; what that input is. What this function does is evaluates that
270 ;; input and produces `output' which gets inserted into the buffer,
271 ;; along with a new prompt. A better way of doing this might have
272 ;; been to actually send the output to the `cat' process, and write
273 ;; this as in output filter that converted sexps in the output
274 ;; stream to their evaluated value. But that would have involved
275 ;; more process coordination than I was happy to deal with.
e848854a
RS
276 ;;
277 ;; NOTE: all temporary variables in this function will be in scope
278 ;; during the eval, and so need to have non-clashing names.
279 (let (ielm-form ; form to evaluate
280 ielm-pos ; End posn of parse in string
281 ielm-result ; Result, or error message
282 ielm-error-type ; string, nil if no error
283 (ielm-output "") ; result to display
284 (ielm-wbuf ielm-working-buffer) ; current buffer after evaluation
285 (ielm-pmark (ielm-pm)))
286 (if (not (ielm-is-whitespace ielm-string))
813f532d
RS
287 (progn
288 (condition-case err
289 (let (rout)
e848854a
RS
290 (setq rout (read-from-string ielm-string))
291 (setq ielm-form (car rout))
292 (setq ielm-pos (cdr rout)))
293 (error (setq ielm-result (ielm-format-error err))
294 (setq ielm-error-type "Read error")))
295 (if ielm-error-type nil
296 ;; Make sure working buffer has not been killed
297 (if (not (buffer-name ielm-working-buffer))
298 (setq ielm-result "Working buffer has been killed"
299 ielm-error-type "IELM Error"
300 ielm-wbuf (current-buffer))
301 (if (ielm-is-whitespace (substring ielm-string ielm-pos))
302 ;; need this awful let convolution to work around
303 ;; an Emacs bug involving local vbls and let binding
304 (let ((:save :)
305 (::save ::)
306 (:::save :::))
307 (save-excursion
308 (set-buffer ielm-working-buffer)
309 (condition-case err
310 (let ((: :save)
311 (:: ::save)
312 (::: :::save)
313 (ielm-obuf (current-buffer)))
314 (setq ielm-result (eval ielm-form))
315 (setq ielm-wbuf (current-buffer))
316 ;; The eval may have changed current-buffer;
317 ;; need to set it back here to avoid a bug
318 ;; in let. Don't want to use save-excursion
319 ;; because we want to allow changes in point.
320 (set-buffer ielm-obuf))
321 (error (setq ielm-result (ielm-format-error err))
322 (setq ielm-error-type "Eval error"))
323 (quit (setq ielm-result "Quit during evaluation")
324 (setq ielm-error-type "Eval error")))))
325 (setq ielm-error-type "IELM error")
326 (setq ielm-result "More than one sexp in input"))))
813f532d
RS
327
328 ;; If the eval changed the current buffer, mention it here
e848854a
RS
329 (if (eq ielm-wbuf ielm-working-buffer) nil
330 (message "current buffer is now: %s" ielm-wbuf)
331 (setq ielm-working-buffer ielm-wbuf))
813f532d 332
e848854a
RS
333 (goto-char ielm-pmark)
334 (if (not ielm-error-type)
813f532d
RS
335 (condition-case err
336 ;; Self-referential objects cause loops in the printer, so
337 ;; trap quits here. May as well do errors, too
e848854a
RS
338 (setq ielm-output (concat ielm-output (pp-to-string ielm-result)))
339 (error (setq ielm-error-type "IELM Error")
340 (setq ielm-result "Error during pretty-printing (bug in pp)"))
341 (quit (setq ielm-error-type "IELM Error")
342 (setq ielm-result "Quit during pretty-printing"))))
343 (if ielm-error-type
813f532d
RS
344 (progn
345 (if ielm-noisy (ding))
e848854a
RS
346 (setq ielm-output (concat ielm-output "*** " ielm-error-type " *** "))
347 (setq ielm-output (concat ielm-output ielm-result)))
813f532d
RS
348 ;; There was no error, so shift the ::: values
349 (setq ::: ::)
350 (setq :: :)
e848854a
RS
351 (setq : ielm-result))
352 (setq ielm-output (concat ielm-output "\n"))))
353 (setq ielm-output (concat ielm-output ielm-prompt))
354 (comint-output-filter (ielm-process) ielm-output)))
813f532d
RS
355
356;;; Process and marker utilities
357
358(defun ielm-process nil
e848854a 359 ;; Return the current buffer's process.
813f532d
RS
360 (get-buffer-process (current-buffer)))
361
362(defun ielm-pm nil
e848854a 363 ;; Return the process mark of the current buffer.
813f532d
RS
364 (process-mark (get-buffer-process (current-buffer))))
365
366(defun ielm-set-pm (pos)
e848854a 367 ;; Set the process mark in the current buffer to POS.
813f532d
RS
368 (set-marker (process-mark (get-buffer-process (current-buffer))) pos))
369
370;;; Major mode
371
95b20f3b
RS
372(put 'inferior-emacs-lisp-mode 'mode-class 'special)
373
813f532d 374(defun inferior-emacs-lisp-mode nil
e848854a
RS
375 "Major mode for interactively evaluating Emacs Lisp expressions.
376Uses the interface provided by `comint-mode' (which see).
377
378* \\<ielm-map>\\[ielm-send-input] evaluates the sexp following the prompt. There must be at most
379 one top-level sexp per prompt.
813f532d 380
e848854a
RS
381* \\[ielm-return] inserts a newline and indents, or evaluates a
382 complete expression (but see variable `ielm-dynamic-return').
383 Inputs longer than one line are moved to the line following the
384 prompt (but see variable `ielm-dynamic-multiline-inputs').
385
386* \\[comint-dynamic-complete] completes Lisp symbols (or filenames, within strings),
387 or indents the line if there is nothing to complete.
813f532d
RS
388
389During evaluations, the values of the variables `:', `::', and `:::'
390are the results of the previous, second previous and third previous
391evaluations respectively.
392
e848854a
RS
393The current working buffer may be changed (with a call to
394`set-buffer', or with \\[ielm-change-working-buffer]), and its value
395is preserved between successive evaluations. In this way, expressions
396may be evaluated in a different buffer than the *ielm* buffer.
397Display the name of the working buffer with \\[ielm-print-working-buffer],
398or the buffer itself with \\[ielm-display-working-buffer].
813f532d 399
e848854a
RS
400Expressions evaluated by IELM are not subject to `debug-on-quit' or
401`debug-on-error'.
813f532d
RS
402
403The behaviour of IELM may be customised with the following variables:
404* To stop beeping on error, set `ielm-noisy' to nil
405* If you don't like the prompt, you can change it by setting `ielm-prompt'.
406* Set `ielm-dynamic-return' to nil for bindings like `lisp-interaction-mode'
407* Entry to this mode runs `comint-mode-hook' and `ielm-mode-hook'
408 (in that order).
409
410Customised bindings may be defined in `ielm-map', which currently contains:
411\\{ielm-map}"
412 (interactive)
413 (comint-mode)
414 (setq comint-prompt-regexp (concat "^" (regexp-quote ielm-prompt)))
415 (make-local-variable 'paragraph-start)
416 (setq paragraph-start comint-prompt-regexp)
417 (setq comint-input-sender 'ielm-input-sender)
418 (setq comint-process-echoes nil)
419 (setq comint-dynamic-complete-functions
420 '(ielm-tab comint-replace-by-expanded-history ielm-complete-filename ielm-complete-symbol))
e848854a 421 (setq comint-get-old-input 'ielm-get-old-input)
34ce46c8
KH
422 (make-local-variable 'comint-completion-addsuffix)
423 (setq comint-completion-addsuffix
424 (cons (char-to-string directory-sep-char) ""))
813f532d
RS
425
426 (setq major-mode 'inferior-emacs-lisp-mode)
427 (setq mode-name "IELM")
428 (use-local-map ielm-map)
429 (set-syntax-table emacs-lisp-mode-syntax-table)
430
431 (make-local-variable 'indent-line-function)
432 (make-local-variable 'ielm-working-buffer)
433 (setq ielm-working-buffer (current-buffer))
434 (setq indent-line-function 'ielm-indent-line)
950086e6
KH
435 (make-local-variable 'fill-paragraph-function)
436 (setq fill-paragraph-function 'lisp-fill-paragraph)
813f532d 437
e848854a 438 ;; Value holders
813f532d
RS
439 (setq : nil)
440 (make-local-variable ':)
441 (setq :: nil)
442 (make-local-variable '::)
443 (setq ::: nil)
444 (make-local-variable ':::)
445
9d42eea3
RS
446 ;; font-lock support
447 (make-local-variable 'font-lock-defaults)
448 (setq font-lock-defaults
449 '(ielm-font-lock-keywords nil nil ((?: . "w") (?- . "w") (?* . "w"))))
450
813f532d
RS
451 ;; A dummy process to keep comint happy. It will never get any input
452 (if (comint-check-proc (current-buffer)) nil
453 (start-process "ielm" (current-buffer) "cat")
454 (process-kill-without-query (ielm-process))
455 (goto-char (point-max))
456 ;; Add a silly header
457 (insert ielm-header)
458 (ielm-set-pm (point-max))
459 (comint-output-filter (ielm-process) ielm-prompt)
460 (set-marker comint-last-input-start (ielm-pm))
461 (set-process-filter (get-buffer-process (current-buffer)) 'comint-output-filter))
462 (run-hooks 'ielm-mode-hook))
463
e848854a
RS
464(defun ielm-get-old-input nil
465 ;; Return the previous input surrounding point
466 (save-excursion
467 (beginning-of-line)
468 (if (looking-at comint-prompt-regexp) nil
469 (re-search-backward comint-prompt-regexp))
470 (comint-skip-prompt)
471 (buffer-substring (point) (progn (forward-sexp 1) (point)))))
472
813f532d
RS
473;;; User command
474
9e983c78
RS
475;;;###autoload (add-hook 'same-window-buffer-names "*ielm*")
476
e848854a 477;;;###autoload
813f532d 478(defun ielm nil
e848854a 479 "Interactively evaluate Emacs Lisp expressions.
9e983c78 480Switches to the buffer `*ielm*', or creates it if it does not exist."
813f532d 481 (interactive)
9e983c78
RS
482 (if (comint-check-proc "*ielm*")
483 nil
484 (save-excursion
813f532d
RS
485 (set-buffer (get-buffer-create "*ielm*"))
486 (inferior-emacs-lisp-mode)))
9e983c78 487 (pop-to-buffer "*ielm*"))
813f532d 488
896546cd
RS
489(provide 'ielm)
490
e848854a 491;;; ielm.el ends here