Mercurial > emacs
comparison lisp/emacs-lisp/smie.el @ 108897:68586a267c40
* lisp/emacs-lisp/smie.el (smie-indent-offset-rule): Rename from
smie-indent-offset-after. Add :prev case. Make a bit more generic.
(smie-indent-virtual): Remove `virtual' arg. Update callers.
(smie-indent-keyword): Add handling of open-paren keywords.
(smie-indent-comment-continue): Don't assume comment-continue.
author | Stefan Monnier <monnier@iro.umontreal.ca> |
---|---|
date | Mon, 07 Jun 2010 15:37:50 -0400 |
parents | ca3bfaa18e56 |
children | c7e85ff4bca6 |
comparison
equal
deleted
inserted
replaced
108896:4a42850741ad | 108897:68586a267c40 |
---|---|
498 ;; structure Foo = | 498 ;; structure Foo = |
499 ;; struct ... end | 499 ;; struct ... end |
500 ;; I.e. the indentation after "=" depends on the parent ("structure") | 500 ;; I.e. the indentation after "=" depends on the parent ("structure") |
501 ;; as well as on the following token ("struct"). | 501 ;; as well as on the following token ("struct"). |
502 "Rules of the following form. | 502 "Rules of the following form. |
503 \((:before . TOK) . OFFSET-RULES) how to indent TOK itself. | |
503 \(TOK . OFFSET-RULES) how to indent right after TOK. | 504 \(TOK . OFFSET-RULES) how to indent right after TOK. |
504 \((T1 . T2) . OFFSET) how to indent token T2 w.r.t T1. | 505 \((T1 . T2) . OFFSET) how to indent token T2 w.r.t T1. |
505 \((t . TOK) . OFFSET) how to indent TOK with respect to its parent. | 506 \((t . TOK) . OFFSET) how to indent TOK with respect to its parent. |
506 \(list-intro . TOKENS) declare TOKENS as being followed by what may look like | 507 \(list-intro . TOKENS) declare TOKENS as being followed by what may look like |
507 a funcall but is just a sequence of expressions. | 508 a funcall but is just a sequence of expressions. |
508 \(t . OFFSET) basic indentation step. | 509 \(t . OFFSET) basic indentation step. |
509 \(args . OFFSET) indentation of arguments. | 510 \(args . OFFSET) indentation of arguments. |
510 | 511 |
511 OFFSET-RULES is list of elements which can either be an integer (the offset to | 512 OFFSET-RULES is a list of elements which can each either be: |
512 use), or a cons of the form | 513 |
513 \(:hanging . OFFSET-RULES) if TOK is hanging, use OFFSET-RULES. | 514 \(: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 \(: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. | 516 \(:next TOKEN . OFFSET-RULES) if TOK is followed by TOKEN, use OFFSET-RULES. |
516 A nil offset defaults to `smie-indent-basic'.") | 517 \(:prev TOKEN . OFFSET-RULES) if TOK is preceded by TOKEN, use OFFSET-RULES. |
518 a number the offset to use. | |
519 `point' align with the token. | |
520 `parent' align with the parent. | |
521 | |
522 A nil offset for indentation after a token defaults to `smie-indent-basic'.") | |
517 | 523 |
518 (defun smie-indent-hanging-p () | 524 (defun smie-indent-hanging-p () |
519 ;; A hanging keyword is one that's at the end of a line except it's not at | 525 ;; A hanging keyword is one that's at the end of a line except it's not at |
520 ;; the beginning of a line. | 526 ;; the beginning of a line. |
521 (and (save-excursion | 527 (and (save-excursion |
532 (defun smie-indent-offset (elem) | 538 (defun smie-indent-offset (elem) |
533 (or (cdr (assq elem smie-indent-rules)) | 539 (or (cdr (assq elem smie-indent-rules)) |
534 (cdr (assq t smie-indent-rules)) | 540 (cdr (assq t smie-indent-rules)) |
535 smie-indent-basic)) | 541 smie-indent-basic)) |
536 | 542 |
537 (defun smie-indent-offset-after (tokinfo after) | 543 (defun smie-indent-offset-rule (tokinfo &optional after) |
538 ;; Presumes we're right before the token corresponding to `tokinfo' | 544 "Apply the OFFSET-RULES in TOKINFO. |
539 ;; and `after' is the position that we're trying to indent. | 545 Point is expected to be right in front of the token corresponding to TOKINFO. |
546 If computing the indentation after the token, then AFTER is the position | |
547 after the token." | |
540 (let ((rules (cdr tokinfo)) | 548 (let ((rules (cdr tokinfo)) |
541 parent next offset) | 549 parent next prev |
550 offset) | |
542 (while (consp rules) | 551 (while (consp rules) |
543 (let ((rule (pop rules))) | 552 (let ((rule (pop rules))) |
544 (cond | 553 (cond |
545 ((not (consp rule)) (setq offset rule)) | 554 ((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) | 555 ((eq (car rule) :hanging) |
557 (when (smie-indent-hanging-p) | 556 (when (smie-indent-hanging-p) |
558 (setq rules (cdr rule)))) | 557 (setq rules (cdr rule)))) |
558 ((eq (car rule) :prev) | |
559 (unless prev | |
560 (save-excursion | |
561 (setq prev (smie-indent-backward-token)))) | |
562 (when (equal (car prev) (cadr rule)) | |
563 (setq rules (cddr rule)))) | |
559 ((eq (car rule) :next) | 564 ((eq (car rule) :next) |
560 (unless next | 565 (unless next |
566 (unless after | |
567 (error "Can't use :next in :before indentation rules")) | |
561 (save-excursion | 568 (save-excursion |
562 (goto-char after) | 569 (goto-char after) |
563 (setq next (funcall smie-forward-token-function)))) | 570 (setq next (smie-indent-forward-token)))) |
564 (when (equal next (cadr rule)) | 571 (when (equal (car next) (cadr rule)) |
565 (setq rules (cddr rule)))) | 572 (setq rules (cddr rule)))) |
566 ((eq (car rule) :parent) | 573 ((eq (car rule) :parent) |
567 (unless parent | 574 (unless parent |
568 (save-excursion | 575 (save-excursion |
569 (goto-char after) | 576 (if after (goto-char after)) |
570 (setq parent (smie-backward-sexp 'halfsexp)))) | 577 (setq parent (smie-backward-sexp 'halfsexp)))) |
571 (when (equal (nth 2 parent) (cadr rule)) | 578 (when (equal (nth 2 parent) (cadr rule)) |
572 (setq rules (cddr rule)))) | 579 (setq rules (cddr rule)))) |
573 (t (error "Unknown rule %s for indentation after %s" | 580 (t (error "Unknown rule %s for indentation of %s" |
574 rule (car tokinfo)))))) | 581 rule (car tokinfo)))))) |
575 (or offset (smie-indent-offset t)))) | 582 offset)) |
576 | 583 |
577 (defun smie-indent-forward-token () | 584 (defun smie-indent-forward-token () |
578 "Skip token forward and return it, along with its levels." | 585 "Skip token forward and return it, along with its levels." |
579 (let ((tok (funcall smie-forward-token-function))) | 586 (let ((tok (funcall smie-forward-token-function))) |
580 (cond | 587 (cond |
591 ;; 4 == Open paren syntax. | 598 ;; 4 == Open paren syntax. |
592 ((eq 4 (syntax-class (syntax-after (1- (point))))) | 599 ((eq 4 (syntax-class (syntax-after (1- (point))))) |
593 (forward-char -1) | 600 (forward-char -1) |
594 (list (buffer-substring (point) (1+ (point))) nil 0))))) | 601 (list (buffer-substring (point) (1+ (point))) nil 0))))) |
595 | 602 |
596 ;; FIXME: The `virtual' arg is fundamentally wrong: the virtual indent | 603 (defun smie-indent-virtual () |
597 ;; of a position should not depend on the caller, since it leads to situations | 604 ;; We used to take an optional arg (with value :not-hanging) to specify that |
598 ;; where two dependent indentations get indented differently. | 605 ;; we should only use (smie-indent-calculate) if we're looking at a hanging |
599 (defun smie-indent-virtual (virtual) | 606 ;; keyword. This was a bad idea, because the virtual indent of a position |
607 ;; should not depend on the caller, since it leads to situations where two | |
608 ;; dependent indentations get indented differently. | |
600 "Compute the virtual indentation to use for point. | 609 "Compute the virtual indentation to use for point. |
601 This is used when we're not trying to indent point but just | 610 This is used when we're not trying to indent point but just |
602 need to compute the column at which point should be indented | 611 need to compute the column at which point should be indented |
603 in order to figure out the indentation of some other (further down) point. | 612 in order to figure out the indentation of some other (further down) point." |
604 VIRTUAL can take two different values: | |
605 - :bolp: means that the current indentation of point can be trusted | |
606 to be good only if it follows a line break. | |
607 - :not-hanging: means that the current indentation of point can be | |
608 trusted to be good except if the following token is hanging." | |
609 ;; Trust pre-existing indentation on other lines. | 613 ;; Trust pre-existing indentation on other lines. |
610 (assert virtual) | 614 (if (smie-bolp) (current-column) (smie-indent-calculate))) |
611 (if (if (eq virtual :not-hanging) (not (smie-indent-hanging-p)) (smie-bolp)) | |
612 (current-column) | |
613 (smie-indent-calculate))) | |
614 | 615 |
615 (defun smie-indent-fixindent () | 616 (defun smie-indent-fixindent () |
616 ;; Obey the `fixindent' special comment. | 617 ;; Obey the `fixindent' special comment. |
617 (and (smie-bolp) | 618 (and (smie-bolp) |
618 (save-excursion | 619 (save-excursion |
638 (while (not (zerop (skip-syntax-forward ")"))) | 639 (while (not (zerop (skip-syntax-forward ")"))) |
639 (skip-chars-forward " \t")) | 640 (skip-chars-forward " \t")) |
640 (condition-case nil | 641 (condition-case nil |
641 (progn | 642 (progn |
642 (backward-sexp 1) | 643 (backward-sexp 1) |
643 (smie-indent-virtual :not-hanging)) | 644 (smie-indent-virtual)) ;:not-hanging |
644 (scan-error nil))))) | 645 (scan-error nil))))) |
645 | 646 |
646 (defun smie-indent-keyword () | 647 (defun smie-indent-keyword () |
647 ;; Align closing token with the corresponding opening one. | 648 ;; Align closing token with the corresponding opening one. |
648 ;; (e.g. "of" with "case", or "in" with "let"). | 649 ;; (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. | |
652 (save-excursion | 650 (save-excursion |
653 (let* ((pos (point)) | 651 (let* ((pos (point)) |
654 (toklevels (smie-indent-forward-token)) | 652 (toklevels (smie-indent-forward-token)) |
655 (token (pop toklevels))) | 653 (token (pop toklevels))) |
656 (when (car toklevels) | 654 (if (null (car toklevels)) |
655 ;; Different case: | |
656 ;; - smie-bolp: "indent according to others". | |
657 ;; - common hanging: "indent according to others". | |
658 ;; - SML-let hanging: "indent like parent". | |
659 ;; - if-after-else: "indent-like parent". | |
660 ;; - middle-of-line: "trust current position". | |
661 (cond | |
662 ((null (cdr toklevels)) nil) ;Not a keyword. | |
663 ((smie-bolp) | |
664 ;; For an open-paren-like thingy at BOL, always indent only | |
665 ;; based on other rules (typically smie-indent-after-keyword). | |
666 nil) | |
667 (t | |
668 (let* ((tokinfo (or (assoc (cons :before token) smie-indent-rules) | |
669 ;; By default use point unless we're hanging. | |
670 (cons (cons :before token) | |
671 '((:hanging nil) point)))) | |
672 (after (prog1 (point) (goto-char pos))) | |
673 (offset (smie-indent-offset-rule tokinfo))) | |
674 (cond | |
675 ((eq offset 'point) (current-column)) | |
676 ((eq offset 'parent) | |
677 (let ((parent (smie-backward-sexp 'halfsexp))) | |
678 (if parent (goto-char (cadr parent)))) | |
679 (smie-indent-virtual)) | |
680 ((eq offset nil) nil) | |
681 (t (error "Unhandled offset %s in %s" | |
682 offset (cons :before token))))))) | |
683 | |
684 ;; FIXME: This still looks too much like black magic!! | |
685 ;; FIXME: Rather than a bunch of rules like (PARENT . TOKEN), we | |
686 ;; want a single rule for TOKEN with different cases for each PARENT. | |
657 (let ((res (smie-backward-sexp 'halfsexp)) tmp) | 687 (let ((res (smie-backward-sexp 'halfsexp)) tmp) |
658 (cond | 688 (cond |
659 ((not (or (< (point) pos) | 689 ((not (or (< (point) pos) |
660 (and (cadr res) (< (cadr res) pos)))) | 690 (and (cadr res) (< (cadr res) pos)))) |
661 ;; If we didn't move at all, that means we didn't really skip | 691 ;; If we didn't move at all, that means we didn't really skip |
667 ;; Don't use (smie-indent-virtual :not-hanging) here, because we | 697 ;; Don't use (smie-indent-virtual :not-hanging) here, because we |
668 ;; want to jump back over a sequence of same-level ops such as | 698 ;; want to jump back over a sequence of same-level ops such as |
669 ;; a -> b -> c | 699 ;; a -> b -> c |
670 ;; -> d | 700 ;; -> d |
671 ;; So as to align with the earliest appropriate place. | 701 ;; So as to align with the earliest appropriate place. |
672 (smie-indent-virtual :bolp)) | 702 (smie-indent-virtual)) |
673 ((equal token (save-excursion | 703 ((equal token (save-excursion |
674 (funcall smie-backward-token-function))) | 704 (funcall smie-backward-token-function))) |
675 ;; in cases such as "fn x => fn y => fn z =>", | 705 ;; in cases such as "fn x => fn y => fn z =>", |
676 ;; jump back to the very first fn. | 706 ;; jump back to the very first fn. |
677 ;; FIXME: should we only do that for special tokens like "=>"? | 707 ;; FIXME: should we only do that for special tokens like "=>"? |
678 (smie-indent-virtual :bolp)) | 708 (smie-indent-virtual)) |
679 ((setq tmp (assoc (cons (caddr res) token) | 709 ((setq tmp (assoc (cons (caddr res) token) |
680 smie-indent-rules)) | 710 smie-indent-rules)) |
681 (goto-char (cadr res)) | 711 (goto-char (cadr res)) |
682 (+ (cdr tmp) (smie-indent-virtual :not-hanging))) | 712 (+ (cdr tmp) (smie-indent-virtual))) ;:not-hanging |
683 ;; FIXME: The rules ((t . TOK) . OFFSET) either indent | 713 ;; FIXME: The rules ((t . TOK) . OFFSET) either indent |
684 ;; relative to "before the parent" or "after the parent", | 714 ;; relative to "before the parent" or "after the parent", |
685 ;; depending on details of the grammar. | 715 ;; depending on details of the grammar. |
686 ((null (car res)) | 716 ((null (car res)) |
687 (assert (eq (point) (cadr res))) | 717 (assert (eq (point) (cadr res))) |
688 (goto-char (cadr res)) | 718 (goto-char (cadr res)) |
689 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0) | 719 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0) |
690 (smie-indent-virtual :not-hanging))) | 720 (smie-indent-virtual))) ;:not-hanging |
691 ((smie-bolp) | 721 ((and (= (point) pos) (smie-bolp)) |
722 ;; Since we started at BOL, we're not computing a virtual | |
723 ;; indentation, and we're still at the starting point, so the | |
724 ;; next (default) rule can't be used since it uses `current-column' | |
725 ;; which would cause. indentation to depend on itself. | |
726 ;; We could just return nil, but OTOH that's not good enough in | |
727 ;; some cases. Instead, we want to combine the offset-rules for | |
728 ;; the current token with the offset-rules of the previous one. | |
692 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0) | 729 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0) |
693 ;; FIXME: This is odd. Can't we make it use | 730 ;; FIXME: This is odd. Can't we make it use |
694 ;; smie-indent-(calculate|virtual) somehow? | 731 ;; smie-indent-(calculate|virtual) somehow? |
695 (smie-indent-after-keyword))) | 732 (smie-indent-after-keyword))) |
696 (t | 733 (t |
705 (skip-chars-forward " \t\r\n") | 742 (skip-chars-forward " \t\r\n") |
706 (smie-indent-calculate)))) | 743 (smie-indent-calculate)))) |
707 | 744 |
708 (defun smie-indent-comment-continue () | 745 (defun smie-indent-comment-continue () |
709 ;; indentation of comment-continue lines. | 746 ;; indentation of comment-continue lines. |
710 (let ((continue (comment-string-strip comment-continue t t))) | 747 (let ((continue (and comment-continue |
748 (comment-string-strip comment-continue t t)))) | |
711 (and (< 0 (length continue)) | 749 (and (< 0 (length continue)) |
712 (looking-at (regexp-quote continue)) (nth 4 (syntax-ppss)) | 750 (looking-at (regexp-quote continue)) (nth 4 (syntax-ppss)) |
713 (let ((ppss (syntax-ppss))) | 751 (let ((ppss (syntax-ppss))) |
714 (save-excursion | 752 (save-excursion |
715 (forward-line -1) | 753 (forward-line -1) |
731 ;; (if (and tokinfo (null toklevel)) | 769 ;; (if (and tokinfo (null toklevel)) |
732 ;; (error "Token %S has indent rule but has no parsing info" tok)) | 770 ;; (error "Token %S has indent rule but has no parsing info" tok)) |
733 (when toklevel | 771 (when toklevel |
734 (let ((offset | 772 (let ((offset |
735 (cond | 773 (cond |
736 (tokinfo (smie-indent-offset-after tokinfo pos)) | 774 (tokinfo (or (smie-indent-offset-rule tokinfo pos) |
775 (smie-indent-offset t))) | |
737 ;; The default indentation after a keyword/operator | 776 ;; The default indentation after a keyword/operator |
738 ;; is 0 for infix and t for prefix. | 777 ;; is 0 for infix and t for prefix. |
739 ;; Using the BNF syntax, we could come up with | 778 ;; Using the BNF syntax, we could come up with |
740 ;; better defaults, but we only have the | 779 ;; better defaults, but we only have the |
741 ;; precedence levels here. | 780 ;; precedence levels here. |
742 ((null (cadr toklevel)) (smie-indent-offset t)) | 781 ((null (cadr toklevel)) (smie-indent-offset t)) |
743 (t 0)))) | 782 (t 0)))) |
744 ;; For indentation after "(let" in SML-mode, we end up accumulating | 783 ;; For indentation after "(let" in SML-mode, we end up accumulating |
745 ;; the offset of "(" and the offset of "let", so we use `min' to try | 784 ;; the offset of "(" and the offset of "let", so we use `min' to try |
746 ;; and get it right either way. | 785 ;; and get it right either way. |
747 (+ (min (smie-indent-virtual :bolp) (current-column)) offset)))))) | 786 (+ (min (smie-indent-virtual) (current-column)) offset)))))) |
748 | 787 |
749 (defun smie-indent-exps () | 788 (defun smie-indent-exps () |
750 ;; Indentation of sequences of simple expressions without | 789 ;; Indentation of sequences of simple expressions without |
751 ;; intervening keywords or operators. E.g. "a b c" or "g (balbla) f". | 790 ;; intervening keywords or operators. E.g. "a b c" or "g (balbla) f". |
752 ;; Can be a list of expressions or a function call. | 791 ;; Can be a list of expressions or a function call. |
791 (current-column)) | 830 (current-column)) |
792 (positions | 831 (positions |
793 ;; We're the first arg. | 832 ;; We're the first arg. |
794 (goto-char (car positions)) | 833 (goto-char (car positions)) |
795 (+ (smie-indent-offset 'args) | 834 (+ (smie-indent-offset 'args) |
796 ;; We used to use (smie-indent-virtual :bolp), but that | 835 ;; We used to use (smie-indent-virtual), but that |
797 ;; doesn't seem right since it might then indent args less than | 836 ;; doesn't seem right since it might then indent args less than |
798 ;; the function itself. | 837 ;; the function itself. |
799 (current-column))))))) | 838 (current-column))))))) |
800 | 839 |
801 (defvar smie-indent-functions | 840 (defvar smie-indent-functions |