]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix treesit--indent-1 regarding local parsers
authorYuan Fu <casouri@gmail.com>
Wed, 31 Jan 2024 06:18:33 +0000 (22:18 -0800)
committerEshel Yaron <me@eshelyaron.com>
Wed, 31 Jan 2024 20:16:54 +0000 (21:16 +0100)
Take this code as an example:

1 class Foo
2 {
3     /**
4      * Block comment
5      */
6     function foo($c) {
7     }
8 }

Suppose the block comment is covered by a local parser.  When we
indent line 3, treesit--indent-1 will try to get the local parser at
the BOL, and it'll get the local parser.  But it shouldn't use the
local parser to indent this line, it should use the host parser of
that local parser instead.

So now, if treesit--indent-1 gets a local parser, but the local
parser's root node's start coincides with BOL, treesit--indent-1 will
use the host parser to indent this line.

We also need to make treesit--update-ranges-local to save the host
parser along with the local parser, and make
treesit-local-parsers-at/on extract and return the host parser.

I also switch the two cases in the cond form in treesit--indent-1:
(null (treesit-parser-list)) and (car local-parsers), (car
local-parsers) now takes precedence.

* lisp/treesit.el (treesit-local-parsers-at):
(treesit-local-parsers-on): Add WITH-HOST parameter.
(treesit--update-ranges-local): Save the host parser to the local
overlay.
(treesit--indent-1): If the root node of the local parser is at BOL,
use the host parser instead.

(cherry picked from commit f63bcf2dfeb26de511f468adc237e6ea8a3cb6cc)

lisp/treesit.el

index 96222ed81cbdda4b2d3a5138ab52ff88cd3d6cf8..fab2ddd88e6bfa020dd6fa743ace3917d866dca7 100644 (file)
@@ -655,37 +655,47 @@ those inside are kept."
            if (<= start (car range) (cdr range) end)
            collect range))
 
-(defun treesit-local-parsers-at (&optional pos language)
+(defun treesit-local-parsers-at (&optional pos language with-host)
   "Return all the local parsers at POS.
 
 POS defaults to point.
 Local parsers are those which only parse a limited region marked
 by an overlay with non-nil `treesit-parser' property.
-If LANGUAGE is non-nil, only return parsers for LANGUAGE."
+If LANGUAGE is non-nil, only return parsers for LANGUAGE.
+
+If WITH-HOST is non-nil, return a list of (PARSER . HOST-PARSER)
+instead.  HOST-PARSER is the host parser which created the local
+PARSER."
   (let ((res nil))
     (dolist (ov (overlays-at (or pos (point))))
-      (when-let ((parser (overlay-get ov 'treesit-parser)))
+      (when-let ((parser (overlay-get ov 'treesit-parser))
+                 (host-parser (overlay-get ov 'treesit-host-parser)))
         (when (or (null language)
                   (eq (treesit-parser-language parser)
                       language))
-          (push parser res))))
+          (push (if with-host (cons parser host-parser) parser) res))))
     (nreverse res)))
 
-(defun treesit-local-parsers-on (&optional beg end language)
+(defun treesit-local-parsers-on (&optional beg end language with-host)
   "Return all the local parsers between BEG END.
 
 BEG and END default to the beginning and end of the buffer's
 accessible portion.
 Local parsers are those which have an `embedded' tag, and only parse
 a limited region marked by an overlay with a non-nil `treesit-parser'
-property.  If LANGUAGE is non-nil, only return parsers for LANGUAGE."
+property.  If LANGUAGE is non-nil, only return parsers for LANGUAGE.
+
+If WITH-HOST is non-nil, return a list of (PARSER . HOST-PARSER)
+instead.  HOST-PARSER is the host parser which created the local
+PARSER."
   (let ((res nil))
     (dolist (ov (overlays-in (or beg (point-min)) (or end (point-max))))
-      (when-let ((parser (overlay-get ov 'treesit-parser)))
+      (when-let ((parser (overlay-get ov 'treesit-parser))
+                 (host-parser (overlay-get ov 'treesit-host-parser)))
         (when (or (null language)
                   (eq (treesit-parser-language parser)
                       language))
-          (push parser res))))
+          (push (if with-host (cons parser host-parser) parser) res))))
     (nreverse res)))
 
 (defun treesit--update-ranges-local
@@ -701,7 +711,8 @@ parser for EMBEDDED-LANG."
         (treesit-parser-delete parser))))
   ;; Update range.
   (let* ((host-lang (treesit-query-language query))
-         (ranges (treesit-query-range host-lang query beg end)))
+         (host-parser (treesit-parser-create host-lang))
+         (ranges (treesit-query-range host-parser query beg end)))
     (pcase-dolist (`(,beg . ,end) ranges)
       (let ((has-parser nil))
         (dolist (ov (overlays-in beg end))
@@ -719,6 +730,7 @@ parser for EMBEDDED-LANG."
                                   embedded-lang nil t 'embedded))
                 (ov (make-overlay beg end nil nil t)))
             (overlay-put ov 'treesit-parser embedded-parser)
+            (overlay-put ov 'treesit-host-parser host-parser)
             (treesit-parser-set-included-ranges
              embedded-parser `((,beg . ,end)))))))))
 
@@ -1800,11 +1812,17 @@ Return (ANCHOR . OFFSET).  This function is used by
                 (forward-line 0)
                 (skip-chars-forward " \t")
                 (point)))
-         (local-parsers (treesit-local-parsers-at bol))
+         (local-parsers (treesit-local-parsers-at bol nil t))
          (smallest-node
-          (cond ((null (treesit-parser-list)) nil)
-                (local-parsers (treesit-node-at
-                                bol (car local-parsers)))
+          (cond ((car local-parsers)
+                 (let ((local-parser (caar local-parsers))
+                       (host-parser (cdar local-parsers)))
+                   (if (eq (treesit-node-start
+                            (treesit-parser-root-node local-parser))
+                           bol)
+                       (treesit-node-at bol host-parser)
+                     (treesit-node-at bol local-parser))))
+                ((null (treesit-parser-list)) nil)
                 ((eq 1 (length (treesit-parser-list nil nil t)))
                  (treesit-node-at bol))
                 ((treesit-language-at bol)