| 1 | ;;; -*- Log: hemlock.log; Package: Hemlock -*-
|
|---|
| 2 | ;;;
|
|---|
| 3 | ;;; **********************************************************************
|
|---|
| 4 | ;;; This code was written as part of the CMU Common Lisp project at
|
|---|
| 5 | ;;; Carnegie Mellon University, and has been placed in the public domain.
|
|---|
| 6 | ;;;
|
|---|
| 7 | #+CMU (ext:file-comment
|
|---|
| 8 | "$Header$")
|
|---|
| 9 | ;;;
|
|---|
| 10 | ;;; **********************************************************************
|
|---|
| 11 | ;;;
|
|---|
| 12 | ;;; Stuff to do a little lisp hacking in the editor's Lisp environment.
|
|---|
| 13 | ;;;
|
|---|
| 14 |
|
|---|
| 15 | (in-package :hemlock)
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 | (defmacro in-lisp (&body body)
|
|---|
| 19 | "Evaluates body inside HANDLE-LISP-ERRORS. *package* is bound to the package
|
|---|
| 20 | named by \"Current Package\" if it is non-nil."
|
|---|
| 21 | (let ((name (gensym)) (package (gensym)))
|
|---|
| 22 | `(handle-lisp-errors
|
|---|
| 23 | (let* ((,name (value current-package))
|
|---|
| 24 | (,package (and ,name (find-package ,name))))
|
|---|
| 25 | (progv (if ,package '(*package*)) (if ,package (list ,package))
|
|---|
| 26 | ,@body)))))
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 | (define-file-option "Package" (buffer value)
|
|---|
| 30 | (defhvar "Current Package"
|
|---|
| 31 | "The package used for evaluation of Lisp in this buffer."
|
|---|
| 32 | :buffer buffer
|
|---|
| 33 | :value
|
|---|
| 34 | (let* ((eof (list nil))
|
|---|
| 35 | (thing (read-from-string value nil eof)))
|
|---|
| 36 | (when (eq thing eof) (error "Bad package file option value."))
|
|---|
| 37 | (cond
|
|---|
| 38 | ((stringp thing)
|
|---|
| 39 | thing)
|
|---|
| 40 | ((symbolp thing)
|
|---|
| 41 | (symbol-name thing))
|
|---|
| 42 | ((characterp thing)
|
|---|
| 43 | (string thing))
|
|---|
| 44 | (t
|
|---|
| 45 | (message
|
|---|
| 46 | "Ignoring \"package\" file option -- cannot convert to a string."))))
|
|---|
| 47 | :hooks (list 'package-name-change-hook)))
|
|---|
| 48 |
|
|---|
| 49 | |
|---|
| 50 |
|
|---|
| 51 | ;;;; Eval Mode Interaction.
|
|---|
| 52 |
|
|---|
| 53 | (declaim (special * ** *** - + ++ +++ / // ///))
|
|---|
| 54 |
|
|---|
| 55 |
|
|---|
| 56 | (defun get-prompt ()
|
|---|
| 57 | #+cmu (locally (declare (special ext:*prompt*))
|
|---|
| 58 | (if (functionp ext:*prompt*)
|
|---|
| 59 | (funcall ext:*prompt*)
|
|---|
| 60 | ext:*prompt*))
|
|---|
| 61 | #+sbcl (with-output-to-string (out)
|
|---|
| 62 | (funcall sb-int:*repl-prompt-fun* out))
|
|---|
| 63 | #-(or cmu sbcl) "* ")
|
|---|
| 64 |
|
|---|
| 65 |
|
|---|
| 66 | (defun show-prompt (&optional (stream *standard-output*))
|
|---|
| 67 | #-sbcl (princ (get-prompt) stream)
|
|---|
| 68 | #+sbcl (funcall sb-int:*repl-prompt-fun* stream))
|
|---|
| 69 |
|
|---|
| 70 |
|
|---|
| 71 | (defun setup-eval-mode (buffer)
|
|---|
| 72 | (let ((point (buffer-point buffer)))
|
|---|
| 73 | (setf (buffer-minor-mode buffer "Eval") t)
|
|---|
| 74 | (setf (buffer-minor-mode buffer "Editor") t)
|
|---|
| 75 | (setf (buffer-major-mode buffer) "Lisp")
|
|---|
| 76 | (buffer-end point)
|
|---|
| 77 | (defhvar "Current Package"
|
|---|
| 78 | "This variable holds the name of the package currently used for Lisp
|
|---|
| 79 | evaluation and compilation. If it is Nil, the value of *Package* is used
|
|---|
| 80 | instead."
|
|---|
| 81 | :value nil
|
|---|
| 82 | :buffer buffer)
|
|---|
| 83 | (unless (hemlock-bound-p 'buffer-input-mark :buffer buffer)
|
|---|
| 84 | (defhvar "Buffer Input Mark"
|
|---|
| 85 | "Mark used for Eval Mode input."
|
|---|
| 86 | :buffer buffer
|
|---|
| 87 | :value (copy-mark point :right-inserting))
|
|---|
| 88 | (defhvar "Eval Output Stream"
|
|---|
| 89 | "Output stream used for Eval Mode output in this buffer."
|
|---|
| 90 | :buffer buffer
|
|---|
| 91 | :value (make-hemlock-output-stream point))
|
|---|
| 92 | (defhvar "Interactive History"
|
|---|
| 93 | "A ring of the regions input to an interactive mode (Eval or Typescript)."
|
|---|
| 94 | :buffer buffer
|
|---|
| 95 | :value (make-ring (value interactive-history-length)))
|
|---|
| 96 | (defhvar "Interactive Pointer"
|
|---|
| 97 | "Pointer into \"Interactive History\"."
|
|---|
| 98 | :buffer buffer
|
|---|
| 99 | :value 0)
|
|---|
| 100 | (defhvar "Searching Interactive Pointer"
|
|---|
| 101 | "Pointer into \"Interactive History\"."
|
|---|
| 102 | :buffer buffer
|
|---|
| 103 | :value 0))
|
|---|
| 104 | (let ((*standard-output*
|
|---|
| 105 | (variable-value 'eval-output-stream :buffer buffer)))
|
|---|
| 106 | (fresh-line)
|
|---|
| 107 | (show-prompt))
|
|---|
| 108 | (move-mark (variable-value 'buffer-input-mark :buffer buffer) point)))
|
|---|
| 109 |
|
|---|
| 110 | (defmode "Eval" :major-p nil :setup-function #'setup-eval-mode)
|
|---|
| 111 |
|
|---|
| 112 | (defun eval-mode-lisp-mode-hook (buffer on)
|
|---|
| 113 | "Turn on Lisp mode when we go into Eval Mode."
|
|---|
| 114 | (when on
|
|---|
| 115 | (setf (buffer-major-mode buffer) "Lisp")))
|
|---|
| 116 | ;;;
|
|---|
| 117 | (add-hook eval-mode-hook 'eval-mode-lisp-mode-hook)
|
|---|
| 118 |
|
|---|
| 119 | (defhvar "Editor Definition Info"
|
|---|
| 120 | "When this is non-nil, the editor Lisp is used to determine definition
|
|---|
| 121 | editing information; otherwise, the slave Lisp is used."
|
|---|
| 122 | :value t
|
|---|
| 123 | :mode "Eval")
|
|---|
| 124 |
|
|---|
| 125 |
|
|---|
| 126 | (defvar *selected-eval-buffer* nil)
|
|---|
| 127 |
|
|---|
| 128 | (defcommand "Select Eval Buffer" (p)
|
|---|
| 129 | "Goto buffer in \"Eval\" mode, creating one if necessary."
|
|---|
| 130 | "Goto buffer in \"Eval\" mode, creating one if necessary."
|
|---|
| 131 | (declare (ignore p))
|
|---|
| 132 | (unless *selected-eval-buffer*
|
|---|
| 133 | (when (getstring "Eval" *buffer-names*)
|
|---|
| 134 | (editor-error "There is already a buffer named \"Eval\"!"))
|
|---|
| 135 | (setf *selected-eval-buffer*
|
|---|
| 136 | (make-buffer "Eval"
|
|---|
| 137 | :delete-hook
|
|---|
| 138 | (list #'(lambda (buf)
|
|---|
| 139 | (declare (ignore buf))
|
|---|
| 140 | (setf *selected-eval-buffer* nil)))))
|
|---|
| 141 | (setf (buffer-minor-mode *selected-eval-buffer* "Eval") t))
|
|---|
| 142 | (change-to-buffer *selected-eval-buffer*))
|
|---|
| 143 |
|
|---|
| 144 |
|
|---|
| 145 | (defvar lispbuf-eof '(nil))
|
|---|
| 146 |
|
|---|
| 147 | (defhvar "Unwedge Interactive Input Confirm"
|
|---|
| 148 | "When set (the default), trying to confirm interactive input when the
|
|---|
| 149 | point is not after the input mark causes Hemlock to ask the user if he
|
|---|
| 150 | needs to be unwedged. When not set, an editor error is signaled
|
|---|
| 151 | informing the user that the point is before the input mark."
|
|---|
| 152 | :value t)
|
|---|
| 153 |
|
|---|
| 154 | (defun unwedge-eval-buffer ()
|
|---|
| 155 | (abort-eval-input-command nil))
|
|---|
| 156 |
|
|---|
| 157 | (defhvar "Unwedge Interactive Input Fun"
|
|---|
| 158 | "Function to call when input is confirmed, but the point is not past the
|
|---|
| 159 | input mark."
|
|---|
| 160 | :value #'unwedge-eval-buffer
|
|---|
| 161 | :mode "Eval")
|
|---|
| 162 |
|
|---|
| 163 | (defhvar "Unwedge Interactive Input String"
|
|---|
| 164 | "String to add to \"Point not past input mark. \" explaining what will
|
|---|
| 165 | happen if the the user chooses to be unwedged."
|
|---|
| 166 | :value "Prompt again at the end of the buffer? "
|
|---|
| 167 | :mode "Eval")
|
|---|
| 168 |
|
|---|
| 169 | (defcommand "Confirm Eval Input" (p)
|
|---|
| 170 | "Evaluate Eval Mode input between point and last prompt."
|
|---|
| 171 | "Evaluate Eval Mode input between point and last prompt."
|
|---|
| 172 | (declare (ignore p))
|
|---|
| 173 | (let ((input-region (get-interactive-input)))
|
|---|
| 174 | (when input-region
|
|---|
| 175 | (let* ((output (value eval-output-stream))
|
|---|
| 176 | (*standard-output* output)
|
|---|
| 177 | (*error-output* output)
|
|---|
| 178 | (*trace-output* output))
|
|---|
| 179 | (fresh-line)
|
|---|
| 180 | (in-lisp
|
|---|
| 181 | ;; Copy the region to keep the output and input streams from interacting
|
|---|
| 182 | ;; since input-region is made of permanent marks into the buffer.
|
|---|
| 183 | (with-input-from-region (stream (copy-region input-region))
|
|---|
| 184 | (loop
|
|---|
| 185 | (let ((form (read stream nil lispbuf-eof)))
|
|---|
| 186 | (when (eq form lispbuf-eof)
|
|---|
| 187 | ;; Move the buffer's input mark to the end of the buffer.
|
|---|
| 188 | (move-mark (region-start input-region)
|
|---|
| 189 | (region-end input-region))
|
|---|
| 190 | (return))
|
|---|
| 191 | (setq +++ ++ ++ + + - - form)
|
|---|
| 192 | (let ((this-eval (multiple-value-list (eval form))))
|
|---|
| 193 | (fresh-line)
|
|---|
| 194 | (dolist (x this-eval) (prin1 x) (terpri))
|
|---|
| 195 | (show-prompt)
|
|---|
| 196 | (setq /// // // / / this-eval)
|
|---|
| 197 | (setq *** ** ** * * (car this-eval)))))))))))
|
|---|
| 198 |
|
|---|
| 199 | (defcommand "Abort Eval Input" (p)
|
|---|
| 200 | "Move to the end of the buffer and prompt."
|
|---|
| 201 | "Move to the end of the buffer and prompt."
|
|---|
| 202 | (declare (ignore p))
|
|---|
| 203 | (let ((point (current-point)))
|
|---|
| 204 | (buffer-end point)
|
|---|
| 205 | (insert-character point #\newline)
|
|---|
| 206 | (insert-string point "Aborted.")
|
|---|
| 207 | (insert-character point #\newline)
|
|---|
| 208 | (insert-string point (get-prompt))
|
|---|
| 209 | (move-mark (value buffer-input-mark) point)))
|
|---|
| 210 |
|
|---|
| 211 |
|
|---|
| 212 | |
|---|
| 213 |
|
|---|
| 214 | ;;;; General interactive commands used in eval and typescript buffers.
|
|---|
| 215 |
|
|---|
| 216 | (defun get-interactive-input ()
|
|---|
| 217 | "Tries to return a region. When the point is not past the input mark, and
|
|---|
| 218 | the user has \"Unwedge Interactive Input Confirm\" set, the buffer is
|
|---|
| 219 | optionally fixed up, and nil is returned. Otherwise, an editor error is
|
|---|
| 220 | signalled. When a region is returned, the start is the current buffer's
|
|---|
| 221 | input mark, and the end is the current point moved to the end of the buffer."
|
|---|
| 222 | (let ((point (current-point))
|
|---|
| 223 | (mark (value buffer-input-mark)))
|
|---|
| 224 | (cond
|
|---|
| 225 | ((mark>= point mark)
|
|---|
| 226 | (buffer-end point)
|
|---|
| 227 | (let* ((input-region (region mark point))
|
|---|
| 228 | (string (region-to-string input-region))
|
|---|
| 229 | (ring (value interactive-history)))
|
|---|
| 230 | (when (and (or (zerop (ring-length ring))
|
|---|
| 231 | (string/= string (region-to-string (ring-ref ring 0))))
|
|---|
| 232 | (> (length string) (value minimum-interactive-input-length)))
|
|---|
| 233 | (ring-push (copy-region input-region) ring))
|
|---|
| 234 | input-region))
|
|---|
| 235 | ((value unwedge-interactive-input-confirm)
|
|---|
| 236 | (beep)
|
|---|
| 237 | (when (prompt-for-y-or-n
|
|---|
| 238 | :prompt (concatenate 'simple-string
|
|---|
| 239 | "Point not past input mark. "
|
|---|
| 240 | (value unwedge-interactive-input-string))
|
|---|
| 241 | :must-exist t :default t :default-string "yes")
|
|---|
| 242 | (funcall (value unwedge-interactive-input-fun))
|
|---|
| 243 | (message "Unwedged."))
|
|---|
| 244 | nil)
|
|---|
| 245 | (t
|
|---|
| 246 | (editor-error "Point not past input mark.")))))
|
|---|
| 247 |
|
|---|
| 248 | (defhvar "Interactive History Length"
|
|---|
| 249 | "This is the length used for the history ring in interactive buffers.
|
|---|
| 250 | It must be set before turning on the mode."
|
|---|
| 251 | :value 10)
|
|---|
| 252 |
|
|---|
| 253 | (defhvar "Minimum Interactive Input Length"
|
|---|
| 254 | "When the number of characters in an interactive buffer exceeds this value,
|
|---|
| 255 | it is pushed onto the interactive history, otherwise it is lost forever."
|
|---|
| 256 | :value 2)
|
|---|
| 257 |
|
|---|
| 258 |
|
|---|
| 259 | (defvar *previous-input-search-string* "ignore")
|
|---|
| 260 |
|
|---|
| 261 | (defvar *previous-input-search-pattern*
|
|---|
| 262 | ;; Give it a bogus string since you can't give it the empty string.
|
|---|
| 263 | (new-search-pattern :string-insensitive :forward "ignore"))
|
|---|
| 264 |
|
|---|
| 265 | (defun get-previous-input-search-pattern (string)
|
|---|
| 266 | (if (string= *previous-input-search-string* string)
|
|---|
| 267 | *previous-input-search-pattern*
|
|---|
| 268 | (new-search-pattern :string-insensitive :forward
|
|---|
| 269 | (setf *previous-input-search-string* string)
|
|---|
| 270 | *previous-input-search-pattern*)))
|
|---|
| 271 |
|
|---|
| 272 | (defcommand "Search Previous Interactive Input" (p)
|
|---|
| 273 | "Search backward through the interactive history using the current input as
|
|---|
| 274 | a search string. Consecutive invocations repeat the previous search."
|
|---|
| 275 | "Search backward through the interactive history using the current input as
|
|---|
| 276 | a search string. Consecutive invocations repeat the previous search."
|
|---|
| 277 | (declare (ignore p))
|
|---|
| 278 | (let* ((mark (value buffer-input-mark))
|
|---|
| 279 | (ring (value interactive-history))
|
|---|
| 280 | (point (current-point))
|
|---|
| 281 | (just-invoked (eq (last-command-type) :searching-interactive-input)))
|
|---|
| 282 | (when (mark<= point mark)
|
|---|
| 283 | (editor-error "Point not past input mark."))
|
|---|
| 284 | (when (zerop (ring-length ring))
|
|---|
| 285 | (editor-error "No previous input in this buffer."))
|
|---|
| 286 | (unless just-invoked
|
|---|
| 287 | (get-previous-input-search-pattern (region-to-string (region mark point))))
|
|---|
| 288 | (let ((found-it (find-previous-input ring just-invoked)))
|
|---|
| 289 | (unless found-it
|
|---|
| 290 | (editor-error "Couldn't find ~a." *previous-input-search-string*))
|
|---|
| 291 | (delete-region (region mark point))
|
|---|
| 292 | (insert-region point (ring-ref ring found-it))
|
|---|
| 293 | (setf (value searching-interactive-pointer) found-it))
|
|---|
| 294 | (setf (last-command-type) :searching-interactive-input)))
|
|---|
| 295 |
|
|---|
| 296 | (defun find-previous-input (ring againp)
|
|---|
| 297 | (let ((ring-length (ring-length ring))
|
|---|
| 298 | (base (if againp
|
|---|
| 299 | (+ (value searching-interactive-pointer) 1)
|
|---|
| 300 | 0)))
|
|---|
| 301 | (loop
|
|---|
| 302 | (when (= base ring-length)
|
|---|
| 303 | (if againp
|
|---|
| 304 | (setf base 0)
|
|---|
| 305 | (return nil)))
|
|---|
| 306 | (with-mark ((m (region-start (ring-ref ring base))))
|
|---|
| 307 | (when (find-pattern m *previous-input-search-pattern*)
|
|---|
| 308 | (return base)))
|
|---|
| 309 | (incf base))))
|
|---|
| 310 |
|
|---|
| 311 | (defcommand "Previous Interactive Input" (p)
|
|---|
| 312 | "Insert the previous input in an interactive mode (Eval or Typescript).
|
|---|
| 313 | If repeated, keep rotating the history. With prefix argument, rotate
|
|---|
| 314 | that many times."
|
|---|
| 315 | "Pop the *interactive-history* at the point."
|
|---|
| 316 | (let* ((point (current-point))
|
|---|
| 317 | (mark (value buffer-input-mark))
|
|---|
| 318 | (ring (value interactive-history))
|
|---|
| 319 | (length (ring-length ring))
|
|---|
| 320 | (p (or p 1)))
|
|---|
| 321 | (when (or (mark< point mark) (zerop length)) (editor-error))
|
|---|
| 322 | (cond
|
|---|
| 323 | ((eq (last-command-type) :interactive-history)
|
|---|
| 324 | (let ((base (mod (+ (value interactive-pointer) p) length)))
|
|---|
| 325 | (delete-region (region mark point))
|
|---|
| 326 | (insert-region point (ring-ref ring base))
|
|---|
| 327 | (setf (value interactive-pointer) base)))
|
|---|
| 328 | (t
|
|---|
| 329 | (let ((base (mod (if (minusp p) p (1- p)) length))
|
|---|
| 330 | (region (delete-and-save-region (region mark point))))
|
|---|
| 331 | (insert-region point (ring-ref ring base))
|
|---|
| 332 | (when (mark/= (region-start region) (region-end region))
|
|---|
| 333 | (ring-push region ring)
|
|---|
| 334 | (incf base))
|
|---|
| 335 | (setf (value interactive-pointer) base)))))
|
|---|
| 336 | (setf (last-command-type) :interactive-history))
|
|---|
| 337 |
|
|---|
| 338 | (defcommand "Next Interactive Input" (p)
|
|---|
| 339 | "Rotate the interactive history backwards. The region is left around the
|
|---|
| 340 | inserted text. With prefix argument, rotate that many times."
|
|---|
| 341 | "Call previous-interactive-input-command with negated arg."
|
|---|
| 342 | (previous-interactive-input-command (- (or p 1))))
|
|---|
| 343 |
|
|---|
| 344 | (defcommand "Kill Interactive Input" (p)
|
|---|
| 345 | "Kill any input to an interactive mode (Eval or Typescript)."
|
|---|
| 346 | "Kill any input to an interactive mode (Eval or Typescript)."
|
|---|
| 347 | (declare (ignore p))
|
|---|
| 348 | (let ((point (buffer-point (current-buffer)))
|
|---|
| 349 | (mark (value buffer-input-mark)))
|
|---|
| 350 | (when (mark< point mark) (editor-error))
|
|---|
| 351 | (kill-region (region mark point) :kill-backward)))
|
|---|
| 352 |
|
|---|
| 353 | (defcommand "Interactive Beginning of Line" (p)
|
|---|
| 354 | "If on line with current prompt, go to after it, otherwise do what
|
|---|
| 355 | \"Beginning of Line\" always does."
|
|---|
| 356 | "Go to after prompt when on prompt line."
|
|---|
| 357 | (let ((mark (value buffer-input-mark))
|
|---|
| 358 | (point (current-point)))
|
|---|
| 359 | (if (and (same-line-p point mark) (or (not p) (= p 1)))
|
|---|
| 360 | (move-mark point mark)
|
|---|
| 361 | (beginning-of-line-command p))))
|
|---|
| 362 |
|
|---|
| 363 | (defcommand "Reenter Interactive Input" (p)
|
|---|
| 364 | "Copies the form to the left of point to be after the interactive buffer's
|
|---|
| 365 | input mark. When the current region is active, it is copied instead."
|
|---|
| 366 | "Copies the form to the left of point to be after the interactive buffer's
|
|---|
| 367 | input mark. When the current region is active, it is copied instead."
|
|---|
| 368 | (declare (ignore p))
|
|---|
| 369 | (unless (hemlock-bound-p 'buffer-input-mark)
|
|---|
| 370 | (editor-error "Not in an interactive buffer."))
|
|---|
| 371 | (let ((point (current-point)))
|
|---|
| 372 | (let ((region (if (region-active-p)
|
|---|
| 373 | ;; Copy this, so moving point doesn't affect the region.
|
|---|
| 374 | (copy-region (current-region))
|
|---|
| 375 | (with-mark ((start point)
|
|---|
| 376 | (end point))
|
|---|
| 377 | (pre-command-parse-check start)
|
|---|
| 378 | (unless (form-offset start -1)
|
|---|
| 379 | (editor-error "Not after complete form."))
|
|---|
| 380 | (region (copy-mark start) (copy-mark end))))))
|
|---|
| 381 | (buffer-end point)
|
|---|
| 382 | (push-buffer-mark (copy-mark point))
|
|---|
| 383 | (insert-region point region)
|
|---|
| 384 | (setf (last-command-type) :ephemerally-active))))
|
|---|
| 385 |
|
|---|
| 386 |
|
|---|
| 387 | |
|---|
| 388 |
|
|---|
| 389 | ;;; Other stuff.
|
|---|
| 390 |
|
|---|
| 391 | (defmode "Editor")
|
|---|
| 392 |
|
|---|
| 393 | (defcommand "Editor Mode" (p)
|
|---|
| 394 | "Turn on \"Editor\" mode in the current buffer. If it is already on, turn it
|
|---|
| 395 | off. When in editor mode, most lisp compilation and evaluation commands
|
|---|
| 396 | manipulate the editor process instead of the current eval server."
|
|---|
| 397 | "Toggle \"Editor\" mode in the current buffer."
|
|---|
| 398 | (declare (ignore p))
|
|---|
| 399 | (setf (buffer-minor-mode (current-buffer) "Editor")
|
|---|
| 400 | (not (buffer-minor-mode (current-buffer) "Editor"))))
|
|---|
| 401 |
|
|---|
| 402 | (define-file-option "Editor" (buffer value)
|
|---|
| 403 | (declare (ignore value))
|
|---|
| 404 | (setf (buffer-minor-mode buffer "Editor") t))
|
|---|
| 405 |
|
|---|
| 406 | (defhvar "Editor Definition Info"
|
|---|
| 407 | "When this is non-nil, the editor Lisp is used to determine definition
|
|---|
| 408 | editing information; otherwise, the slave Lisp is used."
|
|---|
| 409 | :value t
|
|---|
| 410 | :mode "Editor")
|
|---|
| 411 |
|
|---|
| 412 | (defcommand "Editor Compile Defun" (p)
|
|---|
| 413 | "Compiles the current or next top-level form in the editor Lisp.
|
|---|
| 414 | First the form is evaluated, then the result of this evaluation
|
|---|
| 415 | is passed to compile. If the current region is active, this
|
|---|
| 416 | compiles the region."
|
|---|
| 417 | "Evaluates the current or next top-level form in the editor Lisp."
|
|---|
| 418 | (declare (ignore p))
|
|---|
| 419 | (if (region-active-p)
|
|---|
| 420 | (editor-compile-region (current-region))
|
|---|
| 421 | (editor-compile-region (defun-region (current-point)) t)))
|
|---|
| 422 |
|
|---|
| 423 | (defcommand "Editor Compile Region" (p)
|
|---|
| 424 | "Compiles lisp forms between the point and the mark in the editor Lisp."
|
|---|
| 425 | "Compiles lisp forms between the point and the mark in the editor Lisp."
|
|---|
| 426 | (declare (ignore p))
|
|---|
| 427 | (editor-compile-region (current-region)))
|
|---|
| 428 |
|
|---|
| 429 | (defun defun-region (mark)
|
|---|
| 430 | "This returns a region around the current or next defun with respect to mark.
|
|---|
| 431 | Mark is not used to form the region. If there is no appropriate top level
|
|---|
| 432 | form, this signals an editor-error. This calls PRE-COMMAND-PARSE-CHECK."
|
|---|
| 433 | (with-mark ((start mark)
|
|---|
| 434 | (end mark))
|
|---|
| 435 | (pre-command-parse-check start)
|
|---|
| 436 | (cond ((not (mark-top-level-form start end))
|
|---|
| 437 | (editor-error "No current or next top level form."))
|
|---|
| 438 | (t (region start end)))))
|
|---|
| 439 |
|
|---|
| 440 | (defun editor-compile-region (region &optional quiet)
|
|---|
| 441 | (unless quiet (message "Compiling region ..."))
|
|---|
| 442 | (in-lisp
|
|---|
| 443 | (with-input-from-region (stream region)
|
|---|
| 444 | (with-pop-up-display (*error-output* :height 19)
|
|---|
| 445 | ;; JDz: We don't record source locations and what not, but this
|
|---|
| 446 | ;; is portable. CMUCL specific implementation removed because
|
|---|
| 447 | ;; it does not work on HEMLOCK-REGION-STREAM (but it can be
|
|---|
| 448 | ;; added back later if CMUCL starts using user-extensible
|
|---|
| 449 | ;; streams internally.)
|
|---|
| 450 | (funcall (compile nil `(lambda ()
|
|---|
| 451 | ,@(loop for form = (read stream nil stream)
|
|---|
| 452 | until (eq form stream)
|
|---|
| 453 | collect form))))))))
|
|---|
| 454 |
|
|---|
| 455 |
|
|---|
| 456 | (defcommand "Editor Evaluate Defun" (p)
|
|---|
| 457 | "Evaluates the current or next top-level form in the editor Lisp.
|
|---|
| 458 | If the current region is active, this evaluates the region."
|
|---|
| 459 | "Evaluates the current or next top-level form in the editor Lisp."
|
|---|
| 460 | (declare (ignore p))
|
|---|
| 461 | (if (region-active-p)
|
|---|
| 462 | (editor-evaluate-region-command nil)
|
|---|
| 463 | (with-input-from-region (stream (defun-region (current-point)))
|
|---|
| 464 | (clear-echo-area)
|
|---|
| 465 | (in-lisp
|
|---|
| 466 | (message "Editor Evaluation returned ~S"
|
|---|
| 467 | (eval (read stream)))))))
|
|---|
| 468 |
|
|---|
| 469 | (defcommand "Editor Evaluate Region" (p)
|
|---|
| 470 | "Evaluates lisp forms between the point and the mark in the editor Lisp."
|
|---|
| 471 | "Evaluates lisp forms between the point and the mark in the editor Lisp."
|
|---|
| 472 | (declare (ignore p))
|
|---|
| 473 | (with-input-from-region (stream (current-region))
|
|---|
| 474 | (clear-echo-area)
|
|---|
| 475 | (write-string "Evaluating region in the editor ..." *echo-area-stream*)
|
|---|
| 476 | (finish-output *echo-area-stream*)
|
|---|
| 477 | (in-lisp
|
|---|
| 478 | (do ((object (read stream nil lispbuf-eof)
|
|---|
| 479 | (read stream nil lispbuf-eof)))
|
|---|
| 480 | ((eq object lispbuf-eof))
|
|---|
| 481 | (eval object)))
|
|---|
| 482 | (message "Evaluation complete.")))
|
|---|
| 483 |
|
|---|
| 484 | (defcommand "Editor Re-evaluate Defvar" (p)
|
|---|
| 485 | "Evaluate the current or next top-level form if it is a DEFVAR. Treat the
|
|---|
| 486 | form as if the variable is not bound. This occurs in the editor Lisp."
|
|---|
| 487 | "Evaluate the current or next top-level form if it is a DEFVAR. Treat the
|
|---|
| 488 | form as if the variable is not bound. This occurs in the editor Lisp."
|
|---|
| 489 | (declare (ignore p))
|
|---|
| 490 | (with-input-from-region (stream (defun-region (current-point)))
|
|---|
| 491 | (clear-echo-area)
|
|---|
| 492 | (in-lisp
|
|---|
| 493 | (let ((form (read stream)))
|
|---|
| 494 | (unless (eq (car form) 'defvar) (editor-error "Not a DEFVAR."))
|
|---|
| 495 | (makunbound (cadr form))
|
|---|
| 496 | (message "Evaluation returned ~S" (eval form))))))
|
|---|
| 497 |
|
|---|
| 498 | (defcommand "Editor Macroexpand Expression" (p)
|
|---|
| 499 | "Show the macroexpansion of the current expression in the null environment.
|
|---|
| 500 | With an argument, use MACROEXPAND instead of MACROEXPAND-1."
|
|---|
| 501 | "Show the macroexpansion of the current expression in the null environment.
|
|---|
| 502 | With an argument, use MACROEXPAND instead of MACROEXPAND-1."
|
|---|
| 503 | (let ((point (buffer-point (current-buffer))))
|
|---|
| 504 | (with-mark ((start point))
|
|---|
| 505 | (pre-command-parse-check start)
|
|---|
| 506 | (with-mark ((end start))
|
|---|
| 507 | (unless (form-offset end 1) (editor-error))
|
|---|
| 508 | (in-lisp
|
|---|
| 509 | (with-pop-up-display (rts)
|
|---|
| 510 | (write-string (with-input-from-region (s (region start end))
|
|---|
| 511 | (prin1-to-string (funcall (if p
|
|---|
| 512 | 'macroexpand
|
|---|
| 513 | 'macroexpand-1)
|
|---|
| 514 | (read s))))
|
|---|
| 515 | rts)))))))
|
|---|
| 516 |
|
|---|
| 517 | (defcommand "Editor Evaluate Expression" (p)
|
|---|
| 518 | "Prompt for an expression to evaluate in the editor Lisp."
|
|---|
| 519 | "Prompt for an expression to evaluate in the editor Lisp."
|
|---|
| 520 | (declare (ignore p))
|
|---|
| 521 | (in-lisp
|
|---|
| 522 | (multiple-value-call #'message "=> ~@{~#[~;~S~:;~S, ~]~}"
|
|---|
| 523 | (eval (prompt-for-expression
|
|---|
| 524 | :prompt "Editor Eval: "
|
|---|
| 525 | :help "Expression to evaluate")))))
|
|---|
| 526 |
|
|---|
| 527 | (defcommand "Editor Evaluate Buffer" (p)
|
|---|
| 528 | "Evaluates the text in the current buffer in the editor Lisp."
|
|---|
| 529 | "Evaluates the text in the current buffer redirecting *Standard-Output* to
|
|---|
| 530 | the echo area. This occurs in the editor Lisp. The prefix argument is
|
|---|
| 531 | ignored."
|
|---|
| 532 | (declare (ignore p))
|
|---|
| 533 | (clear-echo-area)
|
|---|
| 534 | (write-string "Evaluating buffer in the editor ..." *echo-area-stream*)
|
|---|
| 535 | (finish-output *echo-area-stream*)
|
|---|
| 536 | (with-input-from-region (stream (buffer-region (current-buffer)))
|
|---|
| 537 | (let ((*standard-output* *echo-area-stream*))
|
|---|
| 538 | (in-lisp
|
|---|
| 539 | (do ((object (read stream nil lispbuf-eof)
|
|---|
| 540 | (read stream nil lispbuf-eof)))
|
|---|
| 541 | ((eq object lispbuf-eof))
|
|---|
| 542 | (eval object))))
|
|---|
| 543 | (message "Evaluation complete.")))
|
|---|
| 544 |
|
|---|
| 545 |
|
|---|
| 546 |
|
|---|
| 547 | ;;; With-Output-To-Window -- Internal
|
|---|
| 548 | ;;;
|
|---|
| 549 | ;;;
|
|---|
| 550 | (defmacro with-output-to-window ((stream name) &body forms)
|
|---|
| 551 | "With-Output-To-Window (Stream Name) {Form}*
|
|---|
| 552 | Bind Stream to a stream that writes into the buffer named Name a la
|
|---|
| 553 | With-Output-To-Mark. The buffer is created if it does not exist already
|
|---|
| 554 | and a window is created to display the buffer if it is not displayed.
|
|---|
| 555 | For the duration of the evaluation this window is made the current window."
|
|---|
| 556 | (let ((nam (gensym)) (buffer (gensym)) (point (gensym))
|
|---|
| 557 | (window (gensym)) (old-window (gensym)))
|
|---|
| 558 | `(let* ((,nam ,name)
|
|---|
| 559 | (,buffer (or (getstring ,nam *buffer-names*) (make-buffer ,nam)))
|
|---|
| 560 | (,point (buffer-end (buffer-point ,buffer)))
|
|---|
| 561 | (,window (or (car (buffer-windows ,buffer)) (make-window ,point)))
|
|---|
| 562 | (,old-window (current-window)))
|
|---|
| 563 | (unwind-protect
|
|---|
| 564 | (progn (setf (current-window) ,window)
|
|---|
| 565 | (buffer-end ,point)
|
|---|
| 566 | (with-output-to-mark (,stream ,point) ,@forms))
|
|---|
| 567 | (setf (current-window) ,old-window)))))
|
|---|
| 568 |
|
|---|
| 569 | (defcommand "Editor Compile File" (p)
|
|---|
| 570 | "Prompts for file to compile in the editor Lisp. Does not compare source
|
|---|
| 571 | and binary write dates. Does not check any buffer for that file for
|
|---|
| 572 | whether the buffer needs to be saved."
|
|---|
| 573 | "Prompts for file to compile."
|
|---|
| 574 | (declare (ignore p))
|
|---|
| 575 | (let ((pn (prompt-for-file :default
|
|---|
| 576 | (buffer-default-pathname (current-buffer))
|
|---|
| 577 | :prompt "File to compile: ")))
|
|---|
| 578 | (with-output-to-window (*error-output* "Compiler Warnings")
|
|---|
| 579 | (in-lisp (compile-file (namestring pn) #+cmu :error-file #+cmu nil)))))
|
|---|
| 580 |
|
|---|
| 581 |
|
|---|
| 582 | (defun older-or-non-existent-fasl-p (pathname &optional definitely)
|
|---|
| 583 | (let ((obj-pn (probe-file (compile-file-pathname pathname))))
|
|---|
| 584 | (or definitely
|
|---|
| 585 | (not obj-pn)
|
|---|
| 586 | (< (file-write-date obj-pn) (file-write-date pathname)))))
|
|---|
| 587 |
|
|---|
| 588 |
|
|---|
| 589 | (defcommand "Editor Compile Buffer File" (p)
|
|---|
| 590 | "Compile the file in the current buffer in the editor Lisp if its associated
|
|---|
| 591 | binary file (of type .fasl) is older than the source or doesn't exist. When
|
|---|
| 592 | the binary file is up to date, the user is asked if the source should be
|
|---|
| 593 | compiled anyway. When the prefix argument is supplied, compile the file
|
|---|
| 594 | without checking the binary file. When \"Compile Buffer File Confirm\" is
|
|---|
| 595 | set, this command will ask for confirmation when it otherwise would not."
|
|---|
| 596 | "Compile the file in the current buffer in the editor Lisp if the fasl file
|
|---|
| 597 | isn't up to date. When p, always do it."
|
|---|
| 598 | (let* ((buf (current-buffer))
|
|---|
| 599 | (pn (buffer-pathname buf)))
|
|---|
| 600 | (unless pn (editor-error "Buffer has no associated pathname."))
|
|---|
| 601 | (cond ((buffer-modified buf)
|
|---|
| 602 | (when (or (not (value compile-buffer-file-confirm))
|
|---|
| 603 | (prompt-for-y-or-n
|
|---|
| 604 | :default t :default-string "Y"
|
|---|
| 605 | :prompt (list "Save and compile file ~A? "
|
|---|
| 606 | (namestring pn))))
|
|---|
| 607 | (write-buffer-file buf pn)
|
|---|
| 608 | (with-output-to-window (*error-output* "Compiler Warnings")
|
|---|
| 609 | (in-lisp (compile-file (namestring pn) #+cmu :error-file #+cmu nil)))))
|
|---|
| 610 | ((older-or-non-existent-fasl-p pn p)
|
|---|
| 611 | (when (or (not (value compile-buffer-file-confirm))
|
|---|
| 612 | (prompt-for-y-or-n
|
|---|
| 613 | :default t :default-string "Y"
|
|---|
| 614 | :prompt (list "Compile file ~A? " (namestring pn))))
|
|---|
| 615 | (with-output-to-window (*error-output* "Compiler Warnings")
|
|---|
| 616 | (in-lisp (compile-file (namestring pn) #+cmu :error-file #+cmu nil)))))
|
|---|
| 617 | (t (when (or p
|
|---|
| 618 | (prompt-for-y-or-n
|
|---|
| 619 | :default t :default-string "Y"
|
|---|
| 620 | :prompt
|
|---|
| 621 | "Fasl file up to date, compile source anyway? "))
|
|---|
| 622 | (with-output-to-window (*error-output* "Compiler Warnings")
|
|---|
| 623 | (in-lisp (compile-file (namestring pn) #+cmu :error-file #+cmu nil))))))))
|
|---|
| 624 |
|
|---|
| 625 | (defcommand "Editor Compile Group" (p)
|
|---|
| 626 | "Compile each file in the current group which needs it in the editor Lisp.
|
|---|
| 627 | If a file has type LISP and there is a curresponding file with type
|
|---|
| 628 | FASL which has been written less recently (or it doesn't exit), then
|
|---|
| 629 | the file is compiled, with error output directed to the \"Compiler Warnings\"
|
|---|
| 630 | buffer. If a prefix argument is provided, then all the files are compiled.
|
|---|
| 631 | All modified files are saved beforehand."
|
|---|
| 632 | "Do a Compile-File in each file in the current group that seems to need it
|
|---|
| 633 | in the editor Lisp."
|
|---|
| 634 | (save-all-files-command ())
|
|---|
| 635 | (unless *active-file-group* (editor-error "No active file group."))
|
|---|
| 636 | (dolist (file *active-file-group*)
|
|---|
| 637 | (when (string-equal (pathname-type file) "lisp")
|
|---|
| 638 | (let ((tn (probe-file file)))
|
|---|
| 639 | (cond ((not tn)
|
|---|
| 640 | (message "File ~A not found." (namestring file)))
|
|---|
| 641 | ((older-or-non-existent-fasl-p tn p)
|
|---|
| 642 | (with-output-to-window (*error-output* "Compiler Warnings")
|
|---|
| 643 | (in-lisp (compile-file (namestring tn) #+cmu :error-file #+cmu nil)))))))))
|
|---|
| 644 |
|
|---|
| 645 | (defcommand "List Compile Group" (p)
|
|---|
| 646 | "List any files that would be compiled by \"Compile Group\". All Modified
|
|---|
| 647 | files are saved before checking to generate a consistent list."
|
|---|
| 648 | "Do a Compile-File in each file in the current group that seems to need it."
|
|---|
| 649 | (declare (ignore p))
|
|---|
| 650 | (save-all-files-command ())
|
|---|
| 651 | (unless *active-file-group* (editor-error "No active file group."))
|
|---|
| 652 | (with-pop-up-display (s)
|
|---|
| 653 | (write-line "\"Compile Group\" would compile the following files:" s)
|
|---|
| 654 | (force-output s)
|
|---|
| 655 | (dolist (file *active-file-group*)
|
|---|
| 656 | (when (string-equal (pathname-type file) "lisp")
|
|---|
| 657 | (let ((tn (probe-file file)))
|
|---|
| 658 | (cond ((not tn)
|
|---|
| 659 | (format s "File ~A not found.~%" (namestring file)))
|
|---|
| 660 | ((older-or-non-existent-fasl-p tn)
|
|---|
| 661 | (write-line (namestring tn) s)))
|
|---|
| 662 | (force-output s))))))
|
|---|
| 663 |
|
|---|
| 664 | (defhvar "Load Pathname Defaults"
|
|---|
| 665 | "The default pathname used by the load command.")
|
|---|
| 666 |
|
|---|
| 667 | (defcommand "Editor Load File" (p)
|
|---|
| 668 | "Prompt for a file to load into Editor Lisp."
|
|---|
| 669 | "Prompt for a file to load into the Editor Lisp."
|
|---|
| 670 | (declare (ignore p))
|
|---|
| 671 | (let ((name (truename (prompt-for-file
|
|---|
| 672 | :default
|
|---|
| 673 | (or (value load-pathname-defaults)
|
|---|
| 674 | (buffer-default-pathname (current-buffer)))
|
|---|
| 675 | :prompt "Editor file to load: "
|
|---|
| 676 | :help "The name of the file to load"))))
|
|---|
| 677 | (setv load-pathname-defaults name)
|
|---|
| 678 | (in-lisp (load name))))
|
|---|
| 679 |
|
|---|
| 680 |
|
|---|
| 681 | |
|---|
| 682 |
|
|---|
| 683 | ;;;; Lisp documentation stuff.
|
|---|
| 684 |
|
|---|
| 685 | ;;; FUNCTION-TO-DESCRIBE is used in "Editor Describe Function Call" and
|
|---|
| 686 | ;;; "Describe Function Call".
|
|---|
| 687 | ;;;
|
|---|
| 688 | (defmacro function-to-describe (var error-name)
|
|---|
| 689 | `(cond ((not (symbolp ,var))
|
|---|
| 690 | (,error-name "~S is not a symbol." ,var))
|
|---|
| 691 | ((macro-function ,var))
|
|---|
| 692 | ((fboundp ,var)
|
|---|
| 693 | (if (listp (symbol-function ,var))
|
|---|
| 694 | ,var
|
|---|
| 695 | (symbol-function ,var)))
|
|---|
| 696 | (t
|
|---|
| 697 | (,error-name "~S is not a function." ,var))))
|
|---|
| 698 |
|
|---|
| 699 | (defcommand "Editor Describe Function Call" (p)
|
|---|
| 700 | "Describe the most recently typed function name in the editor Lisp."
|
|---|
| 701 | "Describe the most recently typed function name in the editor Lisp."
|
|---|
| 702 | (declare (ignore p))
|
|---|
| 703 | (with-mark ((mark1 (current-point))
|
|---|
| 704 | (mark2 (current-point)))
|
|---|
| 705 | (pre-command-parse-check mark1)
|
|---|
| 706 | (unless (backward-up-list mark1) (editor-error))
|
|---|
| 707 | (form-offset (move-mark mark2 (mark-after mark1)) 1)
|
|---|
| 708 | (with-input-from-region (s (region mark1 mark2))
|
|---|
| 709 | (in-lisp
|
|---|
| 710 | (let* ((sym (read s))
|
|---|
| 711 | (fun (function-to-describe sym editor-error)))
|
|---|
| 712 | (with-pop-up-display (*standard-output*)
|
|---|
| 713 | (editor-describe-function fun sym)))))))
|
|---|
| 714 |
|
|---|
| 715 |
|
|---|
| 716 | (defcommand "Editor Describe Symbol" (p)
|
|---|
| 717 | "Describe the previous s-expression if it is a symbol in the editor Lisp."
|
|---|
| 718 | "Describe the previous s-expression if it is a symbol in the editor Lisp."
|
|---|
| 719 | (declare (ignore p))
|
|---|
| 720 | (with-mark ((mark1 (current-point))
|
|---|
| 721 | (mark2 (current-point)))
|
|---|
| 722 | (mark-symbol mark1 mark2)
|
|---|
| 723 | (with-input-from-region (s (region mark1 mark2))
|
|---|
| 724 | (in-lisp
|
|---|
| 725 | (let ((thing (read s)))
|
|---|
| 726 | (if (symbolp thing)
|
|---|
| 727 | (with-pop-up-display (*standard-output*)
|
|---|
| 728 | (describe thing))
|
|---|
| 729 | (if (and (consp thing)
|
|---|
| 730 | (or (eq (car thing) 'quote)
|
|---|
| 731 | (eq (car thing) 'function))
|
|---|
| 732 | (symbolp (cadr thing)))
|
|---|
| 733 | (with-pop-up-display (*standard-output*)
|
|---|
| 734 | (describe (cadr thing)))
|
|---|
| 735 | (editor-error "~S is not a symbol, or 'symbol, or #'symbol."
|
|---|
| 736 | thing))))))))
|
|---|
| 737 |
|
|---|
| 738 | ;;; MARK-SYMBOL moves mark1 and mark2 around the previous or current symbol.
|
|---|
| 739 | ;;; However, if the marks are immediately before the first constituent char
|
|---|
| 740 | ;;; of the symbol name, we use the next symbol since the marks probably
|
|---|
| 741 | ;;; correspond to the point, and Hemlock's cursor display makes it look like
|
|---|
| 742 | ;;; the point is within the symbol name. This also tries to ignore :prefix
|
|---|
| 743 | ;;; characters such as quotes, commas, etc.
|
|---|
| 744 | ;;;
|
|---|
| 745 | (defun mark-symbol (mark1 mark2)
|
|---|
| 746 | (pre-command-parse-check mark1)
|
|---|
| 747 | (with-mark ((tmark1 mark1)
|
|---|
| 748 | (tmark2 mark1))
|
|---|
| 749 | (cond ((and (form-offset tmark1 1)
|
|---|
| 750 | (form-offset (move-mark tmark2 tmark1) -1)
|
|---|
| 751 | (or (mark= mark1 tmark2)
|
|---|
| 752 | (and (find-attribute tmark2 :lisp-syntax
|
|---|
| 753 | #'(lambda (x) (not (eq x :prefix))))
|
|---|
| 754 | (mark= mark1 tmark2))))
|
|---|
| 755 | (form-offset mark2 1))
|
|---|
| 756 | (t
|
|---|
| 757 | (form-offset mark1 -1)
|
|---|
| 758 | (find-attribute mark1 :lisp-syntax
|
|---|
| 759 | #'(lambda (x) (not (eq x :prefix))))
|
|---|
| 760 | (form-offset (move-mark mark2 mark1) 1)))))
|
|---|
| 761 |
|
|---|
| 762 |
|
|---|
| 763 | (defcommand "Editor Describe" (p)
|
|---|
| 764 | "Call Describe on a Lisp object.
|
|---|
| 765 | Prompt for an expression which is evaluated to yield the object."
|
|---|
| 766 | "Prompt for an object to describe."
|
|---|
| 767 | (declare (ignore p))
|
|---|
| 768 | (in-lisp
|
|---|
| 769 | (let* ((exp (prompt-for-expression
|
|---|
| 770 | :prompt "Object: "
|
|---|
| 771 | :help "Expression to evaluate to get object to describe."))
|
|---|
| 772 | (obj (eval exp)))
|
|---|
| 773 | (with-pop-up-display (*standard-output*)
|
|---|
| 774 | (describe obj)))))
|
|---|
| 775 | |
|---|
| 776 |
|
|---|
| 777 | (defcommand "Filter Region" (p)
|
|---|
| 778 | "Apply a Lisp function to each line of the region.
|
|---|
| 779 | An expression is prompted for which should evaluate to a Lisp function
|
|---|
| 780 | from a string to a string. The function must neither modify its argument
|
|---|
| 781 | nor modify the return value after it is returned."
|
|---|
| 782 | "Call prompt for a function, then call Filter-Region with it and the region."
|
|---|
| 783 | (declare (ignore p))
|
|---|
| 784 | (let* ((exp (prompt-for-expression
|
|---|
| 785 | :prompt "Function: "
|
|---|
| 786 | :help "Expression to evaluate to get function to use as filter."))
|
|---|
| 787 | (fun (in-lisp (eval exp)))
|
|---|
| 788 | (region (current-region)))
|
|---|
| 789 | (let* ((start (copy-mark (region-start region) :left-inserting))
|
|---|
| 790 | (end (copy-mark (region-end region) :left-inserting))
|
|---|
| 791 | (region (region start end))
|
|---|
| 792 | (undo-region (copy-region region)))
|
|---|
| 793 | (filter-region fun region)
|
|---|
| 794 | (make-region-undo :twiddle "Filter Region" region undo-region))))
|
|---|