Initial Commit
[packages] / xemacs-packages / edebug / eval-reg.el
1 ;;; eval-reg.el --- Redefine eval-region, and subrs that use it, in Lisp
2
3 ;; Copyright (C) 1994 Daniel LaLiberte
4
5 ;; Author: Daniel LaLiberte <liberte@cs.uiuc.edu>
6 ;; Keywords: lisp
7
8 ;; This file is part of XEmacs.
9
10 ;; XEmacs is free software; you can redistribute it and/or modify it
11 ;; 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 ;; XEmacs is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 ;; General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with XEmacs; see the file COPYING.  If not, write to the Free
22 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23 ;; 02111-1307, USA.
24
25 ;;; Synched up with: Not in FSF
26
27 ;;; Commentary:
28
29 ;; eval-region, eval-buffer, and eval-current-buffer are redefined in
30 ;; Lisp to allow customizations by Lisp code.  eval-region calls
31 ;; `read', `eval', and `prin1', so Lisp replacements of these
32 ;; functions will affect eval-region and anything else that calls it.
33 ;; eval-buffer and eval-current-buffer are redefined in Lisp to call
34 ;; eval-region on the buffer.  
35
36 ;; Because of dynamic binding, all local variables are protected from
37 ;; being seen by eval by giving them funky names.  But variables in
38 ;; routines that call eval-region are similarly exposed.
39
40 ;; Perhaps this should be one of several files in an `elisp' package
41 ;; that replaces Emacs Lisp subroutines with Lisp versions of the
42 ;; same.
43
44 ;; Eval-region may be installed, after loading, by calling:
45 ;; (elisp-eval-region-install).  Installation can be undone with:
46 ;; (elisp-eval-region-uninstall).
47
48 ;;; Code:
49
50 '(defpackage "elisp-eval-region"
51    (:nicknames "elisp")
52    (:use "elisp")
53    (:export
54     elisp-eval-region-install
55     elisp-eval-region-uninstall
56     elisp-eval-region-level
57     with-elisp-eval-region
58     eval-region
59     eval-buffer
60     eval-current-buffer
61     ))
62 '(in-package elisp-eval-region)
63
64 ;; Save standard versions.
65 (if (not (fboundp 'original-eval-region))
66     (defalias 'original-eval-region (symbol-function 'eval-region)))
67 (if (not (fboundp 'original-eval-buffer))
68     (defalias 'original-eval-buffer 
69           (if (fboundp 'eval-buffer)  ;; only in Emacs 19
70               (symbol-function 'eval-buffer)
71             'undefined)))
72 (if (not (fboundp 'original-eval-current-buffer))
73     (defalias 'original-eval-current-buffer
74           (symbol-function 'eval-current-buffer)))
75
76 (defvar elisp-eval-region-level 0
77   "If the value is 0, use the original version of `elisp-eval-region'.
78 Callers of `elisp-eval-region' should increment `elisp-eval-region-level'
79 while the Lisp version should be used.  Installing `elisp-eval-region'
80 increments it once, and uninstalling decrements it.")
81
82 ;; Installing and uninstalling should always be used in pairs, 
83 ;; or just install once and never uninstall. 
84 (defun elisp-eval-region-install ()
85   (interactive)
86   (defalias 'eval-region 'elisp-eval-region)
87   (defalias 'eval-buffer 'elisp-eval-buffer)
88   (defalias 'eval-current-buffer 'elisp-eval-current-buffer)
89   (setq elisp-eval-region-level (1+ elisp-eval-region-level)))
90
91 (defun elisp-eval-region-uninstall ()
92   (interactive)
93   (if (> 1 elisp-eval-region-level)
94       (setq elisp-eval-region-level (1- elisp-eval-region-level))
95     (setq elisp-eval-region-level 0)
96     (defalias 'eval-region (symbol-function 'original-eval-region))
97     (defalias 'eval-buffer (symbol-function 'original-eval-buffer))
98     (defalias 'eval-current-buffer 
99       (symbol-function 'original-eval-current-buffer))
100     ))
101
102 (put 'with-elisp-eval-region 'lisp-indent-function 1)
103 (put 'with-elisp-eval-region 'lisp-indent-hook 1)
104 (put 'with-elisp-eval-region 'edebug-form-spec t)
105
106 (defmacro with-elisp-eval-region (flag &rest body)
107   "If FLAG is nil, decrement `eval-region-level' while executing BODY.
108 The effect of decrementing all the way to zero is that `eval-region'
109 will use the original `eval-region', which may be the Emacs subr or some
110 previous redefinition.  Before calling this macro, this package should
111 already have been installed, using `elisp-eval-region-install', which
112 increments the count once.  So if another package still requires the
113 Lisp version of the code, the count will still be non-zero.
114
115 The count is not bound locally by this macro, so changes by BODY to
116 its value will not be lost."
117   (` (let ((elisp-code (function (lambda () (,@ body)))))
118        (if (not (, flag))
119            (unwind-protect
120                (progn
121                  (setq elisp-eval-region-level (1- elisp-eval-region-level))
122                  (funcall elisp-code))
123              (setq elisp-eval-region-level (1+ elisp-eval-region-level)))
124          (funcall elisp-code)))))
125
126
127 (defun elisp-eval-region (elisp-start elisp-end &optional elisp-output)
128   "Execute the region as Lisp code.
129 When called from programs, expects two arguments,
130 giving starting and ending indices in the current buffer
131 of the text to be executed.
132 Programs can pass third argument PRINTFLAG which controls printing of output:
133 nil means discard it; anything else is stream for print.
134
135 This version, from `eval-reg.el', allows Lisp customization of read,
136 eval, and the printer."
137
138   ;; Because this doesnt narrow to the region, one other difference 
139   ;; concerns inserting whitespace after the expression being evaluated.
140
141   (interactive "r")
142   (if (= 0 elisp-eval-region-level)
143       (original-eval-region elisp-start elisp-end elisp-output)
144     (let ((elisp-pnt (point))
145           (elisp-buf (current-buffer));; Outside buffer
146           (elisp-inside-buf (current-buffer));; Buffer current while evaling
147           ;; Mark the end because it may move.
148           (elisp-end-marker (set-marker (make-marker) elisp-end))
149           elisp-form
150           elisp-val)
151       (goto-char elisp-start)
152       (elisp-skip-whitespace)
153       (while (< (point) elisp-end-marker)
154         (setq elisp-form (read elisp-buf))
155
156         (let ((elisp-current-buffer (current-buffer)))
157           ;; Restore the inside current-buffer.
158           (set-buffer elisp-inside-buf)
159           (setq elisp-val (eval elisp-form))
160           ;; Remember current buffer for next time.
161           (setq elisp-inside-buf (current-buffer))
162           ;; Should this be protected?
163           (set-buffer elisp-current-buffer))
164
165         (if elisp-output
166             (let ((standard-output (or elisp-output t)))
167               (setq values (cons elisp-val values))
168               (if (eq standard-output t)
169                   (prin1 elisp-val)
170                 (princ "\n")
171                 (prin1 elisp-val)
172                 (princ "\n")
173                 )))
174         (goto-char (min (max elisp-end-marker (point))
175                         (progn (elisp-skip-whitespace) (point))))
176         )                               ; while
177       (if elisp-output nil
178         ;; like save-excursion recovery, but done only if no error occurs
179         ;; but mark is not restored
180         (set-buffer elisp-buf)
181         (goto-char elisp-pnt))
182       nil)))
183
184
185 (defun elisp-skip-whitespace ()
186   ;; Leave point before the next token, skipping white space and comments.
187   (skip-chars-forward " \t\r\n\f")
188   (while (= (following-char) ?\;)
189     (skip-chars-forward "^\n\r")  ; skip the comment
190     (skip-chars-forward " \t\r\n\f")))
191
192
193 (defun elisp-eval-current-buffer (&optional elisp-output)
194   "Execute the current buffer as Lisp code.
195 Programs can pass argument PRINTFLAG which controls printing of output:
196 nil means discard it; anything else is stream for print.
197
198 This version calls `eval-region' on the whole buffer."
199   ;; The standard eval-current-buffer doesn't use eval-region.
200   (interactive)
201   (eval-region (point-min) (point-max) elisp-output))
202
203
204 (defun elisp-eval-buffer (&optional elisp-bufname elisp-printflag)
205   "Execute BUFFER as Lisp code.  Use current buffer if BUFFER is nil.
206 Programs can pass argument PRINTFLAG which controls printing of
207 output: nil means discard it; anything else is stream for print.
208
209 This version calls `eval-region' on the whole buffer."
210   (interactive)
211   (if (null elisp-bufname)
212       (setq elisp-bufname (current-buffer)))
213   (save-excursion
214     (set-buffer (or (get-buffer elisp-bufname) 
215                     (error "No such buffer: %s" elisp-bufname)))
216     (eval-region (point-min) (point-max) elisp-printflag)))
217
218 (provide 'eval-reg)
219
220 ;;; eval-reg.el ends here