emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master f62c1b4cd00 3/3: Tree-sitter: only update range and reparse for c


From: Yuan Fu
Subject: master f62c1b4cd00 3/3: Tree-sitter: only update range and reparse for changed ranges
Date: Fri, 19 Apr 2024 22:08:42 -0400 (EDT)

branch: master
commit f62c1b4cd00e5b2f1cdc94796cf55d006c3113eb
Author: Yuan Fu <casouri@gmail.com>
Commit: Yuan Fu <casouri@gmail.com>

    Tree-sitter: only update range and reparse for changed ranges
    
    In the very beginning, there's bug#66732, to solve that bug, we added
    treesit--pre-redisplay and treesit--syntax-propertize-notifier.
    However, to fix bug#66732, we were updating ranges for the whole
    buffer which makes Emacs extremely slow when there are a lot of local
    parsers in a large buffer.  Then to solve that we introduced a
    workaround where we only update ranges in a fixed range around point.
    
    This change fixes the original problem (bug#66732) without using that
    workaround.
    
    * lisp/treesit.el (treesit--font-lock-notifier):
    (treesit--syntax-propertize-notifier): Remove functions
    (treesit--pre-redisplay): Use the new function
    treesit-parser-changed-ranges to get the changed ranges of the primary
    parser, and only update ranges for those ranges.  Plus do the work of
    the removed function.
    (treesit-major-mode-setup): Remove setup for the removed functions.
---
 lisp/treesit.el | 97 ++++++++++++++++++++++++---------------------------------
 1 file changed, 40 insertions(+), 57 deletions(-)

diff --git a/lisp/treesit.el b/lisp/treesit.el
index 2b899a84183..03df169da44 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -1328,18 +1328,6 @@ non-nil, print debugging information."
                        (max node-start start) (min node-end end)
                        face (treesit-node-type node)))))))))
 
