]> git.eshelyaron.com Git - emacs.git/commitdiff
Make sure treesit.el doesn't create parsers by itself (bug#75456)
authorYuan Fu <casouri@gmail.com>
Sat, 18 Jan 2025 01:40:04 +0000 (17:40 -0800)
committerEshel Yaron <me@eshelyaron.com>
Sat, 18 Jan 2025 22:03:02 +0000 (23:03 +0100)
Up to this point, functions in
treesit.el (font-lock/indentation/etc) creates a parser if one
doesn't exist.  This doesn't work well with local parsers,
because local parsers are not visible at global level and
font-lock or indentation functions might accidentally create a
global parser for an embedded language.

The solution is to not create parsers automatically.  This has
the potential of breaking things, but I don't expect any actual
breakage: all the major modes create parsers they need in the
major mode body, even though previously it isn't technically
necessary.

* lisp/treesit.el (treesit-buffer-root-node):
(treesit--update-ranges-local):
(treesit-update-ranges):
(treesit--guess-primary-parser): Use treesit-parser-list instead
of treesit-parser-create.

(cherry picked from commit f7e41ba3d0fb5f89b1d2712b699b526ef7d7b82c)

lisp/treesit.el

index ff5fa6317ffe6d84cd46d8659ce26a69bdb7262f..b6f429e407f44f87cae27d881dd57c3f4733d242 100644 (file)
 ;; exposed C API of tree-sitter.  It also contains frameworks for
 ;; integrating tree-sitter with font-lock, indentation, activating and
 ;; deactivating tree-sitter, debugging tree-sitter, etc.
+;;
+;; Some conventions:
+;;
+;; 1. Whenever it makes sense, a function that takes a tree-sitter node
+;; as an argument should also accept nil (and return nil in that case).
+;; This is to help with function chaining.
+;;
+;; 2. In most cases, a function shouldn't implicitly create a parser.
+;; All parsers should be created explicitly by user.  Use
+;;
+;;     (car (treesit-parser-list nil LANG))
+;;
+;; to get a parser for a certain language.
+;;
+;; Initially in Emacs 29, the world is simple and each language has one
+;; parser in the buffer.  So if we need a parser for a language, we can
+;; just create it if it doesn't exist.  But now we have local parsers,
+;; so there will be more than one parser for each language in a buffer.
+;; We can also have local parser of the same language as the host
+;; parser.  All of which means we can't equalize language and parser,
+;; and create paresr for a language willy-nilly anymore.  Major mode
+;; will manage their parsers.
 
 ;;; Code:
 
@@ -299,15 +321,14 @@ If INCLUDE-NODE is non-nil, return NODE if it satisfies PRED."
 
 Use the first parser in the parser list if LANGUAGE is omitted.
 
-If LANGUAGE is non-nil, use the first parser for LANGUAGE with
-TAG in the parser list, or create one if none exists.  TAG
-defaults to nil."
-  (if-let ((parser
-            (if language
-                (treesit-parser-create language nil nil tag)
-              (or (car (treesit-parser-list))
-                  (signal 'treesit-no-parser (list (current-buffer)))))))
-      (treesit-parser-root-node parser)))
+If LANGUAGE is non-nil, use the first parser for LANGUAGE with TAG in
+the parser list.  If there's no such parser, return nil.  TAG defaults
+to nil."
+  (let ((parser
+         (or (car (treesit-parser-list nil language tag))
+             (signal 'treesit-no-parser (list language)))))
+    (when parser
+      (treesit-parser-root-node parser))))
 
 (defun treesit-filter-child (node pred &optional named)
   "Return children of NODE that satisfies predicate PRED.
@@ -762,7 +783,7 @@ MODIFIED-TICK.  This will help Emacs garbage-collect overlays that
 aren't in use anymore."
   ;; Update range.
   (let* ((host-lang (treesit-query-language query))
-         (host-parser (treesit-parser-create host-lang))
+         (host-parser (car (treesit-parser-list nil host-lang)))
          (ranges (treesit-query-range host-parser query beg end)))
     (pcase-dolist (`(,beg . ,end) ranges)
       (let ((has-parser nil))
@@ -817,7 +838,7 @@ region."
            query language modified-tick beg end))
          (t
           (let* ((host-lang (treesit-query-language query))
-                 (parser (treesit-parser-create language))
+                 (parser (car (treesit-parser-list nil language)))
                  (old-ranges (treesit-parser-included-ranges parser))
                  (new-ranges (treesit-query-range
                               host-lang query beg end offset))
@@ -1642,11 +1663,17 @@ parses the entire buffer (as opposed to embedded parsers which only
 parses part of the buffer).  This function tries to find and return that
 parser."
   (if treesit-range-settings
-      (let ((query (car (car treesit-range-settings))))
+      (let* ((query (caar treesit-range-settings))
+             (lang (treesit-query-language query)))
+        ;; Major mode end-user won't see this signal since major mode
+        ;; author surely will see it and correct it.  Also, multi-lang
+        ;; major mode's author should've seen the notice and set the
+        ;; primary parser themselves.
         (if (treesit-query-p query)
-            (treesit-parser-create
-             (treesit-query-language query))
-          (car (treesit-parser-list))))
+            (or (car (treesit-parser-list nil lang))
+                (signal 'treesit-no-parser (list lang)))
+          (or (car (treesit-parser-list))
+              (signal 'treesit-no-parser nil))))
     (car (treesit-parser-list))))
 
 (defun treesit--pre-redisplay (&rest _)