mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2025-02-06 20:49:33 +00:00
Split smie-indent-calculate into more manageable chunks.
* lisp/emacs-lisp/smie.el (smie-indent-virtual, smie-indent-fixindent) (smie-indent-comment, smie-indent-after-keyword, smie-indent-keyword) (smie-indent-close, smie-indent-comment-continue, smie-indent-bob) (smie-indent-exps): Extract from smie-indent-calculate. (smie-indent-functions): New var. (smie-indent-functions): Use them.
This commit is contained in:
parent
927c346bd6
commit
dd2c3c9241
@ -1,3 +1,13 @@
|
||||
2010-06-03 Stefan Monnier <monnier@iro.umontreal.ca>
|
||||
|
||||
Split smie-indent-calculate into more manageable chunks.
|
||||
* emacs-lisp/smie.el (smie-indent-virtual, smie-indent-fixindent)
|
||||
(smie-indent-comment, smie-indent-after-keyword, smie-indent-keyword)
|
||||
(smie-indent-close, smie-indent-comment-continue, smie-indent-bob)
|
||||
(smie-indent-exps): Extract from smie-indent-calculate.
|
||||
(smie-indent-functions): New var.
|
||||
(smie-indent-functions): Use them.
|
||||
|
||||
2010-06-02 Stefan Monnier <monnier@iro.umontreal.ca>
|
||||
|
||||
* emacs-lisp/smie.el (smie-indent-hanging-p): Use smie-bolp.
|
||||
|
@ -524,181 +524,210 @@ A nil offset defaults to `smie-indent-basic'.")
|
||||
(cdr (assq t smie-indent-rules))
|
||||
smie-indent-basic))
|
||||
|
||||
(defun smie-indent-calculate (&optional virtual)
|
||||
"Compute the indentation to use for point.
|
||||
If VIRTUAL is non-nil, it means we're not trying to indent point but just
|
||||
(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
|
||||
need to compute the column at which point should be indented
|
||||
in order to figure out the indentation of some other (further down) point.
|
||||
VIRTUAL can take two different non-nil values:
|
||||
VIRTUAL can take two different values:
|
||||
- :bolp: means that the current indentation of point can be trusted
|
||||
to be good only if it follows a line break.
|
||||
- :hanging: means that the current indentation of point can be
|
||||
- :not-hanging: means that the current indentation of point can be
|
||||
trusted to be good except if the following token is hanging."
|
||||
(or
|
||||
;; Trust pre-existing indentation on other lines.
|
||||
(and virtual
|
||||
(if (eq virtual :hanging) (not (smie-indent-hanging-p)) (smie-bolp))
|
||||
(current-column))
|
||||
;; Obey the `fixindent' special comment.
|
||||
(when (save-excursion
|
||||
(comment-normalize-vars)
|
||||
(re-search-forward (concat comment-start-skip
|
||||
"fixindent"
|
||||
comment-end-skip)
|
||||
;; 1+ to account for the \n comment termination.
|
||||
(1+ (line-end-position)) t))
|
||||
(current-column))
|
||||
;; Start the file at column 0.
|
||||
(save-excursion
|
||||
(forward-comment (- (point)))
|
||||
(if (bobp) 0))
|
||||
;; Align close paren with opening paren.
|
||||
(save-excursion
|
||||
;; (forward-comment (point-max))
|
||||
(when (looking-at "\\s)")
|
||||
(while (not (zerop (skip-syntax-forward ")")))
|
||||
(skip-chars-forward " \t"))
|
||||
(condition-case nil
|
||||
(progn
|
||||
(backward-sexp 1)
|
||||
(smie-indent-calculate :hanging))
|
||||
(scan-error nil))))
|
||||
;; Align closing token with the corresponding opening one.
|
||||
;; (e.g. "of" with "case", or "in" with "let").
|
||||
(save-excursion
|
||||
(let* ((pos (point))
|
||||
(token (funcall smie-forward-token-function))
|
||||
(toklevels (cdr (assoc token smie-op-levels))))
|
||||
(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-calculate :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-calculate :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-calculate :bolp))
|
||||
((setq tmp (assoc (cons (caddr res) token)
|
||||
smie-indent-rules))
|
||||
(goto-char (cadr res))
|
||||
(+ (cdr tmp) (smie-indent-calculate :hanging)))
|
||||
(t
|
||||
(+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0)
|
||||
(current-column)))))))))
|
||||
;; Indentation of a comment.
|
||||
(and (looking-at comment-start-skip)
|
||||
(save-excursion
|
||||
(forward-comment (point-max))
|
||||
(skip-chars-forward " \t\r\n")
|
||||
(smie-indent-calculate nil)))
|
||||
;; indentation of comment-continue lines.
|
||||
(and (< 0 (length comment-continue))
|
||||
(looking-at (regexp-quote comment-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))))))
|
||||
;; 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))))
|
||||
(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))
|
||||
(when toklevel
|
||||
(let ((default-offset
|
||||
;; 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-calculate :bolp)
|
||||
(or (caddr tokinfo) (cadr tokinfo) default-offset))
|
||||
(+ (current-column)
|
||||
(or (cadr tokinfo) default-offset)))))))
|
||||
;; Indentation of sequences of simple expressions without
|
||||
;; intervening keywords or operators. E.g. "a b c" or "g (balbla) f".
|
||||
;; Can be a list of expressions or a function call.
|
||||
;; If it's a function call, the first element is special (it's the
|
||||
;; function). We distinguish function calls from mere lists of
|
||||
;; expressions based on whether the preceding token is listed in
|
||||
;; the `list-intro' entry of smie-indent-rules.
|
||||
;;
|
||||
;; TODO: to indent Lisp code, we should add a way to specify
|
||||
;; particular indentation for particular args depending on the
|
||||
;; function (which would require always skipping back until the
|
||||
;; function).
|
||||
;; TODO: to indent C code, such as "if (...) {...}" we might need
|
||||
;; to add similar indentation hooks for particular positions, but
|
||||
;; based on the preceding token rather than based on the first exp.
|
||||
(save-excursion
|
||||
(let ((positions nil)
|
||||
arg)
|
||||
(while (and (null (car (smie-backward-sexp)))
|
||||
(push (point) positions)
|
||||
(not (smie-bolp))))
|
||||
;; Trust pre-existing indentation on other lines.
|
||||
(assert virtual)
|
||||
(if (if (eq virtual :not-hanging) (not (smie-indent-hanging-p)) (smie-bolp))
|
||||
(current-column)
|
||||
(smie-indent-calculate)))
|
||||
|
||||
(defun smie-indent-fixindent ()
|
||||
;; Obey the `fixindent' special comment.
|
||||
(when (save-excursion
|
||||
(comment-normalize-vars)
|
||||
(re-search-forward (concat comment-start-skip
|
||||
"fixindent"
|
||||
comment-end-skip)
|
||||
;; 1+ to account for the \n comment termination.
|
||||
(1+ (line-end-position)) t))
|
||||
(current-column)))
|
||||
|
||||
(defun smie-indent-bob ()
|
||||
;; Start the file at column 0.
|
||||
(save-excursion
|
||||
(forward-comment (- (point)))
|
||||
(if (bobp) 0)))
|
||||
|
||||
(defun smie-indent-close ()
|
||||
;; Align close paren with opening paren.
|
||||
(save-excursion
|
||||
;; (forward-comment (point-max))
|
||||
(when (looking-at "\\s)")
|
||||
(while (not (zerop (skip-syntax-forward ")")))
|
||||
(skip-chars-forward " \t"))
|
||||
(condition-case nil
|
||||
(progn
|
||||
(backward-sexp 1)
|
||||
(smie-indent-virtual :not-hanging))
|
||||
(scan-error nil)))))
|
||||
|
||||
(defun smie-indent-keyword ()
|
||||
;; Align closing token with the corresponding opening one.
|
||||
;; (e.g. "of" with "case", or "in" with "let").
|
||||
(save-excursion
|
||||
(let* ((pos (point))
|
||||
(token (funcall smie-forward-token-function))
|
||||
(toklevels (cdr (assoc token smie-op-levels))))
|
||||
(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))))))))))
|
||||
|
||||
(defun smie-indent-comment ()
|
||||
;; Indentation of a comment.
|
||||
(and (looking-at comment-start-skip)
|
||||
(save-excursion
|
||||
;; Figure out if the atom we just skipped is an argument rather
|
||||
;; than a function.
|
||||
(setq arg (or (null (car (smie-backward-sexp)))
|
||||
(member (funcall smie-backward-token-function)
|
||||
(cdr (assoc 'list-intro smie-indent-rules))))))
|
||||
(cond
|
||||
((null positions)
|
||||
;; We're the first expression of the list. In that case, the
|
||||
;; indentation should be (have been) determined by its context.
|
||||
nil)
|
||||
(arg
|
||||
;; There's a previous element, and it's not special (it's not
|
||||
;; the function), so let's just align with that one.
|
||||
(goto-char (car positions))
|
||||
(current-column))
|
||||
((cdr positions)
|
||||
;; We skipped some args plus the function and bumped into something.
|
||||
;; Align with the first arg.
|
||||
(goto-char (cadr positions))
|
||||
(current-column))
|
||||
(positions
|
||||
;; We're the first arg.
|
||||
(goto-char (car positions))
|
||||
(+ (smie-indent-offset 'args)
|
||||
;; We used to use (smie-indent-calculate :bolp), but that
|
||||
;; doesn't seem right since it might then indent args less than
|
||||
;; the function itself.
|
||||
(current-column))))))))
|
||||
(forward-comment (point-max))
|
||||
(skip-chars-forward " \t\r\n")
|
||||
(smie-indent-calculate))))
|
||||
|
||||
(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 ((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)))))))
|
||||
|
||||
(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))))
|
||||
(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))
|
||||
(when toklevel
|
||||
(let ((default-offset
|
||||
;; 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))))))))
|
||||
|
||||
(defun smie-indent-exps ()
|
||||
;; Indentation of sequences of simple expressions without
|
||||
;; intervening keywords or operators. E.g. "a b c" or "g (balbla) f".
|
||||
;; Can be a list of expressions or a function call.
|
||||
;; If it's a function call, the first element is special (it's the
|
||||
;; function). We distinguish function calls from mere lists of
|
||||
;; expressions based on whether the preceding token is listed in
|
||||
;; the `list-intro' entry of smie-indent-rules.
|
||||
;;
|
||||
;; TODO: to indent Lisp code, we should add a way to specify
|
||||
;; particular indentation for particular args depending on the
|
||||
;; function (which would require always skipping back until the
|
||||
;; function).
|
||||
;; TODO: to indent C code, such as "if (...) {...}" we might need
|
||||
;; to add similar indentation hooks for particular positions, but
|
||||
;; based on the preceding token rather than based on the first exp.
|
||||
(save-excursion
|
||||
(let ((positions nil)
|
||||
arg)
|
||||
(while (and (null (car (smie-backward-sexp)))
|
||||
(push (point) positions)
|
||||
(not (smie-bolp))))
|
||||
(save-excursion
|
||||
;; Figure out if the atom we just skipped is an argument rather
|
||||
;; than a function.
|
||||
(setq arg (or (null (car (smie-backward-sexp)))
|
||||
(member (funcall smie-backward-token-function)
|
||||
(cdr (assoc 'list-intro smie-indent-rules))))))
|
||||
(cond
|
||||
((null positions)
|
||||
;; We're the first expression of the list. In that case, the
|
||||
;; indentation should be (have been) determined by its context.
|
||||
nil)
|
||||
(arg
|
||||
;; There's a previous element, and it's not special (it's not
|
||||
;; the function), so let's just align with that one.
|
||||
(goto-char (car positions))
|
||||
(current-column))
|
||||
((cdr positions)
|
||||
;; We skipped some args plus the function and bumped into something.
|
||||
;; Align with the first arg.
|
||||
(goto-char (cadr positions))
|
||||
(current-column))
|
||||
(positions
|
||||
;; We're the first arg.
|
||||
(goto-char (car positions))
|
||||
(+ (smie-indent-offset 'args)
|
||||
;; We used to use (smie-indent-virtual :bolp), but that
|
||||
;; doesn't seem right since it might then indent args less than
|
||||
;; the function itself.
|
||||
(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-exps)
|
||||
"Functions to compute the indentation.
|
||||
Each function is called with no argument, shouldn't move point, and should
|
||||
return either nil if it has no opinion, or an integer representing the column
|
||||
to which that point should be aligned, if we were to reindent it.")
|
||||
|
||||
(defun smie-indent-calculate ()
|
||||
"Compute the indentation to use for point."
|
||||
(run-hook-with-args-until-success 'smie-indent-functions))
|
||||
|
||||
(defun smie-indent-line ()
|
||||
"Indent current line using the SMIE indentation engine."
|
||||
|
Loading…
x
Reference in New Issue
Block a user