close Warning: Can't use blame annotator:
No changeset 2092 in the repository

source: release/1.3/source/cocoa-ide/hemlock/src/lispmode.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: 70.6 KB
RevLine 
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;;; Hemlock LISP Mode commands
13;;;
14;;; Written by Ivan Vazquez and Bill Maddox.
15;;;
16
17(in-package :hemlock)
18
19;; (declaim (optimize (speed 2))); turn off byte compilation.
20
21
22
23;;;; Variables and lisp-info structure.
24
25;;; These routines are used to define, for standard LISP mode, the start and end
26;;; of a block to parse. If these need to be changed for a minor mode that sits
27;;; on top of LISP mode, simply do a DEFHVAR with the minor mode and give the
28;;; name of the function to use instead of START-OF-PARSE-BLOCK and
29;;; END-OF-PARSE-BLOCK.
30;;;
31
32(defhvar "Parse Start Function"
33 "Take a mark and move it to the top of a block for paren parsing."
34 :value 'start-of-parse-block)
35
36(defhvar "Parse End Function"
37 "Take a mark and move it to the bottom of a block for paren parsing."
38 :value 'end-of-parse-block)
39
40
41;;; LISP-INFO is the structure used to store the data about the line in its
42;;; Plist.
43;;;
44;;; -> BEGINS-QUOTED, ENDING-QUOTED are both Boolean slots that tell whether
45;;; or not a line's begining and/or ending are quoted.
46;;;
47;;; -> RANGES-TO-IGNORE is a list of cons cells, each having the form
48;;; ( [begining-charpos] [end-charpos] ) each of these cells indicating
49;;; a range to ignore. End is exclusive.
50;;;
51;;; -> NET-OPEN-PARENS, NET-CLOSE-PARENS integers that are the number of
52;;; unmatched opening and closing parens that there are on a line.
53;;;
54;;; -> SIGNATURE-SLOT ...
55;;;
56
57(defstruct (lisp-info (:constructor make-lisp-info ()))
58 (begins-quoted nil) ; (or t nil)
59 (ending-quoted nil) ; (or t nil)
60 (ranges-to-ignore nil) ; (or t nil)
61 (net-open-parens 0 :type fixnum)
62 (net-close-parens 0 :type fixnum)
63 (signature-slot))
64
65
66
67
68;;;; Macros.
69
70;;; The following Macros exist to make it easy to acces the Syntax primitives
71;;; without uglifying the code. They were originally written by Maddox.
72;;;
73
74(defmacro scan-char (mark attribute values)
75 `(find-attribute ,mark ',attribute ,(attr-predicate values)))
76
77(defmacro rev-scan-char (mark attribute values)
78 `(reverse-find-attribute ,mark ',attribute ,(attr-predicate values)))
79
80(defmacro test-char (char attribute values)
81 `(let ((x (character-attribute ',attribute ,char)))
82 ,(attr-predicate-aux values)))
83
84(eval-when (:compile-toplevel :execute :load-toplevel)
85(defun attr-predicate (values)
86 (cond ((eq values 't)
87 '#'plusp)
88 ((eq values 'nil)
89 '#'zerop)
90 (t `#'(lambda (x) ,(attr-predicate-aux values)))))
91
92(defun attr-predicate-aux (values)
93 (cond ((eq values t)
94 '(plusp x))
95 ((eq values nil)
96 '(zerop x))
97 ((symbolp values)
98 `(eq x ',values))
99 ((and (listp values) (member (car values) '(and or not)))
100 (cons (car values) (mapcar #'attr-predicate-aux (cdr values))))
101 (t (error "Illegal form in attribute pattern - ~S" values))))
102
103); Eval-When
104
105;;;
106;;; FIND-LISP-CHAR
107
108(defmacro find-lisp-char (mark)
109 "Move MARK to next :LISP-SYNTAX character, if one isn't found, return NIL."
110 `(find-attribute ,mark :lisp-syntax
111 #'(lambda (x)
112 (member x '(:open-paren :close-paren :newline :comment
113 :char-quote :string-quote)))))
114;;;
115;;; PUSH-RANGE
116
117(defmacro push-range (new-range info-struct)
118 "Insert NEW-RANGE into the LISP-INFO-RANGES-TO-IGNORE slot of the INFO-STRUCT."
119 `(when ,new-range
120 (setf (lisp-info-ranges-to-ignore ,info-struct)
121 (cons ,new-range (lisp-info-ranges-to-ignore ,info-struct)))))
122;;;
123;;; SCAN-DIRECTION
124
125(defmacro scan-direction (mark forwardp &rest forms)
126 "Expand to a form that scans either backward or forward according to Forwardp."
127 (if forwardp
128 `(scan-char ,mark ,@forms)
129 `(rev-scan-char ,mark ,@forms)))
130;;;
131;;; DIRECTION-CHAR
132
133(defmacro direction-char (mark forwardp)
134 "Expand to a form that returns either the previous or next character according
135 to Forwardp."
136 (if forwardp
137 `(next-character ,mark)
138 `(previous-character ,mark)))
139
140;;;
141;;; NEIGHBOR-MARK
142
143(defmacro neighbor-mark (mark forwardp)
144 "Expand to a form that moves MARK either backward or forward one character,
145 depending on FORWARDP."
146 (if forwardp
147 `(mark-after ,mark)
148 `(mark-before ,mark)))
149
150;;;
151;;; NEIGHBOR-LINE
152
153(defmacro neighbor-line (line forwardp)
154 "Expand to return the next or previous line, according to Forwardp."
155 (if forwardp
156 `(line-next ,line)
157 `(line-previous ,line)))
158
159
160
161;;;; Parsing functions.
162
163;;; PRE-COMMAND-PARSE-CHECK -- Public.
164;;;
165(defun pre-command-parse-check (mark &optional (fer-sure-parse nil))
166 "Parse the area before the command is actually executed."
167 (with-mark ((top mark)
168 (bottom mark))
169 (funcall (value parse-start-function) top)
170 (funcall (value parse-end-function) bottom)
171 (parse-over-block (mark-line top) (mark-line bottom) fer-sure-parse)))
172
173;;; PARSE-OVER-BLOCK
174;;;
175(defun parse-over-block (start-line end-line &optional (fer-sure-parse nil))
176 "Parse over an area indicated from END-LINE to START-LINE."
177 (let ((test-line start-line)
178 prev-line-info)
179
180 (with-mark ((mark (mark test-line 0)))
181
182 ; Set the pre-begining and post-ending lines to delimit the range
183 ; of action any command will take. This means set the lisp-info of the
184 ; lines immediately before and after the block to Nil.
185
186 (when (line-previous start-line)
187 (setf (getf (line-plist (line-previous start-line)) 'lisp-info) nil))
188 (when (line-next end-line)
189 (setf (getf (line-plist (line-next end-line)) 'lisp-info) nil))
190
191 (loop
192 (let ((line-info (getf (line-plist test-line) 'lisp-info)))
193
194 ;; Reparse the line when any of the following are true:
195 ;;
196 ;; FER-SURE-PARSE is T
197 ;;
198 ;; LINE-INFO or PREV-LINE-INFO are Nil.
199 ;;
200 ;; If the line begins quoted and the previous one wasn't
201 ;; ended quoted.
202 ;;
203 ;; The Line's signature slot is invalid (the line has changed).
204 ;;
205
206 (when (or fer-sure-parse
207 (not line-info)
208 (not prev-line-info)
209
210 (not (eq (lisp-info-begins-quoted line-info)
211 (lisp-info-ending-quoted prev-line-info)))
212
213 (not (eql (line-signature test-line)
214 (lisp-info-signature-slot line-info))))
215
216 (move-to-position mark 0 test-line)
217
218 (unless line-info
219 (setf line-info (make-lisp-info))
220 (setf (getf (line-plist test-line) 'lisp-info) line-info))
221
222 (parse-lisp-line-info mark line-info prev-line-info))
223
224 (when (eq end-line test-line)
225 (return nil))
226
227 (setq prev-line-info line-info)
228
229 (setq test-line (line-next test-line)))))))
230
231
232
233;;;; Parse block finders.
234
235(defhvar "Minimum Lines Parsed"
236 "The minimum number of lines before and after the point parsed by Lisp mode."
237 :value 50)
238(defhvar "Maximum Lines Parsed"
239 "The maximum number of lines before and after the point parsed by Lisp mode."
240 :value 500)
241(defhvar "Defun Parse Goal"
242 "Lisp mode parses the region obtained by skipping this many defuns forward
243 and backward from the point unless this falls outside of the range specified
244 by \"Minimum Lines Parsed\" and \"Maximum Lines Parsed\"."
245 :value 2)
246
247
248(macrolet ((frob (step end)
249 `(let ((min (value minimum-lines-parsed))
250 (max (value maximum-lines-parsed))
251 (goal (value defun-parse-goal))
252 (last-defun nil))
253 (declare (fixnum min max goal))
254 (do ((line (mark-line mark) (,step line))
255 (count 0 (1+ count)))
256 ((null line)
257 (,end mark))
258 (declare (fixnum count))
259 (when (char= (line-character line 0) #\()
260 (setq last-defun line)
261 (decf goal)
262 (when (and (<= goal 0) (>= count min))
263 (line-start mark line)
264 (return)))
265 (when (> count max)
266 (line-start mark (or last-defun line))
267 (return))))))
268
269 (defun start-of-parse-block (mark)
270 (frob line-previous buffer-start))
271
272 (defun end-of-parse-block (mark)
273 (frob line-next buffer-end)))
274
275;;;
276;;; START-OF-SEARCH-LINE
277
278(defun start-of-search-line (line)
279 "Set LINE to the begining line of the block of text to parse."
280 (with-mark ((mark (mark line 0)))
281 (funcall (value 'Parse-Start-Function) mark)
282 (setq line (mark-line mark))))
283
284;;;
285;;; END-OF-SEACH-LINE
286
287(defun end-of-search-line (line)
288 "Set LINE to the ending line of the block of text to parse."
289 (with-mark ((mark (mark line 0)))
290 (funcall (value 'Parse-End-Function) mark)
291 (setq line (mark-line mark))))
292
293
294
295;;;; PARSE-LISP-LINE-INFO.
296
297;;; PARSE-LISP-LINE-INFO -- Internal.
298;;;
299;;; This parses through the line doing the following things:
300;;;
301;;; Counting/Setting the NET-OPEN-PARENS & NET-CLOSE-PARENS.
302;;;
303;;; Making all areas of the line that should be invalid (comments,
304;;; char-quotes, and the inside of strings) and such be in
305;;; RANGES-TO-IGNORE.
306;;;
307;;; Set BEGINS-QUOTED and ENDING-QUOTED
308;;;
309(defun parse-lisp-line-info (mark line-info prev-line-info)
310 "Parse line and set line information like NET-OPEN-PARENS, NET-CLOSE-PARENS,
311 RANGES-TO-INGORE, and ENDING-QUOTED."
312 (let ((net-open-parens 0)
313 (net-close-parens 0))
314 (declare (fixnum net-open-parens net-close-parens))
315
316 ;; Re-set the slots necessary
317
318 (setf (lisp-info-ranges-to-ignore line-info) nil)
319
320 ;; The only way the current line begins quoted is when there
321 ;; is a previous line and it's ending was quoted.
322
323 (setf (lisp-info-begins-quoted line-info)
324 (and prev-line-info
325 (lisp-info-ending-quoted prev-line-info)))
326
327 (if (lisp-info-begins-quoted line-info)
328 (deal-with-string-quote mark line-info)
329 (setf (lisp-info-ending-quoted line-info) nil))
330
331 (assert (eq (hi::mark-buffer mark) (current-buffer)))
332
333 (unless (lisp-info-ending-quoted line-info)
334 (loop
335
336 (unless (find-lisp-char mark)
337 (error "Expected at least a newline!"))
338 (case (character-attribute :lisp-syntax (next-character mark))
339
340 (:open-paren
341 (setq net-open-parens (1+ net-open-parens))
342 (mark-after mark))
343
344 (:close-paren
345 (if (zerop net-open-parens)
346 (setq net-close-parens (1+ net-close-parens))
347 (setq net-open-parens (1- net-open-parens)))
348 (mark-after mark))
349
350 (:newline
351 (setf (lisp-info-ending-quoted line-info) nil)
352 (return t))
353
354 (:comment
355 (push-range (cons (mark-charpos mark) (line-length (mark-line mark)))
356 line-info)
357 (setf (lisp-info-ending-quoted line-info) nil)
358 (return t))
359
360 (:char-quote
361 (mark-after mark)
362 (push-range (cons (mark-charpos mark) (1+ (mark-charpos mark)))
363 line-info)
364 (mark-after mark))
365
366 (:string-quote
367 (mark-after mark)
368 (unless (deal-with-string-quote mark line-info)
369 (setf (lisp-info-ending-quoted line-info) t)
370 (return t)))
371 (t (ERROR "character attribute of: ~s is ~s, at ~s"
372 (next-character mark)
373 (character-attribute :lisp-syntax (next-character mark))
374 mark)))))
375
376 (setf (lisp-info-net-open-parens line-info) net-open-parens)
377 (setf (lisp-info-net-close-parens line-info) net-close-parens)
378 (setf (lisp-info-signature-slot line-info)
379 (line-signature (mark-line mark)))))
380
381
382
383
384;;;; String quote utilities.
385
386;;; VALID-STRING-QUOTE-P
387;;;
388(defmacro valid-string-quote-p (mark forwardp)
389 "Return T if the string-quote indicated by MARK is valid."
390 (let ((test-mark (gensym)))
391 `(with-mark ((,test-mark ,mark))
392 ,(unless forwardp
393 ;; TEST-MARK should always be right before the String-quote to be
394 ;; checked.
395 `(mark-before ,test-mark))
396 (when (test-char (next-character ,test-mark) :lisp-syntax :string-quote)
397 (let ((slash-count 0))
398 (loop
399 (mark-before ,test-mark)
400 (if (test-char (next-character ,test-mark) :lisp-syntax :char-quote)
401 (incf slash-count)
402 (return t)))
403 (not (oddp slash-count)))))))
404
405;;;
406;;; FIND-VALID-STRING-QUOTE
407
408(defmacro find-valid-string-quote (mark &key forwardp (cease-at-eol nil))
409 "Expand to a form that will leave MARK before a valid string-quote character,
410 in either a forward or backward direction, according to FORWARDP. If
411 CEASE-AT-EOL is T then it will return nil if encountering the EOL before a
412 valid string-quote."
413 (let ((e-mark (gensym)))
414 `(with-mark ((,e-mark ,mark))
415
416 (loop
417 (unless (scan-direction ,e-mark ,forwardp :lisp-syntax
418 ,(if cease-at-eol
419 `(or :newline :string-quote)
420 `:string-quote))
421 (return nil))
422
423 ,@(if cease-at-eol
424 `((when (test-char (direction-char ,e-mark ,forwardp) :lisp-syntax
425 :newline)
426 (return nil))))
427
428 (when (valid-string-quote-p ,e-mark ,forwardp)
429 (move-mark ,mark ,e-mark)
430 (return t))
431
432 (neighbor-mark ,e-mark ,forwardp)))))
433
434
435;;;; DEAL-WITH-STRING-QUOTE.
436
437;;; DEAL-WITH-STRING-QUOTE
438;;;
439;;; Called when a string is begun (i.e. parse hits a #\"). It checks for a
440;;; matching quote on the line that MARK points to, and puts the appropriate
441;;; area in the RANGES-TO-IGNORE slot and leaves MARK pointing after this area.
442;;; The "appropriate area" is from MARK to the end of the line or the matching
443;;; string-quote, whichever comes first.
444;;;
445(defun deal-with-string-quote (mark info-struct)
446 "Alter the current line's info struct as necessary as due to encountering a
447 string quote character."
448 (with-mark ((e-mark mark))
449 (cond ((find-valid-string-quote e-mark :forwardp t :cease-at-eol t)
450 ;; If matching quote is on this line then mark the area between the
451 ;; first quote (MARK) and the matching quote as invalid by pushing
452 ;; its begining and ending into the IGNORE-RANGE.
453 (push-range (cons (mark-charpos mark) (mark-charpos e-mark))
454 info-struct)
455 (setf (lisp-info-ending-quoted info-struct) nil)
456 (mark-after e-mark)
457 (move-mark mark e-mark))
458 ;; If the EOL has been hit before the matching quote then mark the
459 ;; area from MARK to the EOL as invalid.
460 (t
461 (push-range (cons (mark-charpos mark)
462 (line-length (mark-line mark)))
463 info-struct)
464 ;; The Ending is marked as still being quoted.
465 (setf (lisp-info-ending-quoted info-struct) t)
466 (line-end mark)
467 nil))))
468
469
470
471
472;;;; Character validity checking:
473
474;;; Find-Ignore-Region -- Internal
475;;;
476;;; If the character in the specified direction from Mark is in an ignore
477;;; region, then return the region and the line that the region is in as
478;;; values. If there is no ignore region, then return NIL and the Mark-Line.
479;;; If the line is not parsed, or there is no character (because of being at
480;;; the buffer beginning or end), then return both values NIL.
481;;;
482(defun find-ignore-region (mark forwardp)
483 (flet ((scan (line pos)
484 (declare (fixnum pos))
485 (let ((info (getf (line-plist line) 'lisp-info)))
486 (if info
487 (dolist (range (lisp-info-ranges-to-ignore info)
488 (values nil line))
489 (let ((start (car range))
490 (end (cdr range)))
491 (declare (fixnum start end))
492 (when (and (>= pos start) (< pos end))
493 (return (values range line)))))
494 (values nil nil)))))
495 (let ((pos (mark-charpos mark))
496 (line (mark-line mark)))
497 (declare (fixnum pos))
498 (cond (forwardp (scan line pos))
499 ((> pos 0) (scan line (1- pos)))
500 (t
501 (let ((prev (line-previous line)))
502 (if prev
503 (scan prev (line-length prev))
504 (values nil nil))))))))
505
506
507;;; Valid-Spot -- Public
508;;;
509(defun valid-spot (mark forwardp)
510 "Return true if the character pointed to by Mark is not in a quoted context,
511 false otherwise. If Forwardp is true, we use the next character, otherwise
512 we use the previous."
513 (if (and (not forwardp)
514 (null (previous-character mark)))
515 t ;beginning of buffer always a valid spot
516 (multiple-value-bind (region line)
517 (find-ignore-region mark forwardp)
518 (and line (not region)))))
519
520
521;;; Scan-Direction-Valid -- Internal
522;;;
523;;; Like scan-direction, but only stop on valid characters.
524;;;
525(defmacro scan-direction-valid (mark forwardp &rest forms)
526 (let ((n-mark (gensym))
527 (n-line (gensym))
528 (n-region (gensym))
529 (n-won (gensym)))
530 `(let ((,n-mark ,mark) (,n-won nil))
531 (loop
532 (multiple-value-bind (,n-region ,n-line)
533 (find-ignore-region ,n-mark ,forwardp)
534 (unless ,n-line (return nil))
535 (if ,n-region
536 (move-to-position ,n-mark
537 ,(if forwardp
538 `(cdr ,n-region)
539 `(car ,n-region))
540 ,n-line)
541 (when ,n-won (return t)))
542 ;;
543 ;; Peculiar condition when a quoting character terminates a line.
544 ;; The ignore region is off the end of the line causing %FORM-OFFSET
545 ;; to infinitely loop.
546 (when (> (mark-charpos ,n-mark) (line-length ,n-line))
547 (line-offset ,n-mark 1 0))
548 (unless (scan-direction ,n-mark ,forwardp ,@forms)
549 (return nil))
550 (setq ,n-won t))))))
551
552
553
554;;;; List offseting.
555
556;;; %LIST-OFFSET allows for BACKWARD-LIST and FORWARD-LIST to be built
557;;; with the same existing structure, with the altering of one variable.
558;;; This one variable being FORWARDP.
559;;;
560(defmacro %list-offset (actual-mark forwardp &key (extra-parens 0) )
561 "Expand to code that will go forward one list either backward or forward,
562 according to the FORWARDP flag."
563 (let ((mark (gensym)))
564 `(let ((paren-count ,extra-parens))
565 (declare (fixnum paren-count))
566 (with-mark ((,mark ,actual-mark))
567 (loop
568 (scan-direction ,mark ,forwardp :lisp-syntax
569 (or :close-paren :open-paren :newline))
570 (let ((ch (direction-char ,mark ,forwardp)))
571 (unless ch (return nil))
572 (when (valid-spot ,mark ,forwardp)
573 (case (character-attribute :lisp-syntax ch)
574 (:close-paren
575 (decf paren-count)
576 ,(when forwardp
577 ;; When going forward, an unmatching close-paren means the
578 ;; end of list.
579 `(when (<= paren-count 0)
580 (neighbor-mark ,mark ,forwardp)
581 (move-mark ,actual-mark ,mark)
582 (return t))))
583 (:open-paren
584 (incf paren-count)
585 ,(unless forwardp ; Same as above only end of list
586 `(when (>= paren-count 0) ; is opening parens.
587 (neighbor-mark ,mark ,forwardp)
588 (move-mark ,actual-mark ,mark)
589 (return t))))
590
591 (:newline
592 ;; When a #\Newline is hit, then the matching paren must lie
593 ;; on some other line so drop down into the multiple line
594 ;; balancing function: QUEST-FOR-BALANCING-PAREN If no paren
595 ;; seen yet, keep going.
596 (cond ((zerop paren-count))
597 ((quest-for-balancing-paren ,mark paren-count ,forwardp)
598 (move-mark ,actual-mark ,mark)
599 (return t))
600 (t
601 (return nil)))))))
602
603 (neighbor-mark ,mark ,forwardp))))))
604
605;;;
606;;; QUEST-FOR-BALANCING-PAREN
607
608(defmacro quest-for-balancing-paren (mark paren-count forwardp)
609 "Expand to a form that finds the the balancing paren for however many opens or
610 closes are registered by Paren-Count."
611 `(let* ((line (mark-line ,mark)))
612 (loop
613 (setq line (neighbor-line line ,forwardp))
614 (unless line (return nil))
615 (let ((line-info (getf (line-plist line) 'lisp-info))
616 (unbal-paren ,paren-count))
617 (unless line-info (return nil))
618
619 ,(if forwardp
620 `(decf ,paren-count (lisp-info-net-close-parens line-info))
621 `(incf ,paren-count (lisp-info-net-open-parens line-info)))
622
623 (when ,(if forwardp
624 `(<= ,paren-count 0)
625 `(>= ,paren-count 0))
626 ,(if forwardp
627 `(line-start ,mark line)
628 `(line-end ,mark line))
629 (return (goto-correct-paren-char ,mark unbal-paren ,forwardp)))
630
631 ,(if forwardp
632 `(incf ,paren-count (lisp-info-net-open-parens line-info))
633 `(decf ,paren-count (lisp-info-net-close-parens line-info)))))))
634
635
636;;;
637;;; GOTO-CORRECT-PAREN-CHAR
638
639(defmacro goto-correct-paren-char (mark paren-count forwardp)
640 "Expand to a form that will leave MARK on the correct balancing paren matching
641 however many are indicated by COUNT."
642 `(with-mark ((m ,mark))
643 (let ((count ,paren-count))
644 (loop
645 (scan-direction m ,forwardp :lisp-syntax
646 (or :close-paren :open-paren :newline))
647 (when (valid-spot m ,forwardp)
648 (ecase (character-attribute :lisp-syntax (direction-char m ,forwardp))
649 (:close-paren
650 (decf count)
651 ,(when forwardp
652 `(when (zerop count)
653 (neighbor-mark m ,forwardp)
654 (move-mark ,mark m)
655 (return t))))
656
657 (:open-paren
658 (incf count)
659 ,(unless forwardp
660 `(when (zerop count)
661 (neighbor-mark m ,forwardp)
662 (move-mark ,mark m)
663 (return t))))))
664 (neighbor-mark m ,forwardp)))))
665
666
667(defun list-offset (mark offset)
668 (if (plusp offset)
669 (dotimes (i offset t)
670 (unless (%list-offset mark t) (return nil)))
671 (dotimes (i (- offset) t)
672 (unless (%list-offset mark nil) (return nil)))))
673
674(defun forward-up-list (mark)
675 "Moves mark just past the closing paren of the immediately containing list."
676 (%list-offset mark t :extra-parens 1))
677
678(defun backward-up-list (mark)
679 "Moves mark just before the opening paren of the immediately containing list."
680 (%list-offset mark nil :extra-parens -1))
681
682
683
684
685;;;; Top level form location hacks (open parens beginning lines).
686
687;;; NEIGHBOR-TOP-LEVEL is used only in TOP-LEVEL-OFFSET.
688;;;
689(eval-when (:compile-toplevel :execute)
690(defmacro neighbor-top-level (line forwardp)
691 `(loop
692 (when (test-char (line-character ,line 0) :lisp-syntax :open-paren)
693 (return t))
694 (setf ,line ,(if forwardp `(line-next ,line) `(line-previous ,line)))
695 (unless ,line (return nil))))
696) ;eval-when
697
698(defun top-level-offset (mark offset)
699 "Go forward or backward offset number of top level forms. Mark is
700 returned if offset forms exists, otherwise nil."
701 (declare (fixnum offset))
702 (let* ((line (mark-line mark))
703 (at-start (test-char (line-character line 0) :lisp-syntax :open-paren)))
704 (cond ((zerop offset) mark)
705 ((plusp offset)
706 (do ((offset (if at-start offset (1- offset))
707 (1- offset)))
708 (nil)
709 (declare (fixnum offset))
710 (unless (neighbor-top-level line t) (return nil))
711 (when (zerop offset) (return (line-start mark line)))
712 (unless (setf line (line-next line)) (return nil))))
713 (t
714 (do ((offset (if (and at-start (start-line-p mark))
715 offset
716 (1+ offset))
717 (1+ offset)))
718 (nil)
719 (declare (fixnum offset))
720 (unless (neighbor-top-level line nil) (return nil))
721 (when (zerop offset) (return (line-start mark line)))
722 (unless (setf line (line-previous line)) (return nil)))))))
723
724
725(defun mark-top-level-form (mark1 mark2)
726 "Moves mark1 and mark2 to the beginning and end of the current or next defun.
727 Mark1 one is used as a reference. The marks may be altered even if
728 unsuccessful. if successful, return mark2, else nil."
729 (let ((winp (cond ((inside-defun-p mark1)
730 (cond ((not (top-level-offset mark1 -1)) nil)
731 ((not (form-offset (move-mark mark2 mark1) 1)) nil)
732 (t mark2)))
733 ((start-defun-p mark1)
734 (form-offset (move-mark mark2 mark1) 1))
735 ((and (top-level-offset (move-mark mark2 mark1) -1)
736 (start-defun-p mark2)
737 (form-offset mark2 1)
738 (same-line-p mark1 mark2))
739 (form-offset (move-mark mark1 mark2) -1)
740 mark2)
741 ((top-level-offset mark1 1)
742 (form-offset (move-mark mark2 mark1) 1)))))
743 (when winp
744 (when (blank-after-p mark2) (line-offset mark2 1 0))
745 mark2)))
746
747(defun inside-defun-p (mark)
748 "T if the current point is (supposedly) in a top level form."
749 (with-mark ((m mark))
750 (when (top-level-offset m -1)
751 (form-offset m 1)
752 (mark> m mark))))
753
754(defun start-defun-p (mark)
755 "Returns t if mark is sitting before an :open-paren at the beginning of a
756 line."
757 (and (start-line-p mark)
758 (test-char (next-character mark) :lisp-syntax :open-paren)))
759
760
761
762
763;;;; Form offseting.
764
765(defmacro %form-offset (mark forwardp)
766 `(with-mark ((m ,mark))
767 (when (scan-direction-valid m ,forwardp :lisp-syntax
768 (or :open-paren :close-paren
769 :char-quote :string-quote
770 :constituent))
771 (ecase (character-attribute :lisp-syntax (direction-char m ,forwardp))
772 (:open-paren
773 (when ,(if forwardp `(list-offset m 1) `(mark-before m))
774 ,(unless forwardp
775 '(scan-direction m nil :lisp-syntax (not :prefix)))
776 (move-mark ,mark m)
777 t))
778 (:close-paren
779 (when ,(if forwardp `(mark-after m) `(list-offset m -1))
780 ,(unless forwardp
781 '(scan-direction m nil :lisp-syntax (not :prefix)))
782 (move-mark ,mark m)
783 t))
784 ((:constituent :char-quote)
785 (scan-direction-valid m ,forwardp :lisp-syntax
786 (not (or :constituent :char-quote)))
787 ,(if forwardp
788 `(scan-direction-valid m t :lisp-syntax
789 (not (or :constituent :char-quote)))
790 `(scan-direction-valid m nil :lisp-syntax
791 (not (or :constituent :char-quote
792 :prefix))))
793 (move-mark ,mark m)
794 t)
795 (:string-quote
796 (cond ((valid-spot m ,(not forwardp))
797 (neighbor-mark m ,forwardp)
798 (when (scan-direction-valid m ,forwardp :lisp-syntax
799 :string-quote)
800 (neighbor-mark m ,forwardp)
801 (move-mark ,mark m)
802 t))
803 (t (neighbor-mark m ,forwardp)
804 (move-mark ,mark m)
805 t)))))))
806
807
808(defun form-offset (mark offset)
809 "Move mark offset number of forms, after if positive, before if negative.
810 Mark is always moved. If there weren't enough forms, returns nil instead of
811 mark."
812 (if (plusp offset)
813 (dotimes (i offset t)
814 (unless (%form-offset mark t) (return nil)))
815 (dotimes (i (- offset) t)
816 (unless (%form-offset mark nil) (return nil)))))
817
818
819
820
821;;;; Table of special forms with special indenting requirements.
822
823(defhvar "Indent Defanything"
824 "This is the number of special arguments implicitly assumed to be supplied
825 in calls to functions whose names begin with \"DEF\". If set to NIL, this
826 feature is disabled."
827 :value 2)
828
829(defhvar "Indent With-anything"
830 "This is the number of special arguments implicitly assumed to be supplied
831 in calls to functions whose names begin with \"WITH-\". If set to NIL, this
832 feature is disabled."
833 :value 1)
834
835(defvar *special-forms* (make-hash-table :test #'equal))
836
837(defun defindent (fname args)
838 "Define Fname to have Args special arguments. If args is null then remove
839 any special arguments information."
840 (check-type fname string)
841 (let ((fname (string-upcase fname)))
842 (cond ((null args) (remhash fname *special-forms*))
843 (t
844 (check-type args integer)
845 (setf (gethash fname *special-forms*) args)))))
846
847
848;;; Hemlock forms.
849;;;
850(defindent "defhvar" 1)
851(defindent "hlet" 1)
852(defindent "defcommand" 2)
853(defindent "defattribute" 1)
854(defindent "command-case" 1)
855(defindent "do-strings" 1)
856(defindent "save-for-undo" 1)
857(defindent "do-alpha-chars" 1)
858(defindent "do-headers-buffers" 1)
859(defindent "do-headers-lines" 1)
860(defindent "frob" 1) ;cover silly FLET and MACROLET names for Rob and Bill.
861(defindent "modifying-buffer" 1)
862
863;;; Common Lisp forms.
864;;;
865(defindent "block" 1)
866(defindent "case" 1)
867(defindent "catch" 1)
868(defindent "ccase" 1)
869(defindent "compiler-let" 1)
870(defindent "ctypecase" 1)
871(defindent "defconstant" 1)
872(defindent "define-compiler-macro" 2)
873(defindent "define-setf-method" 2)
874(defindent "destructuring-bind" 2)
875(defindent "defmacro" 2)
876(defindent "defpackage" 1)
877(defindent "defparameter" 1)
878(defindent "defstruct" 1)
879(defindent "deftype" 2)
880(defindent "defun" 2)
881(defindent "defvar" 1)
882(defindent "do" 2)
883(defindent "do*" 2)
884(defindent "do-all-symbols" 1)
885(defindent "do-external-symbols" 1)
886(defindent "do-symbols" 1)
887(defindent "dolist" 1)
888(defindent "dotimes" 1)
889(defindent "ecase" 1)
890(defindent "etypecase" 1)
891(defindent "eval-when" 1)
892(defindent "flet" 1)
893(defindent "if" 1)
894(defindent "labels" 1)
895(defindent "lambda" 1)
896(defindent "let" 1)
897(defindent "let*" 1)
898(defindent "locally" 0)
899(defindent "loop" 0)
900(defindent "macrolet" 1)
901(defindent "multiple-value-bind" 2)
902(defindent "multiple-value-call" 1)
903(defindent "multiple-value-prog1" 1)
904(defindent "multiple-value-setq" 1)
905(defindent "prog1" 1)
906(defindent "progv" 2)
907(defindent "progn" 0)
908(defindent "typecase" 1)
909(defindent "unless" 1)
910(defindent "unwind-protect" 1)
911(defindent "when" 1)
912
913;;; Error/condition system forms.
914;;;
915(defindent "define-condition" 2)
916(defindent "handler-bind" 1)
917(defindent "handler-case" 1)
918(defindent "restart-bind" 1)
919(defindent "restart-case" 1)
920;;; These are for RESTART-CASE branch formatting.
921(defindent "store-value" 1)
922(defindent "use-value" 1)
923(defindent "muffle-warning" 1)
924(defindent "abort" 1)
925(defindent "continue" 1)
926
927;;; Debug-internals forms.
928;;;
929(defindent "do-debug-function-blocks" 1)
930(defindent "di:do-debug-function-blocks" 1)
931(defindent "do-debug-function-variables" 1)
932(defindent "di:do-debug-function-variables" 1)
933(defindent "do-debug-block-locations" 1)
934(defindent "di:do-debug-block-locations" 1)
935;;;
936;;; Debug-internals conditions
937;;; (define these to make uses of HANDLER-CASE indent branches correctly.)
938;;;
939(defindent "debug-condition" 1)
940(defindent "di:debug-condition" 1)
941(defindent "no-debug-info" 1)
942(defindent "di:no-debug-info" 1)
943(defindent "no-debug-function-returns" 1)
944(defindent "di:no-debug-function-returns" 1)
945(defindent "no-debug-blocks" 1)
946(defindent "di:no-debug-blocks" 1)
947(defindent "lambda-list-unavailable" 1)
948(defindent "di:lambda-list-unavailable" 1)
949(defindent "no-debug-variables" 1)
950(defindent "di:no-debug-variables" 1)
951(defindent "invalid-value" 1)
952(defindent "di:invalid-value" 1)
953(defindent "ambiguous-variable-name" 1)
954(defindent "di:ambiguous-variable-name" 1)
955(defindent "debug-error" 1)
956(defindent "di:debug-error" 1)
957(defindent "unhandled-condition" 1)
958(defindent "di:unhandled-condition" 1)
959(defindent "unknown-code-location" 1)
960(defindent "di:unknown-code-location" 1)
961(defindent "unknown-debug-variable" 1)
962(defindent "di:unknown-debug-variable" 1)
963(defindent "invalid-control-stack-pointer" 1)
964(defindent "di:invalid-control-stack-pointer" 1)
965(defindent "frame-function-mismatch" 1)
966(defindent "di:frame-function-mismatch" 1)
967
968
969;;; CLOS forms.
970;;;
971(defindent "with-accessors" 2)
972(defindent "defclass" 2)
973(defindent "print-unreadable-object" 1)
974(defindent "defmethod" 2)
975(defindent "make-instance" 1)
976
977;;; System forms.
978;;;
979(defindent "rlet" 1)
980
981;;; Multiprocessing forms.
982(defindent "process-wait" 1)
983
984
985
986
987;;;; Indentation.
988
989;;; LISP-INDENTATION -- Internal Interface.
990
991(defun strip-package-prefix (string)
992 (let* ((p (position #\: string :from-end t)))
993 (if p
994 (subseq string (1+ p))
995 string)))
996;;;
997(defun lisp-indentation (mark)
998 "Compute number of spaces which mark should be indented according to
999 local context and lisp grinding conventions. This assumes mark is at the
1000 beginning of the line to be indented."
1001 (with-mark ((m mark)
1002 (temp mark))
1003 ;; See if we are in a quoted context.
1004 (unless (valid-spot m nil)
1005 (return-from lisp-indentation (lisp-generic-indentation m)))
1006 ;; Look for the paren that opens the containing form.
1007 (unless (backward-up-list m)
1008 (return-from lisp-indentation 0))
1009 ;; Move after the paren, save the start, and find the form name.
1010 (mark-after m)
1011 (with-mark ((start m))
1012 (unless (and (scan-char m :lisp-syntax
1013 (not (or :space :prefix :char-quote)))
1014 (test-char (next-character m) :lisp-syntax :constituent))
1015 (return-from lisp-indentation (mark-column start)))
1016 (with-mark ((fstart m))
1017 (scan-char m :lisp-syntax (not :constituent))
1018 (let* ((fname (nstring-upcase
1019 (strip-package-prefix (region-to-string (region fstart m)))))
1020 (special-args (or (gethash fname *special-forms*)
1021 (and (> (length fname) 2)
1022 (string= fname "DEF" :end1 3)
1023 (value indent-defanything))
1024 (and (> (length fname) 4)
1025 (string= fname "WITH-" :end1 5)
1026 (value indent-with-anything)))))
1027 (declare (simple-string fname))
1028 ;; Now that we have the form name, did it have special syntax?
1029 (cond (special-args
1030 (with-mark ((spec m))
1031 (cond ((and (form-offset spec special-args)
1032 (mark<= spec mark))
1033 (1+ (mark-column start)))
1034 ((skip-valid-space m)
1035 (mark-column m))
1036 (t
1037 (+ (mark-column start) 3)))))
1038 ;; See if the user seems to have altered the editor's
1039 ;; indentation, and if so, try to adhere to it. This usually
1040 ;; happens when you type in a quoted list constant that line
1041 ;; wraps. You want all the items on successive lines to fall
1042 ;; under the first character after the opening paren, not as if
1043 ;; you are calling a function.
1044 ((and (form-offset temp -1)
1045 (or (blank-before-p temp) (not (same-line-p temp fstart)))
1046 (not (same-line-p temp mark)))
1047 (unless (blank-before-p temp)
1048 (line-start temp)
1049 (find-attribute temp :space #'zerop))
1050 (mark-column temp))
1051 ;; Appears to be a normal form. Is the first arg on the same
1052 ;; line as the form name?
1053 ((skip-valid-space m)
1054 (or (lisp-indentation-check-for-local-def
1055 mark temp fstart start t)
1056 (mark-column m)))
1057 ;; Okay, fall under the first character after the opening paren.
1058 (t
1059 (or (lisp-indentation-check-for-local-def
1060 mark temp fstart start nil)
1061 (mark-column start)))))))))
1062
1063(defhvar "Lisp Indentation Local Definers"
1064 "Forms with syntax like LABELS, MACROLET, etc."
1065 :value '("LABELS" "MACROLET" "FLET"))
1066
1067;;; LISP-INDENTATION-CHECK-FOR-LOCAL-DEF -- Internal.
1068;;;
1069;;; This is a temporary hack to see how it performs. When we are indenting
1070;;; what appears to be a function call, let's look for FLET or MACROLET to see
1071;;; if we really are indenting a local definition. If we are, return the
1072;;; indentation for a DEFUN; otherwise, nil
1073;;;
1074;;; Mark is the argument to LISP-INDENTATION. Start is just inside the paren
1075;;; of what looks like a function call. If we are in an FLET, arg-list
1076;;; indicates whether the local function's arg-list has been entered, that is,
1077;;; whether we need to normally indent for a DEFUN body or indent specially for
1078;;; the arg-list.
1079;;;
1080(defun lisp-indentation-check-for-local-def (mark temp1 temp2 start arg-list)
1081 ;; We know this succeeds from LISP-INDENTATION.
1082 (backward-up-list (move-mark temp1 mark)) ;Paren for local definition.
1083 (cond ((and (backward-up-list temp1) ;Paren opening the list of defs
1084 (form-offset (move-mark temp2 temp1) -1)
1085 (mark-before temp2)
1086 (backward-up-list temp1) ;Paren for FLET or MACROLET.
1087 (mark= temp1 temp2)) ;Must be in first arg form.
1088 ;; See if the containing form is named FLET or MACROLET.
1089 (mark-after temp1)
1090 (unless (and (scan-char temp1 :lisp-syntax
1091 (not (or :space :prefix :char-quote)))
1092 (test-char (next-character temp1) :lisp-syntax
1093 :constituent))
1094 (return-from lisp-indentation-check-for-local-def nil))
1095 (move-mark temp2 temp1)
1096 (scan-char temp2 :lisp-syntax (not :constituent))
1097 (let ((fname (nstring-upcase (region-to-string (region temp1 temp2)))))
1098 (cond ((not (member fname (value lisp-indentation-local-definers)
1099 :test #'string=))
1100 nil)
1101 (arg-list
1102 (1+ (mark-column start)))
1103 (t
1104 (+ (mark-column start) 3)))))))
1105
1106;;; LISP-GENERIC-INDENTATION -- Internal.
1107;;;
1108;;; LISP-INDENTATION calls this when mark is in a invalid spot, or quoted
1109;;; context. If we are inside a string, we return the column one greater
1110;;; than the opening double quote. Otherwise, we just use the indentation
1111;;; of the first preceding non-blank line.
1112;;;
1113(defun lisp-generic-indentation (mark)
1114 (with-mark ((m mark))
1115 (form-offset m -1)
1116 (cond ((eq (character-attribute :lisp-syntax (next-character m))
1117 :string-quote)
1118 (1+ (mark-column m)))
1119 (t
1120 (let* ((line (mark-line mark))
1121 (prev (do ((line (line-previous line) (line-previous line)))
1122 ((not (and line (blank-line-p line))) line))))
1123 (cond (prev
1124 (line-start mark prev)
1125 (find-attribute mark :space #'zerop)
1126 (mark-column mark))
1127 (t 0)))))))
1128
1129;;; Skip-Valid-Space -- Internal
1130;;;
1131;;; Skip over any space on the line Mark is on, stopping at the first valid
1132;;; non-space character. If there is none on the line, return nil.
1133;;;
1134(defun skip-valid-space (mark)
1135 (loop
1136 (scan-char mark :lisp-syntax (not :space))
1137 (let ((val (character-attribute :lisp-syntax
1138 (next-character mark))))
1139 (cond ((eq val :newline) (return nil))
1140 ((valid-spot mark t) (return mark))))
1141 (mark-after mark)))
1142
1143;; (declaim (optimize (speed 0))); byte compile again
1144
1145
1146
1147;;;; Indentation commands and hook functions.
1148
1149(defcommand "Defindent" (p)
1150 "Define the Lisp indentation for the current function.
1151 The indentation is a non-negative integer which is the number
1152 of special arguments for the form. Examples: 2 for Do, 1 for Dolist.
1153 If a prefix argument is supplied, then delete the indentation information."
1154 "Do a defindent, man!"
1155 (with-mark ((m (current-point)))
1156 (pre-command-parse-check m)
1157 (unless (backward-up-list m) (editor-error))
1158 (mark-after m)
1159 (with-mark ((n m))
1160 (scan-char n :lisp-syntax (not :constituent))
1161 (let ((s (region-to-string (region m n))))
1162 (declare (simple-string s))
1163 (when (zerop (length s)) (editor-error))
1164 (if p
1165 (defindent s nil)
1166 (let ((i (prompt-for-integer
1167 :prompt (format nil "Indentation for ~A: " s)
1168 :help "Number of special arguments.")))
1169 (when (minusp i)
1170 (editor-error "Indentation must be non-negative."))
1171 (defindent s i))))))
1172 (indent-command nil))
1173
1174(defcommand "Indent Form" (p)
1175 "Indent Lisp code in the next form."
1176 "Indent Lisp code in the next form."
1177 (declare (ignore p))
1178 (let ((point (current-point)))
1179 (pre-command-parse-check point)
1180 (with-mark ((m point))
1181 (unless (form-offset m 1) (editor-error))
1182 (lisp-indent-region (region point m) "Indent Form"))))
1183
1184;;; LISP-INDENT-REGION -- Internal.
1185;;;
1186;;; This indents a region of Lisp code without doing excessive redundant
1187;;; computation. We parse the entire region once, then scan through doing
1188;;; indentation on each line. We forcibly reparse each line that we indent so
1189;;; that the list operations done to determine indentation of subsequent lines
1190;;; will work. This is done undoably with save1, save2, buf-region, and
1191;;; undo-region.
1192;;;
1193(defun lisp-indent-region (region &optional (undo-text "Lisp region indenting")) (let* ((start (region-start region))
1194 (end (region-end region))
1195 (buffer (hi::line-%buffer (mark-line start))))
1196 (with-mark ((m1 start)
1197 (m2 end))
1198 (funcall (value parse-start-function) m1)
1199 (funcall (value parse-end-function) m2)
1200 (parse-over-block (mark-line m1) (mark-line m2)))
1201 (hi::check-buffer-modification buffer start)
1202 (hi::check-buffer-modification buffer end)
1203 (let* ((first-line (mark-line start))
1204 (last-line (mark-line end))
1205 (prev (line-previous first-line))
1206 (prev-line-info
1207 (and prev (getf (line-plist prev) 'lisp-info)))
1208 (save1 (line-start (copy-mark start :right-inserting)))
1209 (save2 (line-end (copy-mark end :left-inserting)))
1210 (buf-region (region save1 save2))
1211 (undo-region (copy-region buf-region)))
1212 (with-mark ((bol start :left-inserting))
1213 (do ((line first-line (line-next line)))
1214 (nil)
1215 (line-start bol line)
1216 (ensure-lisp-indentation bol)
1217 (let ((line-info (getf (line-plist line) 'lisp-info)))
1218 (parse-lisp-line-info bol line-info prev-line-info)
1219 (setq prev-line-info line-info))
1220 (when (eq line last-line) (return nil))))
1221 (make-region-undo :twiddle undo-text buf-region undo-region))))
1222
1223;;; INDENT-FOR-LISP -- Internal.
1224;;;
1225;;; This is the value of "Indent Function" for "Lisp" mode.
1226;;;
1227(defun indent-for-lisp (mark)
1228 (line-start mark)
1229 (pre-command-parse-check mark)
1230 (ensure-lisp-indentation mark))
1231
1232(defun count-leading-whitespace (mark)
1233 (with-mark ((m mark))
1234 (line-start m)
1235 (do* ((p 0)
1236 (q 0 (1+ q))
1237 (tab-width (value spaces-per-tab)))
1238 ()
1239 (case (next-character m)
1240 (#\space (incf p))
1241 (#\tab (setq p (* tab-width (ceiling (1+ p) tab-width))))
1242 (t (return (values p q))))
1243 (character-offset m 1))))
1244
1245;;; Don't do anything if M's line is already correctly indented.
1246(defun ensure-lisp-indentation (m)
1247 (let* ((col (lisp-indentation m)))
1248 (multiple-value-bind (curcol curpos) (count-leading-whitespace m)
1249 (cond ((= curcol col) (setf (mark-charpos m) curpos))
1250 (t
1251 (delete-horizontal-space m)
1252 (indent-to-column m col))))))
1253
1254
1255
1256
1257
1258;;;; Most "Lisp" mode commands.
1259
1260(defcommand "Beginning of Defun" (p)
1261 "Move the point to the beginning of a top-level form, collapsing the selection.
1262 with an argument, skips the previous p top-level forms."
1263 "Move the point to the beginning of a top-level form, collapsing the selection."
1264 (let ((point (current-point-collapsing-selection))
1265 (count (or p 1)))
1266 (pre-command-parse-check point)
1267 (if (minusp count)
1268 (end-of-defun-command (- count))
1269 (unless (top-level-offset point (- count))
1270 (editor-error)))))
1271
1272(defcommand "Select to Beginning of Defun" (p)
1273 "Move the point to the beginning of a top-level form, extending the selection.
1274 with an argument, skips the previous p top-level forms."
1275 "Move the point to the beginning of a top-level form, extending the selection."
1276 (let ((point (current-point-extending-selection))
1277 (count (or p 1)))
1278 (pre-command-parse-check point)
1279 (if (minusp count)
1280 (end-of-defun-command (- count))
1281 (unless (top-level-offset point (- count))
1282 (editor-error)))))
1283
1284;;; "End of Defun", with a positive p (the normal case), does something weird.
1285;;; Get a mark at the beginning of the defun, and then offset it forward one
1286;;; less top level form than we want. This sets us up to use FORM-OFFSET which
1287;;; allows us to leave the point immediately after the defun. If we used
1288;;; TOP-LEVEL-OFFSET one less than p on the mark at the end of the current
1289;;; defun, point would be left at the beginning of the p+1'st form instead of
1290;;; at the end of the p'th form.
1291;;;
1292(defcommand "End of Defun" (p)
1293 "Move the point to the end of a top-level form, collapsing the selection.
1294 With an argument, skips the next p top-level forms."
1295 "Move the point to the end of a top-level form, collapsing the selection."
1296 (let ((point (current-point-collapsing-selection))
1297 (count (or p 1)))
1298 (pre-command-parse-check point)
1299 (if (minusp count)
1300 (beginning-of-defun-command (- count))
1301 (with-mark ((m point)
1302 (dummy point))
1303 (cond ((not (mark-top-level-form m dummy))
1304 (editor-error "No current or next top level form."))
1305 (t
1306 (unless (top-level-offset m (1- count))
1307 (editor-error "Not enough top level forms."))
1308 ;; We might be one unparsed for away.
1309 (pre-command-parse-check m)
1310 (unless (form-offset m 1)
1311 (editor-error "Not enough top level forms."))
1312 (when (blank-after-p m) (line-offset m 1 0))
1313 (move-mark point m)))))))
1314
1315(defcommand "Select to End of Defun" (p)
1316 "Move the point to the end of a top-level form, extending the selection.
1317 With an argument, skips the next p top-level forms."
1318 "Move the point to the end of a top-level form, extending the selection."
1319 (let ((point (current-point-extending-selection))
1320 (count (or p 1)))
1321 (pre-command-parse-check point)
1322 (if (minusp count)
1323 (beginning-of-defun-command (- count))
1324 (with-mark ((m point)
1325 (dummy point))
1326 (cond ((not (mark-top-level-form m dummy))
1327 (editor-error "No current or next top level form."))
1328 (t
1329 (unless (top-level-offset m (1- count))
1330 (editor-error "Not enough top level forms."))
1331 ;; We might be one unparsed for away.
1332 (pre-command-parse-check m)
1333 (unless (form-offset m 1)
1334 (editor-error "Not enough top level forms."))
1335 (when (blank-after-p m) (line-offset m 1 0))
1336 (move-mark point m)))))))
1337
1338(defcommand "Forward List" (p)
1339 "Skip over the next Lisp list, collapsing the selection.
1340 With argument, skips the next p lists."
1341 "Skip over the next Lisp list, collapsing the selection."
1342 (let ((point (current-point-collapsing-selection))
1343 (count (or p 1)))
1344 (pre-command-parse-check point)
1345 (unless (list-offset point count) (editor-error))))
1346
1347(defcommand "Select Forward List" (p)
1348 "Skip over the next Lisp list, extending the selection.
1349 With argument, skips the next p lists."
1350 "Skip over the next Lisp list, extending the selection."
1351 (let ((point (current-point-extending-selection))
1352 (count (or p 1)))
1353 (pre-command-parse-check point)
1354 (unless (list-offset point count) (editor-error))))
1355
1356(defcommand "Backward List" (p)
1357 "Skip over the previous Lisp list, collapsing the selection.
1358 With argument, skips the previous p lists."
1359 "Skip over the previous Lisp list, collapsing the selection."
1360 (let ((point (current-point-collapsing-selection))
1361 (count (- (or p 1))))
1362 (pre-command-parse-check point)
1363 (unless (list-offset point count) (editor-error))))
1364
1365(defcommand "Select Backward List" (p)
1366 "Skip over the previous Lisp list, extending the selection.
1367 With argument, skips the previous p lists."
1368 "Skip over the previous Lisp list, extending the selection."
1369 (let ((point (current-point-extending-selection))
1370 (count (- (or p 1))))
1371 (pre-command-parse-check point)
1372 (unless (list-offset point count) (editor-error))))
1373
1374(defcommand "Forward Form" (p)
1375 "Skip over the next Form, collapsing the selection.
1376 With argument, skips the next p Forms."
1377 "Skip over the next Form, collapsing the selection."
1378 (let ((point (current-point-collapsing-selection))
1379 (count (or p 1)))
1380 (pre-command-parse-check point)
1381 (unless (form-offset point count) (editor-error))))
1382
1383(defcommand "Select Forward Form" (p)
1384 "Skip over the next Form, extending the selection.
1385 With argument, skips the next p Forms."
1386 "Skip over the next Form, extending the selection."
1387 (let ((point (current-point-extending-selection))
1388 (count (or p 1)))
1389 (pre-command-parse-check point)
1390 (unless (form-offset point count) (editor-error))))
1391
1392(defcommand "Backward Form" (p)
1393 "Skip over the previous Form, collapsing the selection.
1394 With argument, skips the previous p Forms."
1395 "Skip over the previous Form, collaspsing the selection."
1396 (let ((point (current-point-collapsing-selection))
1397 (count (- (or p 1))))
1398 (pre-command-parse-check point)
1399 (unless (form-offset point count) (editor-error))))
1400
1401(defcommand "Select Backward Form" (p)
1402 "Skip over the previous Form, extending the selection.
1403 With argument, skips the previous p Forms."
1404 "Skip over the previous Form, extending the selection."
1405 (let ((point (current-point-extending-selection))
1406 (count (- (or p 1))))
1407 (pre-command-parse-check point)
1408 (unless (form-offset point count) (editor-error))))
1409
1410(defcommand "Mark Form" (p)
1411 "Set the mark at the end of the next Form.
1412 With a positive argument, set the mark after the following p
1413 Forms. With a negative argument, set the mark before
1414 the preceding -p Forms."
1415 "Set the mark at the end of the next Form."
1416 (with-mark ((m (current-point)))
1417 (pre-command-parse-check m)
1418 (let ((count (or p 1))
1419 (mark (push-new-buffer-mark m t)))
1420 (if (form-offset m count)
1421 (move-mark mark m)
1422 (editor-error)))))
1423
1424(defcommand "Mark Defun" (p)
1425 "Puts the region around the next or containing top-level form.
1426 The point is left before the form and the mark is placed immediately
1427 after it."
1428 "Puts the region around the next or containing top-level form."
1429 (declare (ignore p))
1430 (let ((point (current-point)))
1431 (pre-command-parse-check point)
1432 (with-mark ((start point)
1433 (end point))
1434 (cond ((not (mark-top-level-form start end))
1435 (editor-error "No current or next top level form."))
1436 (t
1437 (move-mark point start)
1438 (move-mark (push-new-buffer-mark point t) end))))))
1439
1440(defcommand "Forward Kill Form" (p)
1441 "Kill the next Form.
1442 With a positive argument, kills the next p Forms.
1443 Kills backward with a negative argument."
1444 "Kill the next Form."
1445 (with-mark ((m1 (current-point))
1446 (m2 (current-point)))
1447 (pre-command-parse-check m1)
1448 (let ((count (or p 1)))
1449 (unless (form-offset m1 count) (editor-error))
1450 (if (minusp count)
1451 (kill-region (region m1 m2) :kill-backward)
1452 (kill-region (region m2 m1) :kill-forward)))))
1453
1454(defcommand "Backward Kill Form" (p)
1455 "Kill the previous Form.
1456 With a positive argument, kills the previous p Forms.
1457 Kills forward with a negative argument."
1458 "Kill the previous Form."
1459 (forward-kill-form-command (- (or p 1))))
1460
1461(defcommand "Extract Form" (p)
1462 "Replace the current containing list with the next form. The entire affected
1463 area is pushed onto the kill ring. If an argument is supplied, that many
1464 upward levels of list nesting is replaced by the next form."
1465 "Replace the current containing list with the next form. The entire affected
1466 area is pushed onto the kill ring. If an argument is supplied, that many
1467 upward levels of list nesting is replaced by the next form."
1468 (let ((point (current-point)))
1469 (pre-command-parse-check point)
1470 (with-mark ((form-start point :right-inserting)
1471 (form-end point))
1472 (unless (form-offset form-end 1) (editor-error))
1473 (form-offset (move-mark form-start form-end) -1)
1474 (with-mark ((containing-start form-start :left-inserting)
1475 (containing-end form-end :left-inserting))
1476 (dotimes (i (or p 1))
1477 (unless (and (forward-up-list containing-end)
1478 (backward-up-list containing-start))
1479 (editor-error)))
1480 (let ((r (copy-region (region form-start form-end))))
1481 (ring-push (delete-and-save-region
1482 (region containing-start containing-end))
1483 *kill-ring*)
1484 (ninsert-region point r)
1485 (move-mark point form-start))))))
1486
1487(defcommand "Extract List" (p)
1488 "Extract the current list.
1489 The current list replaces the surrounding list. The entire affected
1490 area is pushed on the kill-ring. With prefix argument, remove that
1491 many surrounding lists."
1492 "Replace the P containing lists with the current one."
1493 (let ((point (current-point)))
1494 (pre-command-parse-check point)
1495 (with-mark ((lstart point :right-inserting)
1496 (lend point))
1497 (if (eq (character-attribute :lisp-syntax (next-character lstart))
1498 :open-paren)
1499 (mark-after lend)
1500 (unless (backward-up-list lstart) (editor-error)))
1501 (unless (forward-up-list lend) (editor-error))
1502 (with-mark ((rstart lstart)
1503 (rend lend))
1504 (dotimes (i (or p 1))
1505 (unless (and (forward-up-list rend) (backward-up-list rstart))
1506 (editor-error)))
1507 (let ((r (copy-region (region lstart lend))))
1508 (ring-push (delete-and-save-region (region rstart rend))
1509 *kill-ring*)
1510 (ninsert-region point r)
1511 (move-mark point lstart))))))
1512
1513(defcommand "Transpose Forms" (p)
1514 "Transpose Forms immediately preceding and following the point.
1515 With a zero argument, tranposes the Forms at the point and the mark.
1516 With a positive argument, transposes the Form preceding the point
1517 with the p-th one following it. With a negative argument, transposes the
1518 Form following the point with the p-th one preceding it."
1519 "Transpose Forms immediately preceding and following the point."
1520 (let ((point (current-point))
1521 (count (or p 1)))
1522 (pre-command-parse-check point)
1523 (if (zerop count)
1524 (let ((mark (current-mark)))
1525 (with-mark ((s1 mark :left-inserting)
1526 (s2 point :left-inserting))
1527 (scan-char s1 :whitespace nil)
1528 (scan-char s2 :whitespace nil)
1529 (with-mark ((e1 s1 :right-inserting)
1530 (e2 s2 :right-inserting))
1531 (unless (form-offset e1 1) (editor-error))
1532 (unless (form-offset e2 1) (editor-error))
1533 (ninsert-region s1 (delete-and-save-region (region s2 e2)))
1534 (ninsert-region s2 (delete-and-save-region (region s1 e1))))))
1535 (let ((fcount (if (plusp count) count 1))
1536 (bcount (if (plusp count) 1 count)))
1537 (with-mark ((s1 point :left-inserting)
1538 (e2 point :right-inserting))
1539 (dotimes (i bcount)
1540 (unless (form-offset s1 -1) (editor-error)))
1541 (dotimes (i fcount)
1542 (unless (form-offset e2 1) (editor-error)))
1543 (with-mark ((e1 s1 :right-inserting)
1544 (s2 e2 :left-inserting))
1545 (unless (form-offset e1 1) (editor-error))
1546 (unless (form-offset s2 -1) (editor-error))
1547 (ninsert-region s1 (delete-and-save-region (region s2 e2)))
1548 (ninsert-region s2 (delete-and-save-region (region s1 e1)))
1549 (move-mark point s2)))))))
1550
1551
1552(defcommand "Insert ()" (count)
1553 "Insert a pair of parentheses (). With positive argument, puts
1554 parentheses around the next COUNT Forms, or previous COUNT forms, if
1555 COUNT is negative. The point is positioned after the open parenthesis."
1556 "Insert a pair of parentheses ()."
1557 ;; TODO Form navigation is broken, so this is broken too -- it is
1558 ;; possible to put parens around more forms than there are in current
1559 ;; expression. It works by moving past as many forms as there is, and
1560 ;; then each delimiting paren also counts as a form.
1561 (let ((point (current-point)))
1562 (pre-command-parse-check point)
1563 (cond (count
1564 (when (minusp count)
1565 (form-offset point count)
1566 (setq count (- count)))
1567 (insert-character point #\()
1568 (with-mark ((m point))
1569 (unless (form-offset m count)
1570 (editor-error "Could not find that many forms."))
1571 (insert-character m #\))))
1572 ;; The simple case with no prefix argument
1573 (t
1574 (insert-character point #\()
1575 (insert-character point #\))
1576 (mark-before point)))))
1577
1578
1579(defcommand "Move Over )" (p)
1580 "Move past the next close parenthesis, and start a new line. Any
1581 indentation preceding the preceding the parenthesis is deleted, and the
1582 new line is indented. If there is only whitespace preceding the close
1583 paren, the paren is moved to the end of the previous line. With prefix
1584 argument, this command moves past next closing paren and inserts space."
1585 "Move past the next close parenthesis, and start a new line."
1586 ;; TODO This is still not complete, because SCAN-CHAR finds the next
1587 ;; close-paren, but we need to find the next paren that closes current
1588 ;; expression. This will have to be updated when form navigation is
1589 ;; fixed.
1590 (let ((point (current-point)))
1591 (pre-command-parse-check point)
1592 (with-mark ((m point :right-inserting))
1593 (cond ((scan-char m :lisp-syntax :close-paren)
1594 (cond ((same-line-p point m)
1595 (delete-horizontal-space m))
1596 (t
1597 (move-mark point m)
1598 (reverse-find-attribute point :whitespace #'zerop)
1599 (delete-region (region point m))))
1600 (cond ((not p)
1601 ;; Move to the previous line if current is empty
1602 (when (zerop (mark-charpos m))
1603 (delete-characters m -1))
1604 (mark-after m)
1605 (move-mark point m)
1606 (indent-new-line-command 1))
1607 (t
1608 (mark-after m)
1609 (move-mark point m)
1610 (insert-character m #\space))))
1611 (t
1612 (editor-error "Could not find closing paren."))))))
1613
1614
1615(defcommand "Forward Up List" (p)
1616 "Move forward past a one containing )."
1617 "Move forward past a one containing )."
1618 (let ((point (current-point-collapsing-selection))
1619 (count (or p 1)))
1620 (pre-command-parse-check point)
1621 (if (minusp count)
1622 (backward-up-list-command (- count))
1623 (with-mark ((m point))
1624 (dotimes (i count (move-mark point m))
1625 (unless (forward-up-list m) (editor-error)))))))
1626
1627
1628(defcommand "Backward Up List" (p)
1629 "Move backward past a one containing (."
1630 "Move backward past a one containing (."
1631 (let ((point (current-point-collapsing-selection))
1632 (count (or p 1)))
1633 (pre-command-parse-check point)
1634 (if (minusp count)
1635 (forward-up-list-command (- count))
1636 (with-mark ((m point))
1637 (dotimes (i count (move-mark point m))
1638 (unless (backward-up-list m) (editor-error)))))))
1639
1640
1641(defcommand "Down List" (p)
1642 "Move down a level in list structure. With positive argument, moves down
1643 p levels. With negative argument, moves down backward, but only one
1644 level."
1645 "Move down a level in list structure."
1646 (let ((point (current-point-collapsing-selection))
1647 (count (or p 1)))
1648 (pre-command-parse-check point)
1649 (with-mark ((m point))
1650 (cond ((plusp count)
1651 (loop repeat count
1652 do (unless (and (scan-char m :lisp-syntax :open-paren)
1653 (mark-after m))
1654 (editor-error))))
1655 (t
1656 (unless (and (rev-scan-char m :lisp-syntax :close-paren)
1657 (mark-before m))
1658 (editor-error))))
1659 (move-mark point m))))
1660
1661
1662
1663
1664;;;; Filling Lisp comments, strings, and indented text.
1665
1666(defhvar "Fill Lisp Comment Paragraph Confirm"
1667 "This determines whether \"Fill Lisp Comment Paragraph\" will prompt for
1668 confirmation to fill contiguous lines with the same initial whitespace when
1669 it is invoked outside of a comment or string."
1670 :value t)
1671
1672(defcommand "Fill Lisp Comment Paragraph" (p)
1673 "This fills a flushleft or indented Lisp comment.
1674 This also fills Lisp string literals using the proper indentation as a
1675 filling prefix. When invoked outside of a comment or string, this tries
1676 to fill all contiguous lines beginning with the same initial, non-empty
1677 blankspace. When filling a comment, the current line is used to determine a
1678 fill prefix by taking all the initial whitespace on the line, the semicolons,
1679 and any whitespace following the semicolons."
1680 "Fills a flushleft or indented Lisp comment."
1681 (declare (ignore p))
1682 (let ((point (current-point)))
1683 (pre-command-parse-check point)
1684 (with-mark ((start point)
1685 (end point)
1686 (m point))
1687 (let ((commentp (fill-lisp-comment-paragraph-prefix start end)))
1688 (cond (commentp
1689 (fill-lisp-comment-or-indented-text start end))
1690 ((and (not (valid-spot m nil))
1691 (form-offset m -1)
1692 (eq (character-attribute :lisp-syntax (next-character m))
1693 :string-quote))
1694 (fill-lisp-string m))
1695 ((or (not (value fill-lisp-comment-paragraph-confirm))
1696 (prompt-for-y-or-n
1697 :prompt '("Not in a comment or string. Fill contiguous ~
1698 lines with the same initial whitespace? ")))
1699 (fill-lisp-comment-or-indented-text start end)))))))
1700
1701;;; FILL-LISP-STRING -- Internal.
1702;;;
1703;;; This fills the Lisp string containing mark as if it had been entered using
1704;;; Hemlock's Lisp string indentation, "Indent Function" for "Lisp" mode. This
1705;;; assumes the area around mark has already been PRE-COMMAND-PARSE-CHECK'ed,
1706;;; and it ensures the string ends before doing any filling. This function
1707;;; is undo'able.
1708;;;
1709(defun fill-lisp-string (mark)
1710 (with-mark ((end mark))
1711 (unless (form-offset end 1)
1712 (editor-error "Attempted to fill Lisp string, but it doesn't end?"))
1713 (let* ((mark (copy-mark mark :left-inserting))
1714 (end (copy-mark end :left-inserting))
1715 (string-region (region mark end))
1716 (undo-region (copy-region string-region))
1717 (hack (make-empty-region)))
1718 ;; Generate prefix.
1719 (indent-to-column (region-end hack) (1+ (mark-column mark)))
1720 ;; Skip opening double quote and fill string starting on its own line.
1721 (mark-after mark)
1722 (insert-character mark #\newline)
1723 (line-start mark)
1724 (setf (mark-kind mark) :right-inserting)
1725 (fill-region string-region (region-to-string hack))
1726 ;; Clean up inserted prefix on first line, delete inserted newline, and
1727 ;; move before the double quote for undo.
1728 (with-mark ((text mark :left-inserting))
1729 (find-attribute text :whitespace #'zerop)
1730 (delete-region (region mark text)))
1731 (delete-characters mark -1)
1732 (mark-before mark)
1733 ;; Save undo.
1734 (make-region-undo :twiddle "Fill Lisp Comment Paragraph"
1735 string-region undo-region))))
1736
1737;;; FILL-LISP-COMMENT-OR-INDENTED-TEXT -- Internal.
1738;;;
1739;;; This fills all contiguous lines around start and end containing fill prefix
1740;;; designated by the region between start and end. These marks can only be
1741;;; equal when there is no comment and no initial whitespace. This is a bad
1742;;; situation since this function in that situation would fill the entire
1743;;; buffer into one paragraph. This function is undo'able.
1744;;;
1745(defun fill-lisp-comment-or-indented-text (start end)
1746 (when (mark= start end)
1747 (editor-error "This command only fills Lisp comments, strings, or ~
1748 indented text, but this line is flushleft."))
1749 ;;
1750 ;; Find comment block.
1751 (let* ((prefix (region-to-string (region start end)))
1752 (length (length prefix)))
1753 (declare (simple-string prefix))
1754 (flet ((frob (mark direction)
1755 (loop
1756 (let* ((line (line-string (mark-line mark)))
1757 (line-len (length line)))
1758 (declare (simple-string line))
1759 (unless (string= line prefix :end1 (min line-len length))
1760 (when (= direction -1)
1761 (unless (same-line-p mark end) (line-offset mark 1 0)))
1762 (return)))
1763 (unless (line-offset mark direction 0)
1764 (when (= direction 1) (line-end mark))
1765 (return)))))
1766 (frob start -1)
1767 (frob end 1))
1768 ;;
1769 ;; Do it undoable.
1770 (let* ((start1 (copy-mark start :right-inserting))
1771 (end2 (copy-mark end :left-inserting))
1772 (region (region start1 end2))
1773 (undo-region (copy-region region)))
1774 (fill-region region prefix)
1775 (make-region-undo :twiddle "Fill Lisp Comment Paragraph"
1776 region undo-region))))
1777
1778;;; FILL-LISP-COMMENT-PARAGRAPH-PREFIX -- Internal.
1779;;;
1780;;; This sets start and end around the prefix to be used for filling. We
1781;;; assume we are dealing with a comment. If there is no ";", then we try to
1782;;; find some initial whitespace. If there is a ";", we make sure the line is
1783;;; blank before it to eliminate ";"'s in the middle of a line of text.
1784;;; Finally, if we really have a comment instead of some indented text, we skip
1785;;; the ";"'s and any immediately following whitespace. We allow initial
1786;;; whitespace, so we can fill strings with the same command.
1787;;;
1788(defun fill-lisp-comment-paragraph-prefix (start end)
1789 (line-start start)
1790 (let ((commentp t)) ; Assumes there's a comment.
1791 (unless (to-line-comment (line-start end) ";")
1792 (find-attribute end :whitespace #'zerop)
1793 #|(when (start-line-p end)
1794 (editor-error "No comment on line, and no initial whitespace."))|#
1795 (setf commentp nil))
1796 (when commentp
1797 (unless (blank-before-p end)
1798 (find-attribute (line-start end) :whitespace #'zerop)
1799 #|(when (start-line-p end)
1800 (editor-error "Semicolon preceded by unindented text."))|#
1801 (setf commentp nil)))
1802 (when commentp
1803 (find-attribute end :lisp-syntax #'(lambda (x) (not (eq x :comment))))
1804 (find-attribute end :whitespace #'zerop))
1805 commentp))
1806
1807
1808
1809
1810;;;; "Lisp" mode.
1811
1812(defcommand "LISP Mode" (p)
1813 "Put current buffer in LISP mode."
1814 "Put current buffer in LISP mode."
1815 (declare (ignore p))
1816 (setf (buffer-major-mode (current-buffer)) "LISP"))
1817
1818
1819(defmode "Lisp" :major-p t :setup-function 'setup-lisp-mode)
1820
1821
1822(defun buffer-first-in-package-form (buffer)
1823 "Returns the package name referenced in the first apparent IN-PACKAGE
1824 form in buffer, or NIL if it can't find an IN-PACKAGE."
1825 (let* ((pattern (new-search-pattern :string-insensitive :forward "in-package" nil))
1826 (mark (copy-mark (buffer-start-mark buffer))))
1827 (with-mark ((start mark)
1828 (end mark))
1829 (loop
1830 (unless (find-pattern mark pattern)
1831 (return))
1832 (pre-command-parse-check mark)
1833 (when (valid-spot mark t)
1834 (move-mark end mark)
1835 (when (form-offset end 1)
1836 (move-mark start end)
1837 (when (backward-up-list start)
1838 (when (scan-char start :lisp-syntax :constituent)
1839 (let* ((s (nstring-upcase (region-to-string (region start end))))
1840 (*package* (find-package "CL-USER")))
1841 (unless (eq (ignore-errors (values (read-from-string s)))
1842 'in-package)
1843 (return)))
1844 (unless (form-offset end 1) (return))
1845 (move-mark start end)
1846 (form-offset start -1)
1847 (let* ((pkgname (ignore-errors (values (read-from-string (region-to-string (region start end)))))))
1848 (return
1849 (if pkgname
1850 (values (ignore-errors (string pkgname))))))))))))))
1851
1852(defparameter *previous-in-package-search-pattern*
1853 (new-search-pattern :string-insensitive :backward "in-package" nil))
1854
1855(defun package-at-mark (start-mark)
1856 (let* ((pattern *previous-in-package-search-pattern*)
1857 (mark (copy-mark start-mark :temporary)))
1858 (with-mark ((start mark)
1859 (end mark)
1860 (list-end mark))
1861 (loop
1862 (unless (find-pattern mark pattern)
1863 (return))
1864 (pre-command-parse-check mark)
1865 (when (valid-spot mark t)
1866 (move-mark end mark)
1867 (when (form-offset end 1)
1868 (move-mark start end)
1869 (when (backward-up-list start)
1870 (move-mark list-end start)
1871 (unless (and (list-offset list-end 1)
1872 (mark<= list-end start-mark))
1873 (return))
1874 (when (scan-char start :lisp-syntax :constituent)
1875 (unless (or (mark= mark start)
1876 (let* ((s (nstring-upcase (region-to-string (region start end))))
1877 (*package* (find-package "CL-USER")))
1878 (eq (ignore-errors (values (read-from-string s)))
1879 'in-package)))
1880 (return))
1881 (unless (form-offset end 1) (format t "~& worse") (return 4))
1882 (move-mark start end)
1883 (form-offset start -1)
1884 (return
1885 (if (eql (next-character start) #\")
1886 (progn
1887 (character-offset start 1)
1888 (character-offset end -1)
1889 (region-to-string (region start end)))
1890 (let* ((pkgname (ignore-errors (values (read-from-string (region-to-string (region start end)))))))
1891 (if pkgname
1892 (values (ignore-errors (string pkgname)))))))))))))))
1893
1894(defun ensure-buffer-package (buffer)
1895 (or (variable-value 'current-package :buffer buffer)
1896 (setf (variable-value 'current-package :buffer buffer)
1897 (buffer-first-in-package-form buffer))))
1898
1899(defun buffer-package (buffer)
1900 (when (hemlock-bound-p 'current-package :buffer buffer)
1901 (let ((package-name (variable-value 'current-package :buffer buffer)))
1902 (find-package package-name))))
1903
1904(defun setup-lisp-mode (buffer)
1905 (unless (hemlock-bound-p 'current-package :buffer buffer)
1906 (defhvar "Current Package"
1907 "The package used for evaluation of Lisp in this buffer."
1908 :buffer buffer
1909 :value "CL-USER"
1910 :hooks (list 'package-name-change-hook))))
1911
1912
1913
1914
1915
1916
1917;;;; Some mode variables to coordinate with other stuff.
1918
1919(defhvar "Auto Fill Space Indent"
1920 "When non-nil, uses \"Indent New Comment Line\" to break lines instead of
1921 \"New Line\"."
1922 :mode "Lisp" :value t)
1923
1924(defhvar "Comment Start"
1925 "String that indicates the start of a comment."
1926 :mode "Lisp" :value ";")
1927
1928(defhvar "Comment Begin"
1929 "String that is inserted to begin a comment."
1930 :mode "Lisp" :value "; ")
1931
1932(defhvar "Indent Function"
1933 "Indentation function which is invoked by \"Indent\" command.
1934 It must take one argument that is the prefix argument."
1935 :value 'indent-for-lisp
1936 :mode "Lisp")
1937
1938(defun string-to-arglist (string buffer &optional quiet-if-unknown)
1939 (multiple-value-bind (name error)
1940 (let* ((*package* (or
1941 (find-package
1942 (variable-value 'current-package :buffer buffer))
1943 *package*)))
1944 (ignore-errors (values (read-from-string string))))
1945 (unless error
1946 (when (typep name 'symbol)
1947 (multiple-value-bind (arglist win)
1948 (ccl::arglist-string name)
1949 (if (or win (not quiet-if-unknown))
1950 (format nil "~S : ~A" name (if win (or arglist "()") "(unknown)"))))))))
1951
1952(defcommand "Current Function Arglist" (p)
1953 "Show arglist of function whose name precedes point."
1954 "Show arglist of function whose name precedes point."
1955 (declare (ignore p))
1956 (let ((point (current-point)))
1957 (pre-command-parse-check point)
1958 (with-mark ((mark1 point)
1959 (mark2 point))
1960 (when (backward-up-list mark1)
1961 (when (form-offset (move-mark mark2 (mark-after mark1)) 1)
1962 (let* ((fun-name (region-to-string (region mark1 mark2)))
1963 (arglist-string (string-to-arglist fun-name (current-buffer))))
1964 (when arglist-string
1965 (message "~a" arglist-string))))))))
1966
1967(defcommand "Arglist On Space" (p)
1968 "Insert a space, then show the current function's arglist."
1969 "Insert a space, then show the current function's arglist."
1970 (declare (ignore p))
1971 (let ((point (current-point)))
1972 (insert-character point #\Space)
1973 (pre-command-parse-check point)
1974 (with-mark ((mark1 point)
1975 (mark2 point))
1976 (when (backward-up-list mark1)
1977 (when (form-offset (move-mark mark2 (mark-after mark1)) 1)
1978 (with-mark ((mark3 mark2))
1979 (do* ()
1980 ((mark= mark3 point)
1981 (let* ((fun-name (region-to-string (region mark1 mark2)))
1982 (arglist-string
1983 (string-to-arglist fun-name (current-buffer) t)))
1984 (when arglist-string
1985 (message "~a" arglist-string))))
1986 (if (ccl::whitespacep (next-character mark3))
1987 (mark-after mark3)
1988 (return nil)))))))))
1989
1990(hi:defcommand "Show Callers" (p)
1991 "Display a scrolling list of the callers of the symbol at point.
1992 Double-click a row to go to the caller's definition."
1993 (declare (ignore p))
1994 (with-mark ((mark1 (current-point))
1995 (mark2 (current-point)))
1996 (mark-symbol mark1 mark2)
1997 (with-input-from-region (s (region mark1 mark2))
1998 (let* ((symbol (read s)))
1999 (hemlock-ext:open-sequence-dialog
2000 :title (format nil "Callers of ~a" symbol)
2001 :sequence (ccl::callers symbol)
2002 :action #'edit-definition)))))
2003
2004;; Note this isn't necessarily called from hemlock, e.g. it might be called by cl:ed,
2005;; from any thread, or it might be called from a sequence dialog, etc.
2006(defun edit-definition (name)
2007 (flet ((get-source-alist (name)
2008 (mapcar #'(lambda (item) (cons name item))
2009 (ccl::get-source-files-with-types&classes name))))
2010 (let* ((info (get-source-alist name)))
2011 (when (null info)
2012 (let* ((seen (list name))
2013 (found ())
2014 (pname (symbol-name name)))
2015 (dolist (pkg (list-all-packages))
2016 (let ((sym (find-symbol pname pkg)))
2017 (when (and sym (not (member sym seen)))
2018 (let ((new (get-source-alist sym)))
2019 (when new
2020 (setq info (nconc new info))
2021 (push sym found)))
2022 (push sym seen))))
2023 (when found
2024 ;; Unfortunately, this puts the message in the wrong buffer (would be better in the destination buffer).
2025 (loud-message "No definitions for ~s, using ~s instead"
2026 name (if (cdr found) found (car found))))))
2027 (if info
2028 (if (cdr info)
2029 (hemlock-ext:open-sequence-dialog
2030 :title (format nil "Definitions of ~s" name)
2031 :sequence info
2032 :action #'(lambda (item) (hemlock-ext:edit-single-definition (car item) (cdr item)))
2033 :printer #'(lambda (item stream) (prin1 (cadr item) stream)))
2034 (hemlock-ext:edit-single-definition (caar info) (cdar info)))
2035 (editor-error "No known definitions for ~s" name)))))
2036
2037#||
2038(defcommand "Set Package Name" (p)
2039 (variable-value 'current-package :buffer buffer)
2040||#
Note: See TracBrowser for help on using the repository browser.