branch: elpa/helm
commit 6309dc2b2f3d884646e8fd3498f52327e97d0f4e
Author: Thierry Volpiatto <thie...@posteo.net>
Commit: Thierry Volpiatto <thie...@posteo.net>

    Add async support for helm-packages
---
 helm-packages.el | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 100 insertions(+), 8 deletions(-)

diff --git a/helm-packages.el b/helm-packages.el
index 79b0865a61..e200c74d0e 100644
--- a/helm-packages.el
+++ b/helm-packages.el
@@ -24,7 +24,13 @@
 (require 'finder)
 (require 'helm-utils) ; For with-helm-display-marked-candidates.
 
+(declare-function dired-async-mode-line-message "ext:dired-async.el")
+
 
+(defgroup helm-packages nil
+  "Helm interface for package.el."
+  :group 'helm)
+
 (defclass helm-packages-class (helm-source-in-buffer)
   ((coerce :initform #'helm-symbolify)
    (find-file-target :initform #'helm-packages-quit-an-find-file)
@@ -35,18 +41,91 @@
         (sort candidates #'helm-generic-sort-fn))))
    (update :initform #'helm-packages--refresh-contents))
   "A class to define `helm-packages' sources.")
+
+(defcustom helm-packages-async nil
+  "Install packages async when non nil."
+  :type 'boolean)
+
+
+;;; Async support
+;;
+(define-minor-mode helm-packages--async-modeline-mode
+    "Notify mode-line that an async process run."
+  :group 'dired-async
+  :global t
+  :lighter (:eval (propertize (format " [%s async job Installing package(s)]"
+                                      (length (dired-async-processes
+                                               'helm-async-pkg-install)))
+                              'face 'helm-delete-async-message))
+  (unless helm-packages--async-modeline-mode
+    (let ((visible-bell t)) (ding))))
+
+;; TODO: log compilation buffer.
+(defun helm-packages-do-async (action packages error-file)
+  "Execute ACTION asynchronously on PACKAGES.
+Argument ACTION can be one of \\='install, \\='upgrade, \\='reinstall.
+Argument PACKAGES is a list of packages (symbols).
+Argument ERROR-FILE is the file where errors are logged, if some."
+  (let ((fn (helm-acase action
+              (install 'package-install)
+              (upgrade 'package-upgrade)
+              (reinstall 'package-reinstall)))
+        (action-string (helm-acase action
+                         (install "Installing")
+                         (upgrade "Upgrading")
+                         (reinstall "Reinstalling"))))
+    (message "%s %s package(s)..." action-string (length packages))
+    (process-put
+     (async-start
+      `(lambda ()
+         (setq package-archives ',package-archives)
+         (package-initialize)
+         (condition-case err
+             (mapc ',fn ',packages)
+           (error
+            (with-temp-file ,error-file
+              (insert
+               (format
+                "%S:\n Please refresh package list before %s"
+                err action-string))))))
+      (lambda (result)
+        (if (file-exists-p error-file)
+            (progn (pop-to-buffer (find-file-noselect error-file))
+                   (delete-file error-file))
+          (when result
+            (setq package-selected-packages
+                  (append result package-selected-packages))
+            (helm-packages--async-modeline-mode -1)
+            (message "%s %s packages done" action-string (length packages))
+            (run-with-timer
+             0.1 nil
+             (lambda (lst)
+               (dired-async-mode-line-message
+                "%s %d package(s) done"
+                'helm-delete-async-message
+                action-string
+                (length lst)))
+             packages)))))
+     'helm-async-pkg-install t)
+    (helm-packages--async-modeline-mode 1)))
+
 
 ;;; Actions
 ;;
 ;;
 (defun helm-packages-upgrade (_candidate)
   "Helm action for upgrading marked packages."
-  (let ((mkd (helm-marked-candidates)))
+  (let ((mkd (helm-marked-candidates))
+        (error-file (expand-file-name
+                     "helm-packages-upgrade-error.txt"
+                     temporary-file-directory)))
     (with-helm-display-marked-candidates
       helm-marked-buffer-name
       (mapcar #'symbol-name mkd)
       (when (y-or-n-p (format "Upgrade %s packages? " (length mkd)))
-        (mapc #'package-upgrade mkd)))))
+        (if helm-packages-async
+            (helm-packages-do-async 'install mkd error-file)
+        (mapc #'package-upgrade mkd))))))
 
 (defun helm-packages-describe (candidate)
   "Helm action for describing package CANDIDATE."
@@ -66,12 +145,17 @@
 
 (defun helm-packages-package-reinstall (_candidate)
   "Helm action for reinstalling marked packages."
-  (let ((mkd (helm-marked-candidates)))
+  (let ((mkd (helm-marked-candidates))
+        (error-file (expand-file-name
+                     "helm-packages-reinstall-error.txt"
+                     temporary-file-directory)))
     (with-helm-display-marked-candidates
       helm-marked-buffer-name
       (mapcar #'symbol-name mkd)
       (when (y-or-n-p (format "Reinstall %s packages? " (length mkd)))
-        (mapc #'package-reinstall mkd)))))
+        (if helm-packages-async
+            (helm-packages-do-async 'reinstall mkd error-file)
+          (mapc #'package-reinstall mkd))))))
 
 (defun helm-packages-delete-1 (packages &optional force)
   "Run `package-delete' on PACKAGES.
@@ -111,16 +195,24 @@ as dependencies."
       (when (y-or-n-p (format "Recompile %s packages? " (length mkd)))
         (mapc #'package-recompile mkd)))))
 
+(defun helm-packages-install--sync (packages)
+  (condition-case err
+      (mapc #'package-install packages)
+    (error "%S:\n Please refresh package list before installing" err)))
+
 (defun helm-packages-install (_candidate)
   "Helm action for installing marked packages."
-  (let ((mkd (helm-marked-candidates)))
+  (let ((mkd (helm-marked-candidates))
+        (error-file (expand-file-name
+                     "helm-packages-install-error.txt"
+                     temporary-file-directory)))
     (with-helm-display-marked-candidates
       helm-marked-buffer-name
       (mapcar #'symbol-name mkd)
       (when (y-or-n-p (format "Install %s packages? " (length mkd)))
-        (condition-case err
-            (mapc #'package-install mkd)
-          (error "%S:\n Please refresh package list before installing" 
err))))))
+        (if helm-packages-async
+            (helm-packages-do-async 'install mkd error-file)
+          (helm-packages-install--sync mkd))))))
 
 (defun helm-packages-isolate-1 (packages)
     "Start an Emacs with only PACKAGES loaded.

Reply via email to