comparison lisp/emacs-lisp/smie.el @ 108894:ca3bfaa18e56

Make (after keyword) indent-rules more flexible. * lisp/emacs-lisp/smie.el (smie-indent-offset-after) (smie-indent-forward-token, smie-indent-backward-token): New functions. (smie-indent-after-keyword): Use them. (smie-indent-fixindent): Only applies to the indentation of the BOL. (smie-indent-keyword): Tweak the black magic. (smie-indent-comment-continue): Strip comment-continue before use. (smie-indent-functions): Indent comments before keywords.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Sun, 06 Jun 2010 22:10:19 -0400
parents c3cbf94d10f1
children 68586a267c40
comparison
equal deleted inserted replaced
108893:cc0a50e33241 108894:ca3bfaa18e56
292 ;;; Parsing using a precedence level table. 292 ;;; Parsing using a precedence level table.
293 293
294 (defvar smie-op-levels 'unset 294 (defvar smie-op-levels 'unset
295 "List of token parsing info. 295 "List of token parsing info.
296 Each element is of the form (TOKEN LEFT-LEVEL RIGHT-LEVEL). 296 Each element is of the form (TOKEN LEFT-LEVEL RIGHT-LEVEL).
297 Parsing is done using an operator precedence parser.") 297 Parsing is done using an operator precedence parser.
298 LEFT-LEVEL and RIGHT-LEVEL can be either numbers or nil, where nil
299 means that this operator does not bind on the corresponding side,
300 i.e. a LEFT-LEVEL of nil means this is a token that behaves somewhat like
301 an open-paren, whereas a RIGHT-LEVEL of nil would correspond to something
302 like a close-paren.")
298 303
299 (defvar smie-forward-token-function 'smie-default-forward-token 304 (defvar smie-forward-token-function 'smie-default-forward-token
300 "Function to scan forward for the next token. 305 "Function to scan forward for the next token.
301 Called with no argument should return a token and move to its end. 306 Called with no argument should return a token and move to its end.
302 If no token is found, return nil or the empty string. 307 If no token is found, return nil or the empty string.
493 ;; structure Foo = 498 ;; structure Foo =
494 ;; struct ... end 499 ;; struct ... end
495 ;; I.e. the indentation after "=" depends on the parent ("structure") 500 ;; I.e. the indentation after "=" depends on the parent ("structure")
496 ;; as well as on the following token ("struct"). 501 ;; as well as on the following token ("struct").
497 "Rules of the following form. 502 "Rules of the following form.
498 \(TOK OFFSET) how to indent right after TOK. 503 \(TOK . OFFSET-RULES) how to indent right after TOK.
499 \(TOK O1 O2) how to indent right after TOK:
500 O1 is the default;
501 O2 is used if TOK is \"hanging\".
502 \((T1 . T2) . OFFSET) how to indent token T2 w.r.t T1. 504 \((T1 . T2) . OFFSET) how to indent token T2 w.r.t T1.
503 \((t . TOK) . OFFSET) how to indent TOK with respect to its parent. 505 \((t . TOK) . OFFSET) how to indent TOK with respect to its parent.
504 \(list-intro . TOKENS) declare TOKENS as being followed by what may look like 506 \(list-intro . TOKENS) declare TOKENS as being followed by what may look like
505 a funcall but is just a sequence of expressions. 507 a funcall but is just a sequence of expressions.
506 \(t . OFFSET) basic indentation step. 508 \(t . OFFSET) basic indentation step.
507 \(args . OFFSET) indentation of arguments. 509 \(args . OFFSET) indentation of arguments.
510
511 OFFSET-RULES is list of elements which can either be an integer (the offset to
512 use), or a cons of the form
513 \(:hanging . OFFSET-RULES) if TOK is hanging, use OFFSET-RULES.
514 \(:parent PARENT . OFFSET-RULES) if TOK's parent is PARENT, use OFFSET-RULES.
515 \(:next TOKEN . OFFSET-RULES) if TOK is followed by TOKEN, use OFFSET-RULES.
508 A nil offset defaults to `smie-indent-basic'.") 516 A nil offset defaults to `smie-indent-basic'.")
509 517
510 (defun smie-indent-hanging-p () 518 (defun smie-indent-hanging-p ()
511 ;; A hanging keyword is one that's at the end of a line except it's not at 519 ;; A hanging keyword is one that's at the end of a line except it's not at
512 ;; the beginning of a line. 520 ;; the beginning of a line.
524 (defun smie-indent-offset (elem) 532 (defun smie-indent-offset (elem)
525 (or (cdr (assq elem smie-indent-rules)) 533 (or (cdr (assq elem smie-indent-rules))
526 (cdr (assq t smie-indent-rules)) 534 (cdr (assq t smie-indent-rules))
527 smie-indent-basic)) 535 smie-indent-basic))
528 536
537 (defun smie-indent-offset-after (tokinfo after)
538 ;; Presumes we're right before the token corresponding to `tokinfo'
539 ;; and `after' is the position that we're trying to indent.
540 (let ((rules (cdr tokinfo))
541 parent next offset)
542 (while (consp rules)
543 (let ((rule (pop rules)))
544 (cond
545 ((not (consp rule)) (setq offset rule))
546 ;; Using this `:hanging' is often "wrong", in that the hangingness
547 ;; of a keyword should usually not affect the indentation of the
548 ;; immediately following expression, but rather should affect the
549 ;; virtual indentation of that keyword (which in turn will affect not
550 ;; only indentation of the immediately following expression, but also
551 ;; other dependent expressions).
552 ;; But there are cases where it's useful: you may want to use it to
553 ;; make the indentation inside parentheses different depending on the
554 ;; hangingness of the open-paren, but without affecting the
555 ;; indentation of the paren-close.
556 ((eq (car rule) :hanging)
557 (when (smie-indent-hanging-p)
558 (setq rules (cdr rule))))
559 ((eq (car rule) :next)
560 (unless next
561 (save-excursion
562 (goto-char after)
563 (setq next (funcall smie-forward-token-function))))
564 (when (equal next (cadr rule))
565 (setq rules (cddr rule))))
566 ((eq (car rule) :parent)
567 (unless parent
568 (save-excursion
569 (goto-char after)
570 (setq parent (smie-backward-sexp 'halfsexp))))
571 (when (equal (nth 2 parent) (cadr rule))
572 (setq rules (cddr rule))))
573 (t (error "Unknown rule %s for indentation after %s"
574 rule (car tokinfo))))))
575 (or offset (smie-indent-offset t))))
576
577 (defun smie-indent-forward-token ()
578 "Skip token forward and return it, along with its levels."
579 (let ((tok (funcall smie-forward-token-function)))
580 (cond
581 ((< 0 (length tok)) (assoc tok smie-op-levels))
582 ((looking-at "\\s(")
583 (forward-char 1)
584 (list (buffer-substring (1- (point)) (point)) nil 0)))))
585
586 (defun smie-indent-backward-token ()
587 "Skip token backward and return it, along with its levels."
588 (let ((tok (funcall smie-backward-token-function)))
589 (cond
590 ((< 0 (length tok)) (assoc tok smie-op-levels))
591 ;; 4 == Open paren syntax.
592 ((eq 4 (syntax-class (syntax-after (1- (point)))))
593 (forward-char -1)
594 (list (buffer-substring (point) (1+ (point))) nil 0)))))
595
596 ;; FIXME: The `virtual' arg is fundamentally wrong: the virtual indent
597 ;; of a position should not depend on the caller, since it leads to situations
598 ;; where two dependent indentations get indented differently.
529 (defun smie-indent-virtual (virtual) 599 (defun smie-indent-virtual (virtual)
530 "Compute the virtual indentation to use for point. 600 "Compute the virtual indentation to use for point.
531 This is used when we're not trying to indent point but just 601 This is used when we're not trying to indent point but just
532 need to compute the column at which point should be indented 602 need to compute the column at which point should be indented
533 in order to figure out the indentation of some other (further down) point. 603 in order to figure out the indentation of some other (further down) point.
542 (current-column) 612 (current-column)
543 (smie-indent-calculate))) 613 (smie-indent-calculate)))
544 614
545 (defun smie-indent-fixindent () 615 (defun smie-indent-fixindent ()
546 ;; Obey the `fixindent' special comment. 616 ;; Obey the `fixindent' special comment.
547 (when (save-excursion 617 (and (smie-bolp)
618 (save-excursion
548 (comment-normalize-vars) 619 (comment-normalize-vars)
549 (re-search-forward (concat comment-start-skip 620 (re-search-forward (concat comment-start-skip
550 "fixindent" 621 "fixindent"
551 comment-end-skip) 622 comment-end-skip)
552 ;; 1+ to account for the \n comment termination. 623 ;; 1+ to account for the \n comment termination.
573 (scan-error nil))))) 644 (scan-error nil)))))
574 645
575 (defun smie-indent-keyword () 646 (defun smie-indent-keyword ()
576 ;; Align closing token with the corresponding opening one. 647 ;; Align closing token with the corresponding opening one.
577 ;; (e.g. "of" with "case", or "in" with "let"). 648 ;; (e.g. "of" with "case", or "in" with "let").
649 ;; FIXME: This still looks too much like black magic!!
650 ;; FIXME: Rather than a bunch of rules like (PARENT . TOKEN), we
651 ;; want a single rule for TOKEN with different cases for each PARENT.
578 (save-excursion 652 (save-excursion
579 (let* ((pos (point)) 653 (let* ((pos (point))
580 (token (funcall smie-forward-token-function)) 654 (toklevels (smie-indent-forward-token))
581 (toklevels (cdr (assoc token smie-op-levels)))) 655 (token (pop toklevels)))
582 (when (car toklevels) 656 (when (car toklevels)
583 (let ((res (smie-backward-sexp 'halfsexp)) tmp) 657 (let ((res (smie-backward-sexp 'halfsexp)) tmp)
584 ;; If we didn't move at all, that means we didn't really skip 658 (cond
585 ;; what we wanted. 659 ((not (or (< (point) pos)
586 (when (< (point) pos) 660 (and (cadr res) (< (cadr res) pos))))
587 (cond 661 ;; If we didn't move at all, that means we didn't really skip
588 ((eq (car res) (car toklevels)) 662 ;; what we wanted.
589 ;; We bumped into a same-level operator. align with it. 663 nil)
590 (goto-char (cadr res)) 664 ((eq (car res) (car toklevels))
591 ;; Don't use (smie-indent-virtual :not-hanging) here, because we 665 ;; We bumped into a same-level operator. align with it.
592 ;; want to jump back over a sequence of same-level ops such as 666 (goto-char (cadr res))
593 ;; a -> b -> c 667 ;; Don't use (smie-indent-virtual :not-hanging) here, because we
594 ;; -> d 668 ;; want to jump back over a sequence of same-level ops such as
595 ;; So as to align with the earliest appropriate place. 669 ;; a -> b -> c
596 (smie-indent-virtual :bolp)) 670 ;; -> d
597 ((equal token (save-excursion 671 ;; So as to align with the earliest appropriate place.
598 (funcall smie-backward-token-function))) 672 (smie-indent-virtual :bolp))
599 ;; in cases such as "fn x => fn y => fn z =>", 673 ((equal token (save-excursion
600 ;; jump back to the very first fn. 674 (funcall smie-backward-token-function)))
601 ;; FIXME: should we only do that for special tokens like "=>"? 675 ;; in cases such as "fn x => fn y => fn z =>",
602 (smie-indent-virtual :bolp)) 676 ;; jump back to the very first fn.
603 ((setq tmp (assoc (cons (caddr res) token) 677 ;; FIXME: should we only do that for special tokens like "=>"?
604 smie-indent-rules)) 678 (smie-indent-virtual :bolp))
605 (goto-char (cadr res)) 679 ((setq tmp (assoc (cons (caddr res) token)
606 (+ (cdr tmp) (smie-indent-virtual :not-hanging))) 680 smie-indent-rules))
607 (t 681 (goto-char (cadr res))
608 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0) 682 (+ (cdr tmp) (smie-indent-virtual :not-hanging)))
609 (current-column)))))))))) 683 ;; FIXME: The rules ((t . TOK) . OFFSET) either indent
684 ;; relative to "before the parent" or "after the parent",
685 ;; depending on details of the grammar.
686 ((null (car res))
687 (assert (eq (point) (cadr res)))
688 (goto-char (cadr res))
689 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0)
690 (smie-indent-virtual :not-hanging)))
691 ((smie-bolp)
692 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0)
693 ;; FIXME: This is odd. Can't we make it use
694 ;; smie-indent-(calculate|virtual) somehow?
695 (smie-indent-after-keyword)))
696 (t
697 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0)
698 (current-column)))))))))
610 699
611 (defun smie-indent-comment () 700 (defun smie-indent-comment ()
612 ;; Indentation of a comment. 701 ;; Indentation of a comment.
613 (and (looking-at comment-start-skip) 702 (and (looking-at comment-start-skip)
614 (save-excursion 703 (save-excursion
616 (skip-chars-forward " \t\r\n") 705 (skip-chars-forward " \t\r\n")
617 (smie-indent-calculate)))) 706 (smie-indent-calculate))))
618 707
619 (defun smie-indent-comment-continue () 708 (defun smie-indent-comment-continue ()
620 ;; indentation of comment-continue lines. 709 ;; indentation of comment-continue lines.
621 (and (< 0 (length comment-continue)) 710 (let ((continue (comment-string-strip comment-continue t t)))
622 (looking-at (regexp-quote comment-continue)) (nth 4 (syntax-ppss)) 711 (and (< 0 (length continue))
712 (looking-at (regexp-quote continue)) (nth 4 (syntax-ppss))
623 (let ((ppss (syntax-ppss))) 713 (let ((ppss (syntax-ppss)))
624 (save-excursion 714 (save-excursion
625 (forward-line -1) 715 (forward-line -1)
626 (if (<= (point) (nth 8 ppss)) 716 (if (<= (point) (nth 8 ppss))
627 (progn (goto-char (1+ (nth 8 ppss))) (current-column)) 717 (progn (goto-char (1+ (nth 8 ppss))) (current-column))
628 (skip-chars-forward " \t") 718 (skip-chars-forward " \t")
629 (if (looking-at (regexp-quote comment-continue)) 719 (if (looking-at (regexp-quote continue))
630 (current-column))))))) 720 (current-column))))))))
631 721
632 (defun smie-indent-after-keyword () 722 (defun smie-indent-after-keyword ()
633 ;; Indentation right after a special keyword. 723 ;; Indentation right after a special keyword.
634 (save-excursion 724 (save-excursion
635 (let* ((tok (funcall smie-backward-token-function)) 725 (let* ((pos (point))
636 (tokinfo (assoc tok smie-indent-rules)) 726 (toklevel (smie-indent-backward-token))
637 (toklevel (if (and (zerop (length tok)) 727 (tok (car toklevel))
638 ;; 4 == Open paren syntax. 728 (tokinfo (assoc tok smie-indent-rules)))
639 (eq (syntax-class (syntax-after (1- (point))))
640 4))
641 (progn (forward-char -1)
642 (setq tok (buffer-substring
643 (point) (1+ (point))))
644 (setq tokinfo (assoc tok smie-indent-rules))
645 (list tok nil 0))
646 (assoc tok smie-op-levels))))
647 (if (and toklevel (null (cadr toklevel)) (null tokinfo)) 729 (if (and toklevel (null (cadr toklevel)) (null tokinfo))
648 (setq tokinfo (list (car toklevel) nil nil))) 730 (setq tokinfo (list (car toklevel))))
649 (if (and tokinfo (null toklevel)) 731 ;; (if (and tokinfo (null toklevel))
650 (error "Token %S has indent rule but has no parsing info" tok)) 732 ;; (error "Token %S has indent rule but has no parsing info" tok))
651 (when toklevel 733 (when toklevel
652 (let ((default-offset 734 (let ((offset
735 (cond
736 (tokinfo (smie-indent-offset-after tokinfo pos))
653 ;; The default indentation after a keyword/operator 737 ;; The default indentation after a keyword/operator
654 ;; is 0 for infix and t for prefix. 738 ;; is 0 for infix and t for prefix.
655 ;; Using the BNF syntax, we could come up with 739 ;; Using the BNF syntax, we could come up with
656 ;; better defaults, but we only have the 740 ;; better defaults, but we only have the
657 ;; precedence levels here. 741 ;; precedence levels here.
658 (if (or tokinfo (null (cadr toklevel))) 742 ((null (cadr toklevel)) (smie-indent-offset t))
659 (smie-indent-offset t) 0))) 743 (t 0))))
660 ;; For indentation after "(let", we end up accumulating the 744 ;; For indentation after "(let" in SML-mode, we end up accumulating
661 ;; offset of "(" and the offset of "let", so we use `min' 745 ;; the offset of "(" and the offset of "let", so we use `min' to try
662 ;; to try and get it right either way. 746 ;; and get it right either way.
663 (min 747 (+ (min (smie-indent-virtual :bolp) (current-column)) offset))))))
664 (+ (smie-indent-virtual :bolp)
665 (or (caddr tokinfo) (cadr tokinfo) default-offset))
666 (+ (current-column)
667 (or (cadr tokinfo) default-offset))))))))
668 748
669 (defun smie-indent-exps () 749 (defun smie-indent-exps ()
670 ;; Indentation of sequences of simple expressions without 750 ;; Indentation of sequences of simple expressions without
671 ;; intervening keywords or operators. E.g. "a b c" or "g (balbla) f". 751 ;; intervening keywords or operators. E.g. "a b c" or "g (balbla) f".
672 ;; Can be a list of expressions or a function call. 752 ;; Can be a list of expressions or a function call.
717 ;; doesn't seem right since it might then indent args less than 797 ;; doesn't seem right since it might then indent args less than
718 ;; the function itself. 798 ;; the function itself.
719 (current-column))))))) 799 (current-column)))))))
720 800
721 (defvar smie-indent-functions 801 (defvar smie-indent-functions
722 '(smie-indent-fixindent smie-indent-bob smie-indent-close smie-indent-keyword 802 '(smie-indent-fixindent smie-indent-bob smie-indent-close smie-indent-comment
723 smie-indent-comment smie-indent-comment-continue smie-indent-after-keyword 803 smie-indent-comment-continue smie-indent-keyword smie-indent-after-keyword
724 smie-indent-exps) 804 smie-indent-exps)
725 "Functions to compute the indentation. 805 "Functions to compute the indentation.
726 Each function is called with no argument, shouldn't move point, and should 806 Each function is called with no argument, shouldn't move point, and should
727 return either nil if it has no opinion, or an integer representing the column 807 return either nil if it has no opinion, or an integer representing the column
728 to which that point should be aligned, if we were to reindent it.") 808 to which that point should be aligned, if we were to reindent it.")