| 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 | ||#
|
|---|