Subject: Re: bug#76704: 30.1; Indentation of braces on separate line in js-ts-mode

From: Vincenzo Pupillo <[email-masked]>
Date: Sat, 19 Apr 2025 23:09:32 +0200

Ciao Eli,

In data sabato 19 aprile 2025 15:40:31 Ora legale dell’Europa centrale, Eli 
Zaretskii ha scritto:
> > From: Vincenzo Pupillo <[email-masked]>
> > Cc: [email-masked]
> > Date: Wed, 02 Apr 2025 23:17:10 +0200
> > 
> > Ciao Fabian, I've fixed these two things (I hope!).
> > 
> > The new patch is attached.
> 
> I wanted to install this, but it no longer applies cleanly.  Could you
> please rebase on the current master and resubmit?
Done, the attached patch is rebased against the master.

Thanks.

Vincenzo

>From a6ade9f29f476898d3e993987d8d8c536e2fe60c Mon Sep 17 00:00:00 2001
From: Vincenzo Pupillo <[email-masked]>
Date: Sat, 19 Apr 2025 22:58:39 +0200
Subject: [PATCH] Fix indentation of "{" when on a new line of a function
 declaration.

* lisp/progmodes/js.el
(js--treesit-switch-body-helper): New anchor helper function for the
switch_body.
(js--treesit-member-chained-expression-helper): New anchor helper
function for chained calls in member_expression.
(js--treesit-arrow-function-helper): New anchor helper function for
arrow_function.
(js--treesit-indent-rules): Fix rule for the indentation of "{" when
of a new line of a function declaration. (bug#76704)
Fix the indentation of the parent of arrow_function, member_expression,
, switch_body, ternary_expression and sequence_expression.
---
 lisp/progmodes/js.el | 89 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 77 insertions(+), 12 deletions(-)

diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index 243329de7ae..aa0d35efab8 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -3449,25 +3449,86 @@ js-jsx--treesit-indent-compatibility-bb1f97b
      `(((match "<" "jsx_text") parent 0)
        ((parent-is "jsx_text") parent js-indent-level)))))
 
+(defun js--treesit-switch-body-helper (_node parent _bol &rest _args)
+  "Anchor helper for the switch body..
+If \"{\" is on a newline return the PARENT bol plus an offset,
+otherwise return the usual `parent-bol' value.  If `js-switch-indent-offset' > 0
+this is the offset, otherwise is `js-indent-level'."
+  (save-excursion
+    (goto-char (treesit-node-start parent))
+    (back-to-indentation)
+    (if (not (eq ?{ (char-after)))
+        (+ (point) js-switch-indent-offset)
+      (let ((offset ; offset relative to "{"
+             (if (= js-switch-indent-offset 0)
+                 js-indent-level
+               js-switch-indent-offset))
+            (pos ; position relative to the "statement_block"
+             (treesit-node-start
+              (treesit-node-parent
+               parent))))
+        (+ pos offset)))))
+
+(defun js--treesit-member-chained-expression-helper (_node parent _bol &rest _args)
+  "Anchor helper for member chained expressions.
+Returns a position relative to PARENT context and the value of
+`js-chain-indent'.
+See `js-chain-indent' and `js--chained-expression-p'."
+  (let ((parent-start (treesit-node-start parent)))
+    (if (not js-chain-indent)
+	(if-let* ((ancestor-node
+		   (treesit-parent-until
+		    parent
+		    "variable_declarator")))
+	    (treesit-node-start ancestor-node)
+	  (save-excursion
+	    (goto-char parent-start)
+	    (back-to-indentation)
+	    (if (eq parent-start (point))
+		(+ parent-start js-indent-level)
+	      parent-start)))
+      (save-excursion
+        (goto-char parent-start)
+        (let ((pos (search-forward "." (pos-eol) t 1)))
+          (if (and pos (> pos 0))
+              (- pos 1)
+            parent-start))))))
+
+(defun js--treesit-arrow-function-helper (node parent bol &rest args)
+  "Anchor helper for arrow_function, return a position based on context.
+If and arrow function has a variable_declarator ancestor, returns the
+same value of `grand-parent' otherwise return `parent-bol' plus
+`js-indent-level'.
+PARENT is NODE's parent, BOL is the beginning of non-whitespace
+characters of the current line."
+  (if-let* ((ancestor-node
+	     (treesit-parent-until
+	      parent
+	      "variable_declarator")))
+      (apply (alist-get 'grand-parent treesit-simple-indent-presets)
+             node parent bol args)
+    (+ (apply (alist-get 'parent-bol treesit-simple-indent-presets)
+              node parent bol args)
+       js-indent-level)))
+
 (defvar js--treesit-indent-rules
-  (let ((switch-case (rx "switch_" (or "case" "default"))))
     `((javascript
        ((parent-is "program") parent-bol 0)
-       ((node-is "}") parent-bol 0)
+       ((node-is "}") standalone-parent 0)
        ((node-is ")") parent-bol 0)
        ((node-is "]") parent-bol 0)
        ((node-is ">") parent-bol 0)
        ((and (parent-is "comment") c-ts-common-looking-at-star)
         c-ts-common-comment-start-after-first-star -1)
        ((parent-is "comment") prev-adaptive-prefix 0)
+       ((n-p-gp "identifier" "ternary_expression" "parenthesized_expression")
+	parent 0)
        ((parent-is "ternary_expression") parent-bol js-indent-level)
-       ((parent-is "member_expression") parent-bol js-indent-level)
-       ((node-is ,switch-case) parent-bol 0)
-       ;; "{" on the newline.
-       ((node-is "statement_block") parent-bol js-indent-level)
+       ((parent-is "sequence_expression") parent 0)
+       ((parent-is "member_expression") js--treesit-member-chained-expression-helper 0)
        ((parent-is "named_imports") parent-bol js-indent-level)
-       ((parent-is "statement_block") parent-bol js-indent-level)
-       ((parent-is "variable_declarator") parent-bol js-indent-level)
+       ((parent-is "statement_block") standalone-parent js-indent-level)
+       ((parent-is "variable_declarator") parent 0)
        ((parent-is "arguments") parent-bol js-indent-level)
        ((parent-is "array") parent-bol js-indent-level)
        ((parent-is "formal_parameters") parent-bol js-indent-level)
@@ -3476,12 +3537,16 @@ js--treesit-indent-rules
        ((parent-is "object_pattern") parent-bol js-indent-level)
        ((parent-is "object") parent-bol js-indent-level)
        ((parent-is "pair") parent-bol js-indent-level)
-       ((parent-is "arrow_function") parent-bol js-indent-level)
+       ((parent-is "arrow_function") js--treesit-arrow-function-helper 0)
        ((parent-is "parenthesized_expression") parent-bol js-indent-level)
        ((parent-is "binary_expression") parent-bol js-indent-level)
+       ((parent-is "assignment_expression") parent-bol js-indent-level)
        ((parent-is "class_body") parent-bol js-indent-level)
-       ((parent-is ,switch-case) parent-bol js-indent-level)
-       ((parent-is "statement_block") parent-bol js-indent-level)
+       ;; "{" on the newline, should stay here.
+       ((node-is "statement_block") parent-bol 0)
+       ((parent-is "switch_statement") parent-bol 0)
+       ((parent-is "switch_body") js--treesit-switch-body-helper 0)
+       ((parent-is ,(rx "switch_" (or "case" "default"))) parent-bol js-indent-level)
        ((match "while" "do_statement") parent-bol 0)
        ((match "else" "if_statement") parent-bol 0)
        ((parent-is ,(rx (or (seq (or "if" "for" "for_in" "while" "do") "_statement")
@@ -3502,7 +3567,7 @@ js--treesit-indent-rules
        (no-node parent-bol 0))
       (jsdoc
        ((and (parent-is "document") c-ts-common-looking-at-star)
-        c-ts-common-comment-start-after-first-star -1)))))
+        c-ts-common-comment-start-after-first-star -1))))
 
 (defvar js--treesit-keywords
   '("as" "async" "await" "break" "case" "catch" "class" "const" "continue"
-- 
2.49.0