Add treesit-simple-indent-standalone-predicate (bug#74386)

* lisp/treesit.el:
(treesit-simple-indent-standalone-predicate): New variable.
(treesit-simple-indent-presets): Use the predicate.
(treesit-simple-indent-presets): Update docstring.
* lisp/progmodes/c-ts-common.el:
(c-ts-common--standalone-parent):
(c-ts-common--prev-standalone-sibling): Use the predicate if
non-nil.  Also, handle method chaining by default.
* doc/lispref/modes.texi (Parser-based Indentation): Add
documentation.
This commit is contained in:
Yuan Fu 2024-12-01 18:26:40 -08:00
parent b8c8ae92db
commit 4b020b936c
No known key found for this signature in database
GPG key ID: 56E19BC57664A442
3 changed files with 86 additions and 15 deletions

View file

@ -5416,11 +5416,14 @@ on the line which @var{parent}'s start is on.
@item standalone-parent
This anchor is a function that is called with 3 arguments: @var{node},
@var{parent}, and @var{bol}. It finds the first ancestor node
(parent, grandparent, etc.@:) of @var{node} that starts on its own
line, and return the start of that node. ``Starting on its own line''
means there is only whitespace character before the node on the line
which the node's start is on.
@var{parent}, and @var{bol}. It finds the first ancestor node (parent,
grandparent, etc.@:) of @var{node} that starts on its own line, and
return the start of that node. ``Starting on its own line'' means there
is only whitespace character before the node on the line which the
node's start is on. The exact definition of ``Starting on its own
line'' can be relaxed by setting
@code{treesit-simple-indent-standalone-predicate}, some major mode might
want to do that for easier indentation for method chaining.
@item prev-sibling
This anchor is a function that is called with 3 arguments: @var{node},

View file

@ -558,26 +558,52 @@ const a = [
"Find the first parent that starts on a new line.
Start searching from PARENT, so if PARENT satisfies the condition, it'll
be returned. Return the starting position of the parent, return nil if
no parent satisfies the condition."
no parent satisfies the condition.
Unlike simple-indent's standalone preset, this function handles method
chaining like
func
.method() <-- Considered standalone even if there's a \".\" in
.method() front of the node.
But ff `treesit-simple-indent-standalone-predicate' is non-nil, use that
for determining standlone line."
(save-excursion
(catch 'term
(while parent
(goto-char (treesit-node-start parent))
(when (looking-back (rx bol (* whitespace))
(line-beginning-position))
(when (if treesit-simple-indent-standalone-predicate
(funcall treesit-simple-indent-standalone-predicate
parent)
(looking-back (rx bol (* whitespace) (? "."))
(line-beginning-position)))
(throw 'term (point)))
(setq parent (treesit-node-parent parent))))))
(defun c-ts-common--prev-standalone-sibling (node)
"Return the previous sibling of NODE that starts on a new line.
Return nil if no sibling satisfies the condition."
Return nil if no sibling satisfies the condition.
Unlike simple-indent's standalone preset, this function handles method
chaining like
func
.method() <-- Considered standalone even if there's a \".\" in
.method() front of the node.
But ff `treesit-simple-indent-standalone-predicate' is non-nil, use that
for determining standlone line."
(save-excursion
(setq node (treesit-node-prev-sibling node 'named))
(goto-char (treesit-node-start node))
(while (and node
(goto-char (treesit-node-start node))
(not (looking-back (rx bol (* whitespace))
(pos-bol))))
(not (if treesit-simple-indent-standalone-predicate
(funcall treesit-simple-indent-standalone-predicate
node)
(looking-back (rx bol (* whitespace) (? "."))
(pos-bol)))))
(setq node (treesit-node-prev-sibling node 'named)))
node))
@ -629,7 +655,13 @@ This rule tries to be smart and ignore proprocessor node in some
situations. By default, any node that has \"proproc\" in its type are
considered a preprocessor node. If that heuristic is inaccurate, define
a `preproc' thing in `treesit-thing-settings', and this rule will use
the thing definition instead."
the thing definition instead.
The rule also handles method chaining like
func
.method() <-- Considered \"starts at a newline\" even if there's
.method() a \".\" in front of the node."
(let ((prev-line-node (treesit--indent-prev-line-node bol))
(offset (symbol-value c-ts-common-indent-offset)))
(cond

View file

@ -1792,6 +1792,37 @@ over `treesit-simple-indent-rules'.")
(back-to-indentation)
(treesit--indent-largest-node-at (point)))))
(defvar treesit-simple-indent-standalone-predicate nil
"Function used to determine if a node is \"standalone\".
\"Standalone\" means the node starts on a new line. For example, if we
look at the opening bracket, then it's standalone in this case:
{ <-- Standalone.
return 1;
}
but not in this case:
if (true) { <-- Not standalone.
return 1;
}
The value of this variable affects the `standalone-parent' indent preset
for treesit-simple-indent. If the value is nil, the standlone condition
is as described. Some major mode might want to relax the condition a
little bit, so that it ignores some punctuation like \".\". For
example, a Javascript mode might want to consider the method call below
to be standalone too:
obj
.method(() => { <-- Consider \".method\" to be standalone,
return 1; <-- so this line anchors on \".method\".
});
The value should be a function that takes a node, and return t if it's
standalone.")
(defvar treesit-simple-indent-presets
(list (cons 'match
(lambda
@ -1931,8 +1962,10 @@ over `treesit-simple-indent-rules'.")
(catch 'term
(while parent
(goto-char (treesit-node-start parent))
(when (looking-back (rx bol (* whitespace))
(line-beginning-position))
(when (if (null treesit-simple-indent-standalone-predicate)
(looking-back (rx bol (* whitespace))
(line-beginning-position))
(funcall parent))
(throw 'term (point)))
(setq parent (treesit-node-parent parent)))))))
(cons 'prev-sibling (lambda (node parent bol &rest _)
@ -2065,7 +2098,10 @@ parent-bol
standalone-parent
Finds the first ancestor node (parent, grandparent, etc.) that
starts on its own line, and returns the start of that node.
starts on its own line, and returns the start of that node. The
definition of \"standalone\" can be customized by setting
`treesit-simple-indent-standalone-predicate'. Some major mode might
want to do that for easier indentation for method chaining.
prev-sibling