aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2010-10-29 15:20:28 -0400
committerStefan Monnier2010-10-29 15:20:28 -0400
commitc4d17d50ea180c405fc58f9ffe693b6c633571ac (patch)
treef1bf3bbd1d417fa9853b6a98260e25a42083b638
parent2ec4c9665d3766eea7bf2d131cabbc177d049b6b (diff)
downloademacs-c4d17d50ea180c405fc58f9ffe693b6c633571ac.tar.gz
emacs-c4d17d50ea180c405fc58f9ffe693b6c633571ac.zip
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.
-rw-r--r--etc/NEWS2
-rw-r--r--lisp/ChangeLog32
-rw-r--r--lisp/emacs-lisp/smie.el339
-rw-r--r--lisp/progmodes/octave-mod.el55
-rw-r--r--lisp/progmodes/prolog.el23
5 files changed, 235 insertions, 216 deletions
diff --git a/etc/NEWS b/etc/NEWS
index ff2c2101f1b..4e14402216e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -587,6 +587,8 @@ Notifications API. It requires D-Bus for communication.
587 587
588* Incompatible Lisp Changes in Emacs 24.1 588* Incompatible Lisp Changes in Emacs 24.1
589 589
590** Remove obsolete name `e' (use `float-e' instead).
591
590** A backquote not followed by a space is now always treated as new-style. 592** A backquote not followed by a space is now always treated as new-style.
591 593
592** Test for special mode-class was moved from view-file to view-buffer. 594** Test for special mode-class was moved from view-file to view-buffer.
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 4842d677fb4..0fb702e7f66 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,35 @@
12010-10-29 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 SMIE: change indent rules format, improve smie-setup.
4 * emacs-lisp/smie.el (smie-precs-precedence-table)
5 (smie-merge-prec2s, smie-bnf-precedence-table, smie-prec2-levels):
6 Mark them pure so the tables gets built at compile time.
7 (smie-bnf-precedence-table): Store the closer-alist in the table.
8 (smie-prec2-levels): Preserve the closer-alist.
9 (smie-blink-matching-open): Be more forgiving in case of indentation.
10 (smie-hanging-p): Rename from smie-indent--hanging-p.
11 (smie-bolp): Rename from smie-indent--bolp.
12 (smie--parent, smie--after): New dynamic vars.
13 (smie-parent-p, smie-next-p, smie-prev-p): New funs.
14 (smie-indent-rules): Remove.
15 (smie-indent--offset-rule): Remove fun.
16 (smie-rules-function): New var.
17 (smie-indent--rule): New fun.
18 (smie-indent--offset, smie-indent-keyword, smie-indent-after-keyword)
19 (smie-indent-exps): Use it.
20 (smie-setup): Setup paren blinking; add keyword args for token
21 functions; extract closer-alist from op-levels.
22 (smie-indent-debug-log): Remove var.
23 (smie-indent-debug): Remove fun.
24 * progmodes/prolog.el (prolog-smie-indent-rules): Remove.
25 (prolog-smie-rules): New fun to replace it.
26 (prolog-mode-variables): Simplify.
27 * progmodes/octave-mod.el (octave-smie-closer-alist): Remove, now that
28 it's setup automatically.
29 (octave-smie-indent-rules): Remove.
30 (octave-smie-rules): New fun to replace it.
31 (octave-mode): Simplify.
32
12010-10-29 Glenn Morris <rgm@gnu.org> 332010-10-29 Glenn Morris <rgm@gnu.org>
2 34
3 * files.el (temporary-file-directory): Remove (already defined in C). 35 * files.el (temporary-file-directory): Remove (already defined in C).
diff --git a/lisp/emacs-lisp/smie.el b/lisp/emacs-lisp/smie.el
index 4f5b2046150..6cd434ff790 100644
--- a/lisp/emacs-lisp/smie.el
+++ b/lisp/emacs-lisp/smie.el
@@ -109,6 +109,7 @@
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.
114PRECS should be a list, sorted by precedence (e.g. \"+\" will 115PRECS should be a list, sorted by precedence (e.g. \"+\" will
@@ -132,6 +133,7 @@ one of those elements share the same precedence level and associativity."
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)
@@ -147,6 +149,7 @@ one of those elements share the same precedence level and associativity."
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 ())
@@ -233,6 +236,7 @@ one of those elements share the same precedence level and associativity."
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)
@@ -377,6 +381,7 @@ CSTS is a list of pairs representing arcs in a graph."
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:
@@ -479,6 +484,8 @@ PREC2 is a table as returned by `smie-precs-precedence-table' or
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.
@@ -803,14 +810,22 @@ If non-nil, it will blink not only for \"begin..end\" but also for \"if...else\"
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.
805This uses SMIE's tables and is expected to be placed on `post-self-insert-hook'." 812This 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)))
@@ -818,17 +833,20 @@ This uses SMIE's tables and is expected to be placed on `post-self-insert-hook'.
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.
@@ -850,34 +868,25 @@ This uses SMIE's tables and is expected to be placed on `post-self-insert-hook'.
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 = 873It takes two arguments METHOD and ARG where the meaning of ARG
856 ;; Bar (toto) 874and the expected return value depends on METHOD.
857 ;; and 875METHOD 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)). 887When ARG is a token, the function is called with point just before that token.
870 888A return value of nil always means to fallback on the default behavior, so the
871OFFSET-RULES is a list of elements which can each either be: 889function 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.
878OFFSET the offset to use.
879
880PARENT can be either the name of the parent or a list of such names.
881 890
882OFFSET can be of the form: 891OFFSET can be of the form:
883`point' align with the token. 892`point' align with the token.
@@ -886,91 +895,69 @@ NUMBER offset by NUMBER.
886\(+ OFFSETS...) use the sum of OFFSETS. 895\(+ OFFSETS...) use the sum of OFFSETS.
887VARIABLE use the value of VARIABLE as offset. 896VARIABLE use the value of VARIABLE as offset.
888 897
889The precise meaning of `point' depends on various details: it can 898This function will often use some of the following functions designed
890either mean the position of the token we're indenting, or the 899specifically for it:
891position 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
893A nil offset for indentation after an opening token defaults
894to `smie-indent-basic'.")
895 901
896(defun smie-indent--hanging-p () 902(defun smie-hanging-p ()
897 ;; A hanging keyword is one that's at the end of a line except it's not at 903 "Return non-nil if the current token is \"hanging\".
898 ;; the beginning of a line. 904A hanging keyword is one that's at the end of a line except it's not at
899 (and (save-excursion 905the beginning of a line."
906 (and (not (smie-bolp))
907 (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))))
906 913
907(defun smie-indent--bolp () 914(defun smie-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.
922Only 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.
935Only 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)))
919Point is expected to be right in front of the token corresponding to TOKINFO.
920If computing the indentation after the token, then AFTER is the position
921after the token, otherwise it should be nil.
922PARENT 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.
@@ -1012,6 +999,9 @@ If VIRTUAL-POINT is non-nil, then `point' is virtual."
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))))
@@ -1046,11 +1036,11 @@ This is used when we're not trying to indent point but just
1046need to compute the column at which point should be indented 1036need to compute the column at which point should be indented
1047in order to figure out the indentation of some other (further down) point." 1037in 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
@@ -1090,43 +1080,31 @@ in order to figure out the indentation of some other (further down) point."
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.
@@ -1151,10 +1129,10 @@ in order to figure out the indentation of some other (further down) point."
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;
@@ -1190,8 +1168,8 @@ in order to figure out the indentation of some other (further down) point."
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
@@ -1209,7 +1187,7 @@ in order to figure out the indentation of some other (further down) point."
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)
@@ -1254,27 +1232,18 @@ in order to figure out the indentation of some other (further down) point."
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
@@ -1297,13 +1266,14 @@ in order to figure out the indentation of some other (further down) point."
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
@@ -1362,18 +1332,51 @@ to which that point should be aligned, if we were to reindent it.")
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) 1337OP-LEVELS is a grammar table generated by `smie-prec2-levels'.
1368 (let ((smie-indent-debug-log '())) 1338RULES-FUNCTION is a set of indentation rules for use on `smie-rules-function'.
1369 (smie-indent-calculate) 1339KEYWORDS 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)
diff --git a/lisp/progmodes/octave-mod.el b/lisp/progmodes/octave-mod.el
index 40931c3d54d..fdd5e867b7b 100644
--- a/lisp/progmodes/octave-mod.el
+++ b/lisp/progmodes/octave-mod.el
@@ -446,9 +446,6 @@ Non-nil means always go to the next Octave code line after sending."
446 ;; (fundesc (atom "=" atom)) 446 ;; (fundesc (atom "=" atom))
447 )) 447 ))
448 448
449(defconst octave-smie-closer-alist
450 (smie-bnf-closer-alist octave-smie-bnf-table))
451
452(defconst octave-smie-op-levels 449(defconst octave-smie-op-levels
453 (smie-prec2-levels 450 (smie-prec2-levels
454 (smie-merge-prec2s 451 (smie-merge-prec2s
@@ -521,15 +518,18 @@ Non-nil means always go to the next Octave code line after sending."
521 (t 518 (t
522 (smie-default-forward-token)))) 519 (smie-default-forward-token))))
523 520
524(defconst octave-smie-indent-rules 521(defun octave-smie-rules (kind token)
525 '((";" 522 (pcase (cons kind token)
526 (:parent ("function" "if" "while" "else" "elseif" "for" "otherwise" 523 (`(:elem . basic) octave-block-offset)
527 "case" "try" "catch" "unwind_protect" "unwind_protect_cleanup") 524 (`(:before . "case") octave-block-offset)
528 ;; FIXME: don't hardcode 2. 525 (`(:after . ";")
529 (+ parent octave-block-offset)) 526 (if (smie-parent-p "function" "if" "while" "else" "elseif" "for"
530 ;; (:parent "switch" 4) ;For (invalid) code between switch and case. 527 "otherwise" "case" "try" "catch" "unwind_protect"
531 0) 528 "unwind_protect_cleanup")
532 ((:before . "case") octave-block-offset))) 529 '(+ parent octave-block-offset)
530 ;; For (invalid) code between switch and case.
531 ;; (if (smie-parent-p "switch") 4)
532 0))))
533 533
534(defvar electric-indent-chars) 534(defvar electric-indent-chars)
535 535
@@ -619,32 +619,15 @@ already added. You just need to add a description of the problem,
619including a reproducible test case and send the message." 619including a reproducible test case and send the message."
620 (setq local-abbrev-table octave-abbrev-table) 620 (setq local-abbrev-table octave-abbrev-table)
621 621
622 (smie-setup octave-smie-op-levels octave-smie-indent-rules) 622 (smie-setup octave-smie-op-levels #'octave-smie-rules
623 :forward-token #'octave-smie-forward-token
624 :backward-token #'octave-smie-backward-token)
623 (set (make-local-variable 'smie-indent-basic) 'octave-block-offset) 625 (set (make-local-variable 'smie-indent-basic) 'octave-block-offset)
624 (set (make-local-variable 'smie-backward-token-function) 626
625 'octave-smie-backward-token)
626 (set (make-local-variable 'smie-forward-token-function)
627 'octave-smie-forward-token)
628 (set (make-local-variable 'forward-sexp-function)
629 'smie-forward-sexp-command)
630 (set (make-local-variable 'smie-closer-alist) octave-smie-closer-alist)
631 ;; Only needed for interactive calls to blink-matching-open.
632 (set (make-local-variable 'blink-matching-check-function)
633 #'smie-blink-matching-check)
634
635 (when octave-blink-matching-block
636 (add-hook 'post-self-insert-hook #'smie-blink-matching-open 'append 'local)
637 (set (make-local-variable 'smie-blink-matching-triggers) 627 (set (make-local-variable 'smie-blink-matching-triggers)
638 (append smie-blink-matching-triggers '(\;) 628 (cons ?\; smie-blink-matching-triggers))
639 ;; Rather than wait for SPC or ; to blink, try to blink as 629 (unless octave-blink-matching-block
640 ;; soon as we type the last char of a block ender. 630 (remove-hook 'post-self-insert-hook #'smie-blink-matching-open 'local))
641 ;; But strip ?d from this list so that we don't blink twice
642 ;; when the user writes "endif" (once at "end" and another
643 ;; time at "endif").
644 (delq ?d (delete-dups
645 (mapcar (lambda (kw)
646 (aref (cdr kw) (1- (length (cdr kw)))))
647 smie-closer-alist))))))
648 631
649 (set (make-local-variable 'electric-indent-chars) 632 (set (make-local-variable 'electric-indent-chars)
650 (cons ?\; electric-indent-chars)) 633 (cons ?\; electric-indent-chars))
diff --git a/lisp/progmodes/prolog.el b/lisp/progmodes/prolog.el
index f3db7fad135..3e388dac56d 100644
--- a/lisp/progmodes/prolog.el
+++ b/lisp/progmodes/prolog.el
@@ -173,10 +173,11 @@ When nil, send actual operating system end of file."
173 ) 173 )
174 "Precedence levels of infix operators.") 174 "Precedence levels of infix operators.")
175 175
176(defconst prolog-smie-indent-rules 176(defun prolog-smie-rules (kind token)
177 '((":-") 177 (pcase (cons kind token)
178 ("->")) 178 (`(:elem . basic) prolog-indent-width)
179 "Prolog indentation rules.") 179 (`(:after . ".") 0) ;; To work around smie-closer-alist.
180 (`(:after . ,(or `":-" `"->")) prolog-indent-width)))
180 181
181(defun prolog-mode-variables () 182(defun prolog-mode-variables ()
182 (make-local-variable 'paragraph-separate) 183 (make-local-variable 'paragraph-separate)
@@ -185,19 +186,17 @@ When nil, send actual operating system end of file."
185 (setq paragraph-ignore-fill-prefix t) 186 (setq paragraph-ignore-fill-prefix t)
186 (make-local-variable 'imenu-generic-expression) 187 (make-local-variable 'imenu-generic-expression)
187 (setq imenu-generic-expression '((nil "^\\sw+" 0))) 188 (setq imenu-generic-expression '((nil "^\\sw+" 0)))
188 (smie-setup prolog-smie-op-levels prolog-smie-indent-rules) 189
189 (set (make-local-variable 'smie-forward-token-function) 190 ;; Setup SMIE.
190 #'prolog-smie-forward-token) 191 (smie-setup prolog-smie-op-levels #'prolog-smie-rules
191 (set (make-local-variable 'smie-backward-token-function) 192 :forward-token #'prolog-smie-forward-token
192 #'prolog-smie-backward-token) 193 :backward-token #'prolog-smie-backward-token)
193 (set (make-local-variable 'forward-sexp-function)
194 'smie-forward-sexp-command)
195 (set (make-local-variable 'smie-indent-basic) prolog-indent-width)
196 (set (make-local-variable 'smie-blink-matching-triggers) '(?.)) 194 (set (make-local-variable 'smie-blink-matching-triggers) '(?.))
197 (set (make-local-variable 'smie-closer-alist) '((t . "."))) 195 (set (make-local-variable 'smie-closer-alist) '((t . ".")))
198 (add-hook 'post-self-insert-hook #'smie-blink-matching-open 'append 'local) 196 (add-hook 'post-self-insert-hook #'smie-blink-matching-open 'append 'local)
199 ;; There's no real closer in Prolog anyway. 197 ;; There's no real closer in Prolog anyway.
200 (set (make-local-variable 'smie-blink-matching-inners) t) 198 (set (make-local-variable 'smie-blink-matching-inners) t)
199
201 (make-local-variable 'comment-start) 200 (make-local-variable 'comment-start)
202 (setq comment-start "%") 201 (setq comment-start "%")
203 (make-local-variable 'comment-start-skip) 202 (make-local-variable 'comment-start-skip)