Permalink
Browse files

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
  • Loading branch information...
1 parent cf2c7bc commit 681738104cf709ffb1eb3209135713eea900552b @abo-abo committed Feb 1, 2016
Showing with 74 additions and 6 deletions.
  1. +7 −6 counsel.el
  2. +67 −0 ivy.el
View
@@ -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."
View
@@ -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.

0 comments on commit 6817381

Please sign in to comment.