Mercurial > emacs
changeset 7275:cd1541f4c87c
(ielm-dynamic-multiline-inputs): New variable.
(ielm-return): Support it.
(ielm-print-working-buffer): New command
(ielm-change-working-buffer): New command
(ielm-display-working-buffer): New command
(ielm-map): Bindings for new commands; bindings from
shared-lisp-mode-map; Lemacs support.
(ielm-eval-input): Give bindings in scope during the eval
non-clashing names (ielm- prefix)
(ielm-get-old-input): New function
(inferior-emacs-lisp-mode): Bind it to comint-get-old-input
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Mon, 02 May 1994 17:13:42 +0000 |
parents | 8f348d7d2b71 |
children | 66f3335be178 |
files | lisp/ielm.el |
diffstat | 1 files changed, 187 insertions(+), 98 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/ielm.el Mon May 02 07:53:23 1994 +0000 +++ b/lisp/ielm.el Mon May 02 17:13:42 1994 +0000 @@ -23,14 +23,14 @@ ;;; Commentary: -;; Provides a nice interface to evaluating Emacs-Lisp expressions. +;; Provides a nice interface to evaluating Emacs Lisp expressions. ;; Input is handled by the comint package, and output is passed ;; through the pretty-printer. ;; To install: copy this file to a directory in your load-path, and -;; add the line +;; add the following line to your .emacs file: ;; -;; (autoload 'ielm "ielm" "Start an inferior emacs-lisp session" t) +;; (autoload 'ielm "ielm" "Start an inferior Emacs Lisp session" t) ;; ;; For completion to work, the comint.el from FSF Emacs 19.23 is ;; required. If you do not have it, or if you are running Lemacs, @@ -58,28 +58,39 @@ ;;; User variables (defvar ielm-noisy t - "*If non-nil, beep on error") + "*If non-nil, IELM will beep on error.") -(defvar ielm-prompt "ELISP> ") +(defvar ielm-prompt "ELISP> " + "Prompt used in IELM.") (defvar ielm-dynamic-return t - "*If non-nil, RET either evaluates input or inserts a newline, -depending on context") + "*Controls whether \\<ielm-map>\\[ielm-return] has intelligent behaviour in IELM. +If non-nil, \\[ielm-return] evaluates input for complete sexps, or inserts a newline +and indents for incomplete sexps. If nil, always inserts newlines.") + +(defvar ielm-dynamic-multiline-inputs t + "*Force multiline inputs to start from column zero? +If non-nil, after entering the first line of an incomplete sexp, a newline +will be inserted after the prompt, moving the input to the next line. +This gives more frame width for large indented sexps, and allows functions +such as `edebug-defun' to work with such inputs.") (defvar ielm-mode-hook nil - "*Hooks to be run when the inferior-emacs-lisp-mode is started") + "*Hooks to be run when IELM (`inferior-emacs-lisp-mode') is started.") ;;; System variables (defvar ielm-working-buffer nil - "Buffer, if any, to use in ielm. Usually buffer-local") + "Buffer in which IELM sexps will be evaluated. +This variable is buffer-local.") (defvar ielm-header (concat - "*** Welcome to IELM mode version " - (substring "$Revision: 1.15 $" 11 -2) + "*** Welcome to IELM version " + (substring "$Revision: 1.25 $" 11 -2) " *** Type (describe-mode) for help.\n" - "IELM has ABSOLUTELY NO WARRANTY; type (describe-no-warranty) for details\n")) + "IELM has ABSOLUTELY NO WARRANTY; type (describe-no-warranty) for details.\n") + "Message to display when IELM is started.") (defvar ielm-map nil) (if ielm-map nil @@ -92,12 +103,23 @@ (setq ielm-map (cons 'keymap comint-mode-map))) (define-key ielm-map "\t" 'comint-dynamic-complete) (define-key ielm-map "\C-m" 'ielm-return) - (define-key ielm-map "\C-j" 'ielm-send-input)) + (define-key ielm-map "\C-j" 'ielm-send-input) + (define-key ielm-map "\e\C-x" 'eval-defun) ; for consistency with + (define-key ielm-map "\e\t" 'lisp-complete-symbol) ; lisp-interaction-mode + ;; These bindings are from shared-lisp-mode-map -- can you inherit + ;; from more than one keymap?? + (define-key ielm-map "\e\C-q" 'indent-sexp) + (define-key ielm-map "\eq" 'lisp-fill-paragraph) + (define-key ielm-map "\177" 'backward-delete-char-untabify) + ;; Some convenience bindings for setting the working buffer + (define-key ielm-map "\C-c\C-b" 'ielm-change-working-buffer) + (define-key ielm-map "\C-c\C-f" 'ielm-display-working-buffer) + (define-key ielm-map "\C-c\C-v" 'ielm-print-working-buffer)) ;;; Completion stuff (defun ielm-tab nil - "Possibly indent the current line as lisp code" + "Possibly indent the current line as lisp code." (interactive) (if (or (eq (preceding-char) ?\n) (eq (char-syntax (preceding-char)) ? )) @@ -106,8 +128,9 @@ t))) (defun ielm-complete-symbol nil - "Just like lisp-complete-symbol" - ;; except that it returns non-nil if completion has occurred + "Complete the lisp symbol before point." + ;; A wrapper for lisp-complete symbol that returns non-nil if + ;; completion has occurred (let* ((btick (buffer-modified-tick)) (cbuffer (get-buffer " *Completions*")) (ctick (and cbuffer (buffer-modified-tick cbuffer)))) @@ -122,22 +145,48 @@ (get-buffer " *Completions*"))))) (defun ielm-complete-filename nil - ;; Completes filenames if in a string + "Dynamically complete filename before point, if in a string." (if (nth 3 (parse-partial-sexp comint-last-input-start (point))) (comint-dynamic-complete-filename))) (defun ielm-indent-line nil - "Indent the current line as lisp code if it is not a prompt line" + "Indent the current line as Lisp code if it is not a prompt line." (if (save-excursion (beginning-of-line) (looking-at comint-prompt-regexp)) nil (lisp-indent-line))) +;;; Working buffer manipulation + +(defun ielm-print-working-buffer nil + "Print the current IELM working buffer's name in the echo area." + (interactive) + (message "The current working buffer is: %s" (buffer-name ielm-working-buffer))) + +(defun ielm-display-working-buffer nil + "Display the current IELM working buffer. +Don't forget that selecting that buffer will change its value of `point' +to its value of `window-point'!" + (interactive) + (display-buffer ielm-working-buffer) + (ielm-print-working-buffer)) + +(defun ielm-change-working-buffer (buf) + "Change the current IELM working buffer to BUF. +This is the buffer in which all sexps entered at the IELM prompt are +evaluated. You can achieve the same effect with a call to +`set-buffer' at the IELM prompt." + (interactive "bSet working buffer to: ") + (setq ielm-working-buffer (or (get-buffer buf) (error "No such buffer"))) + (ielm-print-working-buffer)) + ;;; Other bindings (defun ielm-return nil - "Evaluate the sexp at the prompt if it is complete, otherwise newline -and indent. If ielm-dynamic-return is nil, just insert a newline." + "Newline and indent, or evaluate the sexp before the prompt. +Complete sexps are evaluated; for incomplete sexps inserts a newline +and indents. If however `ielm-dynamic-return' is nil, this always +simply inserts a newline." (interactive) (if ielm-dynamic-return (let ((state @@ -147,14 +196,23 @@ (point))))) (if (and (< (car state) 1) (not (nth 3 state))) (ielm-send-input) + (if (and ielm-dynamic-multiline-inputs + (save-excursion + (beginning-of-line) + (looking-at comint-prompt-regexp))) + (save-excursion + (goto-char (ielm-pm)) + (newline 1))) (newline-and-indent))) (newline))) (defun ielm-input-sender (proc input) + ;; Just sets the variable ielm-input, which is in the scope of + ;; `ielm-send-input's call. (setq ielm-input input)) (defun ielm-send-input nil - "Evaluate the Emacs Lisp expression after the prompt" + "Evaluate the Emacs Lisp expression after the prompt." (interactive) (let ((buf (current-buffer)) ielm-input) ; set by ielm-input-sender @@ -164,7 +222,7 @@ ;;; Utility functions (defun ielm-is-whitespace (string) - "Return non-nil if STRING is all whitespace" + "Return non-nil if STRING is all whitespace." (or (string= string "") (string-match "\\`[ \t\n]+\\'" string))) (defun ielm-format-errors (errlist) @@ -176,7 +234,7 @@ (defun ielm-format-error (err) - "Return a string form of the error ERR" + ;; Return a string form of the error ERR. (format "%s%s" (or (get (car err) 'error-message) "Peculiar error") (if (cdr err) @@ -185,10 +243,10 @@ ;;; Evaluation -(defun ielm-eval-input (string) - "Evaluate the lisp expression STRING, and pretty-print the result" +(defun ielm-eval-input (ielm-string) + "Evaluate the Lisp expression IELM-STRING, and pretty-print the result." ;; This is the function that actually `sends' the input to the - ;; `inferior lisp process'. All comint-send-input does is works out + ;; `inferior Lisp process'. All comint-send-input does is works out ;; what that input is. What this function does is evaluates that ;; input and produces `output' which gets inserted into the buffer, ;; along with a new prompt. A better way of doing this might have @@ -196,111 +254,130 @@ ;; this as in output filter that converted sexps in the output ;; stream to their evaluated value. But that would have involved ;; more process coordination than I was happy to deal with. - (let (form ; form to evaluate - pos ; End posn of parse in string - result ; Result, or error message - error-type ; string, nil if no error - (output "") ; result to display - (wbuf ielm-working-buffer) ; current buffer after evaluation - (pmark (ielm-pm))) - (if (not (ielm-is-whitespace string)) + ;; + ;; NOTE: all temporary variables in this function will be in scope + ;; during the eval, and so need to have non-clashing names. + (let (ielm-form ; form to evaluate + ielm-pos ; End posn of parse in string + ielm-result ; Result, or error message + ielm-error-type ; string, nil if no error + (ielm-output "") ; result to display + (ielm-wbuf ielm-working-buffer) ; current buffer after evaluation + (ielm-pmark (ielm-pm))) + (if (not (ielm-is-whitespace ielm-string)) (progn (condition-case err (let (rout) - (setq rout (read-from-string string)) - (setq form (car rout)) - (setq pos (cdr rout))) - (error (setq result (ielm-format-error err)) - (setq error-type "Read error"))) - (if error-type nil - (if (ielm-is-whitespace (substring string pos)) - ;; need this awful let convolution to work around - ;; an Emacs bug involving local vbls and let binding - (let ((:save :) - (::save ::) - (:::save :::)) - (save-excursion - (set-buffer ielm-working-buffer) - (condition-case err - (let ((: :save) - (:: ::save) - (::: :::save)) - (save-excursion - (setq result (eval form)) - (setq wbuf (current-buffer)))) - (error (setq result (ielm-format-error err)) - (setq error-type "Eval error")) - (quit (setq result "Quit during evaluation") - (setq error-type "Eval error"))))) - (setq error-type "IELM error") - (setq result "More than one sexp in input"))) + (setq rout (read-from-string ielm-string)) + (setq ielm-form (car rout)) + (setq ielm-pos (cdr rout))) + (error (setq ielm-result (ielm-format-error err)) + (setq ielm-error-type "Read error"))) + (if ielm-error-type nil + ;; Make sure working buffer has not been killed + (if (not (buffer-name ielm-working-buffer)) + (setq ielm-result "Working buffer has been killed" + ielm-error-type "IELM Error" + ielm-wbuf (current-buffer)) + (if (ielm-is-whitespace (substring ielm-string ielm-pos)) + ;; need this awful let convolution to work around + ;; an Emacs bug involving local vbls and let binding + (let ((:save :) + (::save ::) + (:::save :::)) + (save-excursion + (set-buffer ielm-working-buffer) + (condition-case err + (let ((: :save) + (:: ::save) + (::: :::save) + (ielm-obuf (current-buffer))) + (setq ielm-result (eval ielm-form)) + (setq ielm-wbuf (current-buffer)) + ;; The eval may have changed current-buffer; + ;; need to set it back here to avoid a bug + ;; in let. Don't want to use save-excursion + ;; because we want to allow changes in point. + (set-buffer ielm-obuf)) + (error (setq ielm-result (ielm-format-error err)) + (setq ielm-error-type "Eval error")) + (quit (setq ielm-result "Quit during evaluation") + (setq ielm-error-type "Eval error"))))) + (setq ielm-error-type "IELM error") + (setq ielm-result "More than one sexp in input")))) ;; If the eval changed the current buffer, mention it here - (if (eq wbuf ielm-working-buffer) nil - (message "current buffer is now: %s" wbuf) - (setq ielm-working-buffer wbuf)) + (if (eq ielm-wbuf ielm-working-buffer) nil + (message "current buffer is now: %s" ielm-wbuf) + (setq ielm-working-buffer ielm-wbuf)) - (goto-char pmark) - (if (not error-type) + (goto-char ielm-pmark) + (if (not ielm-error-type) (condition-case err ;; Self-referential objects cause loops in the printer, so ;; trap quits here. May as well do errors, too - (setq output (concat output (pp-to-string result))) - (error (setq error-type "IELM Error") - (setq result "Error during pretty-printing (bug in pp)")) - (quit (setq error-type "IELM Error") - (setq result "Quit during pretty-printing")))) - (if error-type + (setq ielm-output (concat ielm-output (pp-to-string ielm-result))) + (error (setq ielm-error-type "IELM Error") + (setq ielm-result "Error during pretty-printing (bug in pp)")) + (quit (setq ielm-error-type "IELM Error") + (setq ielm-result "Quit during pretty-printing")))) + (if ielm-error-type (progn (if ielm-noisy (ding)) - (setq output (concat output "*** " error-type " *** ")) - (setq output (concat output result))) + (setq ielm-output (concat ielm-output "*** " ielm-error-type " *** ")) + (setq ielm-output (concat ielm-output ielm-result))) ;; There was no error, so shift the ::: values (setq ::: ::) (setq :: :) - (setq : result)) - (setq output (concat output "\n")))) - (setq output (concat output ielm-prompt)) - (comint-output-filter (ielm-process) output))) + (setq : ielm-result)) + (setq ielm-output (concat ielm-output "\n")))) + (setq ielm-output (concat ielm-output ielm-prompt)) + (comint-output-filter (ielm-process) ielm-output))) ;;; Process and marker utilities (defun ielm-process nil - "Return the current buffer's process" + ;; Return the current buffer's process. (get-buffer-process (current-buffer))) (defun ielm-pm nil - "Return the process mark of the current buffer" + ;; Return the process mark of the current buffer. (process-mark (get-buffer-process (current-buffer)))) (defun ielm-set-pm (pos) - "Set the process mark in the current buffer to POS" + ;; Set the process mark in the current buffer to POS. (set-marker (process-mark (get-buffer-process (current-buffer))) pos)) ;;; Major mode (defun inferior-emacs-lisp-mode nil - "Major mode for interactively evaluating Emacs-Lisp expressions -Uses the interface provided by `comint-mode' (q.v.) + "Major mode for interactively evaluating Emacs Lisp expressions. +Uses the interface provided by `comint-mode' (which see). + +* \\<ielm-map>\\[ielm-send-input] evaluates the sexp following the prompt. There must be at most + one top-level sexp per prompt. -\\[ielm-send-input] evaluates the sexp following the prompt. There must be at most -one top-level sexp per prompt. -\\[ielm-return] inserts a newline and indents. However, if the variable -ielm-dynamic-return is non-nil (the default) then it will also evaluate -a complete expression. -\\[comint-dynamic-complete] completes lisp symbols (or filenames, within strings), -or indents the line if there is nothing to complete. +* \\[ielm-return] inserts a newline and indents, or evaluates a + complete expression (but see variable `ielm-dynamic-return'). + Inputs longer than one line are moved to the line following the + prompt (but see variable `ielm-dynamic-multiline-inputs'). + +* \\[comint-dynamic-complete] completes Lisp symbols (or filenames, within strings), + or indents the line if there is nothing to complete. During evaluations, the values of the variables `:', `::', and `:::' are the results of the previous, second previous and third previous evaluations respectively. -The current buffer may be changed, and its value is preserved between -successive evaluations. In this way, expressions may be evaluated in -a different buffer than the *ielm* buffer. +The current working buffer may be changed (with a call to +`set-buffer', or with \\[ielm-change-working-buffer]), and its value +is preserved between successive evaluations. In this way, expressions +may be evaluated in a different buffer than the *ielm* buffer. +Display the name of the working buffer with \\[ielm-print-working-buffer], +or the buffer itself with \\[ielm-display-working-buffer]. -Expressions evaluated by IELM are not subject to debug-on-quit or -debug-on-error. +Expressions evaluated by IELM are not subject to `debug-on-quit' or +`debug-on-error'. The behaviour of IELM may be customised with the following variables: * To stop beeping on error, set `ielm-noisy' to nil @@ -320,6 +397,7 @@ (setq comint-process-echoes nil) (setq comint-dynamic-complete-functions '(ielm-tab comint-replace-by-expanded-history ielm-complete-filename ielm-complete-symbol)) + (setq comint-get-old-input 'ielm-get-old-input) (setq major-mode 'inferior-emacs-lisp-mode) (setq mode-name "IELM") @@ -331,7 +409,7 @@ (setq ielm-working-buffer (current-buffer)) (setq indent-line-function 'ielm-indent-line) - ;;; Value holders + ;; Value holders (setq : nil) (make-local-variable ':) (setq :: nil) @@ -352,10 +430,21 @@ (set-process-filter (get-buffer-process (current-buffer)) 'comint-output-filter)) (run-hooks 'ielm-mode-hook)) +(defun ielm-get-old-input nil + ;; Return the previous input surrounding point + (save-excursion + (beginning-of-line) + (if (looking-at comint-prompt-regexp) nil + (re-search-backward comint-prompt-regexp)) + (comint-skip-prompt) + (buffer-substring (point) (progn (forward-sexp 1) (point))))) + ;;; User command +;;;###autoload (defun ielm nil - "Switch to or create the buffer *ielm* for evaluating emacs-lisp expressions" + "Interactively evaluate Emacs Lisp expressions. +Switches to the buffer *ielm*, or creates it if it does not exist." (interactive) (if (comint-check-proc "*ielm*") nil (progn @@ -363,4 +452,4 @@ (inferior-emacs-lisp-mode))) (switch-to-buffer "*ielm*")) -;; ielm.el ends here +;;; ielm.el ends here