Function: smie-indent-keyword
smie-indent-keyword is a byte-compiled function defined in smie.el.gz.
Signature
(smie-indent-keyword &optional TOKEN)
Documentation
Indent point based on the token that follows it immediately.
If TOKEN is non-nil, assume that that is the token that follows point. Returns either a column number or nil if it considers that indentation should not be computed on the basis of the following token.
Source Code
;; Defined in /usr/src/emacs/lisp/emacs-lisp/smie.el.gz
(defun smie-indent-keyword (&optional token)
"Indent point based on the token that follows it immediately.
If TOKEN is non-nil, assume that that is the token that follows point.
Returns either a column number or nil if it considers that indentation
should not be computed on the basis of the following token."
(save-excursion
(let* ((pos (point))
(toklevels
(if token
(assoc token smie-grammar)
(let* ((res (smie-indent-forward-token)))
;; Ignore tokens on subsequent lines.
(if (and (< pos (line-beginning-position))
;; Make sure `token' also *starts* on another line.
(save-excursion
(let ((endpos (point)))
(goto-char pos)
(forward-line 1)
;; As seen in bug#22960, pos may be inside
;; a string, and forward-token may then stumble.
(and (ignore-errors
(equal res (smie-indent-forward-token)))
(eq (point) endpos)))))
nil
(goto-char pos)
res)))))
(setq token (pop toklevels))
(cond
((null (cdr toklevels)) nil) ;Not a keyword.
((not (numberp (car toklevels)))
;; Different cases:
;; - smie-indent--bolp: "indent according to others".
;; - common hanging: "indent according to others".
;; - SML-let hanging: "indent like parent".
;; - if-after-else: "indent-like parent".
;; - middle-of-line: "trust current position".
(cond
((smie-indent--rule :before token))
((smie-indent--bolp-1) ;I.e. non-virtual indent.
;; For an open-paren-like thingy at BOL, always indent only
;; based on other rules (typically smie-indent-after-keyword).
;; FIXME: we do the same if after a comment, since we may be trying
;; to compute the indentation of this comment and we shouldn't indent
;; based on the indentation of subsequent code.
nil)
(t
;; By default use point unless we're hanging.
(unless (smie-indent--hanging-p) (current-column)))))
(t
;; FIXME: This still looks too much like black magic!!
(let* ((parent (smie-backward-sexp token)))
;; Different behaviors:
;; - align with parent.
;; - parent + offset.
;; - after parent's column + offset (actually, after or before
;; depending on where backward-sexp stopped).
;; ? let it drop to some other indentation function (almost never).
;; ? parent + offset + parent's own offset.
;; Different cases:
;; - bump into a same-level operator.
;; - bump into a specific known parent.
;; - find a matching open-paren thingy.
;; - bump into some random parent.
;; ? borderline case (almost never).
;; ? bump immediately into a parent.
(cond
((not (or (< (point) pos)
(and (cadr parent) (< (cadr parent) pos))))
;; If we didn't move at all, that means we didn't really skip
;; what we wanted. Should almost never happen, other than
;; maybe when an infix or close-paren is at the beginning
;; of a buffer.
nil)
((save-excursion
(goto-char pos)
(smie-indent--rule :before token nil parent (cadr parent))))
((eq (car parent) (car toklevels))
;; We bumped into a same-level operator; align with it.
(if (and (smie-indent--bolp) (/= (point) pos)
(save-excursion
(goto-char (goto-char (cadr parent)))
(not (smie-indent--bolp))))
;; If the parent is at EOL and its children are indented like
;; itself, then we can just obey the indentation chosen for the
;; child.
;; This is important for operators like ";" which
;; are usually at EOL (and have an offset of 0): otherwise we'd
;; always go back over all the statements, which is
;; a performance problem and would also mean that fixindents
;; in the middle of such a sequence would be ignored.
;;
;; This is a delicate point!
;; Even if the offset is not 0, we could follow the same logic
;; and subtract the offset from the child's indentation.
;; But that would more often be a bad idea: OT1H we generally
;; want to reuse the closest similar indentation point, so that
;; the user's choice (or the fixindents) are obeyed. But OTOH
;; we don't want this to affect "unrelated" parts of the code.
;; E.g. a fixindent in the body of a "begin..end" should not
;; affect the indentation of the "end".
(current-column)
(goto-char (cadr parent))
;; 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)))
(t
(if (and (= (point) pos) (smie-indent--bolp))
;; Since we started at BOL, we're not computing a virtual
;; indentation, and we're still at the starting point, so
;; we can't use `current-column' which would cause
;; indentation to depend on itself and we can't use
;; smie-indent-virtual since that would be an inf-loop.
nil
;; In indent-keyword, if we're indenting `then' wrt `if', we
;; want to use indent-virtual rather than use just
;; current-column, so that we can apply the (:before . "if")
;; rule which does the "else if" dance in SML. But in other
;; cases, we do not want to use indent-virtual (e.g. indentation
;; of "*" w.r.t "+", or ";" wrt "("). We could just always use
;; indent-virtual and then have indent-rules say explicitly to
;; use `point' after things like "(" or "+" when they're not at
;; EOL, but you'd end up with lots of those rules.
;; So we use a heuristic here, which is that we only use virtual
;; if the parent is tightly linked to the child token (they're
;; part of the same BNF rule).
(if (car parent)
(smie-indent--current-column)
(smie-indent-virtual)))))))))))