Subject: Re: bug#64647: treesit-query-error due to a recent change to tree-sitter-javascript grammar definition

From: Vincenzo Pupillo <[email-masked]>
Date: Thu, 20 Jul 2023 12:14:07 +0200

In data mercoledì 19 luglio 2023 07:11:05 CEST, Theodor Thornhill ha scritto:

> For Typescript these changes should go into 'tsx-ts-mode, not
> 'typescript-ts-mode. That may be why you are seeing some strange results?
No, exactly the same problem occurs, unfortunately. After all, the sources of 
libtree-sitter-tsx and libtree-sitter-typescript come from the same repository 
and the content of  tree-sitter-typescript/tsx/grammar.js is just that:
const defineGrammar = require('../common/define-grammar');
module.exports = defineGrammar('tsx');

> > ---
> 
> "... function to handle ..."

fixed

> "Indent rules helper, to handle different releases of
> tree-sitter-javascript."

fixed

> 
> The indentation here looks off. Can you format this?
> 

fixed

> 
> This seems to not be properly converted to tsx from javascript, both in
> the docstring and code.  Also, I think the name is wrong. Maybe it
> should describe its intent a little more closely, something like
> "tsx-ts-mode--indent-compatibility-b893426"?

Sorry for the error. I fixed them, tested and fixed the function names according to your instructions (also in js.el)
Hope the patches are better now.
Thanks.
Vincenzo

>From 99ab9ca3e75f756c7fc9965528db6d882a383150 Mon Sep 17 00:00:00 2001
From: Vincenzo Pupillo <[email-masked]>
Date: Thu, 20 Jul 2023 12:06:21 +0200
Subject: [PATCH 2/2] Updated TSX support due to changes in
 tree-sitter-typescript

A recent change in tree-sitter-typescript grammar support for
TSX (commit b893426), changed two things:
1. renamed nested_identifier to member_expression
2. removed jsx_fragment, jsx_text is used instead

* lisp/progmodes/typescript-ts-mode.el: (tsx-ts-mode--indent-compatibility-b893426):
  indent helper function for handle different tree-sitter-typescript version
* lisp/progmodes/typescript-ts-mode.el: (typescript-ts-mode--indent-rules):
  use the new function to handle both jsx_fragment and jsx_text
* lisp/progmodes/typescript-ts-mode.el: (tsx-ts-mode--font-lock-compatibility-bb1f97b):
  font lock helper function for handle different tree-sitter-typescript version
* lisp/progmodes/typescript-ts-mode.el: (typescript-ts-mode--font-lock-settings):
  use the new function to handle both nested_identifier and member_expression
---
 lisp/progmodes/typescript-ts-mode.el | 78 ++++++++++++++++++++--------
 1 file changed, 55 insertions(+), 23 deletions(-)

diff --git a/lisp/progmodes/typescript-ts-mode.el b/lisp/progmodes/typescript-ts-mode.el
index 5df34de0472..173ec52f209 100644
--- a/lisp/progmodes/typescript-ts-mode.el
+++ b/lisp/progmodes/typescript-ts-mode.el
@@ -75,6 +75,18 @@
     table)
   "Syntax table for `typescript-ts-mode'.")
 
+(defun tsx-ts-mode--indent-compatibility-b893426 ()
+  "Indent rules helper, to handle different releases of tree-sitter-tsx.
+Check if a node type is available, then return the right indent rules."
+  ;; handle commit b893426
+  (condition-case nil
+      (progn (treesit-query-capture 'tsx '((jsx_fragment) @capture))
+             `(((match "<" "jsx_fragment") parent 0)
+               ((parent-is "jsx_fragment") parent typescript-ts-mode-indent-offset)))
+    (error
+     `(((match "<" "jsx_text") parent 0)
+       ((parent-is "jsx_text") parent typescript-ts-mode-indent-offset)))))
+
 (defun typescript-ts-mode--indent-rules (language)
   "Rules used for indentation.
 Argument LANGUAGE is either `typescript' or `tsx'."
@@ -110,16 +122,15 @@ Argument LANGUAGE is either `typescript' or `tsx'."
      ((parent-is "binary_expression") parent-bol typescript-ts-mode-indent-offset)
 
      ,@(when (eq language 'tsx)
-         `(((match "<" "jsx_fragment") parent 0)
-           ((parent-is "jsx_fragment") parent typescript-ts-mode-indent-offset)
-           ((node-is "jsx_closing_element") parent 0)
-           ((match "jsx_element" "statement") parent typescript-ts-mode-indent-offset)
-           ((parent-is "jsx_element") parent typescript-ts-mode-indent-offset)
-           ((parent-is "jsx_text") parent-bol typescript-ts-mode-indent-offset)
-           ((parent-is "jsx_opening_element") parent typescript-ts-mode-indent-offset)
-           ((parent-is "jsx_expression") parent-bol typescript-ts-mode-indent-offset)
-           ((match "/" "jsx_self_closing_element") parent 0)
-           ((parent-is "jsx_self_closing_element") parent typescript-ts-mode-indent-offset)))
+	 (append (tsx-ts-mode--indent-compatibility-b893426)
+		 `(((node-is "jsx_closing_element") parent 0)
+		   ((match "jsx_element" "statement") parent typescript-ts-mode-indent-offset)
+		   ((parent-is "jsx_element") parent typescript-ts-mode-indent-offset)
+		   ((parent-is "jsx_text") parent-bol typescript-ts-mode-indent-offset)
+		   ((parent-is "jsx_opening_element") parent typescript-ts-mode-indent-offset)
+		   ((parent-is "jsx_expression") parent-bol typescript-ts-mode-indent-offset)
+		   ((match "/" "jsx_self_closing_element") parent 0)
+		   ((parent-is "jsx_self_closing_element") parent typescript-ts-mode-indent-offset))))
      ;; FIXME(Theo): This no-node catch-all should be removed.  When is it needed?
      (no-node parent-bol 0))))
 
