source: release/1.3/source/cocoa-ide/hemlock/src/listener.lisp

Last change on this file was 11927, checked in by R. Matthew Emerson, 16 years ago

Merge trunk changes r11863 through r11898.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.6 KB
RevLine 
[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
154between 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))))
Note: See TracBrowser for help on using the repository browser.