]> git.eshelyaron.com Git - emacs.git/commitdiff
Add treesit-simple-indent-standalone-predicate (bug#74386)
authorYuan Fu <casouri@gmail.com>
Mon, 2 Dec 2024 02:26:40 +0000 (18:26 -0800)
committerEshel Yaron <me@eshelyaron.com>
Fri, 14 Feb 2025 11:44:18 +0000 (12:44 +0100)
* 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)

doc/lispref/modes.texi
lisp/progmodes/c-ts-common.el
lisp/treesit.el

index 03f0ba9e47fdf6cc6962c134fc6e604a63257a9c..1ada6a4cf64429fd43a1083c751570f8705ebbd9 100644 (file)
@@ -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},
index 0ded2de7068b3bdaffd48259e264ee25bbdddf83..90eb7aa64c83c47afe434a450acc0635b90e646d 100644 (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
index 2c9ec62dc3462bf969ec577d15879b1903675bc8..23daeee8036346e319843f6bf8bf4de042d355e5 100644 (file)
@@ -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