Commit dd2c3c92 authored by Stefan Monnier's avatar Stefan Monnier

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.
parent 927c346b
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."
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment