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

source: branches/acode-rewrite/source/cocoa-ide/hemlock/src/lispmode.lisp

Last change on this file was 16082, checked in by Gary Byers, 11 years ago

Merge trunk changes into this branch. Expect some things to explode.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 87.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 slots that tell whether or not
45;;; a line's begining and/or ending are quoted, and if so, how.
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 where :lisp-syntax attributes are ignored. 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) ; nil or quote char attribute or comment nesting depth
59 (ending-quoted nil) ; nil or quote char attribute or comment nesting depth
60 (ranges-to-ignore 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 :prefix-dispatch
113 :char-quote :symbol-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 (eq (lisp-info-begins-quoted line-info)
209 (let ((prev (and prev-line-info (lisp-info-ending-quoted prev-line-info))))
210 (and (not (eq prev :char-quote)) prev))))
211 (not (eql (line-signature test-line)
212 (lisp-info-signature-slot line-info))))
213
214 (move-to-position mark 0 test-line)
215
216 (unless line-info
217 (setf line-info (make-lisp-info))
218 (setf (getf (line-plist test-line) 'lisp-info) line-info))
219
220 (parse-lisp-line-info mark line-info prev-line-info))
221
222 (when (eq end-line test-line)
223 (return nil))
224
225 (setq prev-line-info line-info)
226
227 (setq test-line (line-next test-line)))))))
228
229
230
231;;;; Parse block finders.
232
233
234(defun start-of-parse-block (mark)
235 (buffer-start mark))
236
237(defun end-of-parse-block (mark)
238 (buffer-end mark))
239
240;;;
241;;; START-OF-SEARCH-LINE
242
243(defun start-of-search-line (line)
244 "Set LINE to the begining line of the block of text to parse."
245 (with-mark ((mark (mark line 0)))
246 (funcall (value 'Parse-Start-Function) mark)
247 (setq line (mark-line mark))))
248
249;;;
250;;; END-OF-SEACH-LINE
251
252(defun end-of-search-line (line)
253 "Set LINE to the ending line of the block of text to parse."
254 (with-mark ((mark (mark line 0)))
255 (funcall (value 'Parse-End-Function) mark)
256 (setq line (mark-line mark))))
257
258
259
260;;;; PARSE-LISP-LINE-INFO.
261
262;;; PARSE-LISP-LINE-INFO -- Internal.
263;;;
264;;; This parses through the line doing the following things:
265;;;
266;;; Counting/Setting the NET-OPEN-PARENS & NET-CLOSE-PARENS.
267;;;
268;;; Making all areas of the line that should be invalid (comments,
269;;; char-quotes, and the inside of strings) and such be in
270;;; RANGES-TO-IGNORE.
271;;;
272;;; Set BEGINS-QUOTED and ENDING-QUOTED
273;;;
274(defun parse-lisp-line-info (mark line-info prev-line-info)
275 "Parse line and set line information like NET-OPEN-PARENS, NET-CLOSE-PARENS,
276 RANGES-TO-INGORE, and ENDING-QUOTED."
277 (let ((net-open-parens 0)
278 (net-close-parens 0))
279 (declare (fixnum net-open-parens net-close-parens))
280
281 ;; Re-set the slots necessary
282
283 (setf (lisp-info-ranges-to-ignore line-info) nil)
284
285 (setf (lisp-info-ending-quoted line-info) nil)
286
287 ;; The only way the current line begins quoted is when there
288 ;; is a previous line and it's ending was quoted.
289
290 (setf (lisp-info-begins-quoted line-info)
291 (and prev-line-info
292 (let ((prev (lisp-info-ending-quoted prev-line-info)))
293 (and (not (eq prev :char-quote)) prev))))
294
295 (assert (eq (hi::mark-buffer mark) (current-buffer)))
296
297 (when (lisp-info-begins-quoted line-info)
298 (deal-with-quote (lisp-info-begins-quoted line-info) mark line-info))
299
300 (unless (lisp-info-ending-quoted line-info)
301 (loop
302
303 (unless (find-lisp-char mark)
304 (error "Expected at least a newline!"))
305 (case (character-attribute :lisp-syntax (next-character mark))
306
307 (:open-paren
308 (setq net-open-parens (1+ net-open-parens))
309 (mark-after mark))
310
311 (:close-paren
312 (if (zerop net-open-parens)
313 (setq net-close-parens (1+ net-close-parens))
314 (setq net-open-parens (1- net-open-parens)))
315 (mark-after mark))
316
317 (:newline
318 (setf (lisp-info-ending-quoted line-info) nil)
319 (return t))
320
321 (:comment
322 (push-range (cons (mark-charpos mark) (line-length (mark-line mark)))
323 line-info)
324 (setf (lisp-info-ending-quoted line-info) nil)
325 (return t))
326
327 (:char-quote
328 (mark-after mark)
329 (let* ((charpos (mark-charpos mark))
330 (nextpos (1+ charpos))
331 (linelen (line-length (mark-line mark))))
332 (when (< linelen nextpos)
333 (setf (lisp-info-ending-quoted line-info) :char-quote)
334 (return t))
335 (push-range (cons charpos nextpos) line-info)
336 (mark-after mark)))
337
338 (:prefix-dispatch
339 (mark-after mark)
340 (when (test-char (next-character mark) :lisp-syntax :symbol-quote)
341 (mark-after mark)
342 (unless (deal-with-quote 1 mark line-info (- (mark-charpos mark) 2))
343 (return t))))
344
345 (:symbol-quote
346 (mark-after mark)
347 (unless (deal-with-quote :symbol-quote mark line-info)
348 (return t)))
349
350 (:string-quote
351 (mark-after mark)
352 (unless (deal-with-quote :string-quote mark line-info)
353 (return t)))
354
355 (t (ERROR "character attribute of: ~s is ~s, at ~s"
356 (next-character mark)
357 (character-attribute :lisp-syntax (next-character mark))
358 mark)))))
359 (setf (lisp-info-net-open-parens line-info) net-open-parens)
360 (setf (lisp-info-net-close-parens line-info) net-close-parens)
361 (setf (lisp-info-ranges-to-ignore line-info)
362 (nreverse (lisp-info-ranges-to-ignore line-info)))
363 (setf (lisp-info-signature-slot line-info)
364 (line-signature (mark-line mark)))))
365
366
367
368
369;;;; String/symbol quote utilities.
370
371;;; VALID-QUOTE-P
372;;;
373(defmacro valid-quote-p (quote mark forwardp)
374 "Return T if the string-quote indicated by MARK is valid."
375 `(and (eq (character-attribute :lisp-syntax (direction-char ,mark ,forwardp)) ,quote)
376 (not (char-quoted-at-mark-p ,mark ,forwardp))))
377
378(defun char-quoted-at-mark-p (mark forwardp)
379 (unless forwardp
380 (unless (mark-before mark)
381 (return-from char-quoted-at-mark-p nil)))
382 (loop for count upfrom 0
383 do (unless (test-char (previous-character mark) :lisp-syntax :char-quote)
384 (character-offset mark count) ;; go back to where started
385 (unless forwardp
386 (mark-after mark))
387 (return (oddp count)))
388 do (mark-before mark)))
389
390;;;
391;;; FIND-VALID-QUOTE
392
393(defmacro find-valid-quote (quote mark &key forwardp (cease-at-eol nil))
394 "Expand to a form that will leave MARK before a valid string-quote character,
395 in either a forward or backward direction, according to FORWARDP. If
396 CEASE-AT-EOL is T then it will return nil if encountering the EOL before a
397 valid string-quote."
398 (let ((e-mark (gensym))
399 (pred (gensym)))
400 `(with-mark ((,e-mark ,mark))
401 (let ((,pred ,(if cease-at-eol
402 `#'(lambda (x) (or (eq x :newline) (eq x ,quote)))
403 `#'(lambda (x) (eq x ,quote)))))
404
405 (loop
406 (unless (,(if forwardp 'find-attribute 'reverse-find-attribute)
407 ,e-mark :lisp-syntax ,pred)
408 (return nil))
409
410 ,@(if cease-at-eol
411 `((when (test-char (direction-char ,e-mark ,forwardp) :lisp-syntax
412 :newline)
413 (return nil))))
414
415 (when (valid-quote-p ,quote ,e-mark ,forwardp)
416 (move-mark ,mark ,e-mark)
417 (return t))
418
419 (neighbor-mark ,e-mark ,forwardp))))))
420
421
422;;; DEAL-WITH-QUOTE
423;;;
424;;; Called when a quoted area is begun (i.e. parse hits a #\" or #\|). It checks for a
425;;; matching quote on the line that MARK points to, and puts the appropriate
426;;; area in the RANGES-TO-IGNORE slot and leaves MARK pointing after this area.
427;;; The "appropriate area" is from MARK to the end of the line or the matching
428;;; string-quote, whichever comes first.
429;;;
430
431(defun deal-with-quote (quote mark info-struct &optional (start (mark-charpos mark)))
432 "Alter the current line's info struct as necessary as due to encountering a
433 string or symbol quote character."
434 (if (fixnump quote) ;; nesting multi-line comments
435 (loop
436 (unless (and (scan-char mark :lisp-syntax (or :newline :symbol-quote))
437 (test-char (next-character mark) :lisp-syntax :symbol-quote))
438 (line-end mark)
439 (push-range (cons start (mark-charpos mark)) info-struct)
440 (setf (lisp-info-ending-quoted info-struct) quote)
441 (return nil))
442 (if (prog1 (test-char (previous-character mark) :lisp-syntax :prefix-dispatch) (mark-after mark))
443 (incf quote)
444 (when (test-char (next-character mark) :lisp-syntax :prefix-dispatch)
445 (mark-after mark)
446 (decf quote)
447 (when (<= quote 0)
448 (push-range (cons start (mark-charpos mark)) info-struct)
449 (setf (lisp-info-ending-quoted info-struct) nil)
450 (return mark)))))
451 (cond ((find-valid-quote quote mark :forwardp t :cease-at-eol t)
452 ;; If matching quote is on this line then mark the area between the
453 ;; first quote (MARK) and the matching quote as invalid by pushing
454 ;; its begining and ending into the IGNORE-RANGE.
455 (push-range (cons start (mark-charpos mark)) info-struct)
456 (mark-after mark))
457 ;; If the EOL has been hit before the matching quote then mark the
458 ;; area from MARK to the EOL as invalid.
459 (t
460 (line-end mark)
461 (push-range (cons start (mark-charpos mark)) info-struct)
462 ;; The Ending is marked as still being quoted.
463 (setf (lisp-info-ending-quoted info-struct) quote)
464 nil))))
465
466;;;; Character validity checking:
467
468;;; Find-Ignore-Region -- Internal
469;;;
470;;; If the character in the specified direction from Mark is in an ignore
471;;; region, then return the region and the line that the region is in as
472;;; values. If there is no ignore region, then return NIL and the Mark-Line.
473;;; If the line is not parsed, or there is no character (because of being at
474;;; the buffer beginning or end), then return both values NIL.
475;;;
476(defun find-ignore-region (mark forwardp)
477 (flet ((scan (line pos)
478 (declare (fixnum pos))
479 (let ((info (getf (line-plist line) 'lisp-info)))
480 (if info
481 (dolist (range (lisp-info-ranges-to-ignore info)
482 ;; if mark is at end of line, should check ending-quoted so can quote the newline...
483 (values nil line))
484 (let ((start (car range))
485 (end (cdr range)))
486 (declare (fixnum start end))
487 (when (and (>= pos start) (< pos end))
488 (return (values range line)))))
489 (values nil nil)))))
490 (let ((pos (mark-charpos mark))
491 (line (mark-line mark)))
492 (declare (fixnum pos))
493 (cond (forwardp (scan line pos))
494 ((> pos 0) (scan line (1- pos)))
495 (t
496 (let ((prev (line-previous line)))
497 (if prev
498 (scan prev (line-length prev))
499 (values nil nil))))))))
500
501
502;;; Valid-Spot -- Public
503;;;
504(defun valid-spot (mark forwardp)
505 "Return true if the character pointed to by Mark is not in a quoted context,
506 false otherwise. If Forwardp is true, we use the next character, otherwise
507 we use the previous."
508 (if (and (not forwardp)
509 (null (previous-character mark)))
510 t ;beginning of buffer always a valid spot
511 (multiple-value-bind (region line)
512 (find-ignore-region mark forwardp)
513 (and line (not region)))))
514
515;;; Scan-Direction-Valid -- Internal
516;;;
517;;; Like scan-direction, but only stop on valid characters.
518;;;
519(defmacro scan-direction-valid (mark forwardp &rest forms)
520 (let ((n-mark (gensym))
521 (n-line (gensym))
522 (n-region (gensym))
523 (n-won (gensym)))
524 `(let ((,n-mark ,mark) (,n-won nil))
525 (loop
526 (multiple-value-bind (,n-region ,n-line)
527 (find-ignore-region ,n-mark ,forwardp)
528 (unless ,n-line (return nil))
529 (if ,n-region
530 (move-to-position ,n-mark
531 ,(if forwardp
532 `(cdr ,n-region)
533 `(car ,n-region))
534 ,n-line)
535 (when ,n-won (return t)))
536 ;;
537 ;; Peculiar condition when a quoting character terminates a line.
538 ;; The ignore region is off the end of the line causing %FORM-OFFSET
539 ;; to infinitely loop.
540 (when (> (mark-charpos ,n-mark) (line-length ,n-line))
541 #+gz (break "This shouldn't happen any more")
542 (line-offset ,n-mark 1 0))
543 (unless (scan-direction ,n-mark ,forwardp ,@forms)
544 (return nil))
545 (setq ,n-won t))))))
546
547
548
549;;;; List offseting.
550
551;;; %LIST-OFFSET allows for BACKWARD-LIST and FORWARD-LIST to be built
552;;; with the same existing structure, with the altering of one variable.
553;;; This one variable being FORWARDP.
554;;;
555(defmacro %list-offset (actual-mark forwardp &key (extra-parens 0) )
556 "Expand to code that will go forward one list either backward or forward,
557 according to the FORWARDP flag."
558 (let ((mark (gensym)))
559 `(with-mark ((,mark ,actual-mark))
560 (if (valid-spot ,mark ,forwardp)
561 (let ((paren-count ,extra-parens))
562 (declare (fixnum paren-count))
563 (loop
564 (unless (scan-direction-valid ,mark ,forwardp :lisp-syntax
565 (or :close-paren :open-paren :newline))
566 (return nil))
567 (let ((ch (direction-char ,mark ,forwardp)))
568 (unless ch (return nil))
569 (case (character-attribute :lisp-syntax ch)
570 (:close-paren
571 (decf paren-count)
572 ,(when forwardp
573 ;; When going forward, an unmatching close-paren means the
574 ;; end of list.
575 `(when (<= paren-count 0)
576 (neighbor-mark ,mark ,forwardp)
577 (move-mark ,actual-mark ,mark)
578 (return t))))
579 (:open-paren
580 (incf paren-count)
581 ,(unless forwardp ; Same as above only end of list
582 `(when (>= paren-count 0) ; is opening parens.
583 (neighbor-mark ,mark ,forwardp)
584 (move-mark ,actual-mark ,mark)
585 (return t))))
586
587 (:newline
588 ;; When a #\Newline is hit, then the matching paren must lie
589 ;; on some other line so drop down into the multiple line
590 ;; balancing function: QUEST-FOR-BALANCING-PAREN If no paren
591 ;; seen yet, keep going.
592 (cond ((zerop paren-count))
593 ((quest-for-balancing-paren ,mark paren-count ,forwardp)
594 (move-mark ,actual-mark ,mark)
595 (return t))
596 (t
597 (return nil))))))
598 (neighbor-mark ,mark ,forwardp)))
599 ;; We're inside a comment or a string. Try anyway.
600 (when ,(if forwardp
601 `(%forward-list-at-mark ,mark ,extra-parens t)
602 `(%backward-list-at-mark ,mark ,extra-parens t))
603 (move-mark ,actual-mark ,mark))))))
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;;;; Form offseting.
761
762;; Heuristic versions, for navigating inside comments, doesn't make use of line info
763
764(defun unparsed-form-offset (mark forwardp)
765 ;; TODO: if called in "invalid" spot, arrange to stay within bounds of current invalid region.
766 ;; For now, just stop at #||# boundaries, as first approximation.
767 (if forwardp
768 (forward-form mark t)
769 (backward-form mark t)))
770
771(defun forward-form (mark &optional in-comment-p)
772 ;; If in-comment-p is true, tries not to go past a |#.
773 (with-mark ((m mark))
774 (when (and (scan-char m :lisp-syntax (or :open-paren :close-paren :prefix-dispatch
775 :symbol-quote :string-quote :char-quote
776 :comment :constituent))
777 (%forward-form-at-mark m in-comment-p))
778 (move-mark mark m))))
779
780(defun backward-form (mark &optional in-comment-p)
781 ;; If in-comment-p is true, tries not to go past a #|.
782 (with-mark ((m mark))
783 (when (%backward-form-at-mark m in-comment-p)
784 (loop while (test-char (previous-character m) :lisp-syntax (or :prefix :prefix-dispatch)) do (mark-before m))
785 (move-mark mark m))))
786
787(defun %forward-form-at-mark (mark in-comment-p)
788 ;; Warning: moves mark even if returns nil (hence the % in name).
789 (case (character-attribute :lisp-syntax (next-character mark))
790 (:open-paren
791 (mark-after mark)
792 (%forward-list-at-mark mark 1))
793 (:close-paren
794 nil)
795 (:char-quote
796 (%forward-symbol-at-mark mark in-comment-p))
797 (:symbol-quote
798 (mark-after mark)
799 (unless (and in-comment-p (test-char (next-character mark) :lisp-syntax :prefix-dispatch))
800 (mark-before mark)
801 (%forward-symbol-at-mark mark in-comment-p)))
802 (:prefix-dispatch
803 (mark-after mark)
804 (case (character-attribute :lisp-syntax (next-character mark))
805 (:symbol-quote
806 (mark-after mark)
807 (%forward-nesting-comment-at-mark mark 1))
808 (:prefix
809 (mark-after mark)
810 (%forward-form-at-mark mark in-comment-p))
811 (t
812 (mark-before mark)
813 (%forward-symbol-at-mark mark in-comment-p))))
814 (:string-quote
815 (%forward-string-at-mark mark))
816 (:constituent
817 (%forward-symbol-at-mark mark in-comment-p))
818 (:comment
819 (%forward-comments-at-mark mark))
820 (t
821 (mark-after mark)
822 (%forward-form-at-mark mark in-comment-p))))
823
824(defun %backward-form-at-mark (mark in-comment-p)
825 ;; Warning: moves mark even if returns nil (hence the % in name).
826 (let* ((char (previous-character mark))
827 (attrib (character-attribute :lisp-syntax char)))
828 (when char
829 (mark-before mark)
830 (when (char-quoted-at-mark-p mark t)
831 (setq attrib :constituent))
832 (case attrib
833 (:open-paren
834 nil)
835 (:close-paren
836 (%backward-list-at-mark mark 1))
837 (:char-quote ;;; can only happen if starting right after an unquoted char-quote
838 (%backward-symbol-at-mark mark in-comment-p))
839 (:symbol-quote
840 (unless (and in-comment-p (test-char (previous-character mark) :lisp-syntax :prefix-dispatch))
841 (mark-after mark)
842 (%backward-symbol-at-mark mark in-comment-p)))
843 (:prefix-dispatch
844 (if (test-char (previous-character mark) :lisp-syntax :symbol-quote)
845 (progn
846 (mark-before mark)
847 (%backward-nesting-comment-at-mark mark 1))
848 (progn
849 (mark-after mark)
850 (%backward-symbol-at-mark mark in-comment-p))))
851 (:string-quote
852 (mark-after mark)
853 (%backward-string-at-mark mark))
854 (:constituent
855 (mark-after mark)
856 (%backward-symbol-at-mark mark in-comment-p))
857 (:prefix
858 (loop while (test-char (previous-character mark) :lisp-syntax :prefix) do (mark-before mark))
859 mark)
860 (:comment
861 (loop while (test-char (previous-character mark) :lisp-syntax :comment) do (mark-before mark))
862 mark)
863 ;; TODO: it would be nice to skip over ;; comments if starting outside one, i.e. if encounter a newline
864 ;; before a form starts.
865 (t (%backward-form-at-mark mark in-comment-p))))))
866
867(defun %forward-symbol-at-mark (mark in-comment-p)
868 ;; Warning: moves mark even if returns nil (hence the % in name).
869 (loop
870 (unless (scan-char mark :lisp-syntax (not (or :constituent :prefix-dispatch)))
871 (return (buffer-end mark)))
872 (case (character-attribute :lisp-syntax (next-character mark))
873 (:symbol-quote
874 (mark-after mark)
875 (when (and in-comment-p (test-char (next-character mark) :lisp-syntax :prefix-dispatch))
876 (return (mark-before mark)))
877 (unless (loop
878 (unless (scan-char mark :lisp-syntax (or :char-quote :symbol-quote))
879 (return nil))
880 (when (test-char (next-character mark) :lisp-syntax :symbol-quote)
881 (return t))
882 (unless (character-offset mark 2)
883 (return nil)))
884 (return nil))
885 (mark-after mark))
886 (:char-quote
887 (unless (character-offset mark 2)
888 (return nil)))
889 (t (return mark)))))
890
891(defun %backward-symbol-at-mark (mark in-comment-p)
892 (loop
893 (unless (rev-scan-char mark :lisp-syntax (not (or :constituent :prefix-dispatch :char-quote)))
894 (buffer-start mark)
895 (return mark))
896 (mark-before mark)
897 (if (char-quoted-at-mark-p mark t)
898 (mark-before mark)
899 (let* ((char (next-character mark)))
900 (case (character-attribute :lisp-syntax char)
901 (:symbol-quote
902 (when (and in-comment-p (test-char (previous-character mark) :lisp-syntax :prefix-dispatch))
903 (return (mark-after mark)))
904 (unless (loop
905 (unless (rev-scan-char mark :lisp-syntax :symbol-quote)
906 (return nil))
907 (mark-before mark)
908 (unless (char-quoted-at-mark-p mark t)
909 (return t))
910 (mark-before mark))
911 (return nil)))
912 (t (mark-after mark)
913 (return mark)))))))
914
915(defun %forward-nesting-comment-at-mark (mark nesting)
916 ;; Warning: moves mark even if returns nil (hence the % in name).
917 (loop
918 (unless (scan-char mark :lisp-syntax :symbol-quote)
919 (return nil))
920 (let ((prev (previous-character mark)))
921 (mark-after mark)
922 (cond ((test-char prev :lisp-syntax :prefix-dispatch)
923 (incf nesting))
924 ((test-char (next-character mark) :lisp-syntax :prefix-dispatch)
925 (mark-after mark)
926 (when (<= (decf nesting) 0)
927 (return mark)))))))
928
929(defun %backward-nesting-comment-at-mark (mark nesting)
930 ;; Warning: moves mark even if returns nil (hence the % in name).
931 (loop
932 (unless (rev-scan-char mark :lisp-syntax :symbol-quote)
933 (return nil))
934 (let ((next (next-character mark)))
935 (mark-before mark)
936 (cond ((test-char next :lisp-syntax :prefix-dispatch)
937 (incf nesting))
938 ((test-char (previous-character mark) :lisp-syntax :prefix-dispatch)
939 (mark-before mark)
940 (when (<= (decf nesting) 0)
941 (return mark)))))))
942
943
944(defun %scan-to-form (m forwardp)
945 (if forwardp
946 ;; Stop at :prefix-dispatch if it is not followed by :prefix. If it's followed by :prefix,
947 ;; assume it has the semantics of :prefix and skip it.
948 (loop while (scan-direction-valid m t :lisp-syntax
949 (or :open-paren :close-paren
950 :char-quote :string-quote :symbol-quote
951 :prefix-dispatch :constituent))
952 do (unless (and (test-char (next-character m) :lisp-syntax :prefix-dispatch)
953 (mark-after m))
954 (return t))
955 do (unless (test-char (next-character m) :lisp-syntax :prefix)
956 (mark-before m)
957 (return t)))
958 (scan-direction-valid m nil :lisp-syntax
959 (or :open-paren :close-paren
960 :char-quote :string-quote :symbol-quote
961 :prefix-dispatch :constituent))))
962
963;; %FORM-OFFSET
964
965(defmacro %form-offset (mark forwardp)
966 `(if (valid-spot ,mark ,forwardp)
967 (with-mark ((m ,mark))
968 (when (%scan-to-form m ,forwardp)
969 (ecase (character-attribute :lisp-syntax (direction-char m ,forwardp))
970 (:open-paren
971 (when ,(if forwardp `(list-offset m 1) `(mark-before m))
972 ,(unless forwardp
973 '(scan-direction m nil :lisp-syntax (not (or :prefix-dispatch :prefix))))
974 (move-mark ,mark m)
975 t))
976 (:close-paren
977 (when ,(if forwardp `(mark-after m) `(list-offset m -1))
978 ,(unless forwardp
979 '(scan-direction m nil :lisp-syntax (not (or :prefix-dispatch :prefix))))
980 (move-mark ,mark m)
981 t))
982 ((:constituent :char-quote :symbol-quote :prefix-dispatch)
983 ,(if forwardp
984 `(scan-direction-valid m t :lisp-syntax
985 (not (or :constituent :char-quote :symbol-quote :prefix-dispatch)))
986 `(scan-direction-valid m nil :lisp-syntax
987 (not (or :constituent :char-quote :symbol-quote :prefix-dispatch
988 :prefix))))
989 (move-mark ,mark m)
990 t)
991 (:string-quote
992 (neighbor-mark m ,forwardp)
993 (when (scan-direction-valid m ,forwardp :lisp-syntax
994 :string-quote)
995 (neighbor-mark m ,forwardp)
996 (move-mark ,mark m)
997 t)))))
998 ;; Inside a comment or a string. Switch to heuristic method.
999 (unparsed-form-offset ,mark ,forwardp)))
1000
1001(defun %forward-list-at-mark (mark nesting &optional in-comment-p)
1002 ;; Warning: moves mark even if returns nil (hence the % in name).
1003 (loop
1004 (unless (scan-char mark :lisp-syntax (or :open-paren :close-paren :prefix-dispatch
1005 :symbol-quote :string-quote :char-quote :comment))
1006 (return nil))
1007 (case (character-attribute :lisp-syntax (next-character mark))
1008 (:open-paren
1009 (mark-after mark)
1010 (incf nesting))
1011 (:close-paren
1012 (mark-after mark)
1013 (when (<= (decf nesting) 0)
1014 (return (and (eql nesting 0) mark))))
1015 (t
1016 (unless (%forward-form-at-mark mark in-comment-p)
1017 (return nil))))))
1018
1019(defun %backward-list-at-mark (mark nesting &optional in-comment-p)
1020 ;; Warning: moves mark even if returns nil (hence the % in name).
1021 (loop
1022 (unless (rev-scan-char mark :lisp-syntax (or :open-paren :close-paren :prefix-dispatch
1023 :symbol-quote :string-quote :comment))
1024 (return nil))
1025 (mark-before mark)
1026 (if (char-quoted-at-mark-p mark t)
1027 (mark-before mark)
1028 (case (character-attribute :lisp-syntax (next-character mark))
1029 (:close-paren
1030 (incf nesting))
1031 (:open-paren
1032 (when (<= (decf nesting) 0)
1033 (return mark)))
1034 (t
1035 ;(mark-after mark)
1036 (unless (%backward-form-at-mark mark in-comment-p)
1037 (return nil)))))))
1038
1039(defun %forward-string-at-mark (mark)
1040 ;; Warning: moves mark even if returns nil (hence the % in name).
1041 (mark-after mark)
1042 (loop
1043 (unless (scan-char mark :lisp-syntax (or :char-quote :string-quote))
1044 (return nil))
1045 (unless (test-char (next-character mark) :lisp-syntax :char-quote)
1046 (return (mark-after mark)))
1047 (character-offset mark 2)))
1048
1049
1050(defun %backward-string-at-mark (mark)
1051 ;; Warning: moves mark even if returns nil (hence the % in name).
1052 (mark-before mark)
1053 (loop
1054 (unless (rev-scan-char mark :lisp-syntax :string-quote)
1055 (return nil))
1056 (mark-before mark)
1057 (unless (char-quoted-at-mark-p mark t)
1058 (return mark))
1059 (mark-before mark)))
1060
1061(defun %forward-comments-at-mark (mark)
1062 ;; Warning: moves mark even if returns nil (hence the % in name).
1063 (with-mark ((m mark))
1064 (loop
1065 (line-end m)
1066 (mark-after m)
1067 (move-mark mark m)
1068 (unless (and (scan-char m :lisp-syntax (not :space))
1069 (test-char (next-character m) :lisp-syntax :comment))
1070 (return mark)))))
1071
1072(defun form-offset (mark offset)
1073 "Move mark offset number of forms, after if positive, before if negative.
1074 Mark is always moved. If there weren't enough forms, returns nil instead of
1075 mark."
1076 (if (plusp offset)
1077 (dotimes (i offset t)
1078 (unless (%form-offset mark t) (return nil)))
1079 (dotimes (i (- offset) t)
1080 (unless (%form-offset mark nil) (return nil)))))
1081
1082;; Return region for the "current form" at mark.
1083;; TODO: See also mark-nearest-form, should merge them
1084(defun form-region-at-mark (mark)
1085 (with-mark ((bwd-start mark)
1086 (bwd-end mark)
1087 (fwd-start mark)
1088 (fwd-end mark))
1089 (let* ((fwd (and (or (and (char-quoted-at-mark-p mark t) ;; back-up so get whole character
1090 (mark-before fwd-end))
1091 (test-char (next-character mark) :lisp-syntax
1092 (or :open-paren :string-quote
1093 :char-quote :symbol-quote :constituent :prefix-dispatch
1094 :prefix)))
1095 (form-offset fwd-end 1)
1096 (form-offset (move-mark fwd-start fwd-end) -1)
1097 (mark<= fwd-start mark)))
1098 (bwd (and (or (char-quoted-at-mark-p mark nil)
1099 (test-char (previous-character mark) :lisp-syntax
1100 (or :close-paren :string-quote
1101 :char-quote :symbol-quote :constituent :prefix-dispatch)))
1102 ;; Special case - if at an open paren, always select forward because that's
1103 ;; the matching paren that's highlighted.
1104 (not (and fwd (test-char (next-character mark) :lisp-syntax :open-paren)))
1105 ;; Also prefer string over anything but close paren.
1106 (not (and fwd (test-char (next-character mark) :lisp-syntax :string-quote)
1107 (not (test-char (previous-character mark) :lisp-syntax :close-paren))))
1108 (form-offset bwd-start -1)
1109 (form-offset (move-mark bwd-end bwd-start) 1)
1110 (mark<= mark bwd-end))))
1111 (if bwd
1112 (when (or (not fwd) ;; back is only option
1113 (and (mark= bwd-start fwd-start) (mark= bwd-end fwd-end)) ;; or they're the same
1114 (and (mark= bwd-start fwd-end) ;; or had to skip prefix chars to get to forward
1115 (test-char (next-character fwd-start) :lisp-syntax (or :prefix :prefix-dispatch))))
1116 (region bwd-start bwd-end))
1117 (if fwd
1118 (region fwd-start fwd-end))))))
1119
1120;; Return region for the current word at mark, or nil if there isn't one.
1121(defun word-region-at-mark (mark)
1122 (with-mark ((fwd mark)
1123 (bwd mark))
1124 (or (find-attribute fwd :word-delimiter)
1125 (buffer-end fwd))
1126 (or (reverse-find-attribute bwd :word-delimiter)
1127 (buffer-start bwd))
1128 (unless (mark= bwd fwd)
1129 ;; Special-case for keywords (and gensyms)
1130 (when (eq (previous-character bwd) #\:)
1131 (mark-before bwd)
1132 (when (test-char (previous-character bwd) :lisp-syntax :constituent)
1133 (mark-after bwd))) ;; oops, never mind
1134 ;; Special-case for stuff like #_foo.
1135 (when (test-char (previous-character bwd) :lisp-syntax :prefix-dispatch)
1136 ;; let :prefix-dispatch take on the attribute of the following char, which is a word constituent
1137 (mark-before bwd))
1138 (region bwd fwd))))
1139
1140;;;; Table of special forms with special indenting requirements.
1141
1142(defhvar "Indent Defanything"
1143 "This is the number of special arguments implicitly assumed to be supplied
1144 in calls to functions whose names begin with \"DEF\". If set to NIL, this
1145 feature is disabled."
1146 :value 2)
1147
1148(defhvar "Indent With-anything"
1149 "This is the number of special arguments implicitly assumed to be supplied
1150 in calls to functions whose names begin with \"WITH-\". If set to NIL, this
1151 feature is disabled."
1152 :value 1)
1153
1154(defvar *special-forms* (make-hash-table :test #'equal))
1155
1156(defun defindent (fname args)
1157 "Define Fname to have Args special arguments. If args is null then remove
1158 any special arguments information."
1159 (check-type fname string)
1160 (let ((fname (string-upcase fname)))
1161 (cond ((null args) (remhash fname *special-forms*))
1162 (t
1163 (check-type args integer)
1164 (setf (gethash fname *special-forms*) args)))))
1165
1166
1167;;; Hemlock forms.
1168;;;
1169(defindent "defhvar" 1)
1170(defindent "hlet" 1)
1171(defindent "defcommand" 2)
1172(defindent "defattribute" 1)
1173(defindent "command-case" 1)
1174(defindent "do-strings" 1)
1175(defindent "save-for-undo" 1)
1176(defindent "do-alpha-chars" 1)
1177(defindent "do-headers-buffers" 1)
1178(defindent "do-headers-lines" 1)
1179(defindent "frob" 1) ;cover silly FLET and MACROLET names for Rob and Bill.
1180(defindent "modifying-buffer" 1)
1181
1182;;; Common Lisp forms.
1183;;;
1184(defindent "block" 1)
1185(defindent "return-from" 1)
1186(defindent "case" 1)
1187(defindent "catch" 1)
1188(defindent "ccase" 1)
1189(defindent "compiler-let" 1)
1190(defindent "ctypecase" 1)
1191(defindent "defconstant" 1)
1192(defindent "define-compiler-macro" 2)
1193(defindent "define-setf-method" 2)
1194(defindent "destructuring-bind" 2)
1195(defindent "defmacro" 2)
1196(defindent "defpackage" 1)
1197(defindent "defparameter" 1)
1198(defindent "defstruct" 1)
1199(defindent "deftype" 2)
1200(defindent "defun" 2)
1201(defindent "defvar" 1)
1202(defindent "do" 2)
1203(defindent "do*" 2)
1204(defindent "do-all-symbols" 1)
1205(defindent "do-external-symbols" 1)
1206(defindent "do-symbols" 1)
1207(defindent "dolist" 1)
1208(defindent "dotimes" 1)
1209(defindent "ecase" 1)
1210(defindent "etypecase" 1)
1211(defindent "eval-when" 1)
1212(defindent "flet" 1)
1213(defindent "if" 1)
1214(defindent "labels" 1)
1215(defindent "lambda" 1)
1216(defindent "let" 1)
1217(defindent "let*" 1)
1218(defindent "locally" 0)
1219(defindent "loop" 0)
1220(defindent "macrolet" 1)
1221(defindent "multiple-value-bind" 2)
1222(defindent "multiple-value-call" 1)
1223(defindent "multiple-value-prog1" 1)
1224(defindent "multiple-value-setq" 1)
1225(defindent "prog" 1)
1226(defindent "prog*" 1)
1227(defindent "prog1" 0)
1228(defindent "prog2" 0)
1229(defindent "progv" 2)
1230(defindent "progn" 0)
1231(defindent "typecase" 1)
1232(defindent "unless" 1)
1233(defindent "unwind-protect" 1)
1234(defindent "when" 1)
1235
1236;; CCL extensions
1237(defindent "iterate" 2)
1238(defindent "rlet" 1)
1239(defindent "rletz" 1)
1240(defindent "let-globally" 1)
1241
1242;;; Error/condition system forms.
1243;;;
1244(defindent "define-condition" 2)
1245(defindent "handler-bind" 1)
1246(defindent "handler-case" 1)
1247(defindent "restart-bind" 1)
1248(defindent "restart-case" 1)
1249;;; These are for RESTART-CASE branch formatting.
1250(defindent "store-value" 1)
1251(defindent "use-value" 1)
1252(defindent "muffle-warning" 1)
1253(defindent "abort" 1)
1254(defindent "continue" 1)
1255;;; This is for DEFGENERIC method formatting
1256(defindent "method" 1)
1257
1258;;; Common library extensions
1259(defindent "if-let" 1)
1260(defindent "if-let*" 1)
1261(defindent "when-let" 1)
1262(defindent "when-let*" 1)
1263
1264#|
1265;;; Debug-internals forms.
1266;;;
1267(defindent "do-debug-function-blocks" 1)
1268(defindent "di:do-debug-function-blocks" 1)
1269(defindent "do-debug-function-variables" 1)
1270(defindent "di:do-debug-function-variables" 1)
1271(defindent "do-debug-block-locations" 1)
1272(defindent "di:do-debug-block-locations" 1)
1273;;;
1274;;; Debug-internals conditions
1275;;; (define these to make uses of HANDLER-CASE indent branches correctly.)
1276;;;
1277(defindent "debug-condition" 1)
1278(defindent "di:debug-condition" 1)
1279(defindent "no-debug-info" 1)
1280(defindent "di:no-debug-info" 1)
1281(defindent "no-debug-function-returns" 1)
1282(defindent "di:no-debug-function-returns" 1)
1283(defindent "no-debug-blocks" 1)
1284(defindent "di:no-debug-blocks" 1)
1285(defindent "lambda-list-unavailable" 1)
1286(defindent "di:lambda-list-unavailable" 1)
1287(defindent "no-debug-variables" 1)
1288(defindent "di:no-debug-variables" 1)
1289(defindent "invalid-value" 1)
1290(defindent "di:invalid-value" 1)
1291(defindent "ambiguous-variable-name" 1)
1292(defindent "di:ambiguous-variable-name" 1)
1293(defindent "debug-error" 1)
1294(defindent "di:debug-error" 1)
1295(defindent "unhandled-condition" 1)
1296(defindent "di:unhandled-condition" 1)
1297(defindent "unknown-code-location" 1)
1298(defindent "di:unknown-code-location" 1)
1299(defindent "unknown-debug-variable" 1)
1300(defindent "di:unknown-debug-variable" 1)
1301(defindent "invalid-control-stack-pointer" 1)
1302(defindent "di:invalid-control-stack-pointer" 1)
1303(defindent "frame-function-mismatch" 1)
1304(defindent "di:frame-function-mismatch" 1)
1305|#
1306
1307;;; CLOS forms.
1308;;;
1309(defindent "with-accessors" 2)
1310(defindent "defclass" 2)
1311(defindent "print-unreadable-object" 1)
1312(defindent "defmethod" 2)
1313(defindent "make-instance" 1)
1314
1315;;; System forms.
1316;;;
1317
1318;;; Multiprocessing forms.
1319(defindent "process-wait" 1)
1320(defindent "process-run-function" 1)
1321
1322
1323
1324;;;; Indentation.
1325
1326;;; LISP-INDENTATION -- Internal Interface.
1327
1328(defun strip-package-prefix (string)
1329 (let* ((p (position #\: string :from-end t)))
1330 (if p
1331 (subseq string (1+ p))
1332 string)))
1333;;;
1334(defun lisp-indentation (mark)
1335 "Compute number of spaces which mark should be indented according to
1336 local context and lisp grinding conventions. This assumes mark is at the
1337 beginning of the line to be indented."
1338 (with-mark ((m mark)
1339 (temp mark))
1340 ;; See if we are in a quoted context.
1341 (unless (valid-spot m nil)
1342 (return-from lisp-indentation (lisp-generic-indentation m)))
1343 ;; Look for the paren that opens the containing form.
1344 (unless (backward-up-list m)
1345 (return-from lisp-indentation 0))
1346 ;; Move after the paren, save the start, and find the form name.
1347 (mark-after m)
1348 (with-mark ((start m))
1349 (unless (and (scan-char m :lisp-syntax
1350 (not (or :space :prefix :prefix-dispatch :char-quote)))
1351 (test-char (next-character m) :lisp-syntax :constituent))
1352 (return-from lisp-indentation (mark-column start)))
1353 (with-mark ((fstart m))
1354 (scan-char m :lisp-syntax (not :constituent))
1355 (let* ((fname (nstring-upcase
1356 (strip-package-prefix (region-to-string (region fstart m)))))
1357 (special-args (or (gethash fname *special-forms*)
1358 (and (> (length fname) 2)
1359 (string= fname "DEF" :end1 3)
1360 (value indent-defanything))
1361 (and (> (length fname) 4)
1362 (string= fname "WITH-" :end1 5)
1363 (value indent-with-anything)))))
1364 (declare (simple-string fname))
1365 ;; Now that we have the form name, did it have special syntax?
1366 (cond (special-args
1367 (with-mark ((spec m))
1368 (cond ((and (form-offset spec special-args)
1369 (mark<= spec mark))
1370 (1+ (mark-column start)))
1371 ((skip-valid-space m)
1372 (mark-column m))
1373 (t
1374 (+ (mark-column start) 3)))))
1375 ;; See if the user seems to have altered the editor's
1376 ;; indentation, and if so, try to adhere to it. This usually
1377 ;; happens when you type in a quoted list constant that line
1378 ;; wraps. You want all the items on successive lines to fall
1379 ;; under the first character after the opening paren, not as if
1380 ;; you are calling a function.
1381 ((and (form-offset temp -1)
1382 (or (blank-before-p temp) (not (same-line-p temp fstart)))
1383 (not (same-line-p temp mark)))
1384 (unless (blank-before-p temp)
1385 (line-start temp)
1386 (find-attribute temp :space #'zerop))
1387 (mark-column temp))
1388 ;; Appears to be a normal form. Is the first arg on the same
1389 ;; line as the form name?
1390 ((skip-valid-space m)
1391 (or (lisp-indentation-check-for-local-def
1392 mark temp fstart start t)
1393 (mark-column m)))
1394 ;; Okay, fall under the first character after the opening paren.
1395 (t
1396 (or (lisp-indentation-check-for-local-def
1397 mark temp fstart start nil)
1398 (mark-column start)))))))))
1399
1400(defhvar "Lisp Indentation Local Definers"
1401 "Forms with syntax like LABELS, MACROLET, etc."
1402 :value '("LABELS" "MACROLET" "FLET"))
1403
1404;;; LISP-INDENTATION-CHECK-FOR-LOCAL-DEF -- Internal.
1405;;;
1406;;; This is a temporary hack to see how it performs. When we are indenting
1407;;; what appears to be a function call, let's look for FLET or MACROLET to see
1408;;; if we really are indenting a local definition. If we are, return the
1409;;; indentation for a DEFUN; otherwise, nil
1410;;;
1411;;; Mark is the argument to LISP-INDENTATION. Start is just inside the paren
1412;;; of what looks like a function call. If we are in an FLET, arg-list
1413;;; indicates whether the local function's arg-list has been entered, that is,
1414;;; whether we need to normally indent for a DEFUN body or indent specially for
1415;;; the arg-list.
1416;;;
1417(defun lisp-indentation-check-for-local-def (mark temp1 temp2 start arg-list)
1418 ;; We know this succeeds from LISP-INDENTATION.
1419 (backward-up-list (move-mark temp1 mark)) ;Paren for local definition.
1420 (cond ((and (backward-up-list temp1) ;Paren opening the list of defs
1421 (form-offset (move-mark temp2 temp1) -1)
1422 (mark-before temp2)
1423 (backward-up-list temp1) ;Paren for FLET or MACROLET.
1424 (mark= temp1 temp2)) ;Must be in first arg form.
1425 ;; See if the containing form is named FLET or MACROLET.
1426 (mark-after temp1)
1427 (unless (and (scan-char temp1 :lisp-syntax
1428 (not (or :space :prefix :prefix-dispatch :char-quote)))
1429 (test-char (next-character temp1) :lisp-syntax
1430 :constituent))
1431 (return-from lisp-indentation-check-for-local-def nil))
1432 (move-mark temp2 temp1)
1433 (scan-char temp2 :lisp-syntax (not :constituent))
1434 (let ((fname (nstring-upcase (region-to-string (region temp1 temp2)))))
1435 (cond ((not (member fname (value lisp-indentation-local-definers)
1436 :test #'string=))
1437 nil)
1438 (arg-list
1439 (1+ (mark-column start)))
1440 (t
1441 (+ (mark-column start) 3)))))))
1442
1443;;; LISP-GENERIC-INDENTATION -- Internal.
1444;;;
1445;;; LISP-INDENTATION calls this when mark is in a invalid spot, or quoted
1446;;; context. If we are inside a string, we return the column one greater
1447;;; than the opening double quote. Otherwise, we just use the indentation
1448;;; of the first preceding non-blank line.
1449;;;
1450(defun lisp-generic-indentation (mark)
1451 (with-mark ((m mark))
1452 (form-offset m -1)
1453 (cond ((eq (character-attribute :lisp-syntax (next-character m))
1454 :string-quote)
1455 (1+ (mark-column m)))
1456 (t
1457 (let* ((line (mark-line mark))
1458 (prev (do ((line (line-previous line) (line-previous line)))
1459 ((not (and line (blank-line-p line))) line))))
1460 (cond (prev
1461 (line-start mark prev)
1462 (find-attribute mark :space #'zerop)
1463 (mark-column mark))
1464 (t 0)))))))
1465
1466;;; Skip-Valid-Space -- Internal
1467;;;
1468;;; Skip over any space on the line Mark is on, stopping at the first valid
1469;;; non-space character. If there is none on the line, return nil.
1470;;;
1471(defun skip-valid-space (mark)
1472 (loop
1473 (scan-char mark :lisp-syntax (not :space))
1474 (let ((val (character-attribute :lisp-syntax
1475 (next-character mark))))
1476 (cond ((eq val :newline) (return nil))
1477 ((valid-spot mark t) (return mark))))
1478 (mark-after mark)))
1479
1480;; (declaim (optimize (speed 0))); byte compile again
1481
1482
1483
1484;;;; Indentation commands and hook functions.
1485
1486(defcommand "Defindent" (p)
1487 "Define the Lisp indentation for the current function.
1488 The indentation is a non-negative integer which is the number
1489 of special arguments for the form. Examples: 2 for Do, 1 for Dolist.
1490 If a prefix argument is supplied, then delete the indentation information."
1491 "Do a defindent, man!"
1492 (with-mark ((m (current-point)))
1493 (pre-command-parse-check m)
1494 (unless (backward-up-list m) (editor-error))
1495 (mark-after m)
1496 (with-mark ((n m))
1497 (scan-char n :lisp-syntax (not :constituent))
1498 (let ((s (region-to-string (region m n))))
1499 (declare (simple-string s))
1500 (when (zerop (length s)) (editor-error))
1501 (if p
1502 (defindent s nil)
1503 (let ((i (prompt-for-integer
1504 :prompt (format nil "Indentation for ~A: " s)
1505 :help "Number of special arguments.")))
1506 (when (minusp i)
1507 (editor-error "Indentation must be non-negative."))
1508 (defindent s i))))))
1509 (indent-command nil))
1510
1511(defcommand "Indent Form" (p)
1512 "Indent Lisp code in the next form, unless point is to the right of
1513 a closing parenthesis, in which case the previous form will be
1514 indented."
1515 "Indent Lisp code in the next form."
1516 (declare (ignore p))
1517 (let ((point (current-point))
1518 (offset 1))
1519 (pre-command-parse-check point)
1520 (when (eql (previous-character point) #\))
1521 (setq offset -1))
1522 (with-mark ((m point))
1523 (unless (form-offset m offset) (editor-error))
1524 (when (minusp offset)
1525 (rotatef point m))
1526 (lisp-indent-region (region point m) "Indent Form"))))
1527
1528;;; LISP-INDENT-REGION -- Internal.
1529;;;
1530;;; This indents a region of Lisp code without doing excessive redundant
1531;;; computation. We parse the entire region once, then scan through doing
1532;;; indentation on each line. We forcibly reparse each line that we indent so
1533;;; that the list operations done to determine indentation of subsequent lines
1534;;; will work. This is done undoably with save1, save2, buf-region, and
1535;;; undo-region.
1536;;;
1537(defun lisp-indent-region (region &optional (undo-text "Lisp region indenting"))
1538 (let* ((start (region-start region))
1539 (end (region-end region))
1540 (buffer (hi::line-%buffer (mark-line start))))
1541 (with-mark ((m1 start)
1542 (m2 end))
1543 (funcall (value parse-start-function) m1)
1544 (funcall (value parse-end-function) m2)
1545 (parse-over-block (mark-line m1) (mark-line m2)))
1546 (hi::check-buffer-modification buffer start)
1547 (hi::check-buffer-modification buffer end)
1548 (let* ((first-line (mark-line start))
1549 (last-line (mark-line end))
1550 (prev (line-previous first-line))
1551 (prev-line-info
1552 (and prev (getf (line-plist prev) 'lisp-info)))
1553 (save1 (line-start (copy-mark start :right-inserting)))
1554 (save2 (line-end (copy-mark end :left-inserting)))
1555 (buf-region (region save1 save2))
1556 (undo-region (copy-region buf-region)))
1557 (with-mark ((bol start :left-inserting))
1558 (do ((line first-line (line-next line)))
1559 (nil)
1560 (line-start bol line)
1561 (ensure-lisp-indentation bol)
1562 (let ((line-info (getf (line-plist line) 'lisp-info)))
1563 (parse-lisp-line-info bol line-info prev-line-info)
1564 (setq prev-line-info line-info))
1565 (when (eq line last-line) (return nil))))
1566 (make-region-undo :twiddle undo-text buf-region undo-region))))
1567
1568;;; INDENT-FOR-LISP -- Internal.
1569;;;
1570;;; This is the value of "Indent Function" for "Lisp" mode.
1571;;;
1572(defun indent-for-lisp (mark)
1573 (line-start mark)
1574 (pre-command-parse-check mark)
1575 (ensure-lisp-indentation mark))
1576
1577(defun count-leading-whitespace (mark)
1578 (with-mark ((m mark))
1579 (line-start m)
1580 (do* ((p 0)
1581 (q 0 (1+ q))
1582 (tab-width (value spaces-per-tab)))
1583 ()
1584 (case (next-character m)
1585 (#\space (incf p))
1586 (#\tab (setq p (* tab-width (ceiling (1+ p) tab-width))))
1587 (t (return (values p q))))
1588 (character-offset m 1))))
1589
1590;;; Don't do anything if M's line is already correctly indented.
1591(defun ensure-lisp-indentation (m)
1592 (let* ((col (lisp-indentation m)))
1593 (multiple-value-bind (curcol curpos) (count-leading-whitespace m)
1594 (cond ((= curcol col) (setf (mark-charpos m) curpos))
1595 (t
1596 (delete-horizontal-space m)
1597 (indent-to-column m col))))))
1598
1599
1600
1601
1602
1603;;;; Most "Lisp" mode commands.
1604
1605(defcommand "Beginning of Defun" (p)
1606 "Move the point to the beginning of a top-level form, collapsing the selection.
1607 with an argument, skips the previous p top-level forms."
1608 "Move the point to the beginning of a top-level form, collapsing the selection."
1609 (let ((point (current-point-collapsing-selection))
1610 (count (or p 1)))
1611 (pre-command-parse-check point)
1612 (if (minusp count)
1613 (end-of-defun-command (- count))
1614 (unless (top-level-offset point (- count))
1615 (editor-error)))))
1616
1617(defcommand "Select to Beginning of Defun" (p)
1618 "Move the point to the beginning of a top-level form, extending the selection.
1619 with an argument, skips the previous p top-level forms."
1620 "Move the point to the beginning of a top-level form, extending the selection."
1621 (let ((point (current-point-for-selection-start))
1622 (count (or p 1)))
1623 (pre-command-parse-check point)
1624 (if (minusp count)
1625 (end-of-defun-command (- count))
1626 (unless (top-level-offset point (- count))
1627 (editor-error)))))
1628
1629;;; "End of Defun", with a positive p (the normal case), does something weird.
1630;;; Get a mark at the beginning of the defun, and then offset it forward one
1631;;; less top level form than we want. This sets us up to use FORM-OFFSET which
1632;;; allows us to leave the point immediately after the defun. If we used
1633;;; TOP-LEVEL-OFFSET one less than p on the mark at the end of the current
1634;;; defun, point would be left at the beginning of the p+1'st form instead of
1635;;; at the end of the p'th form.
1636;;;
1637(defcommand "End of Defun" (p)
1638 "Move the point to the end of a top-level form, collapsing the selection.
1639 With an argument, skips the next p top-level forms."
1640 "Move the point to the end of a top-level form, collapsing the selection."
1641 (let ((point (current-point-collapsing-selection))
1642 (count (or p 1)))
1643 (pre-command-parse-check point)
1644 (if (minusp count)
1645 (beginning-of-defun-command (- count))
1646 (with-mark ((m point)
1647 (dummy point))
1648 (cond ((not (mark-top-level-form m dummy))
1649 (editor-error "No current or next top level form."))
1650 (t
1651 (unless (top-level-offset m (1- count))
1652 (editor-error "Not enough top level forms."))
1653 ;; We might be one unparsed for away.
1654 (pre-command-parse-check m)
1655 (unless (form-offset m 1)
1656 (editor-error "Not enough top level forms."))
1657 (when (blank-after-p m) (line-offset m 1 0))
1658 (move-mark point m)))))))
1659
1660(defcommand "Select to End of Defun" (p)
1661 "Move the point to the end of a top-level form, extending the selection.
1662 With an argument, skips the next p top-level forms."
1663 "Move the point to the end of a top-level form, extending the selection."
1664 (let ((point (current-point-for-selection-end))
1665 (count (or p 1)))
1666 (pre-command-parse-check point)
1667 (if (minusp count)
1668 (beginning-of-defun-command (- count))
1669 (with-mark ((m point)
1670 (dummy point))
1671 (cond ((not (mark-top-level-form m dummy))
1672 (editor-error "No current or next top level form."))
1673 (t
1674 (unless (top-level-offset m (1- count))
1675 (editor-error "Not enough top level forms."))
1676 ;; We might be one unparsed for away.
1677 (pre-command-parse-check m)
1678 (unless (form-offset m 1)
1679 (editor-error "Not enough top level forms."))
1680 (when (blank-after-p m) (line-offset m 1 0))
1681 (move-mark point m)))))))
1682
1683(defcommand "Forward List" (p)
1684 "Skip over the next Lisp list, collapsing the selection.
1685 With argument, skips the next p lists."
1686 "Skip over the next Lisp list, collapsing the selection."
1687 (or (collapse-if-selection :direction :forward)
1688 (let ((point (current-point-collapsing-selection))
1689 (count (or p 1)))
1690 (pre-command-parse-check point)
1691 (unless (list-offset point count) (editor-error "No next list.")))))
1692
1693(defcommand "Select Forward List" (p)
1694 "Skip over the next Lisp list, extending the selection.
1695 With argument, skips the next p lists."
1696 "Skip over the next Lisp list, extending the selection."
1697 (let ((point (current-point-for-selection-end))
1698 (count (or p 1)))
1699 (pre-command-parse-check point)
1700 (unless (list-offset point count) (editor-error "No next list."))))
1701
1702(defcommand "Backward List" (p)
1703 "Skip over the previous Lisp list, collapsing the selection.
1704 With argument, skips the previous p lists."
1705 "Skip over the previous Lisp list, collapsing the selection."
1706 (or (collapse-if-selection :direction :backward)
1707 (let ((point (current-point-collapsing-selection))
1708 (count (- (or p 1))))
1709 (pre-command-parse-check point)
1710 (unless (list-offset point count) (editor-error "No previous list.")))))
1711
1712(defcommand "Select Backward List" (p)
1713 "Skip over the previous Lisp list, extending the selection.
1714 With argument, skips the previous p lists."
1715 "Skip over the previous Lisp list, extending the selection."
1716 (let ((point (current-point-for-selection-start))
1717 (count (- (or p 1))))
1718 (pre-command-parse-check point)
1719 (unless (list-offset point count) (editor-error "No previous list."))))
1720
1721(defcommand "Forward Form" (p)
1722 "Skip over the next Form, collapsing the selection.
1723 With argument, skips the next p Forms."
1724 "Skip over the next Form, collapsing the selection."
1725 (or (collapse-if-selection :direction :forward)
1726 (let ((point (current-point-collapsing-selection))
1727 (count (or p 1)))
1728 (pre-command-parse-check point)
1729 (unless (form-offset point count) (editor-error "No next form.")))))
1730
1731(defcommand "Select Forward Form" (p)
1732 "Skip over the next Form, extending the selection.
1733 With argument, skips the next p Forms."
1734 "Skip over the next Form, extending the selection."
1735 (let ((point (current-point-for-selection-end))
1736 (count (or p 1)))
1737 (pre-command-parse-check point)
1738 (unless (form-offset point count) (editor-error "No next form."))))
1739
1740(defcommand "Backward Form" (p)
1741 "Skip over the previous Form, collapsing the selection.
1742 With argument, skips the previous p Forms."
1743 "Skip over the previous Form, collapsing the selection."
1744 (or (collapse-if-selection :direction :backward)
1745 (let ((point (current-point-collapsing-selection))
1746 (count (- (or p 1))))
1747 (pre-command-parse-check point)
1748 (unless (form-offset point count) (editor-error "No previous form.")))))
1749
1750(defcommand "Select Backward Form" (p)
1751 "Skip over the previous Form, extending the selection.
1752 With argument, skips the previous p Forms."
1753 "Skip over the previous Form, extending the selection."
1754 (let ((point (current-point-for-selection-start))
1755 (count (- (or p 1))))
1756 (pre-command-parse-check point)
1757 (unless (form-offset point count) (editor-error "No previous form."))))
1758
1759(defcommand "Mark Form" (p)
1760 "Set the mark at the end of the next Form.
1761 With a positive argument, set the mark after the following p
1762 Forms. With a negative argument, set the mark before
1763 the preceding -p Forms."
1764 "Set the mark at the end of the next Form."
1765 (with-mark ((m (current-point)))
1766 (pre-command-parse-check m)
1767 (let ((count (or p 1))
1768 (mark (push-new-buffer-mark m t)))
1769 (if (form-offset m count)
1770 (move-mark mark m)
1771 (editor-error "No next form.")))))
1772
1773(defcommand "Mark Defun" (p)
1774 "Puts the region around the next or containing top-level form.
1775 The point is left before the form and the mark is placed immediately
1776 after it."
1777 "Puts the region around the next or containing top-level form."
1778 (declare (ignore p))
1779 (let ((point (current-point)))
1780 (pre-command-parse-check point)
1781 (with-mark ((start point)
1782 (end point))
1783 (cond ((not (mark-top-level-form start end))
1784 (editor-error "No current or next top level form."))
1785 (t
1786 (move-mark point start)
1787 (move-mark (push-new-buffer-mark point t) end))))))
1788
1789(defcommand "Forward Kill Form" (p)
1790 "Kill the next Form.
1791 With a positive argument, kills the next p Forms.
1792 Kills backward with a negative argument."
1793 "Kill the next Form."
1794 (with-mark ((m1 (current-point))
1795 (m2 (current-point)))
1796 (pre-command-parse-check m1)
1797 (let ((count (or p 1)))
1798 (unless (form-offset m1 count) (editor-error "No ~a form." (if (minusp count) "previous" "next")))
1799 (if (minusp count)
1800 (kill-region (region m1 m2) :kill-backward)
1801 (kill-region (region m2 m1) :kill-forward)))))
1802
1803(defcommand "Backward Kill Form" (p)
1804 "Kill the previous Form.
1805 With a positive argument, kills the previous p Forms.
1806 Kills forward with a negative argument."
1807 "Kill the previous Form."
1808 (forward-kill-form-command (- (or p 1))))
1809
1810(defcommand "Extract Form" (p)
1811 "Replace the current containing list with the next form. The entire affected
1812 area is pushed onto the kill ring. If an argument is supplied, that many
1813 upward levels of list nesting is replaced by the next form."
1814 "Replace the current containing list with the next form. The entire affected
1815 area is pushed onto the kill ring. If an argument is supplied, that many
1816 upward levels of list nesting is replaced by the next form."
1817 (let ((point (current-point)))
1818 (pre-command-parse-check point)
1819 (with-mark ((form-start point :right-inserting)
1820 (form-end point))
1821 (unless (form-offset form-end 1) (editor-error "No next form."))
1822 (form-offset (move-mark form-start form-end) -1)
1823 (with-mark ((containing-start form-start :left-inserting)
1824 (containing-end form-end :left-inserting))
1825 (dotimes (i (or p 1))
1826 (unless (and (forward-up-list containing-end)
1827 (backward-up-list containing-start))
1828 (editor-error "No containing list.")))
1829 (let ((r (copy-region (region form-start form-end))))
1830 (ring-push (delete-and-save-region
1831 (region containing-start containing-end))
1832 *kill-ring*)
1833 (ninsert-region point r)
1834 (move-mark point form-start))))))
1835
1836(defcommand "Extract List" (p)
1837 "Extract the current list.
1838 The current list replaces the surrounding list. The entire affected
1839 area is pushed on the kill-ring. With prefix argument, remove that
1840 many surrounding lists."
1841 "Replace the P containing lists with the current one."
1842 (let ((point (current-point)))
1843 (pre-command-parse-check point)
1844 (with-mark ((lstart point :right-inserting)
1845 (lend point))
1846 (if (eq (character-attribute :lisp-syntax (next-character lstart))
1847 :open-paren)
1848 (mark-after lend)
1849 (unless (backward-up-list lstart) (editor-error "No containing list.")))
1850 (unless (forward-up-list lend) (editor-error "No containing list."))
1851 (with-mark ((rstart lstart)
1852 (rend lend))
1853 (dotimes (i (or p 1))
1854 (unless (and (forward-up-list rend) (backward-up-list rstart))
1855 (editor-error)))
1856 (let ((r (copy-region (region lstart lend))))
1857 (ring-push (delete-and-save-region (region rstart rend))
1858 *kill-ring*)
1859 (ninsert-region point r)
1860 (move-mark point lstart))))))
1861
1862(defcommand "Transpose Forms" (p)
1863 "Transpose Forms immediately preceding and following the point.
1864 With a zero argument, tranposes the Forms at the point and the mark.
1865 With a positive argument, transposes the Form preceding the point
1866 with the p-th one following it. With a negative argument, transposes the
1867 Form following the point with the p-th one preceding it."
1868 "Transpose Forms immediately preceding and following the point."
1869 (let ((point (current-point))
1870 (count (or p 1)))
1871 (pre-command-parse-check point)
1872 (if (zerop count)
1873 (let ((mark (current-mark)))
1874 (with-mark ((s1 mark :left-inserting)
1875 (s2 point :left-inserting))
1876 (scan-char s1 :whitespace nil)
1877 (scan-char s2 :whitespace nil)
1878 (with-mark ((e1 s1 :right-inserting)
1879 (e2 s2 :right-inserting))
1880 (unless (form-offset e1 1) (editor-error "No next form."))
1881 (unless (form-offset e2 1) (editor-error "No next form."))
1882 (ninsert-region s1 (delete-and-save-region (region s2 e2)))
1883 (ninsert-region s2 (delete-and-save-region (region s1 e1))))))
1884 (let ((fcount (if (plusp count) count 1))
1885 (bcount (if (plusp count) 1 count)))
1886 (with-mark ((s1 point :left-inserting)
1887 (e2 point :right-inserting))
1888 (dotimes (i bcount)
1889 (unless (form-offset s1 -1) (editor-error "No previous form.")))
1890 (dotimes (i fcount)
1891 (unless (form-offset e2 1) (editor-error "No next form.")))
1892 (with-mark ((e1 s1 :right-inserting)
1893 (s2 e2 :left-inserting))
1894 (unless (form-offset e1 1) (editor-error))
1895 (unless (form-offset s2 -1) (editor-error))
1896 (ninsert-region s1 (delete-and-save-region (region s2 e2)))
1897 (ninsert-region s2 (delete-and-save-region (region s1 e1)))
1898 (move-mark point s2)))))))
1899
1900
1901(defcommand "Insert ()" (count)
1902 "Insert a pair of parentheses (). With positive argument, puts
1903 parentheses around the next COUNT Forms, or previous COUNT forms, if
1904 COUNT is negative. The point is positioned after the open parenthesis."
1905 "Insert a pair of parentheses ()."
1906 ;; TODO Form navigation is broken, so this is broken too -- it is
1907 ;; possible to put parens around more forms than there are in current
1908 ;; expression. It works by moving past as many forms as there is, and
1909 ;; then each delimiting paren also counts as a form.
1910 (let ((point (current-point)))
1911 (pre-command-parse-check point)
1912 (cond (count
1913 (when (minusp count)
1914 (form-offset point count)
1915 (setq count (- count)))
1916 (insert-character point #\()
1917 (with-mark ((m point))
1918 (unless (form-offset m count)
1919 (editor-error "Could not find that many forms."))
1920 (insert-character m #\))))
1921 ;; The simple case with no prefix argument
1922 (t
1923 (insert-character point #\()
1924 (insert-character point #\))
1925 (mark-before point)))))
1926
1927
1928(defcommand "Move Over )" (p)
1929 "Move past the next close parenthesis, and start a new line. Any
1930 indentation preceding the preceding the parenthesis is deleted, and the
1931 new line is indented. If there is only whitespace preceding the close
1932 paren, the paren is moved to the end of the previous line. With prefix
1933 argument, this command moves past next closing paren and inserts space."
1934 "Move past the next close parenthesis, and start a new line."
1935 ;; TODO This is still not complete, because SCAN-CHAR finds the next
1936 ;; close-paren, but we need to find the next paren that closes current
1937 ;; expression. This will have to be updated when form navigation is
1938 ;; fixed.
1939 (let ((point (current-point)))
1940 (pre-command-parse-check point)
1941 (with-mark ((m point :right-inserting))
1942 (cond ((scan-char m :lisp-syntax :close-paren)
1943 (cond ((same-line-p point m)
1944 (delete-horizontal-space m))
1945 (t
1946 (move-mark point m)
1947 (reverse-find-attribute point :whitespace #'zerop)
1948 (delete-region (region point m))))
1949 (cond ((not p)
1950 ;; Move to the previous line if current is empty
1951 (when (zerop (mark-charpos m))
1952 (delete-characters m -1))
1953 (mark-after m)
1954 (move-mark point m)
1955 (indent-new-line-command 1))
1956 (t
1957 (mark-after m)
1958 (move-mark point m)
1959 (insert-character m #\space))))
1960 (t
1961 (editor-error "Could not find closing paren."))))))
1962
1963
1964(defcommand "Forward Up List" (p)
1965 "Move forward past a one containing )."
1966 "Move forward past a one containing )."
1967 (or (collapse-if-selection :direction :forward)
1968 (let ((point (current-point-collapsing-selection))
1969 (count (or p 1)))
1970 (pre-command-parse-check point)
1971 (if (minusp count)
1972 (backward-up-list-command (- count))
1973 (with-mark ((m point))
1974 (dotimes (i count (move-mark point m))
1975 (unless (forward-up-list m) (editor-error "No containing list."))))))))
1976
1977(defcommand "Backward Up List" (p)
1978 "Move backward past a one containing (."
1979 "Move backward past a one containing (."
1980 (or (collapse-if-selection :direction :backward)
1981 (let ((point (current-point-collapsing-selection))
1982 (count (or p 1)))
1983 (pre-command-parse-check point)
1984 (if (minusp count)
1985 (forward-up-list-command (- count))
1986 (with-mark ((m point))
1987 (dotimes (i count (move-mark point m))
1988 (unless (backward-up-list m) (editor-error "No containing list."))))))))
1989
1990
1991(defcommand "Down List" (p)
1992 "Move down a level in list structure. With positive argument, moves down
1993 p levels. With negative argument, moves down backward, but only one
1994 level."
1995 "Move down a level in list structure."
1996 (let ((point (current-point-collapsing-selection))
1997 (count (or p 1)))
1998 (pre-command-parse-check point)
1999 (with-mark ((m point))
2000 (cond ((plusp count)
2001 (loop repeat count
2002 do (unless (and (scan-char m :lisp-syntax :open-paren)
2003 (mark-after m))
2004 (editor-error "No embedded list."))))
2005 (t
2006 (unless (and (rev-scan-char m :lisp-syntax :close-paren)
2007 (mark-before m))
2008 (editor-error "No embedded list."))))
2009 (move-mark point m))))
2010
2011
2012
2013
2014;;;; Filling Lisp comments, strings, and indented text.
2015
2016(defhvar "Fill Lisp Comment Paragraph Confirm"
2017 "This determines whether \"Fill Lisp Comment Paragraph\" will prompt for
2018 confirmation to fill contiguous lines with the same initial whitespace when
2019 it is invoked outside of a comment or string."
2020 :value t)
2021
2022(defcommand "Fill Lisp Comment Paragraph" (p)
2023 "This fills a flushleft or indented Lisp comment.
2024 This also fills Lisp string literals using the proper indentation as a
2025 filling prefix. When invoked outside of a comment or string, this tries
2026 to fill all contiguous lines beginning with the same initial, non-empty
2027 blankspace. When filling a comment, the current line is used to determine a
2028 fill prefix by taking all the initial whitespace on the line, the semicolons,
2029 and any whitespace following the semicolons."
2030 "Fills a flushleft or indented Lisp comment."
2031 (declare (ignore p))
2032 (let ((point (current-point)))
2033 (pre-command-parse-check point)
2034 (with-mark ((start point)
2035 (end point)
2036 (m point))
2037 (let ((commentp (fill-lisp-comment-paragraph-prefix start end)))
2038 (cond (commentp
2039 (fill-lisp-comment-or-indented-text start end))
2040 ((and (not (valid-spot m nil))
2041 (form-offset m -1)
2042 (eq (character-attribute :lisp-syntax (next-character m))
2043 :string-quote))
2044 (fill-lisp-string m))
2045 ((or (not (value fill-lisp-comment-paragraph-confirm))
2046 (prompt-for-y-or-n
2047 :prompt '("Not in a comment or string. Fill contiguous ~
2048 lines with the same initial whitespace? ")))
2049 (fill-lisp-comment-or-indented-text start end)))))))
2050
2051;;; FILL-LISP-STRING -- Internal.
2052;;;
2053;;; This fills the Lisp string containing mark as if it had been entered using
2054;;; Hemlock's Lisp string indentation, "Indent Function" for "Lisp" mode. This
2055;;; assumes the area around mark has already been PRE-COMMAND-PARSE-CHECK'ed,
2056;;; and it ensures the string ends before doing any filling. This function
2057;;; is undo'able.
2058;;;
2059(defun fill-lisp-string (mark)
2060 (with-mark ((end mark))
2061 (unless (form-offset end 1)
2062 (editor-error "Attempted to fill Lisp string, but it doesn't end?"))
2063 (let* ((mark (copy-mark mark :left-inserting))
2064 (end (copy-mark end :left-inserting))
2065 (string-region (region mark end))
2066 (undo-region (copy-region string-region))
2067 (hack (make-empty-region)))
2068 ;; Generate prefix.
2069 (indent-to-column (region-end hack) (1+ (mark-column mark)))
2070 ;; Skip opening double quote and fill string starting on its own line.
2071 (mark-after mark)
2072 (insert-character mark #\newline)
2073 (line-start mark)
2074 (setf (mark-kind mark) :right-inserting)
2075 (fill-region string-region (region-to-string hack))
2076 ;; Clean up inserted prefix on first line, delete inserted newline, and
2077 ;; move before the double quote for undo.
2078 (with-mark ((text mark :left-inserting))
2079 (find-attribute text :whitespace #'zerop)
2080 (delete-region (region mark text)))
2081 (delete-characters mark -1)
2082 (mark-before mark)
2083 ;; Save undo.
2084 (make-region-undo :twiddle "Fill Lisp Comment Paragraph"
2085 string-region undo-region))))
2086
2087;;; FILL-LISP-COMMENT-OR-INDENTED-TEXT -- Internal.
2088;;;
2089;;; This fills all contiguous lines around start and end containing fill prefix
2090;;; designated by the region between start and end. These marks can only be
2091;;; equal when there is no comment and no initial whitespace. This is a bad
2092;;; situation since this function in that situation would fill the entire
2093;;; buffer into one paragraph. This function is undo'able.
2094;;;
2095(defun fill-lisp-comment-or-indented-text (start end)
2096 (when (mark= start end)
2097 (editor-error "This command only fills Lisp comments, strings, or ~
2098 indented text, but this line is flushleft."))
2099 ;;
2100 ;; Find comment block.
2101 (let* ((prefix (region-to-string (region start end)))
2102 (length (length prefix)))
2103 (declare (simple-string prefix))
2104 (flet ((frob (mark direction)
2105 (loop
2106 (let* ((line (line-string (mark-line mark)))
2107 (line-len (length line)))
2108 (declare (simple-string line))
2109 (unless (string= line prefix :end1 (min line-len length))
2110 (when (= direction -1)
2111 (unless (same-line-p mark end) (line-offset mark 1 0)))
2112 (return)))
2113 (unless (line-offset mark direction 0)
2114 (when (= direction 1) (line-end mark))
2115 (return)))))
2116 (frob start -1)
2117 (frob end 1))
2118 ;;
2119 ;; Do it undoable.
2120 (let* ((start1 (copy-mark start :right-inserting))
2121 (end2 (copy-mark end :left-inserting))
2122 (region (region start1 end2))
2123 (undo-region (copy-region region)))
2124 (fill-region region prefix)
2125 (make-region-undo :twiddle "Fill Lisp Comment Paragraph"
2126 region undo-region))))
2127
2128;;; FILL-LISP-COMMENT-PARAGRAPH-PREFIX -- Internal.
2129;;;
2130;;; This sets start and end around the prefix to be used for filling. We
2131;;; assume we are dealing with a comment. If there is no ";", then we try to
2132;;; find some initial whitespace. If there is a ";", we make sure the line is
2133;;; blank before it to eliminate ";"'s in the middle of a line of text.
2134;;; Finally, if we really have a comment instead of some indented text, we skip
2135;;; the ";"'s and any immediately following whitespace. We allow initial
2136;;; whitespace, so we can fill strings with the same command.
2137;;;
2138(defun fill-lisp-comment-paragraph-prefix (start end)
2139 (line-start start)
2140 (let ((commentp t)) ; Assumes there's a comment.
2141 (unless (to-line-comment (line-start end) ";")
2142 (find-attribute end :whitespace #'zerop)
2143 #|(when (start-line-p end)
2144 (editor-error "No comment on line, and no initial whitespace."))|#
2145 (setf commentp nil))
2146 (when commentp
2147 (unless (blank-before-p end)
2148 (find-attribute (line-start end) :whitespace #'zerop)
2149 #|(when (start-line-p end)
2150 (editor-error "Semicolon preceded by unindented text."))|#
2151 (setf commentp nil)))
2152 (when commentp
2153 (find-attribute end :lisp-syntax #'(lambda (x) (not (eq x :comment))))
2154 (find-attribute end :whitespace #'zerop))
2155 commentp))
2156
2157
2158
2159
2160;;;; "Lisp" mode.
2161
2162(defcommand "LISP Mode" (p)
2163 "Put current buffer in LISP mode."
2164 "Put current buffer in LISP mode."
2165 (declare (ignore p))
2166 (setf (buffer-major-mode (current-buffer)) "LISP"))
2167
2168
2169(defmode "Lisp" :major-p t :setup-function 'setup-lisp-mode)
2170
2171
2172(defun buffer-first-in-package-form (buffer)
2173 "Returns the package name referenced in the first apparent IN-PACKAGE
2174 form in buffer, or NIL if it can't find an IN-PACKAGE."
2175 (let* ((pattern (new-search-pattern :string-insensitive :forward "in-package" nil))
2176 (mark (copy-mark (buffer-start-mark buffer))))
2177 (with-mark ((start mark)
2178 (end mark))
2179 (loop
2180 (unless (find-pattern mark pattern)
2181 (return))
2182 (pre-command-parse-check mark)
2183 (when (valid-spot mark t)
2184 (move-mark end mark)
2185 (when (form-offset end 1)
2186 (move-mark start end)
2187 (when (backward-up-list start)
2188 (when (scan-char start :lisp-syntax :constituent)
2189 (let* ((s (nstring-upcase (region-to-string (region start end))))
2190 (*package* (find-package "CL-USER")))
2191 (unless (eq (ignore-errors (values (read-from-string s)))
2192 'in-package)
2193 (return)))
2194 (unless (form-offset end 1) (return))
2195 (move-mark start end)
2196 (form-offset start -1)
2197 (let* ((pkgname (ignore-errors (values (read-from-string (region-to-string (region start end)))))))
2198 (return
2199 (if pkgname
2200 (values (ignore-errors (string pkgname))))))))))))))
2201
2202(defparameter *previous-in-package-search-pattern*
2203 (new-search-pattern :string-insensitive :backward "in-package" nil))
2204
2205(defun package-at-mark (start-mark &optional return-mark)
2206 (let* ((pattern *previous-in-package-search-pattern*)
2207 (mark (copy-mark start-mark :temporary)))
2208 (with-mark ((start mark)
2209 (end mark)
2210 (list-end mark))
2211 (loop for i from 0 do
2212 (tagbody ; I'm not proud of this
2213 (unless (find-pattern mark pattern)
2214 (return))
2215 (pre-command-parse-check mark)
2216 (when (valid-spot mark t)
2217 (move-mark end mark)
2218 (when (form-offset end 1)
2219 (move-mark start end)
2220 (when (backward-up-list start)
2221 (move-mark list-end start)
2222 (unless (and (list-offset list-end 1)
2223 (mark<= list-end start-mark))
2224 (go again))
2225 (when (scan-char start :lisp-syntax :constituent)
2226 (unless (or (mark= mark start)
2227 (let* ((s (nstring-upcase (region-to-string (region start end))))
2228 (*package* (find-package "CL-USER")))
2229 (eq (ignore-errors (values (read-from-string s)))
2230 'in-package)))
2231 (go again))
2232 (unless (form-offset end 1) (go again))
2233 (move-mark start end)
2234 (unless (form-offset start -1) (go again))
2235 (return
2236 (if (eql (next-character start) #\")
2237 (progn
2238 (character-offset start 1)
2239 (character-offset end -1)
2240 (when return-mark (move-mark return-mark list-end))
2241 (region-to-string (region start end)))
2242 (let* ((pkgname (ignore-errors (read-from-string (region-to-string (region start end))))))
2243 (when (and pkgname (setq pkgname (ignore-errors (string pkgname))))
2244 (when return-mark (move-mark return-mark list-end))
2245 pkgname))))))))
2246 again)))))
2247
2248(defun ensure-buffer-package (buffer)
2249 (or (variable-value 'current-package :buffer buffer)
2250 (setf (variable-value 'current-package :buffer buffer)
2251 (buffer-first-in-package-form buffer))))
2252
2253(defun buffer-package (buffer)
2254 (when (hemlock-bound-p 'current-package :buffer buffer)
2255 (let ((package-name (variable-value 'current-package :buffer buffer)))
2256 (find-package package-name))))
2257
2258(defun setup-lisp-mode (buffer)
2259 (unless (hemlock-bound-p 'current-package :buffer buffer)
2260 (defhvar "Current Package"
2261 "The package used for evaluation of Lisp in this buffer."
2262 :buffer buffer
2263 :value nil
2264 :hooks (list 'package-name-change-hook)))
2265 (unless (hemlock-bound-p 'default-package :buffer buffer)
2266 (defhvar "Default Package"
2267 "The package to use if the current package doesn't exist or isn't set."
2268 :buffer buffer
2269 :value (package-name *package*))))
2270
2271
2272
2273
2274
2275
2276;;;; Some mode variables to coordinate with other stuff.
2277
2278(defhvar "Auto Fill Space Indent"
2279 "When non-nil, uses \"Indent New Comment Line\" to break lines instead of
2280 \"New Line\"."
2281 :mode "Lisp" :value t)
2282
2283(defhvar "Comment Start"
2284 "String that indicates the start of a comment."
2285 :mode "Lisp" :value ";")
2286
2287(defhvar "Comment Begin"
2288 "String that is inserted to begin a comment."
2289 :mode "Lisp" :value "; ")
2290
2291(defhvar "Indent Function"
2292 "Indentation function which is invoked by \"Indent\" command.
2293 It must take one argument that is the prefix argument."
2294 :value 'indent-for-lisp
2295 :mode "Lisp")
2296
2297(defun string-to-arglist (string buffer &optional quiet-if-unknown)
2298 (multiple-value-bind (name error)
2299 (let* ((*package* (or
2300 (find-package
2301 (variable-value 'current-package :buffer buffer))
2302 *package*)))
2303 (ignore-errors (values (read-from-string string))))
2304 (unless error
2305 (when (typep name 'symbol)
2306 (multiple-value-bind (arglist win)
2307 (ccl::arglist-string name)
2308 (if (or win (not quiet-if-unknown))
2309 (format nil "~S : ~A" name (if win (or arglist "()") "(unknown)"))))))))
2310
2311(defcommand "Current Function Arglist" (p)
2312 "Show arglist of function whose name precedes point."
2313 "Show arglist of function whose name precedes point."
2314 (declare (ignore p))
2315 (let ((point (current-point)))
2316 (pre-command-parse-check point)
2317 (with-mark ((mark1 point)
2318 (mark2 point))
2319 (when (backward-up-list mark1)
2320 (when (form-offset (move-mark mark2 (mark-after mark1)) 1)
2321 (let* ((fun-name (region-to-string (region mark1 mark2)))
2322 (arglist-string (string-to-arglist fun-name (current-buffer))))
2323 (when arglist-string
2324 (message "~a" arglist-string))))))))
2325
2326(defcommand "Arglist On Space" (p)
2327 "Insert a space, then show the current function's arglist."
2328 "Insert a space, then show the current function's arglist."
2329 (declare (ignore p))
2330 (let ((point (current-point-for-insertion)))
2331 (insert-character point #\Space)
2332 (pre-command-parse-check point)
2333 (with-mark ((m point))
2334 (when (backward-up-list m)
2335 (when (and (scan-char m :lisp-syntax :open-paren)
2336 (mark-after m))
2337 (with-mark ((n m))
2338 (forward-form n)
2339 (let* ((fun-name (region-to-string (region m n)))
2340 (arglist-string (string-to-arglist fun-name (current-buffer) t)))
2341 (when arglist-string
2342 (message "~a" arglist-string)))))))))
2343
2344(hi:defcommand "Show Callers" (p)
2345 "Display a scrolling list of the callers of the symbol at point.
2346 Double-click a row to go to the caller's definition."
2347 (declare (ignore p))
2348 (with-mark ((mark1 (current-point))
2349 (mark2 (current-point)))
2350 (let* ((*package* (or (find-package (package-at-mark mark1)) *package*)))
2351 (mark-symbol mark1 mark2)
2352 (with-input-from-region (s (region mark1 mark2))
2353 (let* ((symbol (read s)))
2354 (hemlock-ext:open-sequence-dialog
2355 :title (format nil "Callers of ~a" symbol)
2356 :sequence (ccl::callers symbol)
2357 :action #'edit-definition))))))
2358
2359#||
2360(defcommand "Set Package Name" (p)
2361 (variable-value 'current-package :buffer buffer)
2362||#
2363
2364(defcommand "Insert Sharp Comment" (p)
2365 "Inserts #| |# around the selection and puts point between them."
2366 (declare (ignore p))
2367 (multiple-value-bind (start end) (buffer-selection-range (current-buffer))
2368 (let ((point (current-point)))
2369 (cond ((= start end)
2370 (insert-string point "#|")
2371 (insert-character point #\newline)
2372 (insert-character point #\newline)
2373 (insert-string point "|#")
2374 (character-offset point -3))
2375 (t
2376 (with-mark ((start-mark point :left-inserting)
2377 (end-mark point :left-inserting))
2378 (move-to-absolute-position start-mark start)
2379 (move-to-absolute-position end-mark end)
2380 (insert-string start-mark "#|
2381")
2382 (insert-string end-mark "
2383|#")))))))
Note: See TracBrowser for help on using the repository browser.