emacs-diffs
[Top][All Lists]
Advanced

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

master 8e103eb 2/2: Speed up xref rendering for matches on very long lin


From: Dmitry Gutov
Subject: master 8e103eb 2/2: Speed up xref rendering for matches on very long lines
Date: Sun, 7 Mar 2021 21:31:51 -0500 (EST)

branch: master
commit 8e103ebef12bb723723c7e6ec8e1053e86878a5b
Author: Dmitry Gutov <dgutov@yandex.ru>
Commit: Dmitry Gutov <dgutov@yandex.ru>

    Speed up xref rendering for matches on very long lines
    
    * lisp/progmodes/xref.el (xref--insert-xrefs): Cut up the current
    line into pieces here for multiple matches's summaries, so that
    xref--insert-xrefs can do less work (bug#46859).
    (xref--insert-xrefs): Do less work.
    (xref--outdated-p):
    Update accordingly to how the summary creation logic changed.
    (xref--buf-pairs-iterator): Update to the new calling convention.
    (xref-location-column): Effectively rename back to
    xref-file-location-column since the generic version is now unused.
    
    * test/lisp/progmodes/xref-tests.el
    (xref-matches-in-directory-finds-two-matches-on-the-same-line)
    (xref-matches-in-directory-finds-an-empty-line-regexp-match):
    Adjust to the xref-location-column change.
    (xref-matches-in-files-trims-summary-for-matches-on-same-line):
    New test.
    
    * test/lisp/progmodes/xref-resources/file1.txt:
    Change contents slightly to test the new xref--outdated-p code.
---
 lisp/progmodes/xref.el                       | 110 ++++++++++++++++-----------
 test/lisp/progmodes/xref-resources/file1.txt |   2 +-
 test/lisp/progmodes/xref-resources/file3.txt |   1 +
 test/lisp/progmodes/xref-tests.el            |  15 +++-
 4 files changed, 78 insertions(+), 50 deletions(-)

diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index 18fdd96..af46365 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -97,10 +97,6 @@ This is typically the filename.")
   "Return the line number corresponding to the location."
   nil)
 
-(cl-defgeneric xref-location-column (_location)
-  "Return the exact column corresponding to the location."
-  nil)
-
 (cl-defgeneric xref-match-length (_item)
   "Return the length of the match."
   nil)
@@ -130,7 +126,7 @@ in its full absolute form."
 (defclass xref-file-location (xref-location)
   ((file :type string :initarg :file)
    (line :type fixnum :initarg :line :reader xref-location-line)
-   (column :type fixnum :initarg :column :reader xref-location-column))
+   (column :type fixnum :initarg :column :reader xref-file-location-column))
   :documentation "A file location is a file/line/column triple.
 Line numbers start from 1 and columns from 0.")
 
@@ -713,10 +709,7 @@ references displayed in the current *xref* buffer."
                    (push pair all-pairs)
                    ;; Perform sanity check first.
                    (xref--goto-location loc)
-                   (if (xref--outdated-p item
-                                         (buffer-substring-no-properties
-                                          (line-beginning-position)
-                                          (line-end-position)))
+                   (if (xref--outdated-p item)
                        (message "Search result out of date, skipping")
                      (cond
                       ((null file-buf)
@@ -733,18 +726,38 @@ references displayed in the current *xref* buffer."
            (move-marker (car pair) nil)
            (move-marker (cdr pair) nil)))))))
 