@@ -142,6 +153,38 @@ Argument LANGUAGE is either `typescript' or `tsx'."
     "&&" "||" "!" "?.")
   "TypeScript operators for tree-sitter font-locking.")
 
+(defun tsx-ts-mode--font-lock-compatibility-bb1f97b ()
+  "Font lock rules helper, to handle different releases of tree-sitter-tsx.
+Check if a node type is available, then return the right font lock rules."
+  ;; handle commit bb1f97b
+  ;; Warning: treesitter-query-capture says both node types are valid,
+  ;; but then raises an error if the wrong node type is used. So it is
+  ;; important to check with the new node type (member_expression)
+  (condition-case nil
+      (progn (treesit-query-capture 'tsx '((member_expression) @capture))
+	     '((jsx_opening_element
+		[(member_expression (identifier)) (identifier)]
+		@typescript-ts-jsx-tag-face)
+
+	       (jsx_closing_element
+		[(member_expression (identifier)) (identifier)]
+		@typescript-ts-jsx-tag-face)
+
+	       (jsx_self_closing_element
+		[(member_expression (identifier)) (identifier)]
+		@typescript-ts-jsx-tag-face)))
+    (error '((jsx_opening_element
+	      [(nested_identifier (identifier)) (identifier)]
+	      @typescript-ts-jsx-tag-face)
+
+	     (jsx_closing_element
+	      [(nested_identifier (identifier)) (identifier)]
+	      @typescript-ts-jsx-tag-face)
+
+	     (jsx_self_closing_element
+	      [(nested_identifier (identifier)) (identifier)]
+	      @typescript-ts-jsx-tag-face)))))
+
 (defun typescript-ts-mode--font-lock-settings (language)
   "Tree-sitter font-lock settings.
 Argument LANGUAGE is either `typescript' or `tsx'."
@@ -293,19 +336,8 @@ Argument LANGUAGE is either `typescript' or `tsx'."
 
    :language language
    :feature 'jsx
-   `((jsx_opening_element
-      [(nested_identifier (identifier)) (identifier)]
-      @typescript-ts-jsx-tag-face)
-
-     (jsx_closing_element
-      [(nested_identifier (identifier)) (identifier)]
-      @typescript-ts-jsx-tag-face)
-
-     (jsx_self_closing_element
-      [(nested_identifier (identifier)) (identifier)]
-      @typescript-ts-jsx-tag-face)
-
-     (jsx_attribute (property_identifier) @typescript-ts-jsx-attribute-face))
+   (append (tsx-ts-mode--font-lock-compatibility-bb1f97b)
+	   `((jsx_attribute (property_identifier) @typescript-ts-jsx-attribute-face)))
 
    :language language
    :feature 'number
-- 
2.41.0

>From 3294c731f4a231ece6f1a82e5ea31dd1c0ef0c10 Mon Sep 17 00:00:00 2001
From: Vincenzo Pupillo <[email-masked]>
Date: Thu, 20 Jul 2023 12:03:11 +0200
Subject: [PATCH 1/2] Updated JSX support due to changes in
 tree-sitter-javascript

A recent change in tree-sitter-javascript grammar support for
JSX (commit bb1f97b), changed two things:
1. renamed nested_identifier to member_expression
2. removed jsx_fragment, jsx_text is used instead

