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)))))))))))