Mercurial > emacs
comparison lisp/emacs-lisp/smie.el @ 111440:b72ff43b041f
* lisp/emacs-lisp/smie.el: Simplify the smie-rules-function return values.
(smie-precs->prec2): Rename from smie-precs-precedence-table.
(smie-bnf->prec2): Rename from smie-bnf-precedence-table.
(smie-prec2->grammar): Rename from smie-prec2-levels.
(smie-grammar): Rename from smie-op-levels.
(smie-indent--hanging-p): Rename from smie-hanging-p.
(smie-rule-hanging-p): New alias.
(smie-indent--bolp): Rename from smie-bolp.
(smie-indent--hanging-p): New alias.
(smie--token): New dynamically bound variable.
(smie-indent--parent): New function.
(smie-rule-parent-p): Use it; rename from smie-parent-p.
(smie-rule-next-p): Rename from smie-next-p.
(smie-rule-prev-p): Rename from smie-prev-p.
(smie-rule-sibling-p, smie-rule-parent)
(smie-indent--separator-outdent, smie-rule-separator): New functions.
(smie-rule-separator-outdent): New var.
(smie-indent--rule): Merge with smie-indent--column.
(smie-indent-forward-token, smie-indent-backward-token):
Also recognize close parens.
(smie-indent-keyword): Don't use smie-indent--column any more.
(smie-indent-after-keyword): Ignore closers by default.
(smie-indent-line): Use with-demoted-errors.
* lisp/progmodes/octave-mod.el (octave-smie-grammar):
Rename from octave-smie-op-levels.
(octave-smie-rules): Adjust to new behavior.
* lisp/progmodes/prolog.el (prolog-smie-grammar):
Rename from prolog-smie-op-levels.
author | Stefan Monnier <monnier@iro.umontreal.ca> |
---|---|
date | Sun, 07 Nov 2010 10:45:45 -0500 |
parents | 0c36e585b866 |
children | 3655cc4062e4 |
comparison
equal
deleted
inserted
replaced
111439:8426207480fa | 111440:b72ff43b041f |
---|---|
50 ;; - they typically have no error handling and can't even detect a parsing | 50 ;; - they typically have no error handling and can't even detect a parsing |
51 ;; error, so we don't have to worry about what to do in case of a syntax | 51 ;; error, so we don't have to worry about what to do in case of a syntax |
52 ;; error because the parser just automatically does something. Better yet, | 52 ;; error because the parser just automatically does something. Better yet, |
53 ;; we can afford to use a sloppy grammar. | 53 ;; we can afford to use a sloppy grammar. |
54 | 54 |
55 ;; The development (especially the parts building the 2D precedence | 55 ;; A good background to understand the development (especially the parts |
56 ;; tables and then computing the precedence levels from it) is largely | 56 ;; building the 2D precedence tables and then computing the precedence levels |
57 ;; inspired from page 187-194 of "Parsing techniques" by Dick Grune | 57 ;; from it) can be found in pages 187-194 of "Parsing techniques" by Dick Grune |
58 ;; and Ceriel Jacobs (BookBody.pdf available at | 58 ;; and Ceriel Jacobs (BookBody.pdf available at |
59 ;; http://www.cs.vu.nl/~dick/PTAPG.html). | 59 ;; http://www.cs.vu.nl/~dick/PTAPG.html). |
60 ;; | 60 ;; |
61 ;; OTOH we had to kill many chickens, read many coffee grounds, and practice | 61 ;; OTOH we had to kill many chickens, read many coffee grounds, and practice |
62 ;; untold numbers of black magic spells, to come up with the indentation code. | 62 ;; untold numbers of black magic spells, to come up with the indentation code. |
89 ;; have the form (ASSOCIATIVITY TERMINAL1 .. TERMINALn), where | 89 ;; have the form (ASSOCIATIVITY TERMINAL1 .. TERMINALn), where |
90 ;; ASSOCIATIVITY can be `assoc', `left', `right' or `nonassoc'. | 90 ;; ASSOCIATIVITY can be `assoc', `left', `right' or `nonassoc'. |
91 ;; - a 2 dimensional precedence table (key word "prec2"), is a 2D | 91 ;; - a 2 dimensional precedence table (key word "prec2"), is a 2D |
92 ;; table recording the precedence relation (can be `<', `=', `>', or | 92 ;; table recording the precedence relation (can be `<', `=', `>', or |
93 ;; nil) between each pair of tokens. | 93 ;; nil) between each pair of tokens. |
94 ;; - a precedence-level table (key word "levels"), while is a alist | 94 ;; - a precedence-level table (key word "grammar"), which is a alist |
95 ;; giving for each token its left and right precedence level (a | 95 ;; giving for each token its left and right precedence level (a |
96 ;; number or nil). This is used in `smie-op-levels'. | 96 ;; number or nil). This is used in `smie-grammar'. |
97 ;; The prec2 tables are only intermediate data structures: the source | 97 ;; The prec2 tables are only intermediate data structures: the source |
98 ;; code normally provides a mix of BNF and precs tables, and then | 98 ;; code normally provides a mix of BNF and precs tables, and then |
99 ;; turns them into a levels table, which is what's used by the rest of | 99 ;; turns them into a levels table, which is what's used by the rest of |
100 ;; the SMIE code. | 100 ;; the SMIE code. |
101 | 101 |
111 ;; don't hide real conflicts. | 111 ;; don't hide real conflicts. |
112 (puthash key (gethash key override) table) | 112 (puthash key (gethash key override) table) |
113 (display-warning 'smie (format "Conflict: %s %s/%s %s" x old val y))) | 113 (display-warning 'smie (format "Conflict: %s %s/%s %s" x old val y))) |
114 (puthash key val table)))) | 114 (puthash key val table)))) |
115 | 115 |
116 (put 'smie-precs-precedence-table 'pure t) | 116 (put 'smie-precs->prec2 'pure t) |
117 (defun smie-precs-precedence-table (precs) | 117 (defun smie-precs->prec2 (precs) |
118 "Compute a 2D precedence table from a list of precedences. | 118 "Compute a 2D precedence table from a list of precedences. |
119 PRECS should be a list, sorted by precedence (e.g. \"+\" will | 119 PRECS should be a list, sorted by precedence (e.g. \"+\" will |
120 come before \"*\"), of elements of the form \(left OP ...) | 120 come before \"*\"), of elements of the form \(left OP ...) |
121 or (right OP ...) or (nonassoc OP ...) or (assoc OP ...). All operators in | 121 or (right OP ...) or (nonassoc OP ...) or (assoc OP ...). All operators in |
122 one of those elements share the same precedence level and associativity." | 122 one of those elements share the same precedence level and associativity." |
151 (error "Conflicting values for %s property" k) | 151 (error "Conflicting values for %s property" k) |
152 (puthash k v prec2)))) | 152 (puthash k v prec2)))) |
153 table)) | 153 table)) |
154 prec2))) | 154 prec2))) |
155 | 155 |
156 (put 'smie-bnf-precedence-table 'pure t) | 156 (put 'smie-bnf->prec2 'pure t) |
157 (defun smie-bnf-precedence-table (bnf &rest precs) | 157 (defun smie-bnf->prec2 (bnf &rest precs) |
158 (let ((nts (mapcar 'car bnf)) ;Non-terminals | 158 (let ((nts (mapcar 'car bnf)) ;Non-terminals |
159 (first-ops-table ()) | 159 (first-ops-table ()) |
160 (last-ops-table ()) | 160 (last-ops-table ()) |
161 (first-nts-table ()) | 161 (first-nts-table ()) |
162 (last-nts-table ()) | 162 (last-nts-table ()) |
163 (prec2 (make-hash-table :test 'equal)) | 163 (prec2 (make-hash-table :test 'equal)) |
164 (override (apply 'smie-merge-prec2s | 164 (override (apply 'smie-merge-prec2s |
165 (mapcar 'smie-precs-precedence-table precs))) | 165 (mapcar 'smie-precs->prec2 precs))) |
166 again) | 166 again) |
167 (dolist (rules bnf) | 167 (dolist (rules bnf) |
168 (let ((nt (car rules)) | 168 (let ((nt (car rules)) |
169 (last-ops ()) | 169 (last-ops ()) |
170 (first-ops ()) | 170 (first-ops ()) |
236 (smie-set-prec2tab prec2 (car rhs) (car (cddr rhs)) | 236 (smie-set-prec2tab prec2 (car rhs) (car (cddr rhs)) |
237 '= override))) | 237 '= override))) |
238 (t (smie-set-prec2tab prec2 (car rhs) (cadr rhs) '= override))) | 238 (t (smie-set-prec2tab prec2 (car rhs) (cadr rhs) '= override))) |
239 (setq rhs (cdr rhs))))) | 239 (setq rhs (cdr rhs))))) |
240 ;; Keep track of which tokens are openers/closer, so they can get a nil | 240 ;; Keep track of which tokens are openers/closer, so they can get a nil |
241 ;; precedence in smie-prec2-levels. | 241 ;; precedence in smie-prec2->grammar. |
242 (puthash :smie-open/close-alist (smie-bnf-classify bnf) prec2) | 242 (puthash :smie-open/close-alist (smie-bnf-classify bnf) prec2) |
243 (puthash :smie-closer-alist (smie-bnf-closer-alist bnf) prec2) | 243 (puthash :smie-closer-alist (smie-bnf-closer-alist bnf) prec2) |
244 prec2)) | 244 prec2)) |
245 | 245 |
246 ;; (defun smie-prec2-closer-alist (prec2 include-inners) | 246 ;; (defun smie-prec2-closer-alist (prec2 include-inners) |
320 ;; which is important for smie-close-block. | 320 ;; which is important for smie-close-block. |
321 (dolist (term (reverse (cdr rhs))) | 321 (dolist (term (reverse (cdr rhs))) |
322 (unless (member term nts) | 322 (unless (member term nts) |
323 (pushnew (cons (car rhs) term) alist :test #'equal))))))) | 323 (pushnew (cons (car rhs) term) alist :test #'equal))))))) |
324 (nreverse alist))) | 324 (nreverse alist))) |
325 | 325 |
326 (defun smie-bnf-classify (bnf) | 326 (defun smie-bnf-classify (bnf) |
327 "Return a table classifying terminals. | 327 "Return a table classifying terminals. |
328 Each terminal can either be an `opener', a `closer', or neither." | 328 Each terminal can either be an `opener', a `closer', or neither." |
329 (let ((table (make-hash-table :test #'equal)) | 329 (let ((table (make-hash-table :test #'equal)) |
330 (alist '())) | 330 (alist '())) |
365 (if (eq (cdr cst) (car path)) | 365 (if (eq (cdr cst) (car path)) |
366 (setq cycle path) | 366 (setq cycle path) |
367 (push (cons (car path) (cons (cdr cst) (cdr path))) | 367 (push (cons (car path) (cons (cdr cst) (cdr path))) |
368 paths)))))) | 368 paths)))))) |
369 (cons (car cycle) (nreverse (cdr cycle))))) | 369 (cons (car cycle) (nreverse (cdr cycle))))) |
370 | 370 |
371 (defun smie-debug--describe-cycle (table cycle) | 371 (defun smie-debug--describe-cycle (table cycle) |
372 (let ((names | 372 (let ((names |
373 (mapcar (lambda (val) | 373 (mapcar (lambda (val) |
374 (let ((res nil)) | 374 (let ((res nil)) |
375 (dolist (elem table) | 375 (dolist (elem table) |
383 (mapconcat | 383 (mapconcat |
384 (lambda (elems) (mapconcat 'identity elems "=")) | 384 (lambda (elems) (mapconcat 'identity elems "=")) |
385 (append names (list (car names))) | 385 (append names (list (car names))) |
386 " < "))) | 386 " < "))) |
387 | 387 |
388 (put 'smie-prec2-levels 'pure t) | 388 (put 'smie-prec2->grammar 'pure t) |
389 (defun smie-prec2-levels (prec2) | 389 (defun smie-prec2->grammar (prec2) |
390 ;; FIXME: Rather than only return an alist of precedence levels, we should | |
391 ;; also extract other useful data from it: | |
392 ;; - better default indentation rules (i.e. non-zero indentation after inner | |
393 ;; keywords like the "in" of "let..in..end") for smie-indent-after-keyword. | |
394 ;; Of course, maybe those things would be even better handled in the | |
395 ;; bnf->prec function. | |
396 "Take a 2D precedence table and turn it into an alist of precedence levels. | 390 "Take a 2D precedence table and turn it into an alist of precedence levels. |
397 PREC2 is a table as returned by `smie-precs-precedence-table' or | 391 PREC2 is a table as returned by `smie-precs->prec2' or |
398 `smie-bnf-precedence-table'." | 392 `smie-bnf->prec2'." |
399 ;; For each operator, we create two "variables" (corresponding to | 393 ;; For each operator, we create two "variables" (corresponding to |
400 ;; the left and right precedence level), which are represented by | 394 ;; the left and right precedence level), which are represented by |
401 ;; cons cells. Those are the very cons cells that appear in the | 395 ;; cons cells. Those are the very cons cells that appear in the |
402 ;; final `table'. The value of each "variable" is kept in the `car'. | 396 ;; final `table'. The value of each "variable" is kept in the `car'. |
403 (let ((table ()) | 397 (let ((table ()) |
492 (when ca (push (cons :smie-closer-alist ca) table))) | 486 (when ca (push (cons :smie-closer-alist ca) table))) |
493 table)) | 487 table)) |
494 | 488 |
495 ;;; Parsing using a precedence level table. | 489 ;;; Parsing using a precedence level table. |
496 | 490 |
497 (defvar smie-op-levels 'unset | 491 (defvar smie-grammar 'unset |
498 "List of token parsing info. | 492 "List of token parsing info. |
493 This list is normally built by `smie-prec2->grammar'. | |
499 Each element is of the form (TOKEN LEFT-LEVEL RIGHT-LEVEL). | 494 Each element is of the form (TOKEN LEFT-LEVEL RIGHT-LEVEL). |
500 Parsing is done using an operator precedence parser. | 495 Parsing is done using an operator precedence parser. |
501 LEFT-LEVEL and RIGHT-LEVEL can be either numbers or nil, where nil | 496 LEFT-LEVEL and RIGHT-LEVEL can be either numbers or nil, where nil |
502 means that this operator does not bind on the corresponding side, | 497 means that this operator does not bind on the corresponding side, |
503 i.e. a LEFT-LEVEL of nil means this is a token that behaves somewhat like | 498 i.e. a LEFT-LEVEL of nil means this is a token that behaves somewhat like |
536 (point)))) | 531 (point)))) |
537 | 532 |
538 (defun smie--associative-p (toklevels) | 533 (defun smie--associative-p (toklevels) |
539 ;; in "a + b + c" we want to stop at each +, but in | 534 ;; in "a + b + c" we want to stop at each +, but in |
540 ;; "if a then b elsif c then d else c" we don't want to stop at each keyword. | 535 ;; "if a then b elsif c then d else c" we don't want to stop at each keyword. |
541 ;; To distinguish the two cases, we made smie-prec2-levels choose | 536 ;; To distinguish the two cases, we made smie-prec2->grammar choose |
542 ;; different levels for each part of "if a then b else c", so that | 537 ;; different levels for each part of "if a then b else c", so that |
543 ;; by checking if the left-level is equal to the right level, we can | 538 ;; by checking if the left-level is equal to the right level, we can |
544 ;; figure out that it's an associative operator. | 539 ;; figure out that it's an associative operator. |
545 ;; This is not 100% foolproof, tho, since the "elsif" will have to have | 540 ;; This is not 100% foolproof, tho, since the "elsif" will have to have |
546 ;; equal left and right levels (since it's optional), so smie-next-sexp | 541 ;; equal left and right levels (since it's optional), so smie-next-sexp |
566 (catch 'return | 561 (catch 'return |
567 (let ((levels ())) | 562 (let ((levels ())) |
568 (while | 563 (while |
569 (let* ((pos (point)) | 564 (let* ((pos (point)) |
570 (token (funcall next-token)) | 565 (token (funcall next-token)) |
571 (toklevels (cdr (assoc token smie-op-levels)))) | 566 (toklevels (cdr (assoc token smie-grammar)))) |
572 (cond | 567 (cond |
573 ((null toklevels) | 568 ((null toklevels) |
574 (when (zerop (length token)) | 569 (when (zerop (length token)) |
575 (condition-case err | 570 (condition-case err |
576 (progn (goto-char pos) (funcall next-sexp 1) nil) | 571 (progn (goto-char pos) (funcall next-sexp 1) nil) |
708 (backward-up-list 1) | 703 (backward-up-list 1) |
709 (if (looking-at "\\s(") | 704 (if (looking-at "\\s(") |
710 (string (cdr (syntax-after (point)))) | 705 (string (cdr (syntax-after (point)))) |
711 (let* ((open (funcall smie-forward-token-function)) | 706 (let* ((open (funcall smie-forward-token-function)) |
712 (closer (cdr (assoc open smie-closer-alist))) | 707 (closer (cdr (assoc open smie-closer-alist))) |
713 (levels (list (assoc open smie-op-levels))) | 708 (levels (list (assoc open smie-grammar))) |
714 (seen '()) | 709 (seen '()) |
715 (found '())) | 710 (found '())) |
716 (cond | 711 (cond |
717 ;; Even if we improve the auto-computation of closers, | 712 ;; Even if we improve the auto-computation of closers, |
718 ;; there are still cases where we need manual | 713 ;; there are still cases where we need manual |
720 ;; as a pseudo-closer of `do'. | 715 ;; as a pseudo-closer of `do'. |
721 (closer) | 716 (closer) |
722 ((or (equal levels '(nil)) (nth 1 (car levels))) | 717 ((or (equal levels '(nil)) (nth 1 (car levels))) |
723 (error "Doesn't look like a block")) | 718 (error "Doesn't look like a block")) |
724 (t | 719 (t |
725 ;; FIXME: With grammars like Octave's, every closer ("end", | 720 ;; Now that smie-setup automatically sets smie-closer-alist |
726 ;; "endif", "endwhile", ...) has the same level, so we'd need | 721 ;; from the BNF, this is not really needed any more. |
727 ;; to look at the BNF or at least at the 2D prec-table, in | |
728 ;; order to find the right closer for a given opener. | |
729 (while levels | 722 (while levels |
730 (let ((level (pop levels))) | 723 (let ((level (pop levels))) |
731 (dolist (other smie-op-levels) | 724 (dolist (other smie-grammar) |
732 (when (and (eq (nth 2 level) (nth 1 other)) | 725 (when (and (eq (nth 2 level) (nth 1 other)) |
733 (not (memq other seen))) | 726 (not (memq other seen))) |
734 (push other seen) | 727 (push other seen) |
735 (if (nth 2 other) | 728 (if (nth 2 other) |
736 (push other levels) | 729 (push other levels) |
761 (while (/= arg 0) | 754 (while (/= arg 0) |
762 (setq arg (- arg inc)) | 755 (setq arg (- arg inc)) |
763 (while | 756 (while |
764 (let* ((pos (point)) | 757 (let* ((pos (point)) |
765 (token (funcall next-token)) | 758 (token (funcall next-token)) |
766 (levels (assoc token smie-op-levels))) | 759 (levels (assoc token smie-grammar))) |
767 (cond | 760 (cond |
768 ((zerop (length token)) | 761 ((zerop (length token)) |
769 (if (if (< inc 0) (looking-back "\\s(\\|\\s)" (1- (point))) | 762 (if (if (< inc 0) (looking-back "\\s(\\|\\s)" (1- (point))) |
770 (looking-at "\\s(\\|\\s)")) | 763 (looking-at "\\s(\\|\\s)")) |
771 ;; Go back to `start' in case of an error. This presumes | 764 ;; Go back to `start' in case of an error. This presumes |
815 (defun smie-blink-matching-open () | 808 (defun smie-blink-matching-open () |
816 "Blink the matching opener when applicable. | 809 "Blink the matching opener when applicable. |
817 This uses SMIE's tables and is expected to be placed on `post-self-insert-hook'." | 810 This uses SMIE's tables and is expected to be placed on `post-self-insert-hook'." |
818 (let ((pos (point)) ;Position after the close token. | 811 (let ((pos (point)) ;Position after the close token. |
819 token) | 812 token) |
820 (when (and blink-matching-paren | 813 (when (and blink-matching-paren |
821 smie-closer-alist ; Optimization. | 814 smie-closer-alist ; Optimization. |
822 (or (eq (char-before) last-command-event) ;; Sanity check. | 815 (or (eq (char-before) last-command-event) ;; Sanity check. |
823 (save-excursion | 816 (save-excursion |
824 (or (progn (skip-chars-backward " \t") | 817 (or (progn (skip-chars-backward " \t") |
825 (setq pos (point)) | 818 (setq pos (point)) |
826 (eq (char-before) last-command-event)) | 819 (eq (char-before) last-command-event)) |
827 (progn (skip-chars-backward " \n\t") | 820 (progn (skip-chars-backward " \n\t") |
828 (setq pos (point)) | 821 (setq pos (point)) |
829 (eq (char-before) last-command-event))))) | 822 (eq (char-before) last-command-event))))) |
830 (memq last-command-event smie-blink-matching-triggers) | 823 (memq last-command-event smie-blink-matching-triggers) |
831 (not (nth 8 (syntax-ppss)))) | 824 (not (nth 8 (syntax-ppss)))) |
832 (save-excursion | 825 (save-excursion |
833 (setq token (funcall smie-backward-token-function)) | 826 (setq token (funcall smie-backward-token-function)) |
834 (when (and (eq (point) (1- pos)) | 827 (when (and (eq (point) (1- pos)) |
835 (= 1 (length token)) | 828 (= 1 (length token)) |
836 (not (rassoc token smie-closer-alist))) | 829 (not (rassoc token smie-closer-alist))) |
837 ;; The trigger char is itself a token but is not one of the | 830 ;; The trigger char is itself a token but is not one of the |
857 ;; inserting a subsequent trigger char like SPC. | 850 ;; inserting a subsequent trigger char like SPC. |
858 (or (eq (point) pos) | 851 (or (eq (point) pos) |
859 (not (memq (char-before) | 852 (not (memq (char-before) |
860 smie-blink-matching-triggers))) | 853 smie-blink-matching-triggers))) |
861 (or smie-blink-matching-inners | 854 (or smie-blink-matching-inners |
862 (null (nth 2 (assoc token smie-op-levels))))) | 855 (null (nth 2 (assoc token smie-grammar))))) |
863 ;; The major mode might set blink-matching-check-function | 856 ;; The major mode might set blink-matching-check-function |
864 ;; buffer-locally so that interactive calls to | 857 ;; buffer-locally so that interactive calls to |
865 ;; blink-matching-open work right, but let's not presume | 858 ;; blink-matching-open work right, but let's not presume |
866 ;; that's the case. | 859 ;; that's the case. |
867 (let ((blink-matching-check-function #'smie-blink-matching-check)) | 860 (let ((blink-matching-check-function #'smie-blink-matching-check)) |
892 | 885 |
893 When ARG is a token, the function is called with point just before that token. | 886 When ARG is a token, the function is called with point just before that token. |
894 A return value of nil always means to fallback on the default behavior, so the | 887 A return value of nil always means to fallback on the default behavior, so the |
895 function should return nil for arguments it does not expect. | 888 function should return nil for arguments it does not expect. |
896 | 889 |
897 OFFSET can be of the form: | 890 OFFSET can be: |
898 `point' align with the token. | 891 nil use the default indentation rule. |
899 `parent' align with the parent. | 892 `(column . COLUMN) indent to column COLUMN. |
900 NUMBER offset by NUMBER. | 893 NUMBER offset by NUMBER, relative to a base token |
901 \(+ OFFSETS...) use the sum of OFFSETS. | 894 which is the current token for :after and |
902 VARIABLE use the value of VARIABLE as offset. | 895 its parent for :before. |
903 | 896 |
904 This function will often use some of the following functions designed | 897 The functions whose name starts with \"smie-rule-\" are helper functions |
905 specifically for it: | 898 designed specifically for use in this function.") |
906 `smie-bolp', `smie-hanging-p', `smie-parent-p', `smie-next-p', `smie-prev-p'.") | 899 |
907 | 900 (defalias 'smie-rule-hanging-p 'smie-indent--hanging-p) |
908 (defun smie-hanging-p () | 901 (defun smie-indent--hanging-p () |
909 "Return non-nil if the current token is \"hanging\". | 902 "Return non-nil if the current token is \"hanging\". |
910 A hanging keyword is one that's at the end of a line except it's not at | 903 A hanging keyword is one that's at the end of a line except it's not at |
911 the beginning of a line." | 904 the beginning of a line." |
912 (and (not (smie-bolp)) | 905 (and (not (smie-indent--bolp)) |
913 (save-excursion | 906 (save-excursion |
914 (when (zerop (length (funcall smie-forward-token-function))) | 907 (<= (line-end-position) |
915 ;; Could be an open-paren. | 908 (progn |
916 (forward-char 1)) | 909 (when (zerop (length (funcall smie-forward-token-function))) |
917 (skip-chars-forward " \t") | 910 ;; Could be an open-paren. |
918 (eolp)))) | 911 (forward-char 1)) |
919 | 912 (skip-chars-forward " \t") |
920 (defun smie-bolp () | 913 (or (eolp) |
914 (and (looking-at comment-start-skip) | |
915 (forward-comment (point-max)))) | |
916 (point)))))) | |
917 | |
918 (defalias 'smie-rule-bolp 'smie-indent--bolp) | |
919 (defun smie-indent--bolp () | |
921 "Return non-nil if the current token is the first on the line." | 920 "Return non-nil if the current token is the first on the line." |
922 (save-excursion (skip-chars-backward " \t") (bolp))) | 921 (save-excursion (skip-chars-backward " \t") (bolp))) |
923 | 922 |
924 (defvar smie--parent) (defvar smie--after) ;Dynamically scoped. | 923 ;; Dynamically scoped. |
925 | 924 (defvar smie--parent) (defvar smie--after) (defvar smie--token) |
926 (defun smie-parent-p (&rest parents) | 925 |
926 (defun smie-indent--parent () | |
927 (or smie--parent | |
928 (save-excursion | |
929 (let* ((pos (point)) | |
930 (tok (funcall smie-forward-token-function))) | |
931 (unless (cadr (assoc tok smie-grammar)) | |
932 (goto-char pos)) | |
933 (setq smie--parent | |
934 (smie-backward-sexp 'halfsexp)))))) | |
935 | |
936 (defun smie-rule-parent-p (&rest parents) | |
927 "Return non-nil if the current token's parent is among PARENTS. | 937 "Return non-nil if the current token's parent is among PARENTS. |
928 Only meaningful when called from within `smie-rules-function'." | 938 Only meaningful when called from within `smie-rules-function'." |
929 (member (nth 2 (or smie--parent | 939 (member (nth 2 (smie-indent--parent)) parents)) |
930 (save-excursion | 940 |
931 (let* ((pos (point)) | 941 (defun smie-rule-next-p (&rest tokens) |
932 (tok (funcall smie-forward-token-function))) | |
933 (unless (cadr (assoc tok smie-op-levels)) | |
934 (goto-char pos)) | |
935 (setq smie--parent | |
936 (smie-backward-sexp 'halfsexp)))))) | |
937 parents)) | |
938 | |
939 (defun smie-next-p (&rest tokens) | |
940 "Return non-nil if the next token is among TOKENS. | 942 "Return non-nil if the next token is among TOKENS. |
941 Only meaningful when called from within `smie-rules-function'." | 943 Only meaningful when called from within `smie-rules-function'." |
942 (let ((next | 944 (let ((next |
943 (save-excursion | 945 (save-excursion |
944 (unless smie--after | 946 (unless smie--after |
945 (smie-indent-forward-token) (setq smie--after (point))) | 947 (smie-indent-forward-token) (setq smie--after (point))) |
946 (goto-char smie--after) | 948 (goto-char smie--after) |
947 (smie-indent-forward-token)))) | 949 (smie-indent-forward-token)))) |
948 (member (car next) tokens))) | 950 (member (car next) tokens))) |
949 | 951 |
950 (defun smie-prev-p (&rest tokens) | 952 (defun smie-rule-prev-p (&rest tokens) |
951 "Return non-nil if the previous token is among TOKENS." | 953 "Return non-nil if the previous token is among TOKENS." |
952 (let ((prev (save-excursion | 954 (let ((prev (save-excursion |
953 (smie-indent-backward-token)))) | 955 (smie-indent-backward-token)))) |
954 (member (car prev) tokens))) | 956 (member (car prev) tokens))) |
955 | 957 |
958 (defun smie-rule-sibling-p () | |
959 "Return non-nil if the parent is actually a sibling. | |
960 Only meaningful when called from within `smie-rules-function'." | |
961 (eq (car (smie-indent--parent)) | |
962 (cadr (assoc smie--token smie-grammar)))) | |
963 | |
964 (defun smie-rule-parent (&optional offset) | |
965 "Align with parent. | |
966 If non-nil, OFFSET should be an integer giving an additional offset to apply. | |
967 Only meaningful when called from within `smie-rules-function'." | |
968 (save-excursion | |
969 (goto-char (cadr (smie-indent--parent))) | |
970 (cons 'column | |
971 (+ (or offset 0) | |
972 (if (smie-indent--hanging-p) | |
973 (smie-indent-virtual) (current-column)))))) | |
974 | |
975 (defvar smie-rule-separator-outdent 2) | |
976 | |
977 (defun smie-indent--separator-outdent () | |
978 ;; FIXME: Here we actually have several reasonable behaviors. | |
979 ;; E.g. for a parent token of "FOO" and a separator ";" we may want to: | |
980 ;; 1- left-align ; with FOO. | |
981 ;; 2- right-align ; with FOO. | |
982 ;; 3- align content after ; with content after FOO. | |
983 ;; 4- align content plus add/remove spaces so as to align ; with FOO. | |
984 ;; Currently, we try to align the contents (option 3) which actually behaves | |
985 ;; just like option 2 (if the number of spaces after FOO and ; is equal). | |
986 (let ((afterpos (save-excursion | |
987 (let ((tok (funcall smie-forward-token-function))) | |
988 (unless tok | |
989 (with-demoted-errors | |
990 (error "smie-rule-separator: can't skip token %s" | |
991 smie--token)))) | |
992 (skip-chars-forward " ") | |
993 (unless (eolp) (point))))) | |
994 (or (and afterpos | |
995 ;; This should always be true, unless | |
996 ;; smie-forward-token-function skipped a \n. | |
997 (< afterpos (line-end-position)) | |
998 (- afterpos (point))) | |
999 smie-rule-separator-outdent))) | |
1000 | |
1001 (defun smie-rule-separator (method) | |
1002 "Indent current token as a \"separator\". | |
1003 By \"separator\", we mean here a token whose sole purpose is to separate | |
1004 various elements within some enclosing syntactic construct, and which does | |
1005 not have any semantic significance in itself (i.e. it would typically no exist | |
1006 as a node in an abstract syntax tree). | |
1007 Such a token is expected to have an associative syntax and be closely tied | |
1008 to its syntactic parent. Typical examples are \",\" in lists of arguments | |
1009 \(enclosed inside parentheses), or \";\" in sequences of instructions (enclosed | |
1010 in a {..} or begin..end block). | |
1011 METHOD should be the method name that was passed to `smie-rules-function'. | |
1012 Only meaningful when called from within `smie-rules-function'." | |
1013 ;; FIXME: The code below works OK for cases where the separators | |
1014 ;; are placed consistently always at beginning or always at the end, | |
1015 ;; but not if some are at the beginning and others are at the end. | |
1016 ;; I.e. it gets confused in cases such as: | |
1017 ;; ( a | |
1018 ;; , a, | |
1019 ;; b | |
1020 ;; , c, | |
1021 ;; d | |
1022 ;; ) | |
1023 ;; | |
1024 ;; Assuming token is associative, the default rule for associative | |
1025 ;; tokens (which assumes an infix operator) works fine for many cases. | |
1026 ;; We mostly need to take care of the case where token is at beginning of | |
1027 ;; line, in which case we want to align it with its enclosing parent. | |
1028 (cond | |
1029 ((and (eq method :before) (smie-rule-bolp) (not (smie-rule-sibling-p))) | |
1030 ;; FIXME: Rather than consult the number of spaces, we could *set* the | |
1031 ;; number of spaces so as to align the separator with the close-paren | |
1032 ;; while aligning the content with the rest. | |
1033 (let ((parent-col | |
1034 (save-excursion | |
1035 (goto-char (cadr smie--parent)) | |
1036 (if (smie-indent--hanging-p) | |
1037 (smie-indent-virtual) (current-column)))) | |
1038 (parent-pos-col ;FIXME: we knew this when computing smie--parent. | |
1039 (save-excursion | |
1040 (goto-char (cadr smie--parent)) | |
1041 (smie-indent-forward-token) | |
1042 (forward-comment (point-max)) | |
1043 (current-column)))) | |
1044 (cons 'column | |
1045 (max parent-col | |
1046 (min parent-pos-col | |
1047 (- parent-pos-col (smie-indent--separator-outdent))))))) | |
1048 ((and (eq method :after) (smie-indent--bolp)) | |
1049 (smie-indent--separator-outdent)))) | |
956 | 1050 |
957 (defun smie-indent--offset (elem) | 1051 (defun smie-indent--offset (elem) |
958 (or (funcall smie-rules-function :elem elem) | 1052 (or (funcall smie-rules-function :elem elem) |
959 (if (not (eq elem 'basic)) | 1053 (if (not (eq elem 'basic)) |
960 (funcall smie-rules-function :elem 'basic)) | 1054 (funcall smie-rules-function :elem 'basic)) |
961 smie-indent-basic)) | 1055 smie-indent-basic)) |
962 | 1056 |
963 (defun smie-indent--rule (kind token &optional after parent) | 1057 (defun smie-indent--rule (method token |
964 (let ((smie--parent parent) | 1058 ;; FIXME: Too many parameters. |
965 (smie--after after)) | 1059 &optional after parent base-pos) |
966 (funcall smie-rules-function kind token))) | 1060 "Compute indentation column according to `indent-rule-functions'. |
967 | 1061 METHOD and TOKEN are passed to `indent-rule-functions'. |
968 (defun smie-indent--column (offset &optional base parent virtual-point) | 1062 AFTER is the position after TOKEN, if known. |
969 "Compute the actual column to use for a given OFFSET. | 1063 PARENT is the parent info returned by `smie-backward-sexp', if known. |
970 BASE is the base position to use, and PARENT is the parent info, if any. | 1064 BASE-POS is the position relative to which offsets should be applied." |
971 If VIRTUAL-POINT is non-nil, then `point' is virtual." | 1065 ;; This is currently called in 3 cases: |
972 (cond | 1066 ;; - :before opener, where rest=nil but base-pos could as well be parent. |
973 ((eq (car-safe offset) '+) | 1067 ;; - :before other, where |
974 (apply '+ (mapcar (lambda (offset) (smie-indent--column offset nil parent)) | 1068 ;; ; after=nil |
975 (cdr offset)))) | 1069 ;; ; parent is set |
976 ((integerp offset) | 1070 ;; ; base-pos=parent |
977 (+ offset | 1071 ;; - :after tok, where |
978 (case base | 1072 ;; ; after is set; parent=nil; base-pos=point; |
979 ((nil) 0) | 1073 (save-excursion |
980 (parent (goto-char (cadr parent)) | 1074 (let ((offset |
981 (smie-indent-virtual)) | 1075 (let ((smie--parent parent) |
982 (t | 1076 (smie--token token) |
983 (goto-char base) | 1077 (smie--after after)) |
984 ;; For indentation after "(let" in SML-mode, we end up accumulating | 1078 (funcall smie-rules-function method token)))) |
985 ;; the offset of "(" and the offset of "let", so we use `min' to try | 1079 (cond |
986 ;; and get it right either way. | 1080 ((not offset) nil) |
987 (min (smie-indent-virtual) (current-column)))))) | 1081 ((eq (car-safe offset) 'column) (cdr offset)) |
988 ((eq offset 'point) | 1082 ((integerp offset) |
989 ;; In indent-keyword, if we're indenting `then' wrt `if', we want to use | 1083 (+ offset |
990 ;; indent-virtual rather than use just current-column, so that we can | 1084 (if (null base-pos) 0 |
991 ;; apply the (:before . "if") rule which does the "else if" dance in SML. | 1085 (goto-char base-pos) |
992 ;; But in other cases, we do not want to use indent-virtual | 1086 (if (smie-indent--hanging-p) |
993 ;; (e.g. indentation of "*" w.r.t "+", or ";" wrt "("). We could just | 1087 (smie-indent-virtual) (current-column))))) |
994 ;; always use indent-virtual and then have indent-rules say explicitly | 1088 (t (error "Unknown indentation offset %s" offset)))))) |
995 ;; to use `point' after things like "(" or "+" when they're not at EOL, | |
996 ;; but you'd end up with lots of those rules. | |
997 ;; So we use a heuristic here, which is that we only use virtual if | |
998 ;; the parent is tightly linked to the child token (they're part of | |
999 ;; the same BNF rule). | |
1000 (if (and virtual-point (null (car parent))) ;Black magic :-( | |
1001 (smie-indent-virtual) (current-column))) | |
1002 ((eq offset 'parent) | |
1003 (unless parent | |
1004 (setq parent (or (smie-backward-sexp 'halfsexp) :notfound))) | |
1005 (if (consp parent) (goto-char (cadr parent))) | |
1006 (smie-indent-virtual)) | |
1007 ((eq offset nil) nil) | |
1008 ;; FIXME: would be good to get rid of this since smie-rules-function | |
1009 ;; can usually do the lookup trivially, but in cases where | |
1010 ;; smie-rules-function returns (+ point VAR) it's not nearly as trivial. | |
1011 ((and (symbolp offset) (boundp 'offset)) | |
1012 (smie-indent--column (symbol-value offset) base parent virtual-point)) | |
1013 (t (error "Unknown indentation offset %s" offset)))) | |
1014 | 1089 |
1015 (defun smie-indent-forward-token () | 1090 (defun smie-indent-forward-token () |
1016 "Skip token forward and return it, along with its levels." | 1091 "Skip token forward and return it, along with its levels." |
1017 (let ((tok (funcall smie-forward-token-function))) | 1092 (let ((tok (funcall smie-forward-token-function))) |
1018 (cond | 1093 (cond |
1019 ((< 0 (length tok)) (assoc tok smie-op-levels)) | 1094 ((< 0 (length tok)) (assoc tok smie-grammar)) |
1020 ((looking-at "\\s(") | 1095 ((looking-at "\\s(\\|\\s)\\(\\)") |
1021 (forward-char 1) | 1096 (forward-char 1) |
1022 (list (buffer-substring (1- (point)) (point)) nil 0))))) | 1097 (cons (buffer-substring (1- (point)) (point)) |
1098 (if (match-end 1) '(0 nil) '(nil 0))))))) | |
1023 | 1099 |
1024 (defun smie-indent-backward-token () | 1100 (defun smie-indent-backward-token () |
1025 "Skip token backward and return it, along with its levels." | 1101 "Skip token backward and return it, along with its levels." |
1026 (let ((tok (funcall smie-backward-token-function))) | 1102 (let ((tok (funcall smie-backward-token-function)) |
1103 class) | |
1027 (cond | 1104 (cond |
1028 ((< 0 (length tok)) (assoc tok smie-op-levels)) | 1105 ((< 0 (length tok)) (assoc tok smie-grammar)) |
1029 ;; 4 == Open paren syntax. | 1106 ;; 4 == open paren syntax, 5 == close. |
1030 ((eq 4 (syntax-class (syntax-after (1- (point))))) | 1107 ((memq (setq class (syntax-class (syntax-after (1- (point))))) '(4 5)) |
1031 (forward-char -1) | 1108 (forward-char -1) |
1032 (list (buffer-substring (point) (1+ (point))) nil 0))))) | 1109 (cons (buffer-substring (point) (1+ (point))) |
1110 (if (eq class 4) '(nil 0) '(0 nil))))))) | |
1033 | 1111 |
1034 (defun smie-indent-virtual () | 1112 (defun smie-indent-virtual () |
1035 ;; We used to take an optional arg (with value :not-hanging) to specify that | 1113 ;; We used to take an optional arg (with value :not-hanging) to specify that |
1036 ;; we should only use (smie-indent-calculate) if we're looking at a hanging | 1114 ;; we should only use (smie-indent-calculate) if we're looking at a hanging |
1037 ;; keyword. This was a bad idea, because the virtual indent of a position | 1115 ;; keyword. This was a bad idea, because the virtual indent of a position |
1040 "Compute the virtual indentation to use for point. | 1118 "Compute the virtual indentation to use for point. |
1041 This is used when we're not trying to indent point but just | 1119 This is used when we're not trying to indent point but just |
1042 need to compute the column at which point should be indented | 1120 need to compute the column at which point should be indented |
1043 in order to figure out the indentation of some other (further down) point." | 1121 in order to figure out the indentation of some other (further down) point." |
1044 ;; Trust pre-existing indentation on other lines. | 1122 ;; Trust pre-existing indentation on other lines. |
1045 (if (smie-bolp) (current-column) (smie-indent-calculate))) | 1123 (if (smie-indent--bolp) (current-column) (smie-indent-calculate))) |
1046 | 1124 |
1047 (defun smie-indent-fixindent () | 1125 (defun smie-indent-fixindent () |
1048 ;; Obey the `fixindent' special comment. | 1126 ;; Obey the `fixindent' special comment. |
1049 (and (smie-bolp) | 1127 (and (smie-indent--bolp) |
1050 (save-excursion | 1128 (save-excursion |
1051 (comment-normalize-vars) | 1129 (comment-normalize-vars) |
1052 (re-search-forward (concat comment-start-skip | 1130 (re-search-forward (concat comment-start-skip |
1053 "fixindent" | 1131 "fixindent" |
1054 comment-end-skip) | 1132 comment-end-skip) |
1084 (token (pop toklevels))) | 1162 (token (pop toklevels))) |
1085 (if (null (car toklevels)) | 1163 (if (null (car toklevels)) |
1086 (save-excursion | 1164 (save-excursion |
1087 (goto-char pos) | 1165 (goto-char pos) |
1088 ;; Different cases: | 1166 ;; Different cases: |
1089 ;; - smie-bolp: "indent according to others". | 1167 ;; - smie-indent--bolp: "indent according to others". |
1090 ;; - common hanging: "indent according to others". | 1168 ;; - common hanging: "indent according to others". |
1091 ;; - SML-let hanging: "indent like parent". | 1169 ;; - SML-let hanging: "indent like parent". |
1092 ;; - if-after-else: "indent-like parent". | 1170 ;; - if-after-else: "indent-like parent". |
1093 ;; - middle-of-line: "trust current position". | 1171 ;; - middle-of-line: "trust current position". |
1094 (cond | 1172 (cond |
1095 ((null (cdr toklevels)) nil) ;Not a keyword. | 1173 ((null (cdr toklevels)) nil) ;Not a keyword. |
1096 ((smie-bolp) | 1174 ((smie-indent--bolp) |
1097 ;; For an open-paren-like thingy at BOL, always indent only | 1175 ;; For an open-paren-like thingy at BOL, always indent only |
1098 ;; based on other rules (typically smie-indent-after-keyword). | 1176 ;; based on other rules (typically smie-indent-after-keyword). |
1099 nil) | 1177 nil) |
1178 ;; We're only ever here for virtual-indent. | |
1179 ((smie-indent--rule :before token)) | |
1100 (t | 1180 (t |
1101 ;; We're only ever here for virtual-indent, which is why | 1181 ;; By default use point unless we're hanging. |
1102 ;; we can use (current-column) as answer for `point'. | 1182 (unless (smie-indent--hanging-p) (current-column))))) |
1103 (let* ((offset (or (smie-indent--rule :before token) | |
1104 ;; By default use point unless we're hanging. | |
1105 (unless (smie-hanging-p) 'point)))) | |
1106 (smie-indent--column offset))))) | |
1107 | 1183 |
1108 ;; FIXME: This still looks too much like black magic!! | 1184 ;; FIXME: This still looks too much like black magic!! |
1109 (let* ((parent (smie-backward-sexp 'halfsexp)) | 1185 (let* ((parent (smie-backward-sexp 'halfsexp))) |
1110 (offset (save-excursion | |
1111 (goto-char pos) | |
1112 (or (smie-indent--rule :before token nil parent) | |
1113 'point)))) | |
1114 ;; Different behaviors: | 1186 ;; Different behaviors: |
1115 ;; - align with parent. | 1187 ;; - align with parent. |
1116 ;; - parent + offset. | 1188 ;; - parent + offset. |
1117 ;; - after parent's column + offset (actually, after or before | 1189 ;; - after parent's column + offset (actually, after or before |
1118 ;; depending on where backward-sexp stopped). | 1190 ;; depending on where backward-sexp stopped). |
1131 ;; If we didn't move at all, that means we didn't really skip | 1203 ;; If we didn't move at all, that means we didn't really skip |
1132 ;; what we wanted. Should almost never happen, other than | 1204 ;; what we wanted. Should almost never happen, other than |
1133 ;; maybe when an infix or close-paren is at the beginning | 1205 ;; maybe when an infix or close-paren is at the beginning |
1134 ;; of a buffer. | 1206 ;; of a buffer. |
1135 nil) | 1207 nil) |
1208 ((save-excursion | |
1209 (goto-char pos) | |
1210 (smie-indent--rule :before token nil parent (cadr parent)))) | |
1136 ((eq (car parent) (car toklevels)) | 1211 ((eq (car parent) (car toklevels)) |
1137 ;; We bumped into a same-level operator. align with it. | 1212 ;; We bumped into a same-level operator; align with it. |
1138 (if (and (smie-bolp) (/= (point) pos) | 1213 (if (and (smie-indent--bolp) (/= (point) pos) |
1139 (save-excursion | 1214 (save-excursion |
1140 (goto-char (goto-char (cadr parent))) | 1215 (goto-char (goto-char (cadr parent))) |
1141 (not (smie-bolp))) | 1216 (not (smie-indent--bolp)))) |
1142 ;; Check the offset of `token' rather then its parent | |
1143 ;; because its parent may have used a special rule. E.g. | |
1144 ;; function foo; | |
1145 ;; line2; | |
1146 ;; line3; | |
1147 ;; The ; on the first line had a special rule, but when | |
1148 ;; indenting line3, we don't care about it and want to | |
1149 ;; align with line2. | |
1150 (memq offset '(point nil))) | |
1151 ;; If the parent is at EOL and its children are indented like | 1217 ;; If the parent is at EOL and its children are indented like |
1152 ;; itself, then we can just obey the indentation chosen for the | 1218 ;; itself, then we can just obey the indentation chosen for the |
1153 ;; child. | 1219 ;; child. |
1154 ;; This is important for operators like ";" which | 1220 ;; This is important for operators like ";" which |
1155 ;; are usually at EOL (and have an offset of 0): otherwise we'd | 1221 ;; are usually at EOL (and have an offset of 0): otherwise we'd |
1173 ;; a -> b -> c | 1239 ;; a -> b -> c |
1174 ;; -> d | 1240 ;; -> d |
1175 ;; So as to align with the earliest appropriate place. | 1241 ;; So as to align with the earliest appropriate place. |
1176 (smie-indent-virtual))) | 1242 (smie-indent-virtual))) |
1177 (t | 1243 (t |
1178 (if (and (= (point) pos) (smie-bolp) | 1244 (if (and (= (point) pos) (smie-indent--bolp)) |
1179 (or (eq offset 'point) | |
1180 (and (consp offset) (memq 'point offset)))) | |
1181 ;; Since we started at BOL, we're not computing a virtual | 1245 ;; Since we started at BOL, we're not computing a virtual |
1182 ;; indentation, and we're still at the starting point, so | 1246 ;; indentation, and we're still at the starting point, so |
1183 ;; we can't use `current-column' which would cause | 1247 ;; we can't use `current-column' which would cause |
1184 ;; indentation to depend on itself. | 1248 ;; indentation to depend on itself and we can't use |
1249 ;; smie-indent-virtual since that would be an inf-loop. | |
1185 nil | 1250 nil |
1186 (smie-indent--column offset 'parent parent | 1251 ;; In indent-keyword, if we're indenting `then' wrt `if', we |
1187 ;; If we're still at pos, indent-virtual | 1252 ;; want to use indent-virtual rather than use just |
1188 ;; will inf-loop. | 1253 ;; current-column, so that we can apply the (:before . "if") |
1189 (unless (= (point) pos) 'virtual)))))))))) | 1254 ;; rule which does the "else if" dance in SML. But in other |
1255 ;; cases, we do not want to use indent-virtual (e.g. indentation | |
1256 ;; of "*" w.r.t "+", or ";" wrt "("). We could just always use | |
1257 ;; indent-virtual and then have indent-rules say explicitly to | |
1258 ;; use `point' after things like "(" or "+" when they're not at | |
1259 ;; EOL, but you'd end up with lots of those rules. | |
1260 ;; So we use a heuristic here, which is that we only use virtual | |
1261 ;; if the parent is tightly linked to the child token (they're | |
1262 ;; part of the same BNF rule). | |
1263 (if (car parent) (current-column) (smie-indent-virtual)))))))))) | |
1190 | 1264 |
1191 (defun smie-indent-comment () | 1265 (defun smie-indent-comment () |
1192 "Compute indentation of a comment." | 1266 "Compute indentation of a comment." |
1193 ;; Don't do it for virtual indentations. We should normally never be "in | 1267 ;; Don't do it for virtual indentations. We should normally never be "in |
1194 ;; front of a comment" when doing virtual-indentation anyway. And if we are | 1268 ;; front of a comment" when doing virtual-indentation anyway. And if we are |
1195 ;; (as can happen in octave-mode), moving forward can lead to inf-loops. | 1269 ;; (as can happen in octave-mode), moving forward can lead to inf-loops. |
1196 (and (smie-bolp) | 1270 (and (smie-indent--bolp) |
1197 (let ((pos (point))) | 1271 (let ((pos (point))) |
1198 (save-excursion | 1272 (save-excursion |
1199 (beginning-of-line) | 1273 (beginning-of-line) |
1200 (and (re-search-forward comment-start-skip (line-end-position) t) | 1274 (and (re-search-forward comment-start-skip (line-end-position) t) |
1201 (eq pos (or (match-end 1) (match-beginning 0)))))) | 1275 (eq pos (or (match-end 1) (match-beginning 0)))))) |
1237 ;; Indentation right after a special keyword. | 1311 ;; Indentation right after a special keyword. |
1238 (save-excursion | 1312 (save-excursion |
1239 (let* ((pos (point)) | 1313 (let* ((pos (point)) |
1240 (toklevel (smie-indent-backward-token)) | 1314 (toklevel (smie-indent-backward-token)) |
1241 (tok (car toklevel))) | 1315 (tok (car toklevel))) |
1242 (when toklevel | 1316 (cond |
1243 (let ((offset | 1317 ((null toklevel) nil) |
1244 (or (smie-indent--rule :after tok pos) | 1318 ((smie-indent--rule :after tok pos nil (point))) |
1245 ;; The default indentation after a keyword/operator is | 1319 ;; The default indentation after a keyword/operator is |
1246 ;; 0 for infix and t for prefix. | 1320 ;; 0 for infix, t for prefix, and use another rule |
1247 (if (or (null (cadr toklevel)) | 1321 ;; for postfix. |
1248 (rassoc tok smie-closer-alist)) | 1322 ((null (nth 2 toklevel)) nil) ;A closer. |
1249 (smie-indent--offset 'basic) 0))) | 1323 ((or (null (nth 1 toklevel)) ;An opener. |
1250 (before (point))) | 1324 (rassoc tok smie-closer-alist)) ;An inner. |
1251 (goto-char pos) | 1325 (+ (smie-indent-virtual) (smie-indent--offset 'basic))) ; |
1252 (smie-indent--column offset before)))))) | 1326 (t (smie-indent-virtual)))))) ;An infix. |
1253 | 1327 |
1254 (defun smie-indent-exps () | 1328 (defun smie-indent-exps () |
1255 ;; Indentation of sequences of simple expressions without | 1329 ;; Indentation of sequences of simple expressions without |
1256 ;; intervening keywords or operators. E.g. "a b c" or "g (balbla) f". | 1330 ;; intervening keywords or operators. E.g. "a b c" or "g (balbla) f". |
1257 ;; Can be a list of expressions or a function call. | 1331 ;; Can be a list of expressions or a function call. |
1270 (save-excursion | 1344 (save-excursion |
1271 (let ((positions nil) | 1345 (let ((positions nil) |
1272 arg) | 1346 arg) |
1273 (while (and (null (car (smie-backward-sexp))) | 1347 (while (and (null (car (smie-backward-sexp))) |
1274 (push (point) positions) | 1348 (push (point) positions) |
1275 (not (smie-bolp)))) | 1349 (not (smie-indent--bolp)))) |
1276 (save-excursion | 1350 (save-excursion |
1277 ;; Figure out if the atom we just skipped is an argument rather | 1351 ;; Figure out if the atom we just skipped is an argument rather |
1278 ;; than a function. | 1352 ;; than a function. |
1279 (setq arg | 1353 (setq arg |
1280 (or (null (car (smie-backward-sexp))) | 1354 (or (null (car (smie-backward-sexp))) |
1296 (goto-char (cadr positions)) | 1370 (goto-char (cadr positions)) |
1297 (current-column)) | 1371 (current-column)) |
1298 (positions | 1372 (positions |
1299 ;; We're the first arg. | 1373 ;; We're the first arg. |
1300 (goto-char (car positions)) | 1374 (goto-char (car positions)) |
1301 ;; FIXME: Use smie-indent--column. | |
1302 (+ (smie-indent--offset 'args) | 1375 (+ (smie-indent--offset 'args) |
1303 ;; We used to use (smie-indent-virtual), but that | 1376 ;; We used to use (smie-indent-virtual), but that |
1304 ;; doesn't seem right since it might then indent args less than | 1377 ;; doesn't seem right since it might then indent args less than |
1305 ;; the function itself. | 1378 ;; the function itself. |
1306 (current-column))))))) | 1379 (current-column))))))) |
1307 | 1380 |
1308 (defvar smie-indent-functions | 1381 (defvar smie-indent-functions |
1309 '(smie-indent-fixindent smie-indent-bob smie-indent-close | 1382 '(smie-indent-fixindent smie-indent-bob smie-indent-close |
1310 smie-indent-comment smie-indent-comment-continue smie-indent-comment-close | 1383 smie-indent-comment smie-indent-comment-continue smie-indent-comment-close |
1311 smie-indent-comment-inside smie-indent-keyword smie-indent-after-keyword | 1384 smie-indent-comment-inside smie-indent-keyword smie-indent-after-keyword |
1312 smie-indent-exps) | 1385 smie-indent-exps) |
1313 "Functions to compute the indentation. | 1386 "Functions to compute the indentation. |
1314 Each function is called with no argument, shouldn't move point, and should | 1387 Each function is called with no argument, shouldn't move point, and should |
1315 return either nil if it has no opinion, or an integer representing the column | 1388 return either nil if it has no opinion, or an integer representing the column |
1316 to which that point should be aligned, if we were to reindent it.") | 1389 to which that point should be aligned, if we were to reindent it.") |
1317 | 1390 |
1321 | 1394 |
1322 (defun smie-indent-line () | 1395 (defun smie-indent-line () |
1323 "Indent current line using the SMIE indentation engine." | 1396 "Indent current line using the SMIE indentation engine." |
1324 (interactive) | 1397 (interactive) |
1325 (let* ((savep (point)) | 1398 (let* ((savep (point)) |
1326 (indent (condition-case-no-debug nil | 1399 (indent (or (with-demoted-errors |
1327 (save-excursion | 1400 (save-excursion |
1328 (forward-line 0) | 1401 (forward-line 0) |
1329 (skip-chars-forward " \t") | 1402 (skip-chars-forward " \t") |
1330 (if (>= (point) savep) (setq savep nil)) | 1403 (if (>= (point) savep) (setq savep nil)) |
1331 (or (smie-indent-calculate) 0)) | 1404 (or (smie-indent-calculate) 0))) |
1332 (error 0)))) | 1405 0))) |
1333 (if (not (numberp indent)) | 1406 (if (not (numberp indent)) |
1334 ;; If something funny is used (e.g. `noindent'), return it. | 1407 ;; If something funny is used (e.g. `noindent'), return it. |
1335 indent | 1408 indent |
1336 (if (< indent 0) (setq indent 0)) ;Just in case. | 1409 (if (< indent 0) (setq indent 0)) ;Just in case. |
1337 (if savep | 1410 (if savep |
1338 (save-excursion (indent-line-to indent)) | 1411 (save-excursion (indent-line-to indent)) |
1339 (indent-line-to indent))))) | 1412 (indent-line-to indent))))) |
1340 | 1413 |
1341 (defun smie-setup (op-levels rules-function &rest keywords) | 1414 (defun smie-setup (grammar rules-function &rest keywords) |
1342 "Setup SMIE navigation and indentation. | 1415 "Setup SMIE navigation and indentation. |
1343 OP-LEVELS is a grammar table generated by `smie-prec2-levels'. | 1416 GRAMMAR is a grammar table generated by `smie-prec2->grammar'. |
1344 RULES-FUNCTION is a set of indentation rules for use on `smie-rules-function'. | 1417 RULES-FUNCTION is a set of indentation rules for use on `smie-rules-function'. |
1345 KEYWORDS are additional arguments, which can use the following keywords: | 1418 KEYWORDS are additional arguments, which can use the following keywords: |
1346 - :forward-token FUN | 1419 - :forward-token FUN |
1347 - :backward-token FUN" | 1420 - :backward-token FUN" |
1348 (set (make-local-variable 'smie-rules-function) rules-function) | 1421 (set (make-local-variable 'smie-rules-function) rules-function) |
1349 (set (make-local-variable 'smie-op-levels) op-levels) | 1422 (set (make-local-variable 'smie-grammar) grammar) |
1350 (set (make-local-variable 'indent-line-function) 'smie-indent-line) | 1423 (set (make-local-variable 'indent-line-function) 'smie-indent-line) |
1351 (set (make-local-variable 'forward-sexp-function) | 1424 (set (make-local-variable 'forward-sexp-function) |
1352 'smie-forward-sexp-command) | 1425 'smie-forward-sexp-command) |
1353 (while keywords | 1426 (while keywords |
1354 (let ((k (pop keywords)) | 1427 (let ((k (pop keywords)) |
1357 (:forward-token | 1430 (:forward-token |
1358 (set (make-local-variable 'smie-forward-token-function) v)) | 1431 (set (make-local-variable 'smie-forward-token-function) v)) |
1359 (:backward-token | 1432 (:backward-token |
1360 (set (make-local-variable 'smie-backward-token-function) v)) | 1433 (set (make-local-variable 'smie-backward-token-function) v)) |
1361 (t (message "smie-setup: ignoring unknown keyword %s" k))))) | 1434 (t (message "smie-setup: ignoring unknown keyword %s" k))))) |
1362 (let ((ca (cdr (assq :smie-closer-alist op-levels)))) | 1435 (let ((ca (cdr (assq :smie-closer-alist grammar)))) |
1363 (when ca | 1436 (when ca |
1364 (set (make-local-variable 'smie-closer-alist) ca) | 1437 (set (make-local-variable 'smie-closer-alist) ca) |
1365 ;; Only needed for interactive calls to blink-matching-open. | 1438 ;; Only needed for interactive calls to blink-matching-open. |
1366 (set (make-local-variable 'blink-matching-check-function) | 1439 (set (make-local-variable 'blink-matching-check-function) |
1367 #'smie-blink-matching-check) | 1440 #'smie-blink-matching-check) |