* lisp/progmodes/js.el: (js-jsx--treesit-indent-compatibility-bb1f97b):
  indent helper function for handle different tree-sitter-javascript
  version
* lisp/progmodes/js.el: (js--treesit-indent-rules): use the new
  function to handle both jsx_fragment and jsx_text
* lisp/progmodes/js.el: (js-jsx--treesit-font-lock-compatibility-bb1f97b):
  font lock helper function for handle different tree-sitter-javascript
  version
* lisp/progmodes/js.el: (js--treesit-font-lock-settings): use the new
  function to handle both nested_identifier and member_expression
---
 lisp/progmodes/js.el | 61 ++++++++++++++++++++++++++++++++------------
 1 file changed, 44 insertions(+), 17 deletions(-)

diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index a05bd758dbc..70048e5d26c 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -3427,6 +3427,18 @@ This function is intended for use in `after-change-functions'."
 
 ;;; Tree sitter integration
 
+(defun js-jsx--treesit-indent-compatibility-bb1f97b ()
+  "Indent rules helper, to handle different releases of tree-sitter-javascript.
+Check if a node type is available, then return the right indent rules."
+  ;; handle commit bb1f97b
+  (condition-case nil
+      (progn (treesit-query-capture 'javascript '((jsx_fragment) @capture))
+             `(((match "<" "jsx_fragment") parent 0)
+               ((parent-is "jsx_fragment") parent js-indent-level)))
+    (error
+     `(((match "<" "jsx_text") parent 0)
+       ((parent-is "jsx_text") parent js-indent-level)))))
+
 (defvar js--treesit-indent-rules
   (let ((switch-case (rx "switch_" (or "case" "default"))))
     `((javascript
@@ -3462,8 +3474,7 @@ This function is intended for use in `after-change-functions'."
        ((parent-is "statement_block") parent-bol js-indent-level)
 
        ;; JSX
-       ((match "<" "jsx_fragment") parent 0)
-       ((parent-is "jsx_fragment") parent js-indent-level)
+       (js-jsx--treesit-indent-compatibility-bb1f97b)
        ((node-is "jsx_closing_element") parent 0)
        ((match "jsx_element" "statement") parent js-indent-level)
        ((parent-is "jsx_element") parent js-indent-level)
@@ -3490,6 +3501,35 @@ This function is intended for use in `after-change-functions'."
     "&&" "||" "!")
   "JavaScript operators for tree-sitter font-locking.")
 
+(defun js-jsx--treesit-font-lock-compatibility-bb1f97b ()
+  "Font lock rules helper, to handle different releases of tree-sitter-javascript.
+Check if a node type is available, then return the right font lock rules."
+  ;; handle commit bb1f97b
+  (condition-case nil
+      (progn (treesit-query-capture 'javascript '((member_expression) @capture))
+	     '((jsx_opening_element
+		[(member_expression (identifier)) (identifier)]
+		@font-lock-function-call-face)
+
+	       (jsx_closing_element
+		[(member_expression (identifier)) (identifier)]
+		@font-lock-function-call-face)
+
+	       (jsx_self_closing_element
+		[(member_expression (identifier)) (identifier)]
+		@font-lock-function-call-face)))
+    (error '((jsx_opening_element
+	      [(nested_identifier (identifier)) (identifier)]
+	      @font-lock-function-call-face)
+
+	     (jsx_closing_element
+	      [(nested_identifier (identifier)) (identifier)]
+	      @font-lock-function-call-face)
+
+	     (jsx_self_closing_element
+	      [(nested_identifier (identifier)) (identifier)]
+	      @font-lock-function-call-face)))))
+
 (defvar js--treesit-font-lock-settings
   (treesit-font-lock-rules
 
@@ -3599,21 +3639,8 @@ This function is intended for use in `after-change-functions'."
 
    :language 'javascript
    :feature 'jsx
-   '((jsx_opening_element
-      [(nested_identifier (identifier)) (identifier)]
-      @font-lock-function-call-face)
-
-     (jsx_closing_element
-      [(nested_identifier (identifier)) (identifier)]
-      @font-lock-function-call-face)
-
-     (jsx_self_closing_element
-      [(nested_identifier (identifier)) (identifier)]
-      @font-lock-function-call-face)
-
-     (jsx_attribute
-      (property_identifier)
-      @font-lock-constant-face))
+   (append (js-jsx--treesit-font-lock-compatibility-bb1f97b)
+	   '((jsx_attribute (property_identifier) @font-lock-constant-face)))
 
    :language 'javascript
    :feature 'number
-- 
2.41.0