branch: master commit f9df75e708149dd843e3ccc1f52ce3cd1d1ffa90 Author: Oleh Krehel <ohwoeo...@gmail.com> Commit: Oleh Krehel <ohwoeo...@gmail.com>
ivy.el (ivy-index-functions-alist): New variable * ivy.el (ivy-state): Add `caller' field. (ivy-read): Add `caller' keyword arg; update docstring. Use `ivy--recompute-index'. (ivy--recompute-index): New defun, dispatch according to `caller' and `ivy-index-functions-alist'. (ivy-recompute-index-swiper): New defun. (ivy-recompute-index-zero): New defun. Fixes #253 --- counsel.el | 3 +- ivy.el | 108 +++++++++++++++++++++++++++++++++++++---------------------- swiper.el | 6 ++- 3 files changed, 74 insertions(+), 43 deletions(-) diff --git a/counsel.el b/counsel.el index ce23179..800e907 100644 --- a/counsel.el +++ b/counsel.el @@ -425,7 +425,8 @@ INITIAL-INPUT can be given as the initial minibuffer input." :keymap counsel-git-grep-map :action #'counsel-git-grep-action :unwind #'swiper--cleanup - :history 'counsel-git-grep-history))) + :history 'counsel-git-grep-history + :caller 'counsel-git-grep))) (defcustom counsel-find-file-at-point nil "When non-nil, add file-at-point to the list of candidates." diff --git a/ivy.el b/ivy.el index a841a4b..232f228 100644 --- a/ivy.el +++ b/ivy.el @@ -184,7 +184,8 @@ Only \"./\" and \"../\" apply here. They appear in reverse order." re-builder matcher ;; When this is non-nil, call it for each input change to get new candidates - dynamic-collection) + dynamic-collection + caller) (defvar ivy-last nil "The last parameters passed to `ivy-read'. @@ -846,6 +847,16 @@ buffer order, like `org-refile' or `Man-goto-section'. The entry associated to t is used for all fall-through cases.") +(defvar ivy-index-functions-alist + '((swiper . ivy-recompute-index-swiper) + (swiper-multi . ivy-recompute-index-swiper) + (counsel-git-grep . ivy-recompute-index-swiper) + (t . ivy-recompute-index-zero)) + "An alist of index recomputing functions for each collection function. +When the input changes, calling the appropriate function will +return an integer - the index of the matched candidate that +should be selected.") + (defvar ivy-re-builders-alist '((t . ivy--regex-plus)) "An alist of regex building functions for each collection function. @@ -902,7 +913,7 @@ Directories come first." (cl-defun ivy-read (prompt collection &key predicate require-match initial-input history preselect keymap update-fn sort - action unwind re-builder matcher dynamic-collection) + action unwind re-builder matcher dynamic-collection caller) "Read a string in the minibuffer, with completion. PROMPT is a string to prompt with; normally it ends in a colon @@ -911,7 +922,8 @@ the current number of matching candidates. If % appears elsewhere in the PROMPT it should be quoted as %%. See also `ivy-count-format'. -COLLECTION is a list of strings. +COLLECTION is a list of strings, or a function, or an alist, or a +hash table. If INITIAL-INPUT is non-nil, insert it in the minibuffer initially. @@ -934,7 +946,11 @@ RE-BUILDER is a lambda that transforms text into a regex. MATCHER can completely override matching. DYNAMIC-COLLECTION is a function to call to update the list of -candidates with each input." +candidates with each input. + +CALLER is a symbol to uniquely identify the caller to `ivy-read'. +It's used in conjunction with COLLECTION to indentify which +customizations should apply to the current completion session." (let ((extra-actions (plist-get ivy--actions-list this-command))) (when extra-actions (setq action @@ -960,7 +976,8 @@ candidates with each input." :unwind unwind :re-builder re-builder :matcher matcher - :dynamic-collection dynamic-collection)) + :dynamic-collection dynamic-collection + :caller caller)) (ivy--reset-state ivy-last) (prog1 (unwind-protect @@ -970,9 +987,9 @@ candidates with each input." (minibuffer-completion-table collection) (minibuffer-completion-predicate predicate) (resize-mini-windows (cond - ((display-graphic-p) nil) - ((null resize-mini-windows) 'grow-only) - (t resize-mini-windows))) + ((display-graphic-p) nil) + ((null resize-mini-windows) 'grow-only) + (t resize-mini-windows))) (res (read-from-minibuffer prompt (ivy-state-initial-input ivy-last) @@ -1582,44 +1599,55 @@ CANDIDATES are assumed to be static." (let ((re-str (car re))) (lambda (x) (string-match re-str x))) res)))) - res)))) - (tail (nthcdr ivy--index ivy--old-cands)) - idx) - (if (eq (ivy-state-unwind ivy-last) 'swiper--cleanup) - (when (and tail ivy--old-cands (not (equal "^" ivy--old-re))) - (unless (and (not (equal re-str ivy--old-re)) - (or (setq ivy--index - (or - (cl-position (if (and (> (length re-str) 0) - (eq ?^ (aref re-str 0))) - (substring re-str 1) - re-str) cands - :test #'equal) - (and ivy--directory - (cl-position - (concat re-str "/") cands - :test #'equal)))))) - (while (and tail (null idx)) - ;; Compare with eq to handle equal duplicates in cands - (setq idx (cl-position (pop tail) cands))) - (setq ivy--index (or idx 0)))) - (setq ivy--index 0)) - (when (and (or (string= name "") - (string= name "^")) - (not (equal ivy--old-re ""))) - (setq ivy--index - (or (ivy--preselect-index - cands - nil - (ivy-state-preselect ivy-last) - nil) - ivy--index))) + res))))) + (ivy--recompute-index name re-str cands) (setq ivy--old-re (if cands re-str "")) (when (and (require 'flx nil 'noerror) (eq ivy--regex-function 'ivy--regex-fuzzy)) (setq cands (ivy--flx-sort name cands))) (setq ivy--old-cands cands))))) +(defun ivy--recompute-index (name re-str cands) + (let* ((caller (ivy-state-caller ivy-last)) + (func (or (and caller (cdr (assoc caller ivy-index-functions-alist))) + (cdr (assoc t ivy-index-functions-alist)) + #'ivy-recompute-index-zero))) + (funcall func re-str cands) + (when (and (or (string= name "") + (string= name "^")) + (not (equal ivy--old-re ""))) + (setq ivy--index + (or (ivy--preselect-index + cands + nil + (ivy-state-preselect ivy-last) + nil) + ivy--index))))) + +(defun ivy-recompute-index-swiper (re-str cands) + (let ((tail (nthcdr ivy--index ivy--old-cands)) + idx) + (when (and tail ivy--old-cands (not (equal "^" ivy--old-re))) + (unless (and (not (equal re-str ivy--old-re)) + (or (setq ivy--index + (or + (cl-position (if (and (> (length re-str) 0) + (eq ?^ (aref re-str 0))) + (substring re-str 1) + re-str) cands + :test #'equal) + (and ivy--directory + (cl-position + (concat re-str "/") cands + :test #'equal)))))) + (while (and tail (null idx)) + ;; Compare with eq to handle equal duplicates in cands + (setq idx (cl-position (pop tail) cands))) + (setq ivy--index (or idx 0)))))) + +(defun ivy-recompute-index-zero (_re-str _cands) + (setq ivy--index 0)) + (defun ivy--flx-sort (name cands) "Sort according to closeness to string NAME the string list CANDS." (condition-case nil diff --git a/swiper.el b/swiper.el index a7327c3..361769e 100644 --- a/swiper.el +++ b/swiper.el @@ -332,7 +332,8 @@ When non-nil, INITIAL-INPUT is the initial search pattern." :update-fn #'swiper--update-input-ivy :unwind #'swiper--cleanup :re-builder #'swiper--re-builder - :history 'swiper-history)) + :history 'swiper-history + :caller 'swiper)) (if (null ivy-exit) (goto-char swiper--opoint) (swiper--action res ivy-text))))) @@ -483,7 +484,8 @@ Run `swiper' for those buffers." :action 'swiper-multi-action-1) (ivy-read "Swiper: " swiper-multi-candidates :action 'swiper-multi-action-2 - :unwind #'swiper--cleanup)) + :unwind #'swiper--cleanup + :caller 'swiper-multi)) (defun swiper-multi-action-1 (x) (if (member x swiper-multi-buffers)