Initial Commit
[packages] / xemacs-packages / xemacs-devel / eval-expr.el
1 ;;; eval-expr.el --- enhanced eval-expression command
2
3 ;; Copyright (C) 1991 Free Software Foundation, Inc.
4 ;; Copyright (C) 1991 Joe Wells
5 ;; Copyright (C) 1998 Noah S. Friedman
6
7 ;; Author: Noah Friedman <friedman@splode.com>
8 ;; Maintainer: friedman@splode.com
9 ;; Keywords: extensions
10 ;; Created: 1998-07-30
11
12 ;; $Id: eval-expr.el,v 1.2 1998/08/04 02:43:59 friedman Exp $
13
14 ;; This program is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
17 ;; any later version.
18 ;;
19 ;; This program is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 ;; GNU General Public License for more details.
23 ;;
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with this program; if not, you can either send email to this
26 ;; program's maintainer or write to: The Free Software Foundation,
27 ;; Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.
28
29 ;;; Commentary:
30
31 ;; Updates of this program may be available via the URL
32 ;; http://www.splode.com/~friedman/software/emacs-lisp/
33
34 ;; To use this package, put this in your .emacs:
35 ;;
36 ;;    (require 'eval-expr)
37 ;;    (eval-expr-install)
38
39 ;; Highlights:
40 ;;
41 ;;   * When reading the Lisp object interactively from the minibuffer, the
42 ;;     minibuffer uses the Emacs Lisp Mode syntax table.  (Emacs 19.18 or
43 ;;     later only.)
44 ;;
45 ;;   * If you type an incomplete or otherwise syntactically invalid
46 ;;     expression (e.g. you forget a closing paren), you can fix your
47 ;;     mistake without having to type it all over again.
48 ;;
49 ;;   * Displays the result in a buffer if it is too big to fit in the echo
50 ;;     area.  This buffer is placed in Emacs Lisp Mode.
51 ;;     (If you give a prefix arg, the result is placed in the current
52 ;;     buffer instead of the echo area or a separate temporary buffer.)
53 ;;
54 ;;   * The variables `eval-expr-print-level' and `eval-expr-print-length'
55 ;;     can be used to constrain the attempt to print recursive data
56 ;;     structures.  These variables are independent of the global
57 ;;     `print-level' and `print-length' variables so that eval-expression
58 ;;     can be used more easily for debugging.
59
60 ;; This program is loosely based on an earlier implemention written by Joe
61 ;; Wells <jbw@cs.bu.edu> called eval-expression-fix.el (last revised
62 ;; 1991-10-12).  That version was originally written for Emacs 18 and,
63 ;; while it worked with some quirky side effects in Emacs 19, created even
64 ;; more problems in Emacs 20 and didn't work in XEmacs at all.
65 ;;
66 ;; This rewrite should work in Emacs 19.18 or later, Emacs 20,
67 ;; and any version of XEmacs.  It will not work in Emacs 18.
68
69 ;;; Code:
70
71 ;;;###autoload
72 (defvar eval-expr-error-message-delay 3
73   "*Amount of time, in seconds, to display in echo area before continuing.")
74
75 ;;;###autoload
76 (defvar eval-expr-prompt "Eval: "
77   "*Prompt used by eval-expr.")
78
79 ;;;###autoload
80 (defvar eval-expr-honor-debug-on-error t
81   "*If non-nil, do not trap evaluation errors.
82 Instead, allow errors to throw user into the debugger, provided
83 debug-on-error specifies that the particular error is a debuggable condition.")
84
85 ;;;###autoload
86 (defvar eval-expr-print-level  (default-value 'print-level)
87   "*Like print-level, but affect results printed by `eval-expr' only.")
88
89 ;;;###autoload
90 (defvar eval-expr-print-length (default-value 'print-length)
91   "*Like print-length, but affect results printed by `eval-expr' only.")
92
93 (defvar eval-expr-output-buffer-name "*Eval Expression Output*")
94 (defvar eval-expr-error-buffer-name  "*Eval Expression Error*")
95
96 (defvar eval-expr-whitespace
97   (mapcar 'string-to-char '(" " "\t" "\n")))
98
99 (defalias 'eval-expr-orig-command nil)
100
101 \f
102 ;;;###autoload
103 (defun eval-expr-install ()
104   "Replace standard eval-expression command with enhanced eval-expr."
105   (interactive)
106   (or (symbol-function 'eval-expr-orig-command)
107       (fset 'eval-expr-orig-command (symbol-function 'eval-expression)))
108   (defalias 'eval-expression 'eval-expr))
109
110 (defun eval-expr-uninstall ()
111   "Restore original, unenhanced eval-expression command."
112   (interactive)
113   (fset 'eval-expression (symbol-function 'eval-expr-orig-command)))
114
115 ;;;###autoload
116 (defun eval-expr (ee::expression &optional ee::insert-value)
117   "Evaluate EXPRESSION and print value in minibuffer, temp, or current buffer.
118 A temp output buffer is used if there is more than one line in the
119 evaluated result.
120 If invoked with a prefix arg, or second lisp argument EE::INSERT-VALUE is
121 non-nil, then insert final value into the current buffer at point.
122
123 Value is also consed on to front of the variable `values'."
124   (interactive (list (eval-expr-read-lisp-object-minibuffer eval-expr-prompt)
125                      current-prefix-arg))
126   (let* ((ee::error nil)
127          (ee::result (cond ((and eval-expr-honor-debug-on-error
128                                  debug-on-error)
129                             (eval ee::expression))
130                            (t
131                             (condition-case ee::err-data
132                                 (eval ee::expression)
133                               (error
134                                (setq ee::error ee::err-data)))))))
135     (cond (ee::error
136            (eval-expr-error-message ee::error nil t)
137            (beep t))
138           (ee::insert-value
139            (eval-expr-print 'prin1 ee::result (current-buffer)))
140           (t
141            (setq values (cons ee::result values))
142            (eval-expr-display-message eval-expr-output-buffer-name
143              (function (lambda ()
144                          (eval-expr-print 'prin1 ee::result))))))
145     ee::result))
146
147 (defun eval-expr-read-lisp-object-minibuffer (prompt &optional input)
148   (or (null input)
149       (setq input (eval-expr-print 'prin1-to-string input)))
150
151   (let ((minibuffer-setup-hook minibuffer-setup-hook)
152         (retry t)
153         (result nil)
154         (expr nil)
155         (index nil)
156         (i 0))
157     (add-hook 'minibuffer-setup-hook 'eval-expr-minibuffer-setup)
158     (while retry
159       (condition-case err-data
160           (progn
161             (setq input
162                   (read-from-minibuffer prompt
163                                         (if (numberp index)
164                                             (cons input (1+ index))
165                                           input)
166                                         (and (boundp 'read-expression-map)
167                                              read-expression-map)
168                                         nil
169                                         'read-expression-history))
170
171             (setq index nil)
172             (setq result (read-from-string input))
173             (setq expr (car result))
174             (setq index (cdr result))
175             (setq i index)
176
177             ;; This mimics a useful test done in read_minbuf (which is
178             ;; called by Fread_from_minibuffer) when expflag is true.  But
179             ;; this test doesn't happen when calling Fread_from_string
180             ;; directly as we've done here, so do it now in lisp.
181             (while (< i (length input))
182               (or (memq (aref input i) eval-expr-whitespace)
183                   (error "Trailing garbage following expression"))
184               (setq i (1+ i)))
185
186             (setq retry nil))
187         (error
188          (eval-expr-error-message err-data t))))
189     expr))
190
191 (defun eval-expr-minibuffer-setup ()
192   (set-syntax-table emacs-lisp-mode-syntax-table))
193
194 \f
195 ;;; Display routines
196
197 (defun eval-expr-error-message (condition-data &optional waitp raw-error)
198   (let ((error (car condition-data))
199         (data  (cdr condition-data))
200         (cursor-in-echo-area t)
201         (error-str nil))
202
203     (and (consp error)
204          (null (cdr error))
205          (setq error (car error)))
206
207     (and (consp data)
208          (null (cdr data))
209          (setq data (car data)))
210
211     (and (symbolp error)
212          (not raw-error)
213          (setq error-str (get error 'error-message)))
214
215     (and (eval-expr-display-message eval-expr-error-buffer-name
216            (cond ((null data)
217                   (format "Error: %s" (or error-str error)))
218                  ((eq error 'error)
219                   (format "Error: %s" data))
220                  (error-str
221                   (format "%s: %s" error-str data))
222                  (t
223                   (format "Error: %s; Data: %s" error data))))
224          waitp
225          (sit-for eval-expr-error-message-delay))))
226
227 (defun eval-expr-display-message (output-buffer thunk)
228   (let* ((buffer (generate-new-buffer " *"))
229          (standard-output buffer)
230          (echo-area-p t))
231     (save-excursion
232       (set-buffer buffer)
233       (cond ((stringp thunk)  ; this is a cheat
234              (insert thunk))
235             (t
236              (funcall thunk)))
237       (goto-char (point-min))
238       (move-to-column (1- (frame-width)))
239       (cond ((and (eobp)
240                   (< (point) (window-width (minibuffer-window))))
241              (message "%s" (buffer-substring (point-min) (point-max))))
242             (t
243              (setq echo-area-p nil)
244              (with-output-to-temp-buffer output-buffer
245                (save-excursion
246                  (set-buffer output-buffer)
247                  (or (eq major-mode 'emacs-lisp-mode)
248                      (emacs-lisp-mode))
249                  (insert-buffer buffer))))))
250     (kill-buffer buffer)
251     echo-area-p))
252
253 (put 'eval-expr-display-message 'lisp-indent-function 1)
254
255 (defun eval-expr-print (func &rest args)
256   (let ((print-level  eval-expr-print-level)
257         (print-length eval-expr-print-length))
258     (apply func args)))
259
260 (provide 'eval-expr)
261
262 ;;; eval-expr.el ends here.