emacs-elpa-diffs
[Top][All Lists]
Advanced

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

[elpa] externals/consult 1247248ff0 3/3: Implement dynamic consult-line-


From: ELPA Syncer
Subject: [elpa] externals/consult 1247248ff0 3/3: Implement dynamic consult-line-multi search (Fix #644)
Date: Thu, 1 Dec 2022 23:57:24 -0500 (EST)

branch: externals/consult
commit 1247248ff023c970591ec2a99655132e2a81ee45
Author: Daniel Mendler <mail@daniel-mendler.de>
Commit: Daniel Mendler <mail@daniel-mendler.de>

    Implement dynamic consult-line-multi search (Fix #644)
---
 CHANGELOG.org |   3 ++
 consult.el    | 150 +++++++++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 120 insertions(+), 33 deletions(-)

diff --git a/CHANGELOG.org b/CHANGELOG.org
index 4a66b95298..723c6509f8 100644
--- a/CHANGELOG.org
+++ b/CHANGELOG.org
@@ -5,6 +5,9 @@
 * Development
 
 - Bugfixes
+- =consult-line-multi= has been rewritten completely. Buffers are searched
+  dynamically instead, similar to =consult-grep= in order to reduce the startup
+  speed.
 - Add =consult--source-file-register=, and make the registers available in
   =consult-buffer=. Registers are often used as quick access keys for files, 
e.g.,
   =(add-to-list 'register-alist '(?i file . "~/.emacs.d/init.el")))=.
diff --git a/consult.el b/consult.el
index a5d8b97e87..6a66616fa6 100644
--- a/consult.el
+++ b/consult.el
@@ -456,6 +456,7 @@ Used by `consult-completion-in-region', `consult-yank' and 
`consult-history'.")
 (defvar consult--find-history nil)
 (defvar consult--man-history nil)
 (defvar consult--line-history nil)
+(defvar consult--line-multi-history nil)
 (defvar consult--theme-history nil)
 (defvar consult--minor-mode-menu-history nil)
 (defvar consult--kmacro-history nil)
@@ -1022,7 +1023,8 @@ selection change to full Emacs markers."
           (lambda (_)
             (unless (consult--completion-window-p)
               (remove-hook 'window-selection-change-functions hook)
-              (mapc #'consult--get-location candidates))))
+              (mapc #'consult--get-location
+                    (if (functionp candidates) (funcall candidates) 
candidates)))))
     (lambda (action cand)
       (pcase action
         ('setup (add-hook 'window-selection-change-functions hook))
@@ -1124,18 +1126,6 @@ Return the location marker."
             (forward-char column))
           (point-marker))))))
 
-(defun consult--line-group (cand transform)
-  "Group function used by `consult-line-multi'.
-If TRANSFORM non-nil, return transformed CAND, otherwise return title."
-  (if transform
-      cand
-    (let ((marker (car (get-text-property 0 'consult-location cand))))
-      (buffer-name
-       ;; Handle cheap marker
-       (if (consp marker)
-           (car marker)
-         (marker-buffer marker))))))
-
 (defun consult--line-prefix (&optional curr-line)
   "Annotate `consult-location' candidates with line numbers.
 CURR-LINE is the current line number."
@@ -2138,6 +2128,39 @@ The refresh happens after a DELAY, defaulting to 
`consult-async-refresh-delay'."
   "Filter candidates of ASYNC by FUN."
   (consult--async-transform async seq-filter fun))
 
+(defun consult--dynamic-collection-source (async fun)
+  "Dynamic collection source.
+ASYNC is the sink.
+FUN computes the candidates given the input."
+  (let (input current)
+    (lambda (action)
+      (pcase action
+        ('nil
+         (when input
+           (if (equal input current)
+               (funcall async nil)
+             (let ((candidates (funcall fun input)))
+               (funcall async 'flush)
+               (setq current input)
+               (funcall async candidates)))))
+        (""
+         (setq input "" current "")
+         (funcall async 'flush)
+         (funcall async 'refresh))
+        ((pred stringp)
+         (setq input action)
+         (funcall async 'refresh))
+        (_ (funcall async action))))))
+
+(defun consult--dynamic-collection (fun)
+  "Dynamic collection with input splitting.
+FUN computes the candidates given the input."
+  (thread-first
+    (consult--async-sink)
+    (consult--dynamic-collection-source fun)
+    (consult--async-throttle)
+    (consult--async-split)))
+
 (defun consult--command-builder (builder)
   "Return command line builder given CMD.
 BUILDER is the command line builder function."
@@ -2154,7 +2177,8 @@ command line builder function, which takes the input 
string and must either
 return a list of command line arguments or a plist with the command line
 argument list :command and a highlighting function :highlight."
   (declare (indent 1))
-  `(thread-first (consult--async-sink)
+  `(thread-first
+     (consult--async-sink)
      (consult--async-refresh-timer)
      ,@(seq-take-while (lambda (x) (not (keywordp x))) args)
      (consult--async-process
@@ -2859,7 +2883,7 @@ See `multi-occur' for the meaning of the arguments BUFS, 
REGEXP and NLINES."
                (if-let (fun (bound-and-true-p outline-search-function))
                    (funcall fun)
                  (re-search-forward heading-regexp nil t)))
-        (setq line (+ line (consult--count-lines (match-beginning 0))))
+        (cl-incf line (consult--count-lines (match-beginning 0)))
         (push (consult--location-candidate
                (consult--buffer-substring (line-beginning-position)
                                           (line-end-position)
@@ -3084,31 +3108,83 @@ changed if the START prefix argument is set. The symbol 
at point and the last
 
 ;;;;; Command: consult-line-multi
 
-(defun consult--line-multi-candidates (buffers)
-  "Collect the line candidates from multiple buffers.
+(defun consult--line-multi-group (cand transform)
+  "Group function used by `consult-line-multi'.
+If TRANSFORM non-nil, return transformed CAND, otherwise return title."
+  (if transform
+      cand
+    (let ((marker (car (get-text-property 0 'consult-location cand))))
+      (buffer-name
+       ;; Handle cheap marker
+       (if (consp marker)
+           (car marker)
+         (marker-buffer marker))))))
+
+(defun consult--line-multi-candidates (buffers input)
+  "Collect matching candidates from multiple buffers.
+INPUT is the user input which should be matched.
 BUFFERS is the list of buffers."
-  (or (apply #'nconc
-             (consult--buffer-map buffers
-              #'consult--line-candidates 'top most-positive-fixnum))
-      (user-error "No lines")))
+  (pcase-let ((`(,regexps . ,hl)
+               (funcall consult--regexp-compiler
+                        input 'emacs completion-ignore-case))
+              (candidates nil)
+              (inhibit-field-text-motion t))
+    (setq regexps (mapcar (lambda (x) (format "^.*?\\(?:%s\\)" x)) regexps))
+    (dolist (buf buffers (nreverse candidates))
+     (with-current-buffer buf
+       (save-excursion
+         (save-match-data
+           (let ((line (line-number-at-pos (point-min) 
consult-line-numbers-widen)))
+             (goto-char (point-min))
+             (while (save-excursion (re-search-forward (car regexps) nil t))
+               (cl-incf line (consult--count-lines (match-beginning 0)))
+               (let ((beg (line-beginning-position))
+                     (end (line-end-position)))
+                 (when (seq-every-p (lambda (x) (save-excursion 
(re-search-forward x end t)))
+                                    (cdr regexps))
+                   (let ((cand (consult--buffer-substring beg end)))
+                     (funcall hl cand)
+                     (push (consult--location-candidate cand (cons buf beg) 
line)
+                           candidates))))
+               (unless (eobp) (forward-char 1))))))))))
 
 ;;;###autoload
 (defun consult-line-multi (query &optional initial)
   "Search for a matching line in multiple buffers.
 
-By default search across all project buffers. If the prefix argument QUERY is
-non-nil, all buffers are searched. Optional INITIAL input can be provided. See
-`consult-line' for more information. In order to search a subset of buffers,
-QUERY can be set to a plist according to `consult--buffer-query'."
+By default search across all project buffers. If the prefix
+argument QUERY is non-nil, all buffers are searched. Optional
+INITIAL input can be provided. The symbol at point and the last
+`isearch-string' is added to the future history.In order to
+search a subset of buffers, QUERY can be set to a plist according
+to `consult--buffer-query'."
   (interactive "P")
   (unless (keywordp (car-safe query))
-    (setq query (list :sort 'alpha :directory (and (not query) 'project))))
-  (let ((buffers (consult--buffer-query-prompt "Go to line" query)))
-    (consult--line
-     (consult--line-multi-candidates (cdr buffers))
-     :prompt (car buffers)
-     :initial initial
-     :group #'consult--line-group)))
+    (setq query (list :sort 'alpha-current :directory (and (not query) 
'project))))
+  (pcase-let* ((`(,prompt . ,buffers) (consult--buffer-query-prompt "Go to 
line" query))
+               (collection (consult--dynamic-collection
+                            (apply-partially #'consult--line-multi-candidates
+                                             buffers))))
+    (consult--read
+     collection
+     :prompt prompt
+     :annotate (consult--line-prefix)
+     :category 'consult-location
+     :sort nil
+     :require-match t
+     ;; Always add last isearch string to future history
+     :add-history (mapcar #'consult--async-split-initial
+                          (delq nil (list (thing-at-point 'symbol)
+                                          isearch-string)))
+     :history '(:input consult--line-multi-history)
+     :lookup #'consult--lookup-location
+     ;; Add isearch-string as initial input if starting from isearch
+     :initial (consult--async-split-initial
+               (or initial
+                   (and isearch-mode
+                        (prog1 isearch-string (isearch-done)))))
+     :state (consult--location-state (lambda () (funcall collection nil)))
+     :group #'consult--line-multi-group)))
 
 ;;;;; Command: consult-keep-lines
 
@@ -4054,7 +4130,7 @@ The command supports previewing the currently selected 
theme."
 ;;;;; Command: consult-buffer
 
 (defun consult--buffer-sort-alpha (buffers)
-  "Sort BUFFERS alphabetically, but push down starred buffers."
+  "Sort BUFFERS alphabetically, put starred buffers at the end."
   (sort buffers
         (lambda (x y)
           (setq x (buffer-name x) y (buffer-name y))
@@ -4064,6 +4140,14 @@ The command supports previewing the currently selected 
theme."
                 (string< x y)
               (not a))))))
 
+(defun consult--buffer-sort-alpha-current (buffers)
+  "Sort BUFFERS alphabetically, put current at the beginning."
+  (let ((buffers (consult--buffer-sort-alpha buffers))
+        (current (current-buffer)))
+    (if (memq current buffers)
+        (cons current (delq current buffers))
+      buffers)))
+
 (defun consult--buffer-sort-visibility (buffers)
   "Sort BUFFERS by visibility."
   (let ((hidden)



reply via email to

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