aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2010-08-18 12:57:48 +0200
committerStefan Monnier2010-08-18 12:57:48 +0200
commit8723cfa464656bd17dbae6c7ef5c11539603ce55 (patch)
tree03caeefd8cd9793b995dcc9c8dc32981904ffd17
parent0193499fe1b0666b73bdd4a4e628e0af065ea42f (diff)
downloademacs-8723cfa464656bd17dbae6c7ef5c11539603ce55.tar.gz
emacs-8723cfa464656bd17dbae6c7ef5c11539603ce55.zip
Try and remove some of SMIE's black magic by generalizing some rules.
* lisp/emacs-lisp/smie.el (smie-default-backward-token) (smie-default-forward-token): Strip properties. (smie-next-sexp): Be more careful with associative operators. (smie-forward-sexp-command): Generalize. (smie-backward-sexp-command): Simplify. (smie-closer-alist): New var. (smie-close-block): New command. (smie-indent-debug-log): New var. (smie-indent-offset-rule): Add a few more cases. (smie-indent-column): New function. (smie-indent-after-keyword): Use it. (smie-indent-keyword): Use it. Fix up the opener code's point position. (smie-indent-comment): Only applies at BOL. (smie-indent-debug): New command.
-rw-r--r--lisp/ChangeLog16
-rw-r--r--lisp/emacs-lisp/smie.el451
2 files changed, 339 insertions, 128 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 884427a1af1..9a6982c9751 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,5 +1,21 @@
12010-08-18 Stefan Monnier <monnier@iro.umontreal.ca> 12010-08-18 Stefan Monnier <monnier@iro.umontreal.ca>
2 2
3 * emacs-lisp/smie.el (smie-default-backward-token)
4 (smie-default-forward-token): Strip properties.
5 (smie-next-sexp): Be more careful with associative operators.
6 (smie-forward-sexp-command): Generalize.
7 (smie-backward-sexp-command): Simplify.
8 (smie-closer-alist): New var.
9 (smie-close-block): New command.
10 (smie-indent-debug-log): New var.
11 (smie-indent-offset-rule): Add a few more cases.
12 (smie-indent-column): New function.
13 (smie-indent-after-keyword): Use it.
14 (smie-indent-keyword): Use it.
15 Fix up the opener code's point position.
16 (smie-indent-comment): Only applies at BOL.
17 (smie-indent-debug): New command.
18
3 * emacs-lisp/autoload.el (make-autoload): Preload the macros's 19 * emacs-lisp/autoload.el (make-autoload): Preload the macros's
4 declarations that are useful before running the macro. 20 declarations that are useful before running the macro.
5 21
diff --git a/lisp/emacs-lisp/smie.el b/lisp/emacs-lisp/smie.el
index fb1e4737d39..cb8e8fd9843 100644
--- a/lisp/emacs-lisp/smie.el
+++ b/lisp/emacs-lisp/smie.el
@@ -65,6 +65,9 @@
65 65
66;;; Code: 66;;; Code:
67 67
68;; FIXME: I think the behavior on empty lines is wrong. It shouldn't
69;; look at the next token on subsequent lines.
70
68(eval-when-compile (require 'cl)) 71(eval-when-compile (require 'cl))
69 72
70(defvar comment-continue) 73(defvar comment-continue)
@@ -204,6 +207,17 @@ one of those elements share the same precedence level and associativity."
204 prec2)) 207 prec2))
205 208
206(defun smie-prec2-levels (prec2) 209(defun smie-prec2-levels (prec2)
210 ;; FIXME: Rather than only return an alist of precedence levels, we should
211 ;; also extract other useful data from it:
212 ;; - matching sets of block openers&closers (which can otherwise become
213 ;; collapsed into a single equivalence class in smie-op-levels) for
214 ;; smie-close-block as well as to detect mismatches in smie-next-sexp
215 ;; or in blink-paren (as well as to do the blink-paren for inner
216 ;; keywords like the "in" of "let..in..end").
217 ;; - better default indentation rules (i.e. non-zero indentation after inner
218 ;; keywords like the "in" of "let..in..end") for smie-indent-after-keyword.
219 ;; Of course, maybe those things would be even better handled in the
220 ;; bnf->prec function.
207 "Take a 2D precedence table and turn it into an alist of precedence levels. 221 "Take a 2D precedence table and turn it into an alist of precedence levels.
208PREC2 is a table as returned by `smie-precs-precedence-table' or 222PREC2 is a table as returned by `smie-precs-precedence-table' or
209`smie-bnf-precedence-table'." 223`smie-bnf-precedence-table'."
@@ -321,32 +335,30 @@ it should move backward to the beginning of the previous token.")
321 335
322(defun smie-default-backward-token () 336(defun smie-default-backward-token ()
323 (forward-comment (- (point))) 337 (forward-comment (- (point)))
324 (buffer-substring (point) 338 (buffer-substring-no-properties
325 (progn (if (zerop (skip-syntax-backward ".")) 339 (point)
326 (skip-syntax-backward "w_'")) 340 (progn (if (zerop (skip-syntax-backward "."))
327 (point)))) 341 (skip-syntax-backward "w_'"))
342 (point))))
328 343
329(defun smie-default-forward-token () 344(defun smie-default-forward-token ()
330 (forward-comment (point-max)) 345 (forward-comment (point-max))
331 (buffer-substring (point) 346 (buffer-substring-no-properties
332 (progn (if (zerop (skip-syntax-forward ".")) 347 (point)
333 (skip-syntax-forward "w_'")) 348 (progn (if (zerop (skip-syntax-forward "."))
334 (point)))) 349 (skip-syntax-forward "w_'"))
350 (point))))
335 351
336(defun smie-associative-p (toklevels) 352(defun smie-associative-p (toklevels)
337 ;; in "a + b + c" we want to stop at each +, but in 353 ;; in "a + b + c" we want to stop at each +, but in
338 ;; "if a then b else c" we don't want to stop at each keyword. 354 ;; "if a then b elsif c then d else c" we don't want to stop at each keyword.
339 ;; To distinguish the two cases, we made smie-prec2-levels choose 355 ;; To distinguish the two cases, we made smie-prec2-levels choose
340 ;; different levels for each part of "if a then b else c", so that 356 ;; different levels for each part of "if a then b else c", so that
341 ;; by checking if the left-level is equal to the right level, we can 357 ;; by checking if the left-level is equal to the right level, we can
342 ;; figure out that it's an associative operator. 358 ;; figure out that it's an associative operator.
343 ;; This is not 100% foolproof, tho, since a grammar like 359 ;; This is not 100% foolproof, tho, since the "elsif" will have to have
344 ;; (exp ("A" exp "C") ("A" exp "B" exp "C")) 360 ;; equal left and right levels (since it's optional), so smie-next-sexp
345 ;; will cause "B" to have equal left and right levels, even though 361 ;; has to be careful to distinguish those different cases.
346 ;; it is not an associative operator.
347 ;; A better check would be the check the actual previous operator
348 ;; against this one to see if it's the same, but we'd have to change
349 ;; `levels' to keep a stack of operators rather than only levels.
350 (eq (smie-op-left toklevels) (smie-op-right toklevels))) 362 (eq (smie-op-left toklevels) (smie-op-right toklevels)))
351 363
352(defun smie-next-sexp (next-token next-sexp op-forw op-back halfsexp) 364(defun smie-next-sexp (next-token next-sexp op-forw op-back halfsexp)
@@ -371,51 +383,71 @@ Possible return values:
371 (let* ((pos (point)) 383 (let* ((pos (point))
372 (token (funcall next-token)) 384 (token (funcall next-token))
373 (toklevels (cdr (assoc token smie-op-levels)))) 385 (toklevels (cdr (assoc token smie-op-levels))))
374
375 (cond 386 (cond
376 ((null toklevels) 387 ((null toklevels)
377 (when (zerop (length token)) 388 (when (zerop (length token))
378 (condition-case err 389 (condition-case err
379 (progn (goto-char pos) (funcall next-sexp 1) nil) 390 (progn (goto-char pos) (funcall next-sexp 1) nil)
380 (scan-error (throw 'return (list t (caddr err))))) 391 (scan-error (throw 'return
392 (list t (caddr err)
393 (buffer-substring-no-properties
394 (caddr err)
395 (+ (caddr err)
396 (if (< (point) (caddr err))
397 -1 1)))))))
381 (if (eq pos (point)) 398 (if (eq pos (point))
382 ;; We did not move, so let's abort the loop. 399 ;; We did not move, so let's abort the loop.
383 (throw 'return (list t (point)))))) 400 (throw 'return (list t (point))))))
384 ((null (funcall op-back toklevels)) 401 ((null (funcall op-back toklevels))
385 ;; A token like a paren-close. 402 ;; A token like a paren-close.
386 (assert (funcall op-forw toklevels)) ;Otherwise, why mention it? 403 (assert (funcall op-forw toklevels)) ;Otherwise, why mention it?
387 (push (funcall op-forw toklevels) levels)) 404 (push toklevels levels))
388 (t 405 (t
389 (while (and levels (< (funcall op-back toklevels) (car levels))) 406 (while (and levels (< (funcall op-back toklevels)
407 (funcall op-forw (car levels))))
390 (setq levels (cdr levels))) 408 (setq levels (cdr levels)))
391 (cond 409 (cond
392 ((null levels) 410 ((null levels)
393 (if (and halfsexp (funcall op-forw toklevels)) 411 (if (and halfsexp (funcall op-forw toklevels))
394 (push (funcall op-forw toklevels) levels) 412 (push toklevels levels)
395 (throw 'return 413 (throw 'return
396 (prog1 (list (or (car toklevels) t) (point) token) 414 (prog1 (list (or (car toklevels) t) (point) token)
397 (goto-char pos))))) 415 (goto-char pos)))))
398 (t 416 (t
399 (if (and levels (= (funcall op-back toklevels) (car levels))) 417 (let ((lastlevels levels))
418 (if (and levels (= (funcall op-back toklevels)
419 (funcall op-forw (car levels))))
400 (setq levels (cdr levels))) 420 (setq levels (cdr levels)))
421 ;; We may have found a match for the previously pending
422 ;; operator. Is this the end?
401 (cond 423 (cond
402 ((null levels) 424 ;; Keep looking as long as we haven't matched the
403 (cond 425 ;; topmost operator.
426 (levels
427 (if (funcall op-forw toklevels)
428 (push toklevels levels)))
429 ;; We matched the topmost operator. If the new operator
430 ;; is the last in the corresponding BNF rule, we're done.
404 ((null (funcall op-forw toklevels)) 431 ((null (funcall op-forw toklevels))
432 ;; It is the last element, let's stop here.
405 (throw 'return (list nil (point) token))) 433 (throw 'return (list nil (point) token)))
406 ((smie-associative-p toklevels) 434 ;; If the new operator is not the last in the BNF rule,
435 ;; ans is not associative, it's one of the inner operators
436 ;; (like the "in" in "let .. in .. end"), so keep looking.
437 ((not (smie-associative-p toklevels))
438 (push toklevels levels))
439 ;; The new operator is associative. Two cases:
440 ;; - it's really just an associative operator (like + or ;)
441 ;; in which case we should have stopped right before.
442 ((and lastlevels
443 (smie-associative-p (car lastlevels)))
407 (throw 'return 444 (throw 'return
408 (prog1 (list (or (car toklevels) t) (point) token) 445 (prog1 (list (or (car toklevels) t) (point) token)
409 (goto-char pos)))) 446 (goto-char pos))))
410 ;; We just found a match to the previously pending operator 447 ;; - it's an associative operator within a larger construct
411 ;; but this new operator is still part of a larger RHS. 448 ;; (e.g. an "elsif"), so we should just ignore it and keep
412 ;; E.g. we're now looking at the "then" in 449 ;; looking for the closing element.
413 ;; "if a then b else c". So we have to keep parsing the 450 (t (setq levels lastlevels))))))))
414 ;; rest of the construct.
415 (t (push (funcall op-forw toklevels) levels))))
416 (t
417 (if (funcall op-forw toklevels)
418 (push (funcall op-forw toklevels) levels))))))))
419 levels) 451 levels)
420 (setq halfsexp nil))))) 452 (setq halfsexp nil)))))
421 453
@@ -455,37 +487,78 @@ Possible return values:
455 (indirect-function 'smie-op-left) 487 (indirect-function 'smie-op-left)
456 halfsexp)) 488 halfsexp))
457 489
490;;; Miscellanous commands using the precedence parser.
491
458(defun smie-backward-sexp-command (&optional n) 492(defun smie-backward-sexp-command (&optional n)
459 "Move backward through N logical elements." 493 "Move backward through N logical elements."
460 (interactive "p") 494 (interactive "^p")
461 (if (< n 0) 495 (smie-forward-sexp-command (- n)))
462 (smie-forward-sexp-command (- n))
463 (let ((forward-sexp-function nil))
464 (while (> n 0)
465 (decf n)
466 (let ((pos (point))
467 (res (smie-backward-sexp 'halfsexp)))
468 (if (and (car res) (= pos (point)) (not (bolp)))
469 (signal 'scan-error
470 (list "Containing expression ends prematurely"
471 (cadr res) (cadr res)))
472 nil))))))
473 496
474(defun smie-forward-sexp-command (&optional n) 497(defun smie-forward-sexp-command (&optional n)
475 "Move forward through N logical elements." 498 "Move forward through N logical elements."
476 (interactive "p") 499 (interactive "^p")
477 (if (< n 0) 500 (let ((forw (> n 0))
478 (smie-backward-sexp-command (- n)) 501 (forward-sexp-function nil))
479 (let ((forward-sexp-function nil)) 502 (while (/= n 0)
480 (while (> n 0) 503 (setq n (- n (if forw 1 -1)))
481 (decf n)
482 (let ((pos (point)) 504 (let ((pos (point))
483 (res (smie-forward-sexp 'halfsexp))) 505 (res (if forw
484 (if (and (car res) (= pos (point)) (not (bolp))) 506 (smie-forward-sexp 'halfsexp)
507 (smie-backward-sexp 'halfsexp))))
508 (if (and (car res) (= pos (point)) (not (if forw (eolp) (bobp))))
485 (signal 'scan-error 509 (signal 'scan-error
486 (list "Containing expression ends prematurely" 510 (list "Containing expression ends prematurely"
487 (cadr res) (cadr res))) 511 (cadr res) (cadr res)))
488 nil)))))) 512 nil)))))
513
514(defvar smie-closer-alist nil
515 "Alist giving the closer corresponding to an opener.")
516
517(defun smie-close-block ()
518 "Close the closest surrounding block."
519 (interactive)
520 (let ((closer
521 (save-excursion
522 (backward-up-list 1)
523 (if (looking-at "\\s(")
524 (string (cdr (syntax-after (point))))
525 (let* ((open (funcall smie-forward-token-function))
526 (closer (cdr (assoc open smie-closer-alist)))
527 (levels (list (assoc open smie-op-levels)))
528 (seen '())
529 (found '()))
530 (cond
531 ;; Even if we improve the auto-computation of closers,
532 ;; there are still cases where we need manual
533 ;; intervention, e.g. for Octave's use of `until'
534 ;; as a pseudo-closer of `do'.
535 (closer)
536 ((or (equal levels '(nil)) (nth 1 (car levels)))
537 (error "Doesn't look like a block"))
538 (t
539 ;; FIXME: With grammars like Octave's, every closer ("end",
540 ;; "endif", "endwhile", ...) has the same level, so we'd need
541 ;; to look at the BNF or at least at the 2D prec-table, in
542 ;; order to find the right closer for a given opener.
543 (while levels
544 (let ((level (pop levels)))
545 (dolist (other smie-op-levels)
546 (when (and (eq (nth 2 level) (nth 1 other))
547 (not (memq other seen)))
548 (push other seen)
549 (if (nth 2 other)
550 (push other levels)
551 (push (car other) found))))))
552 (cond
553 ((null found) (error "No known closer for opener %s" open))
554 ;; FIXME: what should we do if there are various closers?
555 (t (car found))))))))))
556 (unless (save-excursion (skip-chars-backward " \t") (bolp))
557 (newline))
558 (insert closer)
559 (if (save-excursion (skip-chars-forward " \t") (eolp))
560 (indent-according-to-mode)
561 (reindent-then-newline-and-indent))))
489 562
490;;; The indentation engine. 563;;; The indentation engine.
491 564
@@ -505,22 +578,33 @@ Possible return values:
505 "Rules of the following form. 578 "Rules of the following form.
506\((:before . TOK) . OFFSET-RULES) how to indent TOK itself. 579\((:before . TOK) . OFFSET-RULES) how to indent TOK itself.
507\(TOK . OFFSET-RULES) how to indent right after TOK. 580\(TOK . OFFSET-RULES) how to indent right after TOK.
508\((T1 . T2) . OFFSET) how to indent token T2 w.r.t T1.
509\((t . TOK) . OFFSET) how to indent TOK with respect to its parent.
510\(list-intro . TOKENS) declare TOKENS as being followed by what may look like 581\(list-intro . TOKENS) declare TOKENS as being followed by what may look like
511 a funcall but is just a sequence of expressions. 582 a funcall but is just a sequence of expressions.
512\(t . OFFSET) basic indentation step. 583\(t . OFFSET) basic indentation step.
513\(args . OFFSET) indentation of arguments. 584\(args . OFFSET) indentation of arguments.
585\((T1 . T2) OFFSET) like ((:before . T2) (:parent T1 OFFSET)).
514 586
515OFFSET-RULES is a list of elements which can each either be: 587OFFSET-RULES is a list of elements which can each either be:
516 588
517\(:hanging . OFFSET-RULES) if TOK is hanging, use OFFSET-RULES. 589\(:hanging . OFFSET-RULES) if TOK is hanging, use OFFSET-RULES.
518\(:parent PARENT . OFFSET-RULES) if TOK's parent is PARENT, use OFFSET-RULES. 590\(:parent PARENT . OFFSET-RULES) if TOK's parent is PARENT, use OFFSET-RULES.
519\(:next TOKEN . OFFSET-RULES) if TOK is followed by TOKEN, use OFFSET-RULES. 591\(:next TOKEN . OFFSET-RULES) if TOK is followed by TOKEN, use OFFSET-RULES.
520\(:prev TOKEN . OFFSET-RULES) if TOK is preceded by TOKEN, use OFFSET-RULES. 592\(:prev TOKEN . OFFSET-RULES) if TOK is preceded by TOKEN, use
521a number the offset to use. 593\(:bolp . OFFSET-RULES) If TOK is first on a line, use OFFSET-RULES.
594OFFSET the offset to use.
595
596PARENT can be either the name of the parent or `open' to mean any parent
597which acts as an open-paren (i.e. has a nil left-precedence).
598
599OFFSET can be of the form:
522`point' align with the token. 600`point' align with the token.
523`parent' align with the parent. 601`parent' align with the parent.
602NUMBER offset by NUMBER.
603\(+ OFFSETS...) use the sum of OFFSETS.
604
605The precise meaning of `point' depends on various details: it can
606either mean the position of the token we're indenting, or the
607position of its parent, or the position right after its parent.
524 608
525A nil offset for indentation after a token defaults to `smie-indent-basic'.") 609A nil offset for indentation after a token defaults to `smie-indent-basic'.")
526 610
@@ -543,21 +627,33 @@ A nil offset for indentation after a token defaults to `smie-indent-basic'.")
543 (cdr (assq t smie-indent-rules)) 627 (cdr (assq t smie-indent-rules))
544 smie-indent-basic)) 628 smie-indent-basic))
545 629
546(defun smie-indent-offset-rule (tokinfo &optional after) 630(defvar smie-indent-debug-log)
631
632(defun smie-indent-offset-rule (tokinfo &optional after parent)
547 "Apply the OFFSET-RULES in TOKINFO. 633 "Apply the OFFSET-RULES in TOKINFO.
548Point is expected to be right in front of the token corresponding to TOKINFO. 634Point is expected to be right in front of the token corresponding to TOKINFO.
549If computing the indentation after the token, then AFTER is the position 635If computing the indentation after the token, then AFTER is the position
550after the token." 636after the token, otherwise it should be nil.
637PARENT if non-nil should be the parent info returned by `smie-backward-sexp'."
551 (let ((rules (cdr tokinfo)) 638 (let ((rules (cdr tokinfo))
552 parent next prev 639 next prev
553 offset) 640 offset)
554 (while (consp rules) 641 (while (consp rules)
555 (let ((rule (pop rules))) 642 (let ((rule (pop rules)))
556 (cond 643 (cond
557 ((not (consp rule)) (setq offset rule)) 644 ((not (consp rule)) (setq offset rule))
645 ((eq (car rule) '+) (setq offset rule))
558 ((eq (car rule) :hanging) 646 ((eq (car rule) :hanging)
559 (when (smie-indent-hanging-p) 647 (when (smie-indent-hanging-p)
560 (setq rules (cdr rule)))) 648 (setq rules (cdr rule))))
649 ((eq (car rule) :bolp)
650 (when (smie-bolp)
651 (setq rules (cdr rule))))
652 ((eq (car rule) :eolp)
653 (unless after
654 (error "Can't use :eolp in :before indentation rules"))
655 (when (> after (line-end-position))
656 (setq rules (cdr rule))))
561 ((eq (car rule) :prev) 657 ((eq (car rule) :prev)
562 (unless prev 658 (unless prev
563 (save-excursion 659 (save-excursion
@@ -578,12 +674,60 @@ after the token."
578 (save-excursion 674 (save-excursion
579 (if after (goto-char after)) 675 (if after (goto-char after))
580 (setq parent (smie-backward-sexp 'halfsexp)))) 676 (setq parent (smie-backward-sexp 'halfsexp))))
581 (when (equal (nth 2 parent) (cadr rule)) 677 (when (or (equal (nth 2 parent) (cadr rule))
678 (and (eq (cadr rule) 'open) (null (car parent))))
582 (setq rules (cddr rule)))) 679 (setq rules (cddr rule))))
583 (t (error "Unknown rule %s for indentation of %s" 680 (t (error "Unknown rule %s for indentation of %s"
584 rule (car tokinfo)))))) 681 rule (car tokinfo))))))
682 ;; If `offset' is not set yet, use `rules' to handle the case where
683 ;; the tokinfo uses the old-style ((PARENT . TOK). OFFSET).
684 (unless offset (setq offset rules))
685 (when (boundp 'smie-indent-debug-log)
686 (push (list (point) offset tokinfo) smie-indent-debug-log))
585 offset)) 687 offset))
586 688
689(defun smie-indent-column (offset &optional base parent virtual-point)
690 "Compute the actual column to use for a given OFFSET.
691BASE is the base position to use, and PARENT is the parent info, if any.
692If VIRTUAL-POINT is non-nil, then `point' is virtual."
693 (cond
694 ((eq (car-safe offset) '+)
695 (apply '+ (mapcar (lambda (offset) (smie-indent-column offset nil parent))
696 (cdr offset))))
697 ((integerp offset)
698 (+ offset
699 (case base
700 ((nil) 0)
701 (parent (goto-char (cadr parent))
702 (smie-indent-virtual))
703 (t
704 (goto-char base)
705 ;; For indentation after "(let" in SML-mode, we end up accumulating
706 ;; the offset of "(" and the offset of "let", so we use `min' to try
707 ;; and get it right either way.
708 (min (smie-indent-virtual) (current-column))))))
709 ((eq offset 'point)
710 ;; In indent-keyword, if we're indenting `then' wrt `if', we want to use
711 ;; indent-virtual rather than use just current-column, so that we can
712 ;; apply the (:before . "if") rule which does the "else if" dance in SML.
713 ;; But in other cases, we do not want to use indent-virtual
714 ;; (e.g. indentation of "*" w.r.t "+", or ";" wrt "("). We could just
715 ;; always use indent-virtual and then have indent-rules say explicitly
716 ;; to use `point' after things like "(" or "+" when they're not at EOL,
717 ;; but you'd end up with lots of those rules.
718 ;; So we use a heuristic here, which is that we only use virtual if
719 ;; the parent is tightly linked to the child token (they're part of
720 ;; the same BNF rule).
721 (if (and virtual-point (null (car parent))) ;Black magic :-(
722 (smie-indent-virtual) (current-column)))
723 ((eq offset 'parent)
724 (unless parent
725 (setq parent (or (smie-backward-sexp 'halfsexp) :notfound)))
726 (if (consp parent) (goto-char (cadr parent)))
727 (smie-indent-virtual))
728 ((eq offset nil) nil)
729 (t (error "Unknown indentation offset %s" offset))))
730
587(defun smie-indent-forward-token () 731(defun smie-indent-forward-token ()
588 "Skip token forward and return it, along with its levels." 732 "Skip token forward and return it, along with its levels."
589 (let ((tok (funcall smie-forward-token-function))) 733 (let ((tok (funcall smie-forward-token-function)))
@@ -655,7 +799,9 @@ in order to figure out the indentation of some other (further down) point."
655 (toklevels (smie-indent-forward-token)) 799 (toklevels (smie-indent-forward-token))
656 (token (pop toklevels))) 800 (token (pop toklevels)))
657 (if (null (car toklevels)) 801 (if (null (car toklevels))
658 ;; Different case: 802 (save-excursion
803 (goto-char pos)
804 ;; Different cases:
659 ;; - smie-bolp: "indent according to others". 805 ;; - smie-bolp: "indent according to others".
660 ;; - common hanging: "indent according to others". 806 ;; - common hanging: "indent according to others".
661 ;; - SML-let hanging: "indent like parent". 807 ;; - SML-let hanging: "indent like parent".
@@ -668,72 +814,115 @@ in order to figure out the indentation of some other (further down) point."
668 ;; based on other rules (typically smie-indent-after-keyword). 814 ;; based on other rules (typically smie-indent-after-keyword).
669 nil) 815 nil)
670 (t 816 (t
671 (let* ((tokinfo (or (assoc (cons :before token) smie-indent-rules) 817 ;; We're only ever here for virtual-indent, which is why
818 ;; we can use (current-column) as answer for `point'.
819 (let* ((tokinfo (or (assoc (cons :before token)
820 smie-indent-rules)
672 ;; By default use point unless we're hanging. 821 ;; By default use point unless we're hanging.
673 (cons (cons :before token) 822 `((:before . ,token) (:hanging nil) point)))
674 '((:hanging nil) point)))) 823 ;; (after (prog1 (point) (goto-char pos)))
675 (after (prog1 (point) (goto-char pos)))
676 (offset (smie-indent-offset-rule tokinfo))) 824 (offset (smie-indent-offset-rule tokinfo)))
677 (cond 825 (smie-indent-column offset)))))
678 ((eq offset 'point) (current-column))
679 ((eq offset 'parent)
680 (let ((parent (smie-backward-sexp 'halfsexp)))
681 (if parent (goto-char (cadr parent))))
682 (smie-indent-virtual))
683 ((eq offset nil) nil)
684 (t (error "Unhandled offset %s in %s"
685 offset (cons :before token)))))))
686 826
687 ;; FIXME: This still looks too much like black magic!! 827 ;; FIXME: This still looks too much like black magic!!
688 ;; FIXME: Rather than a bunch of rules like (PARENT . TOKEN), we 828 ;; FIXME: Rather than a bunch of rules like (PARENT . TOKEN), we
689 ;; want a single rule for TOKEN with different cases for each PARENT. 829 ;; want a single rule for TOKEN with different cases for each PARENT.
690 (let ((res (smie-backward-sexp 'halfsexp)) tmp) 830 (let* ((parent (smie-backward-sexp 'halfsexp))
831 (tokinfo
832 (or (assoc (cons (caddr parent) token)
833 smie-indent-rules)
834 (assoc (cons :before token) smie-indent-rules)
835 ;; Default rule.
836 `((:before . ,token)
837 ;; (:parent open 0)
838 point)))
839 (offset (save-excursion
840 (goto-char pos)
841 (smie-indent-offset-rule tokinfo nil parent))))
842 ;; Different behaviors:
843 ;; - align with parent.
844 ;; - parent + offset.
845 ;; - after parent's column + offset (actually, after or before
846 ;; depending on where backward-sexp stopped).
847 ;; ? let it drop to some other indentation function (almost never).
848 ;; ? parent + offset + parent's own offset.
849 ;; Different cases:
850 ;; - bump into a same-level operator.
851 ;; - bump into a specific known parent.
852 ;; - find a matching open-paren thingy.
853 ;; - bump into some random parent.
854 ;; ? borderline case (almost never).
855 ;; ? bump immediately into a parent.
691 (cond 856 (cond
692 ((not (or (< (point) pos) 857 ((not (or (< (point) pos)
693 (and (cadr res) (< (cadr res) pos)))) 858 (and (cadr parent) (< (cadr parent) pos))))
694 ;; If we didn't move at all, that means we didn't really skip 859 ;; If we didn't move at all, that means we didn't really skip
695 ;; what we wanted. 860 ;; what we wanted. Should almost never happen, other than
861 ;; maybe when an infix or close-paren is at the beginning
862 ;; of a buffer.
696 nil) 863 nil)
697 ((eq (car res) (car toklevels)) 864 ((eq (car parent) (car toklevels))
698 ;; We bumped into a same-level operator. align with it. 865 ;; We bumped into a same-level operator. align with it.
699 (goto-char (cadr res)) 866 (if (and (smie-bolp) (/= (point) pos)
867 (save-excursion
868 (goto-char (goto-char (cadr parent)))
869 (not (smie-bolp)))
870 ;; Check the offset of `token' rather then its parent
871 ;; because its parent may have used a special rule. E.g.
872 ;; function foo;
873 ;; line2;
874 ;; line3;
875 ;; The ; on the first line had a special rule, but when
876 ;; indenting line3, we don't care about it and want to
877 ;; align with line2.
878 (memq offset '(point nil)))
879 ;; If the parent is at EOL and its children are indented like
880 ;; itself, then we can just obey the indentation chosen for the
881 ;; child.
882 ;; This is important for operators like ";" which
883 ;; are usually at EOL (and have an offset of 0): otherwise we'd
884 ;; always go back over all the statements, which is
885 ;; a performance problem and would also mean that fixindents
886 ;; in the middle of such a sequence would be ignored.
887 ;;
888 ;; This is a delicate point!
889 ;; Even if the offset is not 0, we could follow the same logic
890 ;; and subtract the offset from the child's indentation.
891 ;; But that would more often be a bad idea: OT1H we generally
892 ;; want to reuse the closest similar indentation point, so that
893 ;; the user's choice (or the fixindents) are obeyed. But OTOH
894 ;; we don't want this to affect "unrelated" parts of the code.
895 ;; E.g. a fixindent in the body of a "begin..end" should not
896 ;; affect the indentation of the "end".
897 (current-column)
898 (goto-char (cadr parent))
700 ;; Don't use (smie-indent-virtual :not-hanging) here, because we 899 ;; Don't use (smie-indent-virtual :not-hanging) here, because we
701 ;; want to jump back over a sequence of same-level ops such as 900 ;; want to jump back over a sequence of same-level ops such as
702 ;; a -> b -> c 901 ;; a -> b -> c
703 ;; -> d 902 ;; -> d
704 ;; So as to align with the earliest appropriate place. 903 ;; So as to align with the earliest appropriate place.
705 (smie-indent-virtual)) 904 (smie-indent-virtual)))
706 ((setq tmp (assoc (cons (caddr res) token) 905 (tokinfo
707 smie-indent-rules)) 906 (if (and (= (point) pos) (smie-bolp)
708 (goto-char (cadr res)) 907 (or (eq offset 'point)
709 (+ (cdr tmp) (smie-indent-virtual))) ;:not-hanging 908 (and (consp offset) (memq 'point offset))))
710 ;; FIXME: The rules ((t . TOK) . OFFSET) either indent
711 ;; relative to "before the parent" or "after the parent",
712 ;; depending on details of the grammar.
713 ((null (car res))
714 (assert (eq (point) (cadr res)))
715 (goto-char (cadr res))
716 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0)
717 (smie-indent-virtual))) ;:not-hanging
718 ((and (= (point) pos) (smie-bolp))
719 ;; Since we started at BOL, we're not computing a virtual 909 ;; Since we started at BOL, we're not computing a virtual
720 ;; indentation, and we're still at the starting point, so the 910 ;; indentation, and we're still at the starting point, so
721 ;; next (default) rule can't be used since it uses `current-column' 911 ;; we can't use `current-column' which would cause
722 ;; which would cause. indentation to depend on itself. 912 ;; indentation to depend on itself.
723 ;; We could just return nil, but OTOH that's not good enough in 913 nil
724 ;; some cases. Instead, we want to combine the offset-rules for 914 (smie-indent-column offset 'parent parent
725 ;; the current token with the offset-rules of the previous one. 915 ;; If we're still at pos, indent-virtual
726 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0) 916 ;; will inf-loop.
727 ;; FIXME: This is odd. Can't we make it use 917 (unless (= (point) pos) 'virtual))))))))))
728 ;; smie-indent-(calculate|virtual) somehow?
729 (smie-indent-after-keyword)))
730 (t
731 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0)
732 (current-column)))))))))
733 918
734(defun smie-indent-comment () 919(defun smie-indent-comment ()
735 ;; Indentation of a comment. 920 "Compute indentation of a comment."
736 (and (looking-at comment-start-skip) 921 ;; Don't do it for virtual indentations. We should normally never be "in
922 ;; front of a comment" when doing virtual-indentation anyway. And if we are
923 ;; (as can happen in octave-mode), moving forward can lead to inf-loops.
924 (and (smie-bolp)
925 (looking-at comment-start-skip)
737 (save-excursion 926 (save-excursion
738 (forward-comment (point-max)) 927 (forward-comment (point-max))
739 (skip-chars-forward " \t\r\n") 928 (skip-chars-forward " \t\r\n")
@@ -761,26 +950,25 @@ in order to figure out the indentation of some other (further down) point."
761 (toklevel (smie-indent-backward-token)) 950 (toklevel (smie-indent-backward-token))
762 (tok (car toklevel)) 951 (tok (car toklevel))
763 (tokinfo (assoc tok smie-indent-rules))) 952 (tokinfo (assoc tok smie-indent-rules)))
953 ;; Set some default indent rules.
764 (if (and toklevel (null (cadr toklevel)) (null tokinfo)) 954 (if (and toklevel (null (cadr toklevel)) (null tokinfo))
765 (setq tokinfo (list (car toklevel)))) 955 (setq tokinfo (list (car toklevel))))
766 ;; (if (and tokinfo (null toklevel)) 956 ;; (if (and tokinfo (null toklevel))
767 ;; (error "Token %S has indent rule but has no parsing info" tok)) 957 ;; (error "Token %S has indent rule but has no parsing info" tok))
768 (when toklevel 958 (when toklevel
959 (unless tokinfo
960 ;; The default indentation after a keyword/operator is 0 for
961 ;; infix and t for prefix.
962 ;; Using the BNF syntax, we could come up with better
963 ;; defaults, but we only have the precedence levels here.
964 (setq tokinfo (list tok 'default-rule
965 (if (cadr toklevel) 0 (smie-indent-offset t)))))
769 (let ((offset 966 (let ((offset
770 (cond 967 (or (smie-indent-offset-rule tokinfo pos)
771 (tokinfo (or (smie-indent-offset-rule tokinfo pos) 968 (smie-indent-offset t))))
772 (smie-indent-offset t))) 969 (let ((before (point)))
773 ;; The default indentation after a keyword/operator 970 (goto-char pos)
774 ;; is 0 for infix and t for prefix. 971 (smie-indent-column offset before)))))))
775 ;; Using the BNF syntax, we could come up with
776 ;; better defaults, but we only have the
777 ;; precedence levels here.
778 ((null (cadr toklevel)) (smie-indent-offset t))
779 (t 0))))
780 ;; For indentation after "(let" in SML-mode, we end up accumulating
781 ;; the offset of "(" and the offset of "let", so we use `min' to try
782 ;; and get it right either way.
783 (+ (min (smie-indent-virtual) (current-column)) offset))))))
784 972
785(defun smie-indent-exps () 973(defun smie-indent-exps ()
786 ;; Indentation of sequences of simple expressions without 974 ;; Indentation of sequences of simple expressions without
@@ -851,7 +1039,7 @@ to which that point should be aligned, if we were to reindent it.")
851 "Indent current line using the SMIE indentation engine." 1039 "Indent current line using the SMIE indentation engine."
852 (interactive) 1040 (interactive)
853 (let* ((savep (point)) 1041 (let* ((savep (point))
854 (indent (condition-case nil 1042 (indent (condition-case-no-debug nil
855 (save-excursion 1043 (save-excursion
856 (forward-line 0) 1044 (forward-line 0)
857 (skip-chars-forward " \t") 1045 (skip-chars-forward " \t")
@@ -866,7 +1054,14 @@ to which that point should be aligned, if we were to reindent it.")
866 (save-excursion (indent-line-to indent)) 1054 (save-excursion (indent-line-to indent))
867 (indent-line-to indent))))) 1055 (indent-line-to indent)))))
868 1056
869;;;###autoload 1057(defun smie-indent-debug ()
1058 "Show the rules used to compute indentation of current line."
1059 (interactive)
1060 (let ((smie-indent-debug-log '()))
1061 (smie-indent-calculate)
1062 ;; FIXME: please improve!
1063 (message "%S" smie-indent-debug-log)))
1064
870(defun smie-setup (op-levels indent-rules) 1065(defun smie-setup (op-levels indent-rules)
871 (set (make-local-variable 'smie-indent-rules) indent-rules) 1066 (set (make-local-variable 'smie-indent-rules) indent-rules)
872 (set (make-local-variable 'smie-op-levels) op-levels) 1067 (set (make-local-variable 'smie-op-levels) op-levels)