]> code.delx.au - gnu-emacs-elpa/commitdiff
Make counsel-git-grep fully async
authorOleh Krehel <ohwoeowho@gmail.com>
Thu, 4 Jun 2015 16:45:40 +0000 (18:45 +0200)
committerOleh Krehel <ohwoeowho@gmail.com>
Thu, 4 Jun 2015 16:45:40 +0000 (18:45 +0200)
* counsel.el (counsel-git-grep-count): Rename to `counsel--gg-count' and
  make it async.
(counsel-git-grep-function): Set `ivy--full-length' to -1. It means that
`ivy--exhibit' should do nothing until the length is re-computed.
(counsel-git-grep): Use the sync version of `counsel-git-grep-count' for
the initial repo line count.
(counsel--gg-candidates): New defun. When called, kill the previous
git grep process and start a new one. The sentinel will insert the
candidates, bypassing the `ivy--exhibit'.
(counsel--gg-sentinel): New defun.
(counsel--gg-count): Rename from `counsel-git-grep-count'. When finished
computing, redisplay.

* ivy.el (ivy--exhibit): Don't expect dynamic collection to return the
  candidates as before. Just call it and expect it to redisplay the
  minibuffer.

counsel.el
ivy.el

index 174a1b98efd2e6725e1b16f6e0a769a18ceeb136..57bad3863163ed1b71ffa41d8a76364ce36d937c 100644 (file)
 (defvar counsel--git-grep-dir nil
   "Store the base git directory.")
 
