branch: master commit 681738104cf709ffb1eb3209135713eea900552b Author: Oleh Krehel <ohwoeo...@gmail.com> Commit: Oleh Krehel <ohwoeo...@gmail.com>
Allow to compose static collections with `counsel--async-command' * ivy.el (ivy--sources-list): New defvar. (ivy-set-sources): New defun that sets `ivy--sources-list' (ivy--extra-candidates): New defvar. (ivy-read): Use `ivy--sources-list' to set `ivy--extra-candidates' - a list that composes itself with `ivy--all-candidates'. (ivy--set-candidates): New defun. Example - stack `recentf' on top of `counsel-locate': (defun small-test () (cl-subseq recentf-list 0 10)) (ivy-set-sources 'counsel-locate '((small-test) (original-source))) Here, (original-source) represents the async candidates of `counsel-locate'. All extra sources are static - each function is called once to generate a list of strings, which will be filtered later. The order matters, so you can have e.g.: (ivy-set-sources 'counsel-locate '((original-source) (small-test))) Fixes #373 --- counsel.el | 13 ++++++------ ivy.el | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/counsel.el b/counsel.el index 682e238..d80b5fb 100644 --- a/counsel.el +++ b/counsel.el @@ -598,9 +598,9 @@ Or the time of the last minibuffer update.") (if (string= event "finished\n") (progn (with-current-buffer (process-buffer process) - (setq ivy--all-candidates - (ivy--sort-maybe - (split-string (buffer-string) "\n" t))) + (ivy--set-candidates + (ivy--sort-maybe + (split-string (buffer-string) "\n" t))) (if (null ivy--old-cands) (setq ivy--index (or (ivy--preselect-index @@ -636,8 +636,8 @@ Update the minibuffer with the amount of lines collected every (with-current-buffer (process-buffer process) (goto-char (point-min)) (setq size (- (buffer-size) (forward-line (buffer-size)))) - (setq ivy--all-candidates - (split-string (buffer-string) "\n" t))) + (ivy--set-candidates + (split-string (buffer-string) "\n" t))) (let ((ivy--prompt (format (concat "%d++ " (ivy-state-prompt ivy-last)) size))) @@ -711,7 +711,8 @@ INITIAL-INPUT can be given as the initial minibuffer input." (with-ivy-window (when file (find-file file)))) - :unwind #'counsel-delete-process)) + :unwind #'counsel-delete-process + :caller 'counsel-locate)) (defun counsel--generic (completion-fn) "Complete thing at point with COMPLETION-FN." diff --git a/ivy.el b/ivy.el index f44dbdf..08719f3 100644 --- a/ivy.el +++ b/ivy.el @@ -167,6 +167,33 @@ Only \"./\" and \"../\" apply here. They appear in reverse order." (setq ivy--actions-list (plist-put ivy--actions-list cmd actions))) +(defvar ivy--sources-list nil + "A list of extra sources per command.") + +(defun ivy-set-sources (cmd sources) + "Attach to CMD a list of extra SOURCES. + +Each static source is a function that takes no argument and +returns a list of strings. + +The '(original-source) determines the position of the original +dynamic source. + +Extra dynamic sources aren't supported yet. + +Example: + + (defun small-recentf () + (cl-subseq recentf-list 0 20)) + + (ivy-set-sources + 'counsel-locate + '((small-recentf) + (original-source))) +" + (setq ivy--sources-list + (plist-put ivy--sources-list cmd sources))) + ;;* Keymap (require 'delsel) (defvar ivy-minibuffer-map @@ -278,6 +305,18 @@ Otherwise, store nil.") (defvar ivy--all-candidates nil "Store the candidates passed to `ivy-read'.") +(defvar ivy--extra-candidates '((original-source)) + "Store candidates added by the extra sources. + +This is an internal-use alist. Each key is a function name, or +original-source (which represents where the current dynamic +candidates should go). + +Each value is an evaluation of the function, in case of static +sources. These values will subsequently be filtered on `ivy-text'. + +This variable is set by `ivy-read' and used by `ivy--set-candidates'.") + (defvar ivy--default nil "Default initial input.") @@ -1138,6 +1177,20 @@ customizations apply to the current completion session." (cons 1 extra-actions)) (t (delete-dups (append action extra-actions))))))) + (let ((extra-sources (plist-get ivy--sources-list caller))) + (if extra-sources + (progn + (setq ivy--extra-candidates nil) + (dolist (source extra-sources) + (cond ((equal source '(original-source)) + (setq ivy--extra-candidates + (cons source ivy--extra-candidates))) + ((null (cdr source)) + (setq ivy--extra-candidates + (cons + (list (car source) (funcall (car source))) + ivy--extra-candidates)))))) + (setq ivy--extra-candidates '((original-source))))) (let ((recursive-ivy-last (and (active-minibuffer-window) ivy-last))) (setq ivy-last (make-ivy-state @@ -1941,6 +1994,20 @@ CANDIDATES are assumed to be static." ""))) (setq ivy--old-cands (ivy--sort name cands)))))) +(defun ivy--set-candidates (x) + "Update `ivy--all-candidates' with X." + (let (res) + (dolist (source ivy--extra-candidates) + (if (equal source '(original-source)) + (if (null res) + (setq res x) + (setq res (append x res))) + (setq ivy--old-re nil) + (setq res (append + (ivy--filter ivy-text (cadr source)) + res)))) + (setq ivy--all-candidates res))) + (defcustom ivy-sort-matches-functions-alist '((t . nil)) "An alist of functions used to sort the matching candidates.