| [595] | 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 | ;;;
|
|---|
| 8 | ;;; **********************************************************************
|
|---|
| 9 | ;;;
|
|---|
| 10 | ;;; Listener mode, dervived (sort of) from Hemlock's "Eval" mode.
|
|---|
| 11 | ;;;
|
|---|
| 12 |
|
|---|
| 13 | (in-package :hemlock)
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 | (defmacro in-lisp (&body body)
|
|---|
| 17 | "Evaluates body inside HANDLE-LISP-ERRORS. *package* is bound to the package
|
|---|
| 18 | named by \"Current Package\" if it is non-nil."
|
|---|
| 19 | (let ((name (gensym)) (package (gensym)))
|
|---|
| 20 | `(handle-lisp-errors
|
|---|
| [7122] | 21 | (let* ((,name (variable-value 'current-package :buffer (current-buffer)))
|
|---|
| [595] | 22 | (,package (and ,name (find-package ,name))))
|
|---|
| 23 | (progv (if ,package '(*package*)) (if ,package (list ,package))
|
|---|
| 24 | ,@body)))))
|
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| [719] | 27 | (defun package-name-change-hook (name kind where new-value)
|
|---|
| [697] | 28 | (declare (ignore name new-value))
|
|---|
| 29 | (if (eq kind :buffer)
|
|---|
| [8428] | 30 | (hi::note-modeline-change where)))
|
|---|
| [697] | 31 |
|
|---|
| [595] | 32 | (define-file-option "Package" (buffer value)
|
|---|
| 33 | (defhvar "Current Package"
|
|---|
| 34 | "The package used for evaluation of Lisp in this buffer."
|
|---|
| 35 | :buffer buffer
|
|---|
| 36 | :value
|
|---|
| [8428] | 37 | (let ((thing (handler-case (read-from-string value t)
|
|---|
| 38 | (error () (editor-error "Bad package file option value")))))
|
|---|
| [595] | 39 | (cond
|
|---|
| [8428] | 40 | ((or (stringp thing) (symbolp thing))
|
|---|
| [595] | 41 | (string thing))
|
|---|
| [8428] | 42 | ((and (consp thing) ;; e.g. Package: (foo :use bar)
|
|---|
| 43 | (or (stringp (car thing)) (symbolp (car thing))))
|
|---|
| 44 | (string (car thing)))
|
|---|
| [595] | 45 | (t
|
|---|
| [8428] | 46 | (message "Ignoring \"package:\" file option ~a" thing)
|
|---|
| 47 | nil)))
|
|---|
| [697] | 48 | :hooks (list 'package-name-change-hook)))
|
|---|
| [595] | 49 |
|
|---|
| 50 | |
|---|
| 51 |
|
|---|
| 52 | ;;;; Listener Mode Interaction.
|
|---|
| 53 |
|
|---|
| 54 |
|
|---|
| 55 |
|
|---|
| 56 | (defun setup-listener-mode (buffer)
|
|---|
| 57 | (let ((point (buffer-point buffer)))
|
|---|
| 58 | (setf (buffer-minor-mode buffer "Listener") t)
|
|---|
| 59 | (setf (buffer-minor-mode buffer "Editor") t)
|
|---|
| 60 | (setf (buffer-major-mode buffer) "Lisp")
|
|---|
| 61 | (buffer-end point)
|
|---|
| 62 | (defhvar "Current Package"
|
|---|
| 63 | "This variable holds the name of the package currently used for Lisp
|
|---|
| 64 | evaluation and compilation. If it is Nil, the value of *Package* is used
|
|---|
| 65 | instead."
|
|---|
| 66 | :value nil
|
|---|
| 67 | :buffer buffer)
|
|---|
| 68 | (unless (hemlock-bound-p 'buffer-input-mark :buffer buffer)
|
|---|
| 69 | (defhvar "Buffer Input Mark"
|
|---|
| 70 | "Mark used for Listener Mode input."
|
|---|
| 71 | :buffer buffer
|
|---|
| [6665] | 72 | :value (copy-mark point :right-inserting))
|
|---|
| 73 | (defhvar "Buffer Output Mark"
|
|---|
| 74 | "Mark used for Listener Mode output."
|
|---|
| 75 | :buffer buffer
|
|---|
| [595] | 76 | :value (copy-mark point :left-inserting))
|
|---|
| 77 | (defhvar "Interactive History"
|
|---|
| 78 | "A ring of the regions input to an interactive mode (Eval or Typescript)."
|
|---|
| 79 | :buffer buffer
|
|---|
| 80 | :value (make-ring (value interactive-history-length)))
|
|---|
| 81 | (defhvar "Interactive Pointer"
|
|---|
| 82 | "Pointer into \"Interactive History\"."
|
|---|
| 83 | :buffer buffer
|
|---|
| 84 | :value 0)
|
|---|
| 85 | (defhvar "Searching Interactive Pointer"
|
|---|
| 86 | "Pointer into \"Interactive History\"."
|
|---|
| [804] | 87 | :buffer buffer
|
|---|
| 88 | :value 0)
|
|---|
| 89 | (defhvar "Input Regions"
|
|---|
| 90 | "Input region history list."
|
|---|
| 91 | :buffer buffer
|
|---|
| 92 | :value nil)
|
|---|
| 93 | (defhvar "Current Input Font Region"
|
|---|
| 94 | "Current font region, for listener input"
|
|---|
| 95 | :buffer buffer
|
|---|
| 96 | :value nil)
|
|---|
| 97 | (defhvar "Current Output Font Region"
|
|---|
| 98 | "Current font region, for listener output"
|
|---|
| 99 | :buffer buffer
|
|---|
| 100 | :value nil)
|
|---|
| 101 | )
|
|---|
| [8428] | 102 | (let* ((input-mark (variable-value 'buffer-input-mark :buffer buffer)))
|
|---|
| [7348] | 103 | (when (hemlock-ext:read-only-listener-p)
|
|---|
| 104 | (setf (hi::buffer-protected-region buffer)
|
|---|
| [804] | 105 | (region (buffer-start-mark buffer) input-mark)))
|
|---|
| 106 | (move-mark input-mark point)
|
|---|
| [595] | 107 | (append-font-regions buffer))))
|
|---|
| 108 |
|
|---|
| 109 | (defmode "Listener" :major-p nil :setup-function #'setup-listener-mode)
|
|---|
| [804] | 110 |
|
|---|
| 111 | (declaim (special hi::*listener-input-style* hi::*listener-output-style*))
|
|---|
| 112 |
|
|---|
| 113 | (defun append-font-regions (buffer)
|
|---|
| 114 | (let* ((end (region-end (buffer-region buffer))))
|
|---|
| 115 | (setf (variable-value 'current-output-font-region :buffer buffer)
|
|---|
| 116 | (hi::new-font-region buffer end end hi::*listener-output-style*))
|
|---|
| 117 | (let* ((input (hi::new-font-region buffer end end hi::*listener-input-style*)))
|
|---|
| 118 | (hi::activate-buffer-font-region buffer input)
|
|---|
| 119 | (setf (variable-value 'current-input-font-region :buffer buffer) input))))
|
|---|
| 120 |
|
|---|
| 121 | (defun append-buffer-output (buffer string)
|
|---|
| 122 | (let* ((output-region (variable-value 'current-output-font-region
|
|---|
| 123 | :buffer buffer))
|
|---|
| 124 | (output-end (region-end output-region)))
|
|---|
| [6665] | 125 | (hi::with-active-font-region (buffer output-region)
|
|---|
| 126 | (with-mark ((output-mark output-end :left-inserting))
|
|---|
| 127 | ;(setf (mark-charpos output-mark) 0)
|
|---|
| [804] | 128 | (insert-string output-mark string))
|
|---|
| 129 | (move-mark (variable-value 'buffer-input-mark :buffer buffer)
|
|---|
| 130 | output-end))))
|
|---|
| 131 |
|
|---|
| 132 |
|
|---|
| [597] | 133 |
|
|---|
| 134 | (defparameter *listener-modeline-fields*
|
|---|
| 135 | (list (modeline-field :package)
|
|---|
| 136 | (modeline-field :modes)
|
|---|
| 137 | (modeline-field :process-info)))
|
|---|
| [595] | 138 |
|
|---|
| 139 | (defun listener-mode-lisp-mode-hook (buffer on)
|
|---|
| 140 | "Turn on Lisp mode when we go into Listener Mode."
|
|---|
| 141 | (when on
|
|---|
| 142 | (setf (buffer-major-mode buffer) "Lisp")))
|
|---|
| 143 | ;;;
|
|---|
| 144 | (add-hook listener-mode-hook 'listener-mode-lisp-mode-hook)
|
|---|
| 145 |
|
|---|
| 146 |
|
|---|
| 147 |
|
|---|
| 148 |
|
|---|
| 149 |
|
|---|
| 150 | (defvar lispbuf-eof '(nil))
|
|---|
| [597] | 151 |
|
|---|
| 152 | (defun balanced-expressions-in-region (region)
|
|---|
| 153 | "Return true if there's at least one syntactically well-formed S-expression
|
|---|
| 154 | between the region's start and end, and if there are no ill-formed expressions in that region."
|
|---|
| 155 | ;; It helps to know that END-MARK immediately follows a #\newline.
|
|---|
| 156 | (let* ((start-mark (region-start region))
|
|---|
| 157 | (end-mark (region-end region))
|
|---|
| 158 | (end-line (mark-line end-mark))
|
|---|
| 159 | (end-charpos (mark-charpos end-mark)))
|
|---|
| 160 | (with-mark ((m start-mark))
|
|---|
| 161 | (pre-command-parse-check m)
|
|---|
| 162 | (when (form-offset m 1)
|
|---|
| 163 | (let* ((skip-whitespace t))
|
|---|
| 164 | (loop
|
|---|
| 165 | (let* ((current-line (mark-line m))
|
|---|
| 166 | (current-charpos (mark-charpos m)))
|
|---|
| 167 | (when (and (eq current-line end-line)
|
|---|
| 168 | (eql current-charpos end-charpos))
|
|---|
| 169 | (return t))
|
|---|
| 170 | (if skip-whitespace
|
|---|
| 171 | (progn
|
|---|
| 172 | (scan-char m :whitespace nil)
|
|---|
| 173 | (setq skip-whitespace nil))
|
|---|
| 174 | (progn
|
|---|
| 175 | (pre-command-parse-check m)
|
|---|
| 176 | (unless (form-offset m 1)
|
|---|
| 177 | (return nil))
|
|---|
| 178 | (setq skip-whitespace t))))))))))
|
|---|
| 179 |
|
|---|
| 180 |
|
|---|
| [595] | 181 |
|
|---|
| 182 | (defcommand "Confirm Listener Input" (p)
|
|---|
| 183 | "Evaluate Listener Mode input between point and last prompt."
|
|---|
| 184 | "Evaluate Listener Mode input between point and last prompt."
|
|---|
| [804] | 185 | (declare (ignore p))
|
|---|
| 186 | (let* ((input-region (get-interactive-input))
|
|---|
| 187 | (r (if input-region
|
|---|
| 188 | (region (copy-mark (region-start input-region))
|
|---|
| 189 | (copy-mark (region-end input-region) :right-inserting)))))
|
|---|
| [595] | 190 |
|
|---|
| [597] | 191 | (when input-region
|
|---|
| 192 | (insert-character (current-point) #\NewLine)
|
|---|
| [804] | 193 | (when (balanced-expressions-in-region input-region)
|
|---|
| 194 | (let* ((string (region-to-string input-region)) )
|
|---|
| [597] | 195 | (push (cons r nil) (value input-regions))
|
|---|
| [804] | 196 | (move-mark (value buffer-input-mark) (current-point))
|
|---|
| [8428] | 197 | (append-font-regions (current-buffer))
|
|---|
| [595] | 198 | (hemlock-ext:send-string-to-listener (current-buffer) string))))))
|
|---|
| [736] | 199 |
|
|---|
| 200 | (defparameter *pop-string* ":POP
|
|---|
| [597] | 201 | " "what you have to type to exit a break loop")
|
|---|
| [697] | 202 |
|
|---|
| 203 | (defcommand "POP or Delete Forward" (p)
|
|---|
| 204 | "Send :POP if input-mark is at buffer's end, else delete forward character."
|
|---|
| [597] | 205 | "Send :POP if input-mark is at buffer's end, else delete forward character."
|
|---|
| [6705] | 206 | (let* ((input-mark (value buffer-input-mark))
|
|---|
| [7118] | 207 | (point (current-point-for-deletion)))
|
|---|
| 208 | (when point
|
|---|
| 209 | (if (and (null (next-character point))
|
|---|
| [8428] | 210 | (null (next-character input-mark)))
|
|---|
| 211 | (hemlock-ext:send-string-to-listener (current-buffer) *pop-string*)
|
|---|
| [597] | 212 | (delete-next-character-command p)))))
|
|---|
| [595] | 213 |
|
|---|
| 214 | |
|---|
| 215 |
|
|---|
| [804] | 216 | ;;;; General interactive commands used in eval and typescript buffers.
|
|---|
| 217 |
|
|---|
| 218 | (defhvar "Interactive History Length"
|
|---|
| 219 | "This is the length used for the history ring in interactive buffers.
|
|---|
| 220 | It must be set before turning on the mode."
|
|---|
| 221 | :value 10)
|
|---|
| 222 |
|
|---|
| 223 | (defun input-region-containing-mark (m history-list)
|
|---|
| 224 | (dolist (pair history-list)
|
|---|
| 225 | (let* ((actual (car pair))
|
|---|
| 226 | (start (region-start actual))
|
|---|
| 227 | (end (region-end actual)))
|
|---|
| 228 | (when (and (mark>= m start)
|
|---|
| 229 | (mark<= m end)) ; sic: inclusive
|
|---|
| 230 | (return (or (cdr pair) (setf (cdr pair) (copy-region actual))))))))
|
|---|
| [595] | 231 |
|
|---|
| 232 |
|
|---|
| 233 | (defun get-interactive-input ()
|
|---|
| 234 | "Tries to return a region. When the point is not past the input mark, and
|
|---|
| 235 | the user has \"Unwedge Interactive Input Confirm\" set, the buffer is
|
|---|
| 236 | optionally fixed up, and nil is returned. Otherwise, an editor error is
|
|---|
| 237 | signalled. When a region is returned, the start is the current buffer's
|
|---|
| 238 | input mark, and the end is the current point moved to the end of the buffer."
|
|---|
| 239 | (let ((point (current-point))
|
|---|
| 240 | (mark (value buffer-input-mark)))
|
|---|
| 241 | (cond
|
|---|
| 242 | ((mark>= point mark)
|
|---|
| 243 | (buffer-end point)
|
|---|
| 244 | (let* ((input-region (region mark point))
|
|---|
| 245 | (string (region-to-string input-region))
|
|---|
| 246 | (ring (value interactive-history)))
|
|---|
| 247 | (when (and (or (zerop (ring-length ring))
|
|---|
| 248 | (string/= string (region-to-string (ring-ref ring 0))))
|
|---|
| 249 | (> (length string) (value minimum-interactive-input-length)))
|
|---|
| 250 | (ring-push (copy-region input-region) ring))
|
|---|
| [804] | 251 | input-region))
|
|---|
| 252 | (t
|
|---|
| 253 | (let* ((region (input-region-containing-mark point (value input-regions ))))
|
|---|
| 254 | (buffer-end point)
|
|---|
| 255 | (if region
|
|---|
| 256 | (progn
|
|---|
| 257 | (delete-region (region mark point))
|
|---|
| 258 | (insert-region point region))
|
|---|
| [595] | 259 | (beep))
|
|---|
| 260 | nil)))))
|
|---|
| 261 |
|
|---|
| 262 |
|
|---|
| 263 | (defhvar "Minimum Interactive Input Length"
|
|---|
| 264 | "When the number of characters in an interactive buffer exceeds this value,
|
|---|
| 265 | it is pushed onto the interactive history, otherwise it is lost forever."
|
|---|
| 266 | :value 2)
|
|---|
| 267 |
|
|---|
| 268 |
|
|---|
| 269 | (defvar *previous-input-search-string* "ignore")
|
|---|
| 270 |
|
|---|
| 271 | (defvar *previous-input-search-pattern*
|
|---|
| 272 | ;; Give it a bogus string since you can't give it the empty string.
|
|---|
| 273 | (new-search-pattern :string-insensitive :forward "ignore"))
|
|---|
| 274 |
|
|---|
| 275 | (defun get-previous-input-search-pattern (string)
|
|---|
| 276 | (if (string= *previous-input-search-string* string)
|
|---|
| 277 | *previous-input-search-pattern*
|
|---|
| 278 | (new-search-pattern :string-insensitive :forward
|
|---|
| 279 | (setf *previous-input-search-string* string)
|
|---|
| 280 | *previous-input-search-pattern*)))
|
|---|
| 281 |
|
|---|
| 282 | (defcommand "Search Previous Interactive Input" (p)
|
|---|
| 283 | "Search backward through the interactive history using the current input as
|
|---|
| 284 | a search string. Consecutive invocations repeat the previous search."
|
|---|
| 285 | "Search backward through the interactive history using the current input as
|
|---|
| 286 | a search string. Consecutive invocations repeat the previous search."
|
|---|
| 287 | (declare (ignore p))
|
|---|
| 288 | (let* ((mark (value buffer-input-mark))
|
|---|
| 289 | (ring (value interactive-history))
|
|---|
| 290 | (point (current-point))
|
|---|
| 291 | (just-invoked (eq (last-command-type) :searching-interactive-input)))
|
|---|
| 292 | (when (mark<= point mark)
|
|---|
| 293 | (editor-error "Point not past input mark."))
|
|---|
| 294 | (when (zerop (ring-length ring))
|
|---|
| 295 | (editor-error "No previous input in this buffer."))
|
|---|
| 296 | (unless just-invoked
|
|---|
| 297 | (get-previous-input-search-pattern (region-to-string (region mark point))))
|
|---|
| 298 | (let ((found-it (find-previous-input ring just-invoked)))
|
|---|
| 299 | (unless found-it
|
|---|
| 300 | (editor-error "Couldn't find ~a." *previous-input-search-string*))
|
|---|
| 301 | (delete-region (region mark point))
|
|---|
| 302 | (insert-region point (ring-ref ring found-it))
|
|---|
| 303 | (setf (value searching-interactive-pointer) found-it))
|
|---|
| 304 | (setf (last-command-type) :searching-interactive-input)))
|
|---|
| 305 |
|
|---|
| 306 | (defun find-previous-input (ring againp)
|
|---|
| 307 | (let ((ring-length (ring-length ring))
|
|---|
| 308 | (base (if againp
|
|---|
| 309 | (+ (value searching-interactive-pointer) 1)
|
|---|
| 310 | 0)))
|
|---|
| 311 | (loop
|
|---|
| 312 | (when (= base ring-length)
|
|---|
| 313 | (if againp
|
|---|
| 314 | (setf base 0)
|
|---|
| 315 | (return nil)))
|
|---|
| 316 | (with-mark ((m (region-start (ring-ref ring base))))
|
|---|
| 317 | (when (find-pattern m *previous-input-search-pattern*)
|
|---|
| 318 | (return base)))
|
|---|
| 319 | (incf base))))
|
|---|
| 320 |
|
|---|
| 321 | (defcommand "Previous Interactive Input" (p)
|
|---|
| 322 | "Insert the previous input in an interactive mode (Listener or Typescript).
|
|---|
| 323 | If repeated, keep rotating the history. With prefix argument, rotate
|
|---|
| 324 | that many times."
|
|---|
| 325 | "Pop the *interactive-history* at the point."
|
|---|
| 326 | (let* ((point (current-point))
|
|---|
| 327 | (mark (value buffer-input-mark))
|
|---|
| 328 | (ring (value interactive-history))
|
|---|
| [617] | 329 | (length (ring-length ring))
|
|---|
| [595] | 330 | (p (or p 1)))
|
|---|
| 331 | (when (or (mark< point mark) (zerop length)) (editor-error "Can't get command history"))
|
|---|
| 332 | (cond
|
|---|
| 333 | ((eq (last-command-type) :interactive-history)
|
|---|
| 334 | (let ((base (mod (+ (value interactive-pointer) p) length)))
|
|---|
| 335 | (delete-region (region mark point))
|
|---|
| 336 | (insert-region point (ring-ref ring base))
|
|---|
| 337 | (setf (value interactive-pointer) base)))
|
|---|
| 338 | (t
|
|---|
| 339 | (let ((base (mod (if (minusp p) p (1- p)) length))
|
|---|
| 340 | (region (delete-and-save-region (region mark point))))
|
|---|
| 341 | (insert-region point (ring-ref ring base))
|
|---|
| 342 | (when (mark/= (region-start region) (region-end region))
|
|---|
| 343 | (ring-push region ring)
|
|---|
| 344 | (incf base))
|
|---|
| 345 | (setf (value interactive-pointer) base)))))
|
|---|
| 346 | (setf (last-command-type) :interactive-history))
|
|---|
| 347 |
|
|---|
| 348 | (defcommand "Next Interactive Input" (p)
|
|---|
| 349 | "Rotate the interactive history backwards. The region is left around the
|
|---|
| 350 | inserted text. With prefix argument, rotate that many times."
|
|---|
| 351 | "Call previous-interactive-input-command with negated arg."
|
|---|
| 352 | (previous-interactive-input-command (- (or p 1))))
|
|---|
| 353 |
|
|---|
| 354 | (defcommand "Kill Interactive Input" (p)
|
|---|
| 355 | "Kill any input to an interactive mode (Listener or Typescript)."
|
|---|
| 356 | "Kill any input to an interactive mode (Listener or Typescript)."
|
|---|
| 357 | (declare (ignore p))
|
|---|
| 358 | (let ((point (buffer-point (current-buffer)))
|
|---|
| 359 | (mark (value buffer-input-mark)))
|
|---|
| 360 | (when (mark< point mark) (editor-error))
|
|---|
| 361 | (kill-region (region mark point) :kill-backward)))
|
|---|
| 362 |
|
|---|
| 363 | (defcommand "Interactive Beginning of Line" (p)
|
|---|
| 364 | "If on line with current prompt, go to after it, otherwise do what
|
|---|
| 365 | \"Beginning of Line\" always does."
|
|---|
| 366 | "Go to after prompt when on prompt line."
|
|---|
| 367 | (let ((mark (value buffer-input-mark))
|
|---|
| 368 | (point (current-point)))
|
|---|
| 369 | (if (and (same-line-p point mark) (or (not p) (= p 1)))
|
|---|
| 370 | (move-mark point mark)
|
|---|
| 371 | (beginning-of-line-command p))))
|
|---|
| 372 |
|
|---|
| 373 | (defcommand "Reenter Interactive Input" (p)
|
|---|
| 374 | "Copies the form to the left of point to be after the interactive buffer's
|
|---|
| 375 | input mark. When the current region is active, it is copied instead."
|
|---|
| 376 | "Copies the form to the left of point to be after the interactive buffer's
|
|---|
| 377 | input mark. When the current region is active, it is copied instead."
|
|---|
| 378 | (declare (ignore p))
|
|---|
| 379 | (unless (hemlock-bound-p 'buffer-input-mark)
|
|---|
| 380 | (editor-error "Not in an interactive buffer."))
|
|---|
| 381 | (let ((point (current-point)))
|
|---|
| 382 | (let ((region (if (region-active-p)
|
|---|
| 383 | ;; Copy this, so moving point doesn't affect the region.
|
|---|
| 384 | (copy-region (current-region))
|
|---|
| 385 | (with-mark ((start point)
|
|---|
| 386 | (end point))
|
|---|
| 387 | (pre-command-parse-check start)
|
|---|
| 388 | (unless (form-offset start -1)
|
|---|
| 389 | (editor-error "Not after complete form."))
|
|---|
| [8428] | 390 | (region (copy-mark start) (copy-mark end))))))
|
|---|
| [595] | 391 | (buffer-end point)
|
|---|
| 392 | (push-new-buffer-mark point)
|
|---|
| 393 | (insert-region point region)
|
|---|
| 394 | (setf (last-command-type) :ephemerally-active))))
|
|---|
| 395 |
|
|---|
| 396 |
|
|---|
| 397 | |
|---|
| [597] | 398 |
|
|---|
| [595] | 399 | ;;; Other stuff.
|
|---|
| 400 |
|
|---|
| 401 | (defmode "Editor" :hidden t)
|
|---|
| 402 |
|
|---|
| 403 | (defcommand "Editor Mode" (p)
|
|---|
| 404 | "Turn on \"Editor\" mode in the current buffer. If it is already on, turn it
|
|---|
| 405 | off. When in editor mode, most lisp compilation and evaluation commands
|
|---|
| 406 | manipulate the editor process instead of the current eval server."
|
|---|
| 407 | "Toggle \"Editor\" mode in the current buffer."
|
|---|
| 408 | (declare (ignore p))
|
|---|
| 409 | (setf (buffer-minor-mode (current-buffer) "Editor")
|
|---|
| 410 | (not (buffer-minor-mode (current-buffer) "Editor"))))
|
|---|
| 411 |
|
|---|
| 412 | (define-file-option "Editor" (buffer value)
|
|---|
| 413 | (declare (ignore value))
|
|---|
| [6705] | 414 | (setf (buffer-minor-mode buffer "Editor") t))
|
|---|
| [595] | 415 |
|
|---|
| 416 |
|
|---|
| 417 |
|
|---|
| 418 | (defcommand "Editor Compile Defun" (p)
|
|---|
| 419 | "Compiles the current or next top-level form in the editor Lisp.
|
|---|
| 420 | First the form is evaluated, then the result of this evaluation
|
|---|
| 421 | is passed to compile. If the current region is active, this
|
|---|
| 422 | compiles the region."
|
|---|
| 423 | "Evaluates the current or next top-level form in the editor Lisp."
|
|---|
| 424 | (declare (ignore p))
|
|---|
| 425 | (if (region-active-p)
|
|---|
| 426 | (editor-compile-region (current-region))
|
|---|
| 427 | (editor-compile-region (defun-region (current-point)) t)))
|
|---|
| 428 |
|
|---|
| 429 | (defcommand "Editor Compile Region" (p)
|
|---|
| 430 | "Compiles lisp forms between the point and the mark in the editor Lisp."
|
|---|
| 431 | "Compiles lisp forms between the point and the mark in the editor Lisp."
|
|---|
| 432 | (declare (ignore p))
|
|---|
| 433 | (editor-compile-region (current-region)))
|
|---|
| 434 |
|
|---|
| 435 | (defun defun-region (mark)
|
|---|
| 436 | "This returns a region around the current or next defun with respect to mark.
|
|---|
| 437 | Mark is not used to form the region. If there is no appropriate top level
|
|---|
| 438 | form, this signals an editor-error. This calls PRE-COMMAND-PARSE-CHECK."
|
|---|
| 439 | (with-mark ((start mark)
|
|---|
| 440 | (end mark))
|
|---|
| 441 | (pre-command-parse-check start)
|
|---|
| 442 | (cond ((not (mark-top-level-form start end))
|
|---|
| [719] | 443 | (editor-error "No current or next top level form."))
|
|---|
| 444 | (t (region start end)))))
|
|---|
| [7122] | 445 |
|
|---|
| [719] | 446 | (defun eval-region (region
|
|---|
| 447 | &key
|
|---|
| [6665] | 448 | (package (variable-value 'current-package :buffer (current-buffer)))
|
|---|
| [719] | 449 | (path (buffer-pathname (current-buffer))))
|
|---|
| 450 | (evaluate-input-selection
|
|---|
| 451 | (list package path (region-to-string region))))
|
|---|
| [595] | 452 |
|
|---|
| 453 |
|
|---|
| [719] | 454 |
|
|---|
| [595] | 455 | (defun editor-compile-region (region &optional quiet)
|
|---|
| 456 | (unless quiet (message "Compiling region ..."))
|
|---|
| 457 | (eval-region region))
|
|---|
| 458 |
|
|---|
| 459 |
|
|---|
| 460 | (defcommand "Editor Evaluate Defun" (p)
|
|---|
| 461 | "Evaluates the current or next top-level form in the editor Lisp.
|
|---|
| 462 | If the current region is active, this evaluates the region."
|
|---|
| [719] | 463 | "Evaluates the current or next top-level form in the editor Lisp."
|
|---|
| 464 | (declare (ignore p))
|
|---|
| [595] | 465 | (if (region-active-p)
|
|---|
| 466 | (editor-evaluate-region-command nil)
|
|---|
| 467 | (eval-region (defun-region (current-point)))))
|
|---|
| 468 |
|
|---|
| 469 | (defcommand "Editor Evaluate Region" (p)
|
|---|
| [780] | 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 | (if (region-active-p)
|
|---|
| 474 | (eval-region (current-region))
|
|---|
| 475 | (let* ((point (current-point)))
|
|---|
| 476 | (pre-command-parse-check point)
|
|---|
| [11927] | 477 | (when (valid-spot point nil) ; not in the middle of a comment
|
|---|
| [780] | 478 | (cond ((eql (next-character point) #\()
|
|---|
| 479 | (with-mark ((m point))
|
|---|
| 480 | (if (form-offset m 1)
|
|---|
| [11927] | 481 | (eval-region (region point m)))))
|
|---|
| 482 | ((eql (previous-character point) #\))
|
|---|
| 483 | (with-mark ((m point))
|
|---|
| 484 | (if (form-offset m -1)
|
|---|
| 485 | (eval-region (region m point)))))
|
|---|
| 486 | (t
|
|---|
| 487 | (with-mark ((start point)
|
|---|
| 488 | (end point))
|
|---|
| [595] | 489 | (when (mark-symbol start end)
|
|---|
| 490 | (eval-region (region start end))))))))))
|
|---|
| 491 |
|
|---|
| 492 | (defcommand "Editor Re-evaluate Defvar" (p)
|
|---|
| 493 | "Evaluate the current or next top-level form if it is a DEFVAR. Treat the
|
|---|
| 494 | form as if the variable is not bound. This occurs in the editor Lisp."
|
|---|
| 495 | "Evaluate the current or next top-level form if it is a DEFVAR. Treat the
|
|---|
| 496 | form as if the variable is not bound. This occurs in the editor Lisp."
|
|---|
| 497 | (declare (ignore p))
|
|---|
| 498 | (with-input-from-region (stream (defun-region (current-point)))
|
|---|
| 499 | (clear-echo-area)
|
|---|
| 500 | (in-lisp
|
|---|
| 501 | (let ((form (read stream)))
|
|---|
| 502 | (unless (eq (car form) 'defvar) (editor-error "Not a DEFVAR."))
|
|---|
| [6665] | 503 | (makunbound (cadr form))
|
|---|
| [8428] | 504 | (message "Evaluation returned ~S" (eval form))))))
|
|---|
| 505 |
|
|---|
| 506 | (defun macroexpand-expression (expander)
|
|---|
| 507 | (let* ((point (buffer-point (current-buffer)))
|
|---|
| 508 | (region (if (region-active-p)
|
|---|
| 509 | (current-region)
|
|---|
| 510 | (with-mark ((start point))
|
|---|
| 511 | (pre-command-parse-check start)
|
|---|
| 512 | (with-mark ((end start))
|
|---|
| 513 | (unless (form-offset end 1) (editor-error))
|
|---|
| 514 | (region start end)))))
|
|---|
| 515 | (expr (with-input-from-region (s region)
|
|---|
| 516 | (read s))))
|
|---|
| [6665] | 517 | (let* ((*print-pretty* t)
|
|---|
| 518 | (expansion (funcall expander expr)))
|
|---|
| [595] | 519 | (format t "~&~s~&" expansion))))
|
|---|
| 520 |
|
|---|
| 521 | (defcommand "Editor Macroexpand-1 Expression" (p)
|
|---|
| 522 | "Show the macroexpansion of the current expression in the null environment.
|
|---|
| [6665] | 523 | With an argument, use MACROEXPAND instead of MACROEXPAND-1."
|
|---|
| [595] | 524 | "Show the macroexpansion of the current expression in the null environment.
|
|---|
| [6665] | 525 | With an argument, use MACROEXPAND instead of MACROEXPAND-1."
|
|---|
| 526 | (macroexpand-expression (if p 'macroexpand 'macroexpand-1)))
|
|---|
| 527 |
|
|---|
| 528 | (defcommand "Editor Macroexpand Expression" (p)
|
|---|
| 529 | "Show the macroexpansion of the current expression in the null environment.
|
|---|
| 530 | With an argument, use MACROEXPAND-1 instead of MACROEXPAND."
|
|---|
| 531 | "Show the macroexpansion of the current expression in the null environment.
|
|---|
| 532 | With an argument, use MACROEXPAND-1 instead of MACROEXPAND."
|
|---|
| [595] | 533 | (macroexpand-expression (if p 'macroexpand-1 'macroexpand)))
|
|---|
| 534 |
|
|---|
| 535 |
|
|---|
| 536 | (defcommand "Editor Evaluate Expression" (p)
|
|---|
| 537 | "Prompt for an expression to evaluate in the editor Lisp."
|
|---|
| 538 | "Prompt for an expression to evaluate in the editor Lisp."
|
|---|
| 539 | (declare (ignore p))
|
|---|
| 540 | (in-lisp
|
|---|
| 541 | (multiple-value-call #'message "=> ~@{~#[~;~S~:;~S, ~]~}"
|
|---|
| 542 | (eval (prompt-for-expression
|
|---|
| 543 | :prompt "Editor Eval: "
|
|---|
| 544 | :help "Expression to evaluate")))))
|
|---|
| 545 |
|
|---|
| [8428] | 546 | (defcommand "Editor Evaluate Buffer" (p)
|
|---|
| [595] | 547 | "Evaluates the text in the current buffer in the editor Lisp."
|
|---|
| [8428] | 548 | (declare (ignore p))
|
|---|
| 549 | (message "Evaluating buffer in the editor ...")
|
|---|
| 550 | (with-input-from-region (stream (buffer-region (current-buffer)))
|
|---|
| 551 | (in-lisp
|
|---|
| 552 | (do ((object (read stream nil lispbuf-eof)
|
|---|
| [595] | 553 | (read stream nil lispbuf-eof)))
|
|---|
| 554 | ((eq object lispbuf-eof))
|
|---|
| 555 | (eval object)))
|
|---|
| 556 | (message "Evaluation complete.")))
|
|---|
| 557 |
|
|---|
| 558 |
|
|---|
| 559 |
|
|---|
| 560 | (defcommand "Editor Compile File" (p)
|
|---|
| 561 | "Prompts for file to compile in the editor Lisp. Does not compare source
|
|---|
| 562 | and binary write dates. Does not check any buffer for that file for
|
|---|
| 563 | whether the buffer needs to be saved."
|
|---|
| 564 | "Prompts for file to compile."
|
|---|
| 565 | (declare (ignore p))
|
|---|
| [8428] | 566 | (let ((pn (prompt-for-file :default
|
|---|
| [595] | 567 | (buffer-default-pathname (current-buffer))
|
|---|
| 568 | :prompt "File to compile: ")))
|
|---|
| 569 | (in-lisp (compile-file (namestring pn) #+cmu :error-file #+cmu nil))))
|
|---|
| 570 |
|
|---|
| 571 |
|
|---|
| 572 | (defun older-or-non-existent-fasl-p (pathname &optional definitely)
|
|---|
| 573 | (let ((obj-pn (probe-file (compile-file-pathname pathname))))
|
|---|
| 574 | (or definitely
|
|---|
| 575 | (not obj-pn)
|
|---|
| 576 | (< (file-write-date obj-pn) (file-write-date pathname)))))
|
|---|
| 577 |
|
|---|
| 578 |
|
|---|
| 579 | (defcommand "Editor Compile Buffer File" (p)
|
|---|
| 580 | "Compile the file in the current buffer in the editor Lisp if its associated
|
|---|
| 581 | binary file (of type .fasl) is older than the source or doesn't exist. When
|
|---|
| 582 | the binary file is up to date, the user is asked if the source should be
|
|---|
| 583 | compiled anyway. When the prefix argument is supplied, compile the file
|
|---|
| 584 | without checking the binary file. When \"Compile Buffer File Confirm\" is
|
|---|
| 585 | set, this command will ask for confirmation when it otherwise would not."
|
|---|
| 586 | "Compile the file in the current buffer in the editor Lisp if the fasl file
|
|---|
| 587 | isn't up to date. When p, always do it."
|
|---|
| 588 | (let* ((buf (current-buffer))
|
|---|
| 589 | (pn (buffer-pathname buf)))
|
|---|
| 590 | (unless pn (editor-error "Buffer has no associated pathname."))
|
|---|
| 591 | (cond ((buffer-modified buf)
|
|---|
| 592 | (when (or (not (value compile-buffer-file-confirm))
|
|---|
| 593 | (prompt-for-y-or-n
|
|---|
| 594 | :default t :default-string "Y"
|
|---|
| [8428] | 595 | :prompt (list "Save and compile file ~A? "
|
|---|
| [595] | 596 | (namestring pn))))
|
|---|
| 597 | (write-buffer-file buf pn)
|
|---|
| 598 | (in-lisp (compile-file (namestring pn) #+cmu :error-file #+cmu nil))))
|
|---|
| 599 | ((older-or-non-existent-fasl-p pn p)
|
|---|
| 600 | (when (or (not (value compile-buffer-file-confirm))
|
|---|
| [8428] | 601 | (prompt-for-y-or-n
|
|---|
| [595] | 602 | :default t :default-string "Y"
|
|---|
| 603 | :prompt (list "Compile file ~A? " (namestring pn))))
|
|---|
| 604 | (in-lisp (compile-file (namestring pn) #+cmu :error-file #+cmu nil))))
|
|---|
| 605 | (t (when (or p
|
|---|
| 606 | (prompt-for-y-or-n
|
|---|
| [8428] | 607 | :default t :default-string "Y"
|
|---|
| [595] | 608 | :prompt
|
|---|
| 609 | "Fasl file up to date, compile source anyway? "))
|
|---|
| 610 | (in-lisp (compile-file (namestring pn) #+cmu :error-file #+cmu nil)))))))
|
|---|
| 611 |
|
|---|
| 612 |
|
|---|
| 613 |
|
|---|
| 614 |
|
|---|
| 615 |
|
|---|
| 616 |
|
|---|
| 617 | |
|---|
| 618 |
|
|---|
| 619 | ;;;; Lisp documentation stuff.
|
|---|
| 620 |
|
|---|
| 621 | ;;; FUNCTION-TO-DESCRIBE is used in "Editor Describe Function Call" and
|
|---|
| 622 | ;;; "Describe Function Call".
|
|---|
| [6790] | 623 | ;;;
|
|---|
| [595] | 624 | (defmacro function-to-describe (var error-name)
|
|---|
| [6790] | 625 | `(cond ((not (symbolp ,var))
|
|---|
| [595] | 626 | (,error-name "~S is not a symbol." ,var))
|
|---|
| 627 | ((special-operator-p ,var) ,var)
|
|---|
| 628 | ((macro-function ,var))
|
|---|
| 629 | ((fboundp ,var))
|
|---|
| 630 | (t
|
|---|
| 631 | (,error-name "~S is not a function." ,var))))
|
|---|
| 632 |
|
|---|
| 633 | (defcommand "Editor Describe Function Call" (p)
|
|---|
| 634 | "Describe the most recently typed function name in the editor Lisp."
|
|---|
| 635 | "Describe the most recently typed function name in the editor Lisp."
|
|---|
| 636 | (declare (ignore p))
|
|---|
| 637 | (with-mark ((mark1 (current-point))
|
|---|
| 638 | (mark2 (current-point)))
|
|---|
| 639 | (pre-command-parse-check mark1)
|
|---|
| 640 | (unless (backward-up-list mark1) (editor-error))
|
|---|
| 641 | (form-offset (move-mark mark2 (mark-after mark1)) 1)
|
|---|
| [6790] | 642 | (with-input-from-region (s (region mark1 mark2))
|
|---|
| [595] | 643 | (in-lisp
|
|---|
| 644 | (let* ((sym (read s))
|
|---|
| 645 | (fun (function-to-describe sym editor-error)))
|
|---|
| 646 | (with-pop-up-display (*standard-output* :title (format nil "~s" sym))
|
|---|
| 647 | (editor-describe-function fun sym)))))))
|
|---|
| 648 |
|
|---|
| 649 |
|
|---|
| 650 | (defcommand "Editor Describe Symbol" (p)
|
|---|
| 651 | "Describe the previous s-expression if it is a symbol in the editor Lisp."
|
|---|
| 652 | "Describe the previous s-expression if it is a symbol in the editor Lisp."
|
|---|
| 653 | (declare (ignore p))
|
|---|
| [6773] | 654 | (with-mark ((mark1 (current-point))
|
|---|
| 655 | (mark2 (current-point)))
|
|---|
| [6790] | 656 | (mark-symbol mark1 mark2)
|
|---|
| [6773] | 657 | (with-input-from-region (s (region mark1 mark2))
|
|---|
| 658 | (let ((thing (read s)))
|
|---|
| 659 | (if (symbolp thing)
|
|---|
| 660 | (with-pop-up-display (*standard-output* :title (format nil "~s" thing))
|
|---|
| 661 | (describe thing))
|
|---|
| [6790] | 662 | (if (and (consp thing)
|
|---|
| [6773] | 663 | (or (eq (car thing) 'quote)
|
|---|
| 664 | (eq (car thing) 'function))
|
|---|
| 665 | (symbolp (cadr thing)))
|
|---|
| [595] | 666 | (with-pop-up-display (*standard-output* :title (format nil "~s" (cadr thing)))
|
|---|
| 667 | (describe (cadr thing)))
|
|---|
| 668 | (editor-error "~S is not a symbol, or 'symbol, or #'symbol."
|
|---|
| 669 | thing)))))))
|
|---|
| 670 |
|
|---|
| 671 | ;;; MARK-SYMBOL moves mark1 and mark2 around the previous or current symbol.
|
|---|
| 672 | ;;; However, if the marks are immediately before the first constituent char
|
|---|
| 673 | ;;; of the symbol name, we use the next symbol since the marks probably
|
|---|
| 674 | ;;; correspond to the point, and Hemlock's cursor display makes it look like
|
|---|
| 675 | ;;; the point is within the symbol name. This also tries to ignore :prefix
|
|---|
| 676 | ;;; characters such as quotes, commas, etc.
|
|---|
| 677 | ;;;
|
|---|
| 678 | (defun mark-symbol (mark1 mark2)
|
|---|
| 679 | (pre-command-parse-check mark1)
|
|---|
| 680 | (with-mark ((tmark1 mark1)
|
|---|
| 681 | (tmark2 mark1))
|
|---|
| 682 | (cond ((and (form-offset tmark1 1)
|
|---|
| 683 | (form-offset (move-mark tmark2 tmark1) -1)
|
|---|
| 684 | (or (mark= mark1 tmark2)
|
|---|
| 685 | (and (find-attribute tmark2 :lisp-syntax
|
|---|
| 686 | #'(lambda (x) (not (eq x :prefix))))
|
|---|
| 687 | (mark= mark1 tmark2))))
|
|---|
| 688 | (form-offset mark2 1))
|
|---|
| 689 | (t
|
|---|
| 690 | (form-offset mark1 -1)
|
|---|
| 691 | (find-attribute mark1 :lisp-syntax
|
|---|
| 692 | #'(lambda (x) (not (eq x :prefix))))
|
|---|
| 693 | (form-offset (move-mark mark2 mark1) 1)))))
|
|---|
| 694 |
|
|---|
| 695 |
|
|---|
| 696 | (defcommand "Editor Describe" (p)
|
|---|
| 697 | "Call Describe on a Lisp object.
|
|---|
| 698 | Prompt for an expression which is evaluated to yield the object."
|
|---|
| 699 | "Prompt for an object to describe."
|
|---|
| 700 | (declare (ignore p))
|
|---|
| 701 | (in-lisp
|
|---|
| [6790] | 702 | (let* ((exp (prompt-for-expression
|
|---|
| [595] | 703 | :prompt "Object: "
|
|---|
| 704 | :help "Expression to evaluate to get object to describe."))
|
|---|
| 705 | (obj (eval exp)))
|
|---|
| 706 | (with-pop-up-display (*standard-output* :title (format nil "~s" exp))
|
|---|
| 707 | (describe obj)))))
|
|---|
| 708 | |
|---|
| 709 |
|
|---|
| 710 | (defcommand "Filter Region" (p)
|
|---|
| 711 | "Apply a Lisp function to each line of the region.
|
|---|
| 712 | An expression is prompted for which should evaluate to a Lisp function
|
|---|
| 713 | from a string to a string. The function must neither modify its argument
|
|---|
| 714 | nor modify the return value after it is returned."
|
|---|
| 715 | "Call prompt for a function, then call Filter-Region with it and the region."
|
|---|
| 716 | (declare (ignore p))
|
|---|
| 717 | (let* ((exp (prompt-for-expression
|
|---|
| 718 | :prompt "Function: "
|
|---|
| 719 | :help "Expression to evaluate to get function to use as filter."))
|
|---|
| 720 | (fun (in-lisp (eval exp)))
|
|---|
| 721 | (region (current-region)))
|
|---|
| 722 | (let* ((start (copy-mark (region-start region) :left-inserting))
|
|---|
| 723 | (end (copy-mark (region-end region) :left-inserting))
|
|---|
| 724 | (region (region start end))
|
|---|
| 725 | (undo-region (copy-region region)))
|
|---|
| 726 | (filter-region fun region)
|
|---|
| 727 | (make-region-undo :twiddle "Filter Region" region undo-region))))
|
|---|