Subject: Re: bug#77906: 31.0.50; php-js-mode and js-ts-mode: treesit-show-paren-data--categorize: peculiar error: ("Cannot find the definition of the predicate in `treesit-thing-settings'" list)

From: Juri Linkov <[email-masked]>
Date: Sun, 04 May 2025 19:50:29 +0300

>>> With this patch the following case works correctly in test.heex:
>>> 
>>>  <%= if true do %>
>>>    <p>hello, tree-sitter!</p>
>>>  <% end| %>
>>> 
>>> With point at "|" the command 'backward-sexp' (C-M-b)
>>> correctly jumps to the beginning of "if".
>>
>> Right.  I think it can be done in backward-sexp since there we know
>> we’re moving back.  I assume that’s what you mean.
>
> The problem is that the last time when the arg is still -1
> is in 'treesit-forward-sexp', and then it calls
> 'treesit-beginning-of-thing' with '(abs arg)'.
>
> The complete call chain:
>
> - treesit-forward-sexp
> - treesit-beginning-of-thing
> - treesit-navigate-thing
> - treesit-thing-at
> - treesit-node-at
> - treesit-parsers-at
> - overlays-at
>
> So not clear how to propagate the flag for decrementing the offset.
> Maybe by let-binding a new dynamically bound variable?

Ok, here is the patch:

diff --git a/lisp/treesit.el b/lisp/treesit.el
index 69cc28b0cec..af33ae06610 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -839,6 +839,11 @@ treesit--clip-ranges
            if (<= start (car range) (cdr range) end)
            collect range))
 
+(defvar treesit--parser-overlay-offset 0
+  "Defines at which position to get the parser overlay.
+The commands that move backward need to set it to -1 to be
+able to use the range that ends immediately before point.")
+
 (defun treesit-parsers-at (&optional pos language with-host only)
   "Return all parsers at POS.
 
@@ -869,7 +874,8 @@ treesit-parsers-at
   (let ((res nil))
     ;; Refer to (ref:local-parser-overlay) for more explanation of local
     ;; parser overlays.
-    (dolist (ov (overlays-at (or pos (point))))
+    (dolist (ov (overlays-at (+ (or pos (point))
+                                treesit--parser-overlay-offset)))
       (when-let* ((parser (overlay-get ov 'treesit-parser))
                   (host-parser (or (null with-host)
                                    (overlay-get ov 'treesit-host-parser)))
@@ -3021,7 +3027,8 @@ treesit-forward-sexp
           t)
         (if (> arg 0)
             (treesit-end-of-thing pred (abs arg) 'restricted)
-          (treesit-beginning-of-thing pred (abs arg) 'restricted))
+          (let ((treesit--parser-overlay-offset -1))
+            (treesit-beginning-of-thing pred (abs arg) 'restricted)))
         ;; If we couldn't move, we should signal an error and report
         ;; the obstacle, like `forward-sexp' does.  If we couldn't
         ;; find a parent, we simply return nil without moving point,
@@ -3036,6 +3043,7 @@ treesit--forward-list-with-default
 ARG is described in the docstring of `forward-list'."
   (let* ((pred (or treesit-sexp-type-regexp 'list))
          (arg (or arg 1))
+         (treesit--parser-overlay-offset (if (> arg 0) 0 -1))
          (cnt arg)
          (inc (if (> arg 0) 1 -1)))
     (while (/= cnt 0)
@@ -3163,6 +3171,7 @@ treesit-up-list
   (interactive "^p")
   (let* ((pred (or treesit-sexp-type-regexp 'list))
          (arg (or arg 1))
+         (treesit--parser-overlay-offset -1)
          (cnt arg)
          (inc (if (> arg 0) 1 -1)))
     (while (/= cnt 0)