comparison lisp/emacs-lisp/smie.el @ 111232:a9904c1962db

SMIE: change indent rules format, improve smie-setup. * lisp/emacs-lisp/smie.el (smie-precs-precedence-table) (smie-merge-prec2s, smie-bnf-precedence-table, smie-prec2-levels): Mark them pure so the tables gets built at compile time. (smie-bnf-precedence-table): Store the closer-alist in the table. (smie-prec2-levels): Preserve the closer-alist. (smie-blink-matching-open): Be more forgiving in case of indentation. (smie-hanging-p): Rename from smie-indent--hanging-p. (smie-bolp): Rename from smie-indent--bolp. (smie--parent, smie--after): New dynamic vars. (smie-parent-p, smie-next-p, smie-prev-p): New funs. (smie-indent-rules): Remove. (smie-indent--offset-rule): Remove fun. (smie-rules-function): New var. (smie-indent--rule): New fun. (smie-indent--offset, smie-indent-keyword, smie-indent-after-keyword) (smie-indent-exps): Use it. (smie-setup): Setup paren blinking; add keyword args for token functions; extract closer-alist from op-levels. (smie-indent-debug-log): Remove var. (smie-indent-debug): Remove fun. * lisp/progmodes/prolog.el (prolog-smie-indent-rules): Remove. (prolog-smie-rules): New fun to replace it. (prolog-mode-variables): Simplify. * lisp/progmodes/octave-mod.el (octave-smie-closer-alist): Remove, now that it's setup automatically. (octave-smie-indent-rules): Remove. (octave-smie-rules): New fun to replace it. (octave-mode): Simplify.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Fri, 29 Oct 2010 15:20:28 -0400
parents e544f6cc2447
children 0c36e585b866
comparison
equal deleted inserted replaced
111231:d66d04803828 111232:a9904c1962db
107 ;; don't hide real conflicts. 107 ;; don't hide real conflicts.
108 (puthash key (gethash key override) table) 108 (puthash key (gethash key override) table)
109 (display-warning 'smie (format "Conflict: %s %s/%s %s" x old val y))) 109 (display-warning 'smie (format "Conflict: %s %s/%s %s" x old val y)))
110 (puthash key val table)))) 110 (puthash key val table))))
111 111
112 (put 'smie-precs-precedence-table 'pure t)
112 (defun smie-precs-precedence-table (precs) 113 (defun smie-precs-precedence-table (precs)
113 "Compute a 2D precedence table from a list of precedences. 114 "Compute a 2D precedence table from a list of precedences.
114 PRECS should be a list, sorted by precedence (e.g. \"+\" will 115 PRECS should be a list, sorted by precedence (e.g. \"+\" will
115 come before \"*\"), of elements of the form \(left OP ...) 116 come before \"*\"), of elements of the form \(left OP ...)
116 or (right OP ...) or (nonassoc OP ...) or (assoc OP ...). All operators in 117 or (right OP ...) or (nonassoc OP ...) or (assoc OP ...). All operators in
130 (dolist (other-op (cdr other-prec)) 131 (dolist (other-op (cdr other-prec))
131 (smie-set-prec2tab prec2-table op other-op op2) 132 (smie-set-prec2tab prec2-table op other-op op2)
132 (smie-set-prec2tab prec2-table other-op op op1))))))) 133 (smie-set-prec2tab prec2-table other-op op op1)))))))
133 prec2-table)) 134 prec2-table))
134 135
136 (put 'smie-merge-prec2s 'pure t)
135 (defun smie-merge-prec2s (&rest tables) 137 (defun smie-merge-prec2s (&rest tables)
136 (if (null (cdr tables)) 138 (if (null (cdr tables))
137 (car tables) 139 (car tables)
138 (let ((prec2 (make-hash-table :test 'equal))) 140 (let ((prec2 (make-hash-table :test 'equal)))
139 (dolist (table tables) 141 (dolist (table tables)
145 (error "Conflicting values for %s property" k) 147 (error "Conflicting values for %s property" k)
146 (puthash k v prec2)))) 148 (puthash k v prec2))))
147 table)) 149 table))
148 prec2))) 150 prec2)))
149 151
152 (put 'smie-bnf-precedence-table 'pure t)
150 (defun smie-bnf-precedence-table (bnf &rest precs) 153 (defun smie-bnf-precedence-table (bnf &rest precs)
151 (let ((nts (mapcar 'car bnf)) ;Non-terminals 154 (let ((nts (mapcar 'car bnf)) ;Non-terminals
152 (first-ops-table ()) 155 (first-ops-table ())
153 (last-ops-table ()) 156 (last-ops-table ())
154 (first-nts-table ()) 157 (first-nts-table ())
231 (t (smie-set-prec2tab prec2 (car rhs) (cadr rhs) '= override))) 234 (t (smie-set-prec2tab prec2 (car rhs) (cadr rhs) '= override)))
232 (setq rhs (cdr rhs))))) 235 (setq rhs (cdr rhs)))))
233 ;; Keep track of which tokens are openers/closer, so they can get a nil 236 ;; Keep track of which tokens are openers/closer, so they can get a nil
234 ;; precedence in smie-prec2-levels. 237 ;; precedence in smie-prec2-levels.
235 (puthash :smie-open/close-alist (smie-bnf-classify bnf) prec2) 238 (puthash :smie-open/close-alist (smie-bnf-classify bnf) prec2)
239 (puthash :smie-closer-alist (smie-bnf-closer-alist bnf) prec2)
236 prec2)) 240 prec2))
237 241
238 ;; (defun smie-prec2-closer-alist (prec2 include-inners) 242 ;; (defun smie-prec2-closer-alist (prec2 include-inners)
239 ;; "Build a closer-alist from a PREC2 table. 243 ;; "Build a closer-alist from a PREC2 table.
240 ;; The return value is in the same form as `smie-closer-alist'. 244 ;; The return value is in the same form as `smie-closer-alist'.
375 (mapconcat 379 (mapconcat
376 (lambda (elems) (mapconcat 'identity elems "=")) 380 (lambda (elems) (mapconcat 'identity elems "="))
377 (append names (list (car names))) 381 (append names (list (car names)))
378 " < "))) 382 " < ")))
379 383
384 (put 'smie-prec2-levels 'pure t)
380 (defun smie-prec2-levels (prec2) 385 (defun smie-prec2-levels (prec2)
381 ;; FIXME: Rather than only return an alist of precedence levels, we should 386 ;; FIXME: Rather than only return an alist of precedence levels, we should
382 ;; also extract other useful data from it: 387 ;; also extract other useful data from it:
383 ;; - better default indentation rules (i.e. non-zero indentation after inner 388 ;; - better default indentation rules (i.e. non-zero indentation after inner
384 ;; keywords like the "in" of "let..in..end") for smie-indent-after-keyword. 389 ;; keywords like the "in" of "let..in..end") for smie-indent-after-keyword.
477 (incf i)) ;See other (incf i) above. 482 (incf i)) ;See other (incf i) above.
478 (unless (or (nth 2 x) 483 (unless (or (nth 2 x)
479 (eq 'closer (cdr (assoc (car x) classification-table)))) 484 (eq 'closer (cdr (assoc (car x) classification-table))))
480 (setf (nth 2 x) i) 485 (setf (nth 2 x) i)
481 (incf i))))) ;See other (incf i) above. 486 (incf i))))) ;See other (incf i) above.
487 (let ((ca (gethash :smie-closer-alist prec2)))
488 (when ca (push (cons :smie-closer-alist ca) table)))
482 table)) 489 table))
483 490
484 ;;; Parsing using a precedence level table. 491 ;;; Parsing using a precedence level table.
485 492
486 (defvar smie-op-levels 'unset 493 (defvar smie-op-levels 'unset
801 (not (member (cons starter ender) smie-closer-alist)))))))) 808 (not (member (cons starter ender) smie-closer-alist))))))))
802 809
803 (defun smie-blink-matching-open () 810 (defun smie-blink-matching-open ()
804 "Blink the matching opener when applicable. 811 "Blink the matching opener when applicable.
805 This uses SMIE's tables and is expected to be placed on `post-self-insert-hook'." 812 This uses SMIE's tables and is expected to be placed on `post-self-insert-hook'."
813 (let ((pos (point)) ;Position after the close token.
814 token)
806 (when (and blink-matching-paren 815 (when (and blink-matching-paren
807 smie-closer-alist ; Optimization. 816 smie-closer-alist ; Optimization.
808 (eq (char-before) last-command-event) ; Sanity check. 817 (or (eq (char-before) last-command-event) ;; Sanity check.
818 (save-excursion
819 (or (progn (skip-chars-backward " \t")
820 (setq pos (point))
821 (eq (char-before) last-command-event))
822 (progn (skip-chars-backward " \n\t")
823 (setq pos (point))
824 (eq (char-before) last-command-event)))))
809 (memq last-command-event smie-blink-matching-triggers) 825 (memq last-command-event smie-blink-matching-triggers)
810 (not (nth 8 (syntax-ppss)))) 826 (not (nth 8 (syntax-ppss))))
811 (save-excursion 827 (save-excursion
812 (let ((pos (point)) 828 (setq token (funcall smie-backward-token-function))
813 (token (funcall smie-backward-token-function)))
814 (when (and (eq (point) (1- pos)) 829 (when (and (eq (point) (1- pos))
815 (= 1 (length token)) 830 (= 1 (length token))
816 (not (rassoc token smie-closer-alist))) 831 (not (rassoc token smie-closer-alist)))
817 ;; The trigger char is itself a token but is not one of the 832 ;; The trigger char is itself a token but is not one of the
818 ;; closers (e.g. ?\; in Octave mode), so go back to the 833 ;; closers (e.g. ?\; in Octave mode), so go back to the
819 ;; previous token. 834 ;; previous token.
820 (setq pos (point)) 835 (setq pos (point))
821 (setq token (save-excursion 836 (setq token (funcall smie-backward-token-function)))
822 (funcall smie-backward-token-function))))
823 (when (rassoc token smie-closer-alist) 837 (when (rassoc token smie-closer-alist)
824 ;; We're after a close token. Let's still make sure we 838 ;; We're after a close token. Let's still make sure we
825 ;; didn't skip a comment to find that token. 839 ;; didn't skip a comment to find that token.
826 (funcall smie-forward-token-function) 840 (funcall smie-forward-token-function)
827 (when (and (save-excursion 841 (when (and (save-excursion
828 ;; Trigger can be SPC, or reindent. 842 ;; Skip the trigger char, if applicable.
829 (skip-chars-forward " \n\t") 843 (if (eq (char-after) last-command-event)
844 (forward-char 1))
845 (if (eq ?\n last-command-event)
846 ;; Skip any auto-indentation, if applicable.
847 (skip-chars-forward " \t"))
830 (>= (point) pos)) 848 (>= (point) pos))
831 ;; If token ends with a trigger char, so don't blink for 849 ;; If token ends with a trigger char, don't blink for
832 ;; anything else than this trigger char, lest we'd blink 850 ;; anything else than this trigger char, lest we'd blink
833 ;; both when inserting the trigger char and when 851 ;; both when inserting the trigger char and when
834 ;; inserting a subsequent trigger char like SPC. 852 ;; inserting a subsequent trigger char like SPC.
835 (or (eq (point) pos) 853 (or (eq (point) pos)
836 (not (memq (char-before) 854 (not (memq (char-before)
848 866
849 (defcustom smie-indent-basic 4 867 (defcustom smie-indent-basic 4
850 "Basic amount of indentation." 868 "Basic amount of indentation."
851 :type 'integer) 869 :type 'integer)
852 870
853 (defvar smie-indent-rules 'unset 871 (defvar smie-rules-function 'ignore
854 ;; TODO: For SML, we need more rule formats, so as to handle 872 "Function providing the indentation rules.
855 ;; structure Foo = 873 It takes two arguments METHOD and ARG where the meaning of ARG
856 ;; Bar (toto) 874 and the expected return value depends on METHOD.
857 ;; and 875 METHOD can be:
858 ;; structure Foo = 876 - :after, in which case ARG is a token and the function should return the
859 ;; struct ... end 877 OFFSET to use for indentation after ARG.
860 ;; I.e. the indentation after "=" depends on the parent ("structure") 878 - :before, in which case ARG is a token and the function should return the
861 ;; as well as on the following token ("struct"). 879 OFFSET to use to indent ARG itself.
862 "Rules of the following form. 880 - :elem, in which case the function should return either:
863 \((:before . TOK) . OFFSET-RULES) how to indent TOK itself. 881 - the offset to use to indent function arguments (ARG = `arg')
864 \(TOK . OFFSET-RULES) how to indent right after TOK. 882 - the basic indentation step (ARG = `basic').
865 \(list-intro . TOKENS) declare TOKENS as being followed by what may look like 883 - :list-intro, in which case ARG is a token and the function should return
866 a funcall but is just a sequence of expressions. 884 non-nil if TOKEN is followed by a list of expressions (not separated by any
867 \(t . OFFSET) basic indentation step. 885 token) rather than an expression.
868 \(args . OFFSET) indentation of arguments. 886
869 \((T1 . T2) OFFSET) like ((:before . T2) (:parent T1 OFFSET)). 887 When ARG is a token, the function is called with point just before that token.
870 888 A return value of nil always means to fallback on the default behavior, so the
871 OFFSET-RULES is a list of elements which can each either be: 889 function should return nil for arguments it does not expect.
872
873 \(:hanging . OFFSET-RULES) if TOK is hanging, use OFFSET-RULES.
874 \(:parent PARENT . OFFSET-RULES) if TOK's parent is PARENT, use OFFSET-RULES.
875 \(:next TOKEN . OFFSET-RULES) if TOK is followed by TOKEN, use OFFSET-RULES.
876 \(:prev TOKEN . OFFSET-RULES) if TOK is preceded by TOKEN, use
877 \(:bolp . OFFSET-RULES) If TOK is first on a line, use OFFSET-RULES.
878 OFFSET the offset to use.
879
880 PARENT can be either the name of the parent or a list of such names.
881 890
882 OFFSET can be of the form: 891 OFFSET can be of the form:
883 `point' align with the token. 892 `point' align with the token.
884 `parent' align with the parent. 893 `parent' align with the parent.
885 NUMBER offset by NUMBER. 894 NUMBER offset by NUMBER.
886 \(+ OFFSETS...) use the sum of OFFSETS. 895 \(+ OFFSETS...) use the sum of OFFSETS.
887 VARIABLE use the value of VARIABLE as offset. 896 VARIABLE use the value of VARIABLE as offset.
888 897
889 The precise meaning of `point' depends on various details: it can 898 This function will often use some of the following functions designed
890 either mean the position of the token we're indenting, or the 899 specifically for it:
891 position of its parent, or the position right after its parent. 900 `smie-bolp', `smie-hanging-p', `smie-parent-p', `smie-next-p', `smie-prev-p'.")
892 901
893 A nil offset for indentation after an opening token defaults 902 (defun smie-hanging-p ()
894 to `smie-indent-basic'.") 903 "Return non-nil if the current token is \"hanging\".
895 904 A hanging keyword is one that's at the end of a line except it's not at
896 (defun smie-indent--hanging-p () 905 the beginning of a line."
897 ;; A hanging keyword is one that's at the end of a line except it's not at 906 (and (not (smie-bolp))
898 ;; the beginning of a line. 907 (save-excursion
899 (and (save-excursion
900 (when (zerop (length (funcall smie-forward-token-function))) 908 (when (zerop (length (funcall smie-forward-token-function)))
901 ;; Could be an open-paren. 909 ;; Could be an open-paren.
902 (forward-char 1)) 910 (forward-char 1))
903 (skip-chars-forward " \t") 911 (skip-chars-forward " \t")
904 (eolp)) 912 (eolp))))
905 (not (smie-indent--bolp)))) 913
906 914 (defun smie-bolp ()
907 (defun smie-indent--bolp () 915 "Return non-nil if the current token is the first on the line."
908 (save-excursion (skip-chars-backward " \t") (bolp))) 916 (save-excursion (skip-chars-backward " \t") (bolp)))
909 917
918 (defvar smie--parent) (defvar smie--after) ;Dynamically scoped.
919
920 (defun smie-parent-p (&rest parents)
921 "Return non-nil if the current token's parent is among PARENTS.
922 Only meaningful when called from within `smie-rules-function'."
923 (member (nth 2 (or smie--parent
924 (save-excursion
925 (let* ((pos (point))
926 (tok (funcall smie-forward-token-function)))
927 (unless (cadr (assoc tok smie-op-levels))
928 (goto-char pos))
929 (setq smie--parent
930 (smie-backward-sexp 'halfsexp))))))
931 parents))
932
933 (defun smie-next-p (&rest tokens)
934 "Return non-nil if the next token is among TOKENS.
935 Only meaningful when called from within `smie-rules-function'."
936 (let ((next
937 (save-excursion
938 (unless smie--after
939 (smie-indent-forward-token) (setq smie--after (point)))
940 (goto-char smie--after)
941 (smie-indent-forward-token))))
942 (member (car next) tokens)))
943
944 (defun smie-prev-p (&rest tokens)
945 "Return non-nil if the previous token is among TOKENS."
946 (let ((prev (save-excursion
947 (smie-indent-backward-token))))
948 (member (car prev) tokens)))
949
950
910 (defun smie-indent--offset (elem) 951 (defun smie-indent--offset (elem)
911 (or (cdr (assq elem smie-indent-rules)) 952 (or (funcall smie-rules-function :elem elem)
912 (cdr (assq t smie-indent-rules)) 953 (if (not (eq elem 'basic))
954 (funcall smie-rules-function :elem 'basic))
913 smie-indent-basic)) 955 smie-indent-basic))
914 956
915 (defvar smie-indent-debug-log) 957 (defun smie-indent--rule (kind token &optional after parent)
916 958 (let ((smie--parent parent)
917 (defun smie-indent--offset-rule (tokinfo &optional after parent) 959 (smie--after after))
918 "Apply the OFFSET-RULES in TOKINFO. 960 (funcall smie-rules-function kind token)))
919 Point is expected to be right in front of the token corresponding to TOKINFO.
920 If computing the indentation after the token, then AFTER is the position
921 after the token, otherwise it should be nil.
922 PARENT if non-nil should be the parent info returned by `smie-backward-sexp'."
923 (let ((rules (cdr tokinfo))
924 next prev
925 offset)
926 (while (consp rules)
927 (let ((rule (pop rules)))
928 (cond
929 ((not (consp rule)) (setq offset rule))
930 ((eq (car rule) '+) (setq offset rule))
931 ((eq (car rule) :hanging)
932 (when (smie-indent--hanging-p)
933 (setq rules (cdr rule))))
934 ((eq (car rule) :bolp)
935 (when (smie-indent--bolp)
936 (setq rules (cdr rule))))
937 ((eq (car rule) :eolp)
938 (unless after
939 (error "Can't use :eolp in :before indentation rules"))
940 (when (> after (line-end-position))
941 (setq rules (cdr rule))))
942 ((eq (car rule) :prev)
943 (unless prev
944 (save-excursion
945 (setq prev (smie-indent-backward-token))))
946 (when (equal (car prev) (cadr rule))
947 (setq rules (cddr rule))))
948 ((eq (car rule) :next)
949 (unless next
950 (unless after
951 (error "Can't use :next in :before indentation rules"))
952 (save-excursion
953 (goto-char after)
954 (setq next (smie-indent-forward-token))))
955 (when (equal (car next) (cadr rule))
956 (setq rules (cddr rule))))
957 ((eq (car rule) :parent)
958 (unless parent
959 (save-excursion
960 (if after (goto-char after))
961 (setq parent (smie-backward-sexp 'halfsexp))))
962 (when (if (listp (cadr rule))
963 (member (nth 2 parent) (cadr rule))
964 (equal (nth 2 parent) (cadr rule)))
965 (setq rules (cddr rule))))
966 (t (error "Unknown rule %s for indentation of %s"
967 rule (car tokinfo))))))
968 ;; If `offset' is not set yet, use `rules' to handle the case where
969 ;; the tokinfo uses the old-style ((PARENT . TOK). OFFSET).
970 (unless offset (setq offset rules))
971 (when (boundp 'smie-indent-debug-log)
972 (push (list (point) offset tokinfo) smie-indent-debug-log))
973 offset))
974 961
975 (defun smie-indent--column (offset &optional base parent virtual-point) 962 (defun smie-indent--column (offset &optional base parent virtual-point)
976 "Compute the actual column to use for a given OFFSET. 963 "Compute the actual column to use for a given OFFSET.
977 BASE is the base position to use, and PARENT is the parent info, if any. 964 BASE is the base position to use, and PARENT is the parent info, if any.
978 If VIRTUAL-POINT is non-nil, then `point' is virtual." 965 If VIRTUAL-POINT is non-nil, then `point' is virtual."
1010 (unless parent 997 (unless parent
1011 (setq parent (or (smie-backward-sexp 'halfsexp) :notfound))) 998 (setq parent (or (smie-backward-sexp 'halfsexp) :notfound)))
1012 (if (consp parent) (goto-char (cadr parent))) 999 (if (consp parent) (goto-char (cadr parent)))
1013 (smie-indent-virtual)) 1000 (smie-indent-virtual))
1014 ((eq offset nil) nil) 1001 ((eq offset nil) nil)
1002 ;; FIXME: would be good to get rid of this since smie-rules-function
1003 ;; can usually do the lookup trivially, but in cases where
1004 ;; smie-rules-function returns (+ point VAR) it's not nearly as trivial.
1015 ((and (symbolp offset) (boundp 'offset)) 1005 ((and (symbolp offset) (boundp 'offset))
1016 (smie-indent--column (symbol-value offset) base parent virtual-point)) 1006 (smie-indent--column (symbol-value offset) base parent virtual-point))
1017 (t (error "Unknown indentation offset %s" offset)))) 1007 (t (error "Unknown indentation offset %s" offset))))
1018 1008
1019 (defun smie-indent-forward-token () 1009 (defun smie-indent-forward-token ()
1044 "Compute the virtual indentation to use for point. 1034 "Compute the virtual indentation to use for point.
1045 This is used when we're not trying to indent point but just 1035 This is used when we're not trying to indent point but just
1046 need to compute the column at which point should be indented 1036 need to compute the column at which point should be indented
1047 in order to figure out the indentation of some other (further down) point." 1037 in order to figure out the indentation of some other (further down) point."
1048 ;; Trust pre-existing indentation on other lines. 1038 ;; Trust pre-existing indentation on other lines.
1049 (if (smie-indent--bolp) (current-column) (smie-indent-calculate))) 1039 (if (smie-bolp) (current-column) (smie-indent-calculate)))
1050 1040
1051 (defun smie-indent-fixindent () 1041 (defun smie-indent-fixindent ()
1052 ;; Obey the `fixindent' special comment. 1042 ;; Obey the `fixindent' special comment.
1053 (and (smie-indent--bolp) 1043 (and (smie-bolp)
1054 (save-excursion 1044 (save-excursion
1055 (comment-normalize-vars) 1045 (comment-normalize-vars)
1056 (re-search-forward (concat comment-start-skip 1046 (re-search-forward (concat comment-start-skip
1057 "fixindent" 1047 "fixindent"
1058 comment-end-skip) 1048 comment-end-skip)
1088 (token (pop toklevels))) 1078 (token (pop toklevels)))
1089 (if (null (car toklevels)) 1079 (if (null (car toklevels))
1090 (save-excursion 1080 (save-excursion
1091 (goto-char pos) 1081 (goto-char pos)
1092 ;; Different cases: 1082 ;; Different cases:
1093 ;; - smie-indent--bolp: "indent according to others". 1083 ;; - smie-bolp: "indent according to others".
1094 ;; - common hanging: "indent according to others". 1084 ;; - common hanging: "indent according to others".
1095 ;; - SML-let hanging: "indent like parent". 1085 ;; - SML-let hanging: "indent like parent".
1096 ;; - if-after-else: "indent-like parent". 1086 ;; - if-after-else: "indent-like parent".
1097 ;; - middle-of-line: "trust current position". 1087 ;; - middle-of-line: "trust current position".
1098 (cond 1088 (cond
1099 ((null (cdr toklevels)) nil) ;Not a keyword. 1089 ((null (cdr toklevels)) nil) ;Not a keyword.
1100 ((smie-indent--bolp) 1090 ((smie-bolp)
1101 ;; For an open-paren-like thingy at BOL, always indent only 1091 ;; For an open-paren-like thingy at BOL, always indent only
1102 ;; based on other rules (typically smie-indent-after-keyword). 1092 ;; based on other rules (typically smie-indent-after-keyword).
1103 nil) 1093 nil)
1104 (t 1094 (t
1105 ;; We're only ever here for virtual-indent, which is why 1095 ;; We're only ever here for virtual-indent, which is why
1106 ;; we can use (current-column) as answer for `point'. 1096 ;; we can use (current-column) as answer for `point'.
1107 (let* ((tokinfo (or (assoc (cons :before token) 1097 (let* ((offset (or (smie-indent--rule :before token)
1108 smie-indent-rules)
1109 ;; By default use point unless we're hanging. 1098 ;; By default use point unless we're hanging.
1110 `((:before . ,token) (:hanging nil) point))) 1099 (unless (smie-hanging-p) 'point))))
1111 ;; (after (prog1 (point) (goto-char pos)))
1112 (offset (smie-indent--offset-rule tokinfo)))
1113 (smie-indent--column offset))))) 1100 (smie-indent--column offset)))))
1114 1101
1115 ;; FIXME: This still looks too much like black magic!! 1102 ;; FIXME: This still looks too much like black magic!!
1116 ;; FIXME: Rather than a bunch of rules like (PARENT . TOKEN), we
1117 ;; want a single rule for TOKEN with different cases for each PARENT.
1118 (let* ((parent (smie-backward-sexp 'halfsexp)) 1103 (let* ((parent (smie-backward-sexp 'halfsexp))
1119 (tokinfo
1120 (or (assoc (cons (caddr parent) token)
1121 smie-indent-rules)
1122 (assoc (cons :before token) smie-indent-rules)
1123 ;; Default rule.
1124 `((:before . ,token)
1125 ;; (:parent open 0)
1126 point)))
1127 (offset (save-excursion 1104 (offset (save-excursion
1128 (goto-char pos) 1105 (goto-char pos)
1129 (smie-indent--offset-rule tokinfo nil parent)))) 1106 (or (smie-indent--rule :before token nil parent)
1107 'point))))
1130 ;; Different behaviors: 1108 ;; Different behaviors:
1131 ;; - align with parent. 1109 ;; - align with parent.
1132 ;; - parent + offset. 1110 ;; - parent + offset.
1133 ;; - after parent's column + offset (actually, after or before 1111 ;; - after parent's column + offset (actually, after or before
1134 ;; depending on where backward-sexp stopped). 1112 ;; depending on where backward-sexp stopped).
1149 ;; maybe when an infix or close-paren is at the beginning 1127 ;; maybe when an infix or close-paren is at the beginning
1150 ;; of a buffer. 1128 ;; of a buffer.
1151 nil) 1129 nil)
1152 ((eq (car parent) (car toklevels)) 1130 ((eq (car parent) (car toklevels))
1153 ;; We bumped into a same-level operator. align with it. 1131 ;; We bumped into a same-level operator. align with it.
1154 (if (and (smie-indent--bolp) (/= (point) pos) 1132 (if (and (smie-bolp) (/= (point) pos)
1155 (save-excursion 1133 (save-excursion
1156 (goto-char (goto-char (cadr parent))) 1134 (goto-char (goto-char (cadr parent)))
1157 (not (smie-indent--bolp))) 1135 (not (smie-bolp)))
1158 ;; Check the offset of `token' rather then its parent 1136 ;; Check the offset of `token' rather then its parent
1159 ;; because its parent may have used a special rule. E.g. 1137 ;; because its parent may have used a special rule. E.g.
1160 ;; function foo; 1138 ;; function foo;
1161 ;; line2; 1139 ;; line2;
1162 ;; line3; 1140 ;; line3;
1188 ;; want to jump back over a sequence of same-level ops such as 1166 ;; want to jump back over a sequence of same-level ops such as
1189 ;; a -> b -> c 1167 ;; a -> b -> c
1190 ;; -> d 1168 ;; -> d
1191 ;; So as to align with the earliest appropriate place. 1169 ;; So as to align with the earliest appropriate place.
1192 (smie-indent-virtual))) 1170 (smie-indent-virtual)))
1193 (tokinfo 1171 (t
1194 (if (and (= (point) pos) (smie-indent--bolp) 1172 (if (and (= (point) pos) (smie-bolp)
1195 (or (eq offset 'point) 1173 (or (eq offset 'point)
1196 (and (consp offset) (memq 'point offset)))) 1174 (and (consp offset) (memq 'point offset))))
1197 ;; Since we started at BOL, we're not computing a virtual 1175 ;; Since we started at BOL, we're not computing a virtual
1198 ;; indentation, and we're still at the starting point, so 1176 ;; indentation, and we're still at the starting point, so
1199 ;; we can't use `current-column' which would cause 1177 ;; we can't use `current-column' which would cause
1207 (defun smie-indent-comment () 1185 (defun smie-indent-comment ()
1208 "Compute indentation of a comment." 1186 "Compute indentation of a comment."
1209 ;; Don't do it for virtual indentations. We should normally never be "in 1187 ;; Don't do it for virtual indentations. We should normally never be "in
1210 ;; front of a comment" when doing virtual-indentation anyway. And if we are 1188 ;; front of a comment" when doing virtual-indentation anyway. And if we are
1211 ;; (as can happen in octave-mode), moving forward can lead to inf-loops. 1189 ;; (as can happen in octave-mode), moving forward can lead to inf-loops.
1212 (and (smie-indent--bolp) 1190 (and (smie-bolp)
1213 (let ((pos (point))) 1191 (let ((pos (point)))
1214 (save-excursion 1192 (save-excursion
1215 (beginning-of-line) 1193 (beginning-of-line)
1216 (and (re-search-forward comment-start-skip (line-end-position) t) 1194 (and (re-search-forward comment-start-skip (line-end-position) t)
1217 (eq pos (or (match-end 1) (match-beginning 0)))))) 1195 (eq pos (or (match-end 1) (match-beginning 0))))))
1252 (defun smie-indent-after-keyword () 1230 (defun smie-indent-after-keyword ()
1253 ;; Indentation right after a special keyword. 1231 ;; Indentation right after a special keyword.
1254 (save-excursion 1232 (save-excursion
1255 (let* ((pos (point)) 1233 (let* ((pos (point))
1256 (toklevel (smie-indent-backward-token)) 1234 (toklevel (smie-indent-backward-token))
1257 (tok (car toklevel)) 1235 (tok (car toklevel)))
1258 (tokinfo (assoc tok smie-indent-rules)))
1259 ;; Set some default indent rules.
1260 (if (and toklevel (null (cadr toklevel)) (null tokinfo))
1261 (setq tokinfo (list (car toklevel))))
1262 ;; (if (and tokinfo (null toklevel))
1263 ;; (error "Token %S has indent rule but has no parsing info" tok))
1264 (when toklevel 1236 (when toklevel
1265 (unless tokinfo
1266 ;; The default indentation after a keyword/operator is 0 for
1267 ;; infix and t for prefix.
1268 ;; Using the BNF syntax, we could come up with better
1269 ;; defaults, but we only have the precedence levels here.
1270 (setq tokinfo (list tok 'default-rule
1271 (if (cadr toklevel) 0 (smie-indent--offset t)))))
1272 (let ((offset 1237 (let ((offset
1273 (or (smie-indent--offset-rule tokinfo pos) 1238 (or (smie-indent--rule :after tok pos)
1274 (smie-indent--offset t)))) 1239 ;; The default indentation after a keyword/operator is
1275 (let ((before (point))) 1240 ;; 0 for infix and t for prefix.
1241 (if (or (null (cadr toklevel))
1242 (rassoc tok smie-closer-alist))
1243 (smie-indent--offset 'basic) 0)))
1244 (before (point)))
1276 (goto-char pos) 1245 (goto-char pos)
1277 (smie-indent--column offset before))))))) 1246 (smie-indent--column offset before))))))
1278 1247
1279 (defun smie-indent-exps () 1248 (defun smie-indent-exps ()
1280 ;; Indentation of sequences of simple expressions without 1249 ;; Indentation of sequences of simple expressions without
1281 ;; intervening keywords or operators. E.g. "a b c" or "g (balbla) f". 1250 ;; intervening keywords or operators. E.g. "a b c" or "g (balbla) f".
1282 ;; Can be a list of expressions or a function call. 1251 ;; Can be a list of expressions or a function call.
1295 (save-excursion 1264 (save-excursion
1296 (let ((positions nil) 1265 (let ((positions nil)
1297 arg) 1266 arg)
1298 (while (and (null (car (smie-backward-sexp))) 1267 (while (and (null (car (smie-backward-sexp)))
1299 (push (point) positions) 1268 (push (point) positions)
1300 (not (smie-indent--bolp)))) 1269 (not (smie-bolp))))
1301 (save-excursion 1270 (save-excursion
1302 ;; Figure out if the atom we just skipped is an argument rather 1271 ;; Figure out if the atom we just skipped is an argument rather
1303 ;; than a function. 1272 ;; than a function.
1304 (setq arg (or (null (car (smie-backward-sexp))) 1273 (setq arg
1305 (member (funcall smie-backward-token-function) 1274 (or (null (car (smie-backward-sexp)))
1306 (cdr (assoc 'list-intro smie-indent-rules)))))) 1275 (funcall smie-rules-function :list-intro
1276 (funcall smie-backward-token-function)))))
1307 (cond 1277 (cond
1308 ((null positions) 1278 ((null positions)
1309 ;; We're the first expression of the list. In that case, the 1279 ;; We're the first expression of the list. In that case, the
1310 ;; indentation should be (have been) determined by its context. 1280 ;; indentation should be (have been) determined by its context.
1311 nil) 1281 nil)
1360 (if (< indent 0) (setq indent 0)) ;Just in case. 1330 (if (< indent 0) (setq indent 0)) ;Just in case.
1361 (if savep 1331 (if savep
1362 (save-excursion (indent-line-to indent)) 1332 (save-excursion (indent-line-to indent))
1363 (indent-line-to indent))))) 1333 (indent-line-to indent)))))
1364 1334
1365 (defun smie-indent-debug () 1335 (defun smie-setup (op-levels rules-function &rest keywords)
1366 "Show the rules used to compute indentation of current line." 1336 "Setup SMIE navigation and indentation.
1367 (interactive) 1337 OP-LEVELS is a grammar table generated by `smie-prec2-levels'.
1368 (let ((smie-indent-debug-log '())) 1338 RULES-FUNCTION is a set of indentation rules for use on `smie-rules-function'.
1369 (smie-indent-calculate) 1339 KEYWORDS are additional arguments, which can use the following keywords:
1370 ;; FIXME: please improve! 1340 - :forward-token FUN
1371 (message "%S" smie-indent-debug-log))) 1341 - :backward-token FUN"
1372 1342 (set (make-local-variable 'smie-rules-function) rules-function)
1373 (defun smie-setup (op-levels indent-rules)
1374 (set (make-local-variable 'smie-indent-rules) indent-rules)
1375 (set (make-local-variable 'smie-op-levels) op-levels) 1343 (set (make-local-variable 'smie-op-levels) op-levels)
1376 (set (make-local-variable 'indent-line-function) 'smie-indent-line)) 1344 (set (make-local-variable 'indent-line-function) 'smie-indent-line)
1345 (set (make-local-variable 'forward-sexp-function)
1346 'smie-forward-sexp-command)
1347 (while keywords
1348 (let ((k (pop keywords))
1349 (v (pop keywords)))
1350 (case k
1351 (:forward-token
1352 (set (make-local-variable 'smie-forward-token-function) v))
1353 (:backward-token
1354 (set (make-local-variable 'smie-backward-token-function) v))
1355 (t (message "smie-setup: ignoring unknown keyword %s" k)))))
1356 (let ((ca (cdr (assq :smie-closer-alist op-levels))))
1357 (when ca
1358 (set (make-local-variable 'smie-closer-alist) ca)
1359 ;; Only needed for interactive calls to blink-matching-open.
1360 (set (make-local-variable 'blink-matching-check-function)
1361 #'smie-blink-matching-check)
1362 (add-hook 'post-self-insert-hook
1363 #'smie-blink-matching-open 'append 'local)
1364 (set (make-local-variable 'smie-blink-matching-triggers)
1365 (append smie-blink-matching-triggers
1366 ;; Rather than wait for SPC to blink, try to blink as
1367 ;; soon as we type the last char of a block ender.
1368 (let ((closers (sort (mapcar #'cdr smie-closer-alist)
1369 #'string-lessp))
1370 (triggers ())
1371 closer)
1372 (while (setq closer (pop closers))
1373 (unless (and closers
1374 ;; FIXME: this eliminates prefixes of other
1375 ;; closers, but we should probably elimnate
1376 ;; prefixes of other keywords as well.
1377 (string-prefix-p closer (car closers)))
1378 (push (aref closer (1- (length closer))) triggers)))
1379 (delete-dups triggers)))))))
1377 1380
1378 1381
1379 (provide 'smie) 1382 (provide 'smie)
1380 ;;; smie.el ends here 1383 ;;; smie.el ends here