-(defun xref--outdated-p (item line-text)
-  ;; FIXME: The check should probably be a generic function instead of
-  ;; the assumption that all matches contain the full line as summary.
-  (let ((summary (xref-item-summary item))
-        (strip (lambda (s) (if (string-match "\r\\'" s)
-                          (substring-no-properties s 0 -1)
-                        s))))
+(defun xref--outdated-p (item)
+  "Check that the match location at current position is up-to-date.
+ITEMS is an xref item which "
+  ;; FIXME: The check should most likely be a generic function instead
+  ;; of the assumption that all matches' summaries relate to the
+  ;; buffer text in a particular way.
+  (let* ((summary (xref-item-summary item))
+         ;; Sometimes buffer contents include ^M, and sometimes Grep
+         ;; output includes it, and they don't always match.
+         (strip (lambda (s) (if (string-match "\r\\'" s)
+                           (substring-no-properties s 0 -1)
+                         s)))
+         (stripped-summary (funcall strip summary))
+         (lendpos (line-end-position))
+         (check (lambda ()
+                  (let ((comparison-end
+                         (+ (point) (length stripped-summary))))
+                    (and (>= lendpos comparison-end)
+                         (equal stripped-summary
+                                (buffer-substring-no-properties
+                                 (point) comparison-end)))))))
     (not
-     ;; Sometimes buffer contents include ^M, and sometimes Grep
-     ;; output includes it, and they don't always match.
-     (equal (funcall strip line-text)
-            (funcall strip summary)))))
+     (or
+      ;; Either summary contains match text and after
+      ;; (2nd+ match on the line)...
+      (funcall check)
+      ;; ...or it starts at bol, includes the match and after.
+      (and (< (point) (+ (line-beginning-position)
+                         (length stripped-summary)))
+           (save-excursion
+             (forward-line 0)
+             (funcall check)))))))
 
 ;; FIXME: Write a nicer UI.
 (defun xref--query-replace-1 (from to iter)
@@ -886,30 +899,24 @@ GROUP is a string for decoration purposes and XREF is an
                                (length (and line (format "%d" line)))))
            for line-format = (and max-line-width
                                   (format "%%%dd: " max-line-width))
