From: Yuan Fu Date: Mon, 2 Dec 2024 02:26:40 +0000 (-0800) Subject: Add treesit-simple-indent-standalone-predicate (bug#74386) X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=5b4dba22352de2d9221a731b39ff8f2d10197a45;p=emacs.git 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. (cherry picked from commit 4b020b936c5adf472319d1ea253c85c5ee54135f) --- diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index 03f0ba9e47f..1ada6a4cf64 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -5380,11 +5380,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}, diff --git a/lisp/progmodes/c-ts-common.el b/lisp/progmodes/c-ts-common.el index 0ded2de7068..90eb7aa64c8 100644 --- a/lisp/progmodes/c-ts-common.el +++ b/lisp/progmodes/c-ts-common.el @@ -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 diff --git a/lisp/treesit.el b/lisp/treesit.el index 2c9ec62dc34..23daeee8036 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1788,6 +1788,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 @@ -1927,8 +1958,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 _) @@ -2061,7 +2094,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