Fix treesit--merge-ranges (bug#73324)
authorYuan Fu <casouri@gmail.com>
Sat, 21 Sep 2024 03:40:26 +0000 (20:40 -0700)
committerYuan Fu <casouri@gmail.com>
Sat, 21 Sep 2024 03:54:23 +0000 (20:54 -0700)
* lisp/treesit.el (treesit--merge-ranges): Make sure that old
ranges that intersects with START-END are actually discarded.
* test/src/treesit-tests.el (treesit-range-merge): New test.

lisp/treesit.el
test/src/treesit-tests.el

index 67be1e7d583a6a4a5b9864d657d750cf40c4fc84..bedcc2607501d6f8aff29b40c3cf1437304dff4f 100644 (file)
@@ -634,20 +634,39 @@ Return the merged list of ranges."
          ;; New range and old range don't intersect, new comes
          ;; before, push new.
          ((<= new-end old-beg)
-          (push (car new-ranges) result)
+          (unless (eq new-beg new-end)
+            (push (car new-ranges) result))
           (setq new-ranges (cdr new-ranges)))
          ;; New range and old range don't intersect, old comes
          ;; before, push old.
          ((<= old-end new-beg)
-          (push (car old-ranges) result)
+          (unless (eq old-beg old-end)
+            (push (car old-ranges) result))
           (setq old-ranges (cdr old-ranges)))
          (t ;; New and old range intersect, discard old.
           (setq old-ranges (cdr old-ranges))))))
-    (let ((left-over (or new-ranges old-ranges)))
-      (dolist (range left-over)
-        (push range result)))
+    ;; At this point, either old-ranges has left-over or new-ranges has
+    ;; left-over, but not both.
+    (while old-ranges
+      ;; For each left-over old range, push to result unless it
+      ;; intersects with START-END.
+      (let ((old-beg (caar old-ranges))
+            (old-end (cdar old-ranges)))
+        (unless (or (and (< start old-end)
+                         (< old-beg end))
+                    (eq old-beg old-end))
+          (push (car old-ranges) result)))
+      (setq old-ranges (cdr old-ranges)))
+    ;; Unconditionally push left-over new ranges to result.
+    (while new-ranges
+      (unless (eq (caar new-ranges) (cdar new-ranges))
+        (push (car new-ranges) result))
+      (setq new-ranges (cdr new-ranges)))
     (nreverse result)))
 
+;; TODO: truncate ranges that exceeds START and END instead of
+;; discarding them.  Merge into treesit--merge-ranges so we don't loop
+;; over the ranges twice (might be premature optimization tho).
 (defun treesit--clip-ranges (ranges start end)
   "Clip RANGES in between START and END.
 RANGES is a list of ranges of the form (BEG . END).  Ranges
@@ -872,6 +891,7 @@ SETTING should be a setting in `treesit-font-lock-settings'."
     (setf (nth 1 new-setting) t)
     new-setting))
 
+;; FIXME: Rewrite this in more readable fashion.
 (defun treesit--font-lock-level-setter (sym val)
   "Custom setter for `treesit-font-lock-level'.
 Set the default value of SYM to VAL, recompute fontification
index 24cd755fcf0a22efe0358bf56fd3af1aa5d02df5..700b0505d713720f088321fd3b5d6c94ea1c1c97 100644 (file)
@@ -684,6 +684,11 @@ visible_end.)"
       (should (equal '((16 . 28)) (treesit-query-range
                                    'javascript query nil nil '(1 . -1)))))))
 
+(ert-deftest treesit-range-merge ()
+  "Test merging ranges."
+  (should (equal (treesit--merge-ranges '((1 . 1) (3 . 483)) nil 1 488)
+                 nil)))
+
 (ert-deftest treesit-range-fixup-after-edit ()
   "Tests if Emacs can fix OOB ranges after deleting text or narrowing."
   (skip-unless (treesit-language-available-p 'json))