-(defun counsel-git-grep-count (str)
-  "Quickly count the amount of git grep STR matches."
-  (let* ((default-directory counsel--git-grep-dir)
-         (out (shell-command-to-string
-               (format "git grep -i -c '%s' | sed 's/.*:\\(.*\\)/\\1/g' | awk '{s+=$1} END {print s}'"
-                       (ivy--regex str)))))
-    (string-to-number out)))
-
 (defvar counsel--git-grep-count nil
   "Store the line count in current repository.")
 
       (if (<= counsel--git-grep-count 20000)
           (progn
             (setq res (shell-command-to-string cmd))
-            (setq ivy--full-length nil))
-        (setq res (shell-command-to-string (concat cmd " | head -n 2000")))
-        (setq ivy--full-length (counsel-git-grep-count ivy-text)))
-      (split-string res "\n" t))))
+            (setq ivy--full-length nil)
+            (split-string res "\n" t))
+        (setq ivy--full-length -1)
+        (counsel--gg-candidates (ivy--regex string))
+        nil))))
 
 (defvar counsel-git-grep-map
   (let ((map (make-sparse-keymap)))
 (defun counsel-git-grep-recenter ()
   (interactive)
   (with-selected-window (ivy-state-window ivy-last)
-    (counsel-git-grep-action)
+    (counsel-git-grep-action ivy--current)
     (recenter-top-bottom)))
 
 (defun counsel-git-grep-action (x)
         (locate-dominating-file default-directory ".git"))
   (if (null counsel--git-grep-dir)
       (error "Not in a git repository")
-    (setq counsel--git-grep-count (counsel-git-grep-count ""))
+    (setq counsel--git-grep-count (counsel--gg-count "" t))
     (ivy-read "pattern: " 'counsel-git-grep-function
               :initial-input initial-input
               :matcher #'counsel-git-grep-matcher
@@ -421,6 +414,66 @@ The libraries are offered from `load-path'."
                          (get-text-property 0 'full-name x)))
               :keymap counsel-describe-map)))
 
+(defun counsel--gg-candidates (regex)
+  "Return git grep candidates for REGEX."
+  (counsel--gg-count regex)
+  (let* ((default-directory counsel--git-grep-dir)
+         (counsel-gg-process " *counsel-gg*")
+         (proc (get-process counsel-gg-process))
+         (buff (get-buffer counsel-gg-process)))
+    (when proc
+      (delete-process proc))
+    (when buff
+      (kill-buffer buff))
+    (setq proc (start-process-shell-command
+                counsel-gg-process
+                counsel-gg-process
+                (format "git --no-pager grep --full-name -n --no-color -i -e \"%s\" | head -n 200"
+                        regex)))
+    (set-process-sentinel
+     proc
+     #'counsel--gg-sentinel)))
+
+(defun counsel--gg-sentinel (process event)
+  (if (string= event "finished\n")
+      (progn
+        (with-current-buffer (process-buffer process)
+          (setq ivy--all-candidates (split-string (buffer-string) "\n" t))
+          (setq ivy--old-cands ivy--all-candidates))
+        (unless (eq ivy--full-length -1)
+          (ivy--insert-minibuffer
+           (ivy--format ivy--all-candidates))))
+    (if (string= event "exited abnormally with code 1\n")
+        (message "Error"))))
+
+(defun counsel--gg-count (regex &optional no-async)
+  "Quickly and asynchronously count the amount of git grep REGEX matches.
+When NO-ASYNC is non-nil, do it synchronously."
+  (let ((default-directory counsel--git-grep-dir)
+        (cmd (format "git grep -i -c '%s' | sed 's/.*:\\(.*\\)/\\1/g' | awk '{s+=$1} END {print s}'"
+                     regex))
+        (counsel-ggc-process " *counsel-gg-count*"))
+    (if no-async
+        (string-to-number (shell-command-to-string cmd))
+      (let ((proc (get-process counsel-ggc-process))
+            (buff (get-buffer counsel-ggc-process)))
+        (when proc
+          (delete-process proc))
+        (when buff
+          (kill-buffer buff))
+        (setq proc (start-process-shell-command
+                    counsel-ggc-process
+                    counsel-ggc-process
+                    cmd))
+        (set-process-sentinel
+         proc
+         #'(lambda (process event)
+             (when (string= event "finished\n")
+               (with-current-buffer (process-buffer process)
+                 (setq ivy--full-length (string-to-number (buffer-string))))
+               (ivy--insert-minibuffer
+                (ivy--format ivy--all-candidates)))))))))
+
 (provide 'counsel)
 
 ;;; counsel.el ends here
diff --git a/ivy.el b/ivy.el
index 2115555017c5ba37f76f4cd7978bd1cb993487fc..9ac491794a4d1a104083e5ee5de6a038a400da6d 100644 (file)
--- a/ivy.el
+++ b/ivy.el
@@ -1013,12 +1013,14 @@ Should be run via minibuffer `post-command-hook'."
       ;; while-no-input would cause annoying
       ;; "Waiting for process to die...done" message interruptions
       (let ((inhibit-message t))
-        (while-no-input
-          (unless (equal ivy--old-text ivy-text)
-            (cl-letf ((store (ivy-state-dynamic-collection ivy-last))
-                      ((ivy-state-dynamic-collection ivy-last) nil))
-              (setq ivy--all-candidates (funcall store ivy-text))))
-          (ivy--insert-minibuffer (ivy--format ivy--all-candidates))))
+        (unless (equal ivy--old-text ivy-text)
+          (while-no-input
+            ;; dynamic collection should take care of everything
+            (funcall (ivy-state-dynamic-collection ivy-last) ivy-text)
+            (setq ivy--old-text ivy-text)))
+        (unless (eq ivy--full-length -1)
+          (ivy--insert-minibuffer
+           (ivy--format ivy--all-candidates))))
     (cond (ivy--directory
            (if (string-match "/\\'" ivy-text)
                (if (member ivy-text ivy--all-candidates)
@@ -1041,8 +1043,8 @@ Should be run via minibuffer `post-command-hook'."
              (setq ivy--old-re nil))))
     (ivy--insert-minibuffer
      (ivy--format
-      (ivy--filter ivy-text ivy--all-candidates))))
-  (setq ivy--old-text ivy-text))
+      (ivy--filter ivy-text ivy--all-candidates)))
+    (setq ivy--old-text ivy-text)))
 
 (defun ivy--insert-minibuffer (text)
   "Insert TEXT into minibuffer with appropriate cleanup."