-           with prev-line-key = nil
+           with prev-group = nil
+           with prev-line = nil
            do
            (xref--insert-propertized '(face xref-file-header xref-group t)
                                      group "\n")
            (cl-loop for (xref . more2) on xrefs do
                     (with-slots (summary location) xref
                       (let* ((line (xref-location-line location))
-                             (new-summary summary)
-                             (line-key (list (xref-location-group location) 
line))
                              (prefix
-                              (if line
-                                  (propertize (format line-format line)
-                                              'face 'xref-line-number)
-                                "  ")))
+                              (cond
+                               ((not line) "  ")
+                               ((equal line prev-line) "")
+                               (t (propertize (format line-format line)
+                                              'face 'xref-line-number)))))
                         ;; Render multiple matches on the same line, together.
-                        (when (and line (equal prev-line-key line-key))
-                          (when-let ((column (xref-location-column location)))
-                            (delete-region
-                             (save-excursion
-                               (forward-line -1)
-                               (move-to-column (+ (length prefix) column))
-                               (point))
-                             (point))
-                            (setq new-summary (substring summary column) 
prefix "")))
+                        (when (and (equal prev-group group)
+                                   (not (equal prev-line line)))
+                          (insert "\n"))
                         (xref--insert-propertized
                          (list 'xref-item xref
                                'mouse-face 'highlight
@@ -917,9 +924,10 @@ GROUP is a string for decoration purposes and XREF is an
                                'help-echo
                                (concat "mouse-2: display in another window, "
                                        "RET or mouse-1: follow reference"))
-                         prefix new-summary)
-                        (setq prev-line-key line-key)))
-                    (insert "\n"))))
+                         prefix summary)
+                        (setq prev-line line
+                              prev-group group))))
+           (insert "\n")))
 
 (defun xref--analyze (xrefs)
   "Find common filenames in XREFS.
@@ -1678,20 +1686,30 @@ Such as the current syntax table and the applied syntax 
properties."
                                  syntax-needed)))))
 
 (defun xref--collect-matches-1 (regexp file line line-beg line-end 
syntax-needed)
-  (let (matches)
+  (let (match-pairs matches)
     (when syntax-needed
       (syntax-propertize line-end))
-    ;; FIXME: This results in several lines with the same
-    ;; summary. Solve with composite pattern?
     (while (and
             ;; REGEXP might match an empty string.  Or line.
-            (or (null matches)
+            (or (null match-pairs)
                 (> (point) line-beg))
             (re-search-forward regexp line-end t))
-      (let* ((beg-column (- (match-beginning 0) line-beg))
-             (end-column (- (match-end 0) line-beg))
+      (push (cons (match-beginning 0)
+                  (match-end 0))
+            match-pairs))
+    (setq match-pairs (nreverse match-pairs))
+    (while match-pairs
+      (let* ((beg-end (pop match-pairs))
+             (beg-column (- (car beg-end) line-beg))
+             (end-column (- (cdr beg-end) line-beg))
              (loc (xref-make-file-location file line beg-column))
-             (summary (buffer-substring line-beg line-end)))
+             (summary (buffer-substring (if matches (car beg-end) line-beg)
+                                        (if match-pairs
+                                            (caar match-pairs)
+                                          line-end))))
+        (when matches
+          (cl-decf beg-column (- (car beg-end) line-beg))
+          (cl-decf end-column (- (car beg-end) line-beg)))
         (add-face-text-property beg-column end-column 'xref-match
                                 t summary)
         (push (xref-make-match summary loc (- end-column beg-column))
diff --git a/test/lisp/progmodes/xref-resources/file1.txt 
b/test/lisp/progmodes/xref-resources/file1.txt
index 5d7cc54..85b92f1 100644
--- a/test/lisp/progmodes/xref-resources/file1.txt
+++ b/test/lisp/progmodes/xref-resources/file1.txt
@@ -1,2 +1,2 @@
-foo foo
+ foo foo
 bar
diff --git a/test/lisp/progmodes/xref-resources/file3.txt 
b/test/lisp/progmodes/xref-resources/file3.txt
new file mode 100644
index 0000000..6283185
--- /dev/null
+++ b/test/lisp/progmodes/xref-resources/file3.txt
@@ -0,0 +1 @@
+ match some words match more match ends here
diff --git a/test/lisp/progmodes/xref-tests.el 
b/test/lisp/progmodes/xref-tests.el
index 028c43d..9982c32 100644
--- a/test/lisp/progmodes/xref-tests.el
+++ b/test/lisp/progmodes/xref-tests.el
@@ -59,15 +59,15 @@
     (should (string-match-p "file1\\.txt\\'" (xref-location-group (nth 1 
locs))))
     (should (equal 1 (xref-location-line (nth 0 locs))))
     (should (equal 1 (xref-location-line (nth 1 locs))))
-    (should (equal 0 (xref-location-column (nth 0 locs))))
-    (should (equal 4 (xref-location-column (nth 1 locs))))))
+    (should (equal 1 (xref-file-location-column (nth 0 locs))))
+    (should (equal 5 (xref-file-location-column (nth 1 locs))))))
 
 (ert-deftest xref-matches-in-directory-finds-an-empty-line-regexp-match ()
   (let ((locs (xref-tests--locations-in-data-dir "^$")))
     (should (= 1 (length locs)))
     (should (string-match-p "file2\\.txt\\'" (xref-location-group (nth 0 
locs))))
     (should (equal 1 (xref-location-line (nth 0 locs))))
-    (should (equal 0 (xref-location-column (nth 0 locs))))))
+    (should (equal 0 (xref-file-location-column (nth 0 locs))))))
 
 (ert-deftest xref-matches-in-files-includes-matches-from-all-the-files ()
   (let ((matches (xref-matches-in-files "bar"
@@ -78,6 +78,15 @@
              (lambda (match) (equal (xref-item-summary match) "bar"))
              matches))))
 
+(ert-deftest xref-matches-in-files-trims-summary-for-matches-on-same-line ()
+  (let ((matches (xref-matches-in-files "match"
+                                        (directory-files xref-tests--data-dir t
+                                                         "\\`[^.]"))))
+    (should (= 3 (length matches)))
+    (should
+     (equal (mapcar #'xref-item-summary matches)
+            '(" match some words " "match more " "match ends here")))))
+
 (ert-deftest xref--buf-pairs-iterator-groups-markers-by-buffers-1 ()
   (let* ((xrefs (xref-tests--matches-in-data-dir "foo"))
          (iter (xref--buf-pairs-iterator xrefs))



reply via email to

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