-(defun treesit--font-lock-notifier (ranges parser)
-  "Ensures updated parts of the parse-tree are refontified.
-RANGES is a list of (BEG . END) ranges, PARSER is the tree-sitter
-parser notifying of the change."
-  (with-current-buffer (treesit-parser-buffer parser)
-    (dolist (range ranges)
-      (when treesit--font-lock-verbose
-        (message "Notifier received range: %s-%s"
-                 (car range) (cdr range)))
-      (with-silent-modifications
-        (put-text-property (car range) (cdr range) 'fontified nil)))))
-
 (defvar-local treesit--syntax-propertize-start nil
   "If non-nil, next `syntax-propertize' should start at this position.
 
@@ -1348,20 +1336,6 @@ When tree-sitter parser reparses, it calls
 and that function sets this variable to the start of the affected
 region.")
 
-(defun treesit--syntax-propertize-notifier (ranges parser)
-  "Sets `treesit--syntax-propertize-start' to the smallest start.
-Specifically, the smallest start position among all the ranges in
-RANGES for PARSER."
-  (with-current-buffer (treesit-parser-buffer parser)
-    (when-let* ((range-starts (mapcar #'car ranges))
-                (min-range-start
-                 (seq-reduce
-                  #'min (cdr range-starts) (car range-starts))))
-      (if (null treesit--syntax-propertize-start)
-          (setq treesit--syntax-propertize-start min-range-start)
-        (setq treesit--syntax-propertize-start
-              (min treesit--syntax-propertize-start min-range-start))))))
-
 (defvar-local treesit--pre-redisplay-tick nil
   "The last `buffer-chars-modified-tick' that we've processed.
 Because `pre-redisplay-functions' could be called multiple times
@@ -1369,32 +1343,47 @@ during a single command loop, we use this variable to 
debounce
 calls to `treesit--pre-redisplay'.")
 
 (defun treesit--pre-redisplay (&rest _)
-  "Force reparse and consequently run all notifiers.
-
-One of the notifiers is `treesit--font-lock-notifier', which will
-mark the region whose syntax has changed to \"need to refontify\".
-
-For example, when the user types the final slash of a C block
-comment /* xxx */, not only do we need to fontify the slash, but
-also the whole block comment, which previously wasn't fontified
-as comment due to incomplete parse tree."
+  "Force a reparse on the primary parser and do some work.
+
+After the parser reparses, we get the changed ranges, and
+1) update non-primary parsers' ranges in the changed ranges
+2) mark these ranges as to-be-fontified,
+3) tell syntax-ppss to start reparsing from the min point of the ranges
+
+We need to mark to-be-fontified ranges before redisplay starts working,
+because sometimes the range edited by the user is not the only range
+that needs to be refontified.  For example, when the user types the
+final slash of a C block comment /* xxx */, not only do we need to
+fontify the slash, but also the whole block comment, which previously
+wasn't fontified as comment due to incomplete parse tree."
   (unless (eq treesit--pre-redisplay-tick (buffer-chars-modified-tick))
-    ;; `treesit-update-ranges' will force the host language's parser to
-    ;; reparse and set correct ranges for embedded parsers.  Then
-    ;; `treesit-parser-root-node' will force those parsers to reparse.
-    (let ((len (+ (* (window-body-height) (window-body-width)) 800)))
-      ;; FIXME: As a temporary fix, this prevents Emacs from updating
-      ;; every single local parsers in the buffer every time there's an
-      ;; edit.  Moving forward, we need some way to properly track the
-      ;; regions which need update on parser ranges, like what jit-lock
-      ;; and syntax-ppss does.
-      (treesit-update-ranges
-       (max (point-min) (- (point) len))
-       (min (point-max) (+ (point) len))))
-    ;; Force repase on _all_ the parsers might not be necessary, but
-    ;; this is probably the most robust way.
-    (dolist (parser (treesit-parser-list))
-      (treesit-parser-root-node parser))
+    (let ((primary-parser
+           ;; TODO: We need something less ugly than this for getting
+           ;; the primary parser/language.
+           (if treesit-range-settings
+               (let ((query (car (car treesit-range-settings))))
+                 (if (treesit-query-p query)
+                     (treesit-parser-create
+                      (treesit-query-language query))
+                   (car (treesit-parser-list))))
+             (car (treesit-parser-list)))))
+      ;; Force a reparse on the primary parser.
+      (treesit-parser-root-node primary-parser)
+      (dolist (range (treesit-parser-changed-ranges primary-parser))
+        ;; 1. Update ranges.
+        (treesit-update-ranges (car range) (cdr range))
+        ;; 2. Mark the changed ranges to be fontified.
+        (when treesit--font-lock-verbose
+          (message "Notifier received range: %s-%s"
+                   (car range) (cdr range)))
+        (with-silent-modifications
+          (put-text-property (car range) (cdr range) 'fontified nil))
+        ;; 3. Set `treesit--syntax-propertize-start'.
+        (if (null treesit--syntax-propertize-start)
+            (setq treesit--syntax-propertize-start (car range))
+          (setq treesit--syntax-propertize-start
+                (min treesit--syntax-propertize-start (car range))))))
+
     (setq treesit--pre-redisplay-tick (buffer-chars-modified-tick))))
 
 (defun treesit--pre-syntax-ppss (start end)
@@ -2956,14 +2945,8 @@ before calling this function."
                    (font-lock-fontify-syntactically-function
                     . treesit-font-lock-fontify-region)))
     (treesit-font-lock-recompute-features)
-    (dolist (parser (treesit-parser-list))
-      (treesit-parser-add-notifier
-       parser #'treesit--font-lock-notifier))
     (add-hook 'pre-redisplay-functions #'treesit--pre-redisplay 0 t))
   ;; Syntax
-  (dolist (parser (treesit-parser-list))
-    (treesit-parser-add-notifier
-     parser #'treesit--syntax-propertize-notifier))
   (add-hook 'syntax-propertize-extend-region-functions
             #'treesit--pre-syntax-ppss 0 t)
   ;; Indent.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]