Mercurial > emacs
changeset 108894:ca3bfaa18e56
Make (after keyword) indent-rules more flexible.
* lisp/emacs-lisp/smie.el (smie-indent-offset-after)
(smie-indent-forward-token, smie-indent-backward-token): New functions.
(smie-indent-after-keyword): Use them.
(smie-indent-fixindent): Only applies to the indentation of the BOL.
(smie-indent-keyword): Tweak the black magic.
(smie-indent-comment-continue): Strip comment-continue before use.
(smie-indent-functions): Indent comments before keywords.
author | Stefan Monnier <monnier@iro.umontreal.ca> |
---|---|
date | Sun, 06 Jun 2010 22:10:19 -0400 |
parents | cc0a50e33241 |
children | c4f9f87f04a2 |
files | lisp/ChangeLog lisp/emacs-lisp/smie.el |
diffstat | 2 files changed, 156 insertions(+), 66 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/ChangeLog Sun Jun 06 12:52:13 2010 +0300 +++ b/lisp/ChangeLog Sun Jun 06 22:10:19 2010 -0400 @@ -1,3 +1,13 @@ +2010-06-07 Stefan Monnier <monnier@iro.umontreal.ca> + + * emacs-lisp/smie.el (smie-indent-offset-after) + (smie-indent-forward-token, smie-indent-backward-token): New functions. + (smie-indent-after-keyword): Use them. + (smie-indent-fixindent): Only applies to the indentation of the BOL. + (smie-indent-keyword): Tweak the black magic. + (smie-indent-comment-continue): Strip comment-continue before use. + (smie-indent-functions): Indent comments before keywords. + 2010-06-06 Juri Linkov <juri@jurta.org> * isearch.el (isearch-lazy-highlight-search): Fix looping
--- a/lisp/emacs-lisp/smie.el Sun Jun 06 12:52:13 2010 +0300 +++ b/lisp/emacs-lisp/smie.el Sun Jun 06 22:10:19 2010 -0400 @@ -294,7 +294,12 @@ (defvar smie-op-levels 'unset "List of token parsing info. Each element is of the form (TOKEN LEFT-LEVEL RIGHT-LEVEL). -Parsing is done using an operator precedence parser.") +Parsing is done using an operator precedence parser. +LEFT-LEVEL and RIGHT-LEVEL can be either numbers or nil, where nil +means that this operator does not bind on the corresponding side, +i.e. a LEFT-LEVEL of nil means this is a token that behaves somewhat like +an open-paren, whereas a RIGHT-LEVEL of nil would correspond to something +like a close-paren.") (defvar smie-forward-token-function 'smie-default-forward-token "Function to scan forward for the next token. @@ -495,16 +500,19 @@ ;; I.e. the indentation after "=" depends on the parent ("structure") ;; as well as on the following token ("struct"). "Rules of the following form. -\(TOK OFFSET) how to indent right after TOK. -\(TOK O1 O2) how to indent right after TOK: - O1 is the default; - O2 is used if TOK is \"hanging\". +\(TOK . OFFSET-RULES) how to indent right after TOK. \((T1 . T2) . OFFSET) how to indent token T2 w.r.t T1. \((t . TOK) . OFFSET) how to indent TOK with respect to its parent. \(list-intro . TOKENS) declare TOKENS as being followed by what may look like a funcall but is just a sequence of expressions. \(t . OFFSET) basic indentation step. \(args . OFFSET) indentation of arguments. + +OFFSET-RULES is list of elements which can either be an integer (the offset to +use), or a cons of the form +\(:hanging . OFFSET-RULES) if TOK is hanging, use OFFSET-RULES. +\(:parent PARENT . OFFSET-RULES) if TOK's parent is PARENT, use OFFSET-RULES. +\(:next TOKEN . OFFSET-RULES) if TOK is followed by TOKEN, use OFFSET-RULES. A nil offset defaults to `smie-indent-basic'.") (defun smie-indent-hanging-p () @@ -526,6 +534,68 @@ (cdr (assq t smie-indent-rules)) smie-indent-basic)) +(defun smie-indent-offset-after (tokinfo after) + ;; Presumes we're right before the token corresponding to `tokinfo' + ;; and `after' is the position that we're trying to indent. + (let ((rules (cdr tokinfo)) + parent next offset) + (while (consp rules) + (let ((rule (pop rules))) + (cond + ((not (consp rule)) (setq offset rule)) + ;; Using this `:hanging' is often "wrong", in that the hangingness + ;; of a keyword should usually not affect the indentation of the + ;; immediately following expression, but rather should affect the + ;; virtual indentation of that keyword (which in turn will affect not + ;; only indentation of the immediately following expression, but also + ;; other dependent expressions). + ;; But there are cases where it's useful: you may want to use it to + ;; make the indentation inside parentheses different depending on the + ;; hangingness of the open-paren, but without affecting the + ;; indentation of the paren-close. + ((eq (car rule) :hanging) + (when (smie-indent-hanging-p) + (setq rules (cdr rule)))) + ((eq (car rule) :next) + (unless next + (save-excursion + (goto-char after) + (setq next (funcall smie-forward-token-function)))) + (when (equal next (cadr rule)) + (setq rules (cddr rule)))) + ((eq (car rule) :parent) + (unless parent + (save-excursion + (goto-char after) + (setq parent (smie-backward-sexp 'halfsexp)))) + (when (equal (nth 2 parent) (cadr rule)) + (setq rules (cddr rule)))) + (t (error "Unknown rule %s for indentation after %s" + rule (car tokinfo)))))) + (or offset (smie-indent-offset t)))) + +(defun smie-indent-forward-token () + "Skip token forward and return it, along with its levels." + (let ((tok (funcall smie-forward-token-function))) + (cond + ((< 0 (length tok)) (assoc tok smie-op-levels)) + ((looking-at "\\s(") + (forward-char 1) + (list (buffer-substring (1- (point)) (point)) nil 0))))) + +(defun smie-indent-backward-token () + "Skip token backward and return it, along with its levels." + (let ((tok (funcall smie-backward-token-function))) + (cond + ((< 0 (length tok)) (assoc tok smie-op-levels)) + ;; 4 == Open paren syntax. + ((eq 4 (syntax-class (syntax-after (1- (point))))) + (forward-char -1) + (list (buffer-substring (point) (1+ (point))) nil 0))))) + +;; FIXME: The `virtual' arg is fundamentally wrong: the virtual indent +;; of a position should not depend on the caller, since it leads to situations +;; where two dependent indentations get indented differently. (defun smie-indent-virtual (virtual) "Compute the virtual indentation to use for point. This is used when we're not trying to indent point but just @@ -544,7 +614,8 @@ (defun smie-indent-fixindent () ;; Obey the `fixindent' special comment. - (when (save-excursion + (and (smie-bolp) + (save-excursion (comment-normalize-vars) (re-search-forward (concat comment-start-skip "fixindent" @@ -575,38 +646,56 @@ (defun smie-indent-keyword () ;; Align closing token with the corresponding opening one. ;; (e.g. "of" with "case", or "in" with "let"). + ;; FIXME: This still looks too much like black magic!! + ;; FIXME: Rather than a bunch of rules like (PARENT . TOKEN), we + ;; want a single rule for TOKEN with different cases for each PARENT. (save-excursion (let* ((pos (point)) - (token (funcall smie-forward-token-function)) - (toklevels (cdr (assoc token smie-op-levels)))) + (toklevels (smie-indent-forward-token)) + (token (pop toklevels))) (when (car toklevels) (let ((res (smie-backward-sexp 'halfsexp)) tmp) - ;; If we didn't move at all, that means we didn't really skip - ;; what we wanted. - (when (< (point) pos) - (cond - ((eq (car res) (car toklevels)) - ;; We bumped into a same-level operator. align with it. - (goto-char (cadr res)) - ;; Don't use (smie-indent-virtual :not-hanging) here, because we - ;; want to jump back over a sequence of same-level ops such as - ;; a -> b -> c - ;; -> d - ;; So as to align with the earliest appropriate place. - (smie-indent-virtual :bolp)) - ((equal token (save-excursion - (funcall smie-backward-token-function))) - ;; in cases such as "fn x => fn y => fn z =>", - ;; jump back to the very first fn. - ;; FIXME: should we only do that for special tokens like "=>"? - (smie-indent-virtual :bolp)) - ((setq tmp (assoc (cons (caddr res) token) - smie-indent-rules)) - (goto-char (cadr res)) - (+ (cdr tmp) (smie-indent-virtual :not-hanging))) - (t - (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0) - (current-column)))))))))) + (cond + ((not (or (< (point) pos) + (and (cadr res) (< (cadr res) pos)))) + ;; If we didn't move at all, that means we didn't really skip + ;; what we wanted. + nil) + ((eq (car res) (car toklevels)) + ;; We bumped into a same-level operator. align with it. + (goto-char (cadr res)) + ;; Don't use (smie-indent-virtual :not-hanging) here, because we + ;; want to jump back over a sequence of same-level ops such as + ;; a -> b -> c + ;; -> d + ;; So as to align with the earliest appropriate place. + (smie-indent-virtual :bolp)) + ((equal token (save-excursion + (funcall smie-backward-token-function))) + ;; in cases such as "fn x => fn y => fn z =>", + ;; jump back to the very first fn. + ;; FIXME: should we only do that for special tokens like "=>"? + (smie-indent-virtual :bolp)) + ((setq tmp (assoc (cons (caddr res) token) + smie-indent-rules)) + (goto-char (cadr res)) + (+ (cdr tmp) (smie-indent-virtual :not-hanging))) + ;; FIXME: The rules ((t . TOK) . OFFSET) either indent + ;; relative to "before the parent" or "after the parent", + ;; depending on details of the grammar. + ((null (car res)) + (assert (eq (point) (cadr res))) + (goto-char (cadr res)) + (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0) + (smie-indent-virtual :not-hanging))) + ((smie-bolp) + (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0) + ;; FIXME: This is odd. Can't we make it use + ;; smie-indent-(calculate|virtual) somehow? + (smie-indent-after-keyword))) + (t + (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0) + (current-column))))))))) (defun smie-indent-comment () ;; Indentation of a comment. @@ -618,53 +707,44 @@ (defun smie-indent-comment-continue () ;; indentation of comment-continue lines. - (and (< 0 (length comment-continue)) - (looking-at (regexp-quote comment-continue)) (nth 4 (syntax-ppss)) + (let ((continue (comment-string-strip comment-continue t t))) + (and (< 0 (length continue)) + (looking-at (regexp-quote continue)) (nth 4 (syntax-ppss)) (let ((ppss (syntax-ppss))) (save-excursion (forward-line -1) (if (<= (point) (nth 8 ppss)) (progn (goto-char (1+ (nth 8 ppss))) (current-column)) (skip-chars-forward " \t") - (if (looking-at (regexp-quote comment-continue)) - (current-column))))))) + (if (looking-at (regexp-quote continue)) + (current-column)))))))) (defun smie-indent-after-keyword () ;; Indentation right after a special keyword. (save-excursion - (let* ((tok (funcall smie-backward-token-function)) - (tokinfo (assoc tok smie-indent-rules)) - (toklevel (if (and (zerop (length tok)) - ;; 4 == Open paren syntax. - (eq (syntax-class (syntax-after (1- (point)))) - 4)) - (progn (forward-char -1) - (setq tok (buffer-substring - (point) (1+ (point)))) - (setq tokinfo (assoc tok smie-indent-rules)) - (list tok nil 0)) - (assoc tok smie-op-levels)))) + (let* ((pos (point)) + (toklevel (smie-indent-backward-token)) + (tok (car toklevel)) + (tokinfo (assoc tok smie-indent-rules))) (if (and toklevel (null (cadr toklevel)) (null tokinfo)) - (setq tokinfo (list (car toklevel) nil nil))) - (if (and tokinfo (null toklevel)) - (error "Token %S has indent rule but has no parsing info" tok)) + (setq tokinfo (list (car toklevel)))) + ;; (if (and tokinfo (null toklevel)) + ;; (error "Token %S has indent rule but has no parsing info" tok)) (when toklevel - (let ((default-offset + (let ((offset + (cond + (tokinfo (smie-indent-offset-after tokinfo pos)) ;; The default indentation after a keyword/operator ;; is 0 for infix and t for prefix. ;; Using the BNF syntax, we could come up with ;; better defaults, but we only have the ;; precedence levels here. - (if (or tokinfo (null (cadr toklevel))) - (smie-indent-offset t) 0))) - ;; For indentation after "(let", we end up accumulating the - ;; offset of "(" and the offset of "let", so we use `min' - ;; to try and get it right either way. - (min - (+ (smie-indent-virtual :bolp) - (or (caddr tokinfo) (cadr tokinfo) default-offset)) - (+ (current-column) - (or (cadr tokinfo) default-offset)))))))) + ((null (cadr toklevel)) (smie-indent-offset t)) + (t 0)))) + ;; For indentation after "(let" in SML-mode, we end up accumulating + ;; the offset of "(" and the offset of "let", so we use `min' to try + ;; and get it right either way. + (+ (min (smie-indent-virtual :bolp) (current-column)) offset)))))) (defun smie-indent-exps () ;; Indentation of sequences of simple expressions without @@ -719,8 +799,8 @@ (current-column))))))) (defvar smie-indent-functions - '(smie-indent-fixindent smie-indent-bob smie-indent-close smie-indent-keyword - smie-indent-comment smie-indent-comment-continue smie-indent-after-keyword + '(smie-indent-fixindent smie-indent-bob smie-indent-close smie-indent-comment + smie-indent-comment-continue smie-indent-keyword smie-indent-after-keyword smie-indent-exps) "Functions to compute the indentation. Each function is called with no argument, shouldn't move point, and should