Mercurial > emacs
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 |