"Incremental vertical completion."
:group 'convenience)
+(defgroup ivy-faces nil
+ "Font-lock faces for `ivy'."
+ :group 'ivy)
+
(defface ivy-current-match
'((((class color) (background light))
:background "#1a4b77" :foreground "white")
(((class color) (background dark))
:background "#65a7e2" :foreground "black"))
- "Face used by Ivy for highlighting first match.")
+ "Face used by Ivy for highlighting the current match.")
+
+(defface ivy-minibuffer-match-face-1
+ '((((class color) (background light))
+ :background "#d3d3d3")
+ (((class color) (background dark))
+ :background "#555555"))
+ "The background face for `ivy' minibuffer matches.")
+
+(defface ivy-minibuffer-match-face-2
+ '((((class color) (background light))
+ :background "#e99ce8" :weight bold)
+ (((class color) (background dark))
+ :background "#777777" :weight bold))
+ "Face for `ivy' minibuffer matches modulo 1.")
+
+(defface ivy-minibuffer-match-face-3
+ '((((class color) (background light))
+ :background "#bbbbff" :weight bold)
+ (((class color) (background dark))
+ :background "#7777ff" :weight bold))
+ "Face for `ivy' minibuffer matches modulo 2.")
+
+(defface ivy-minibuffer-match-face-4
+ '((((class color) (background light))
+ :background "#ffbbff" :weight bold)
+ (((class color) (background dark))
+ :background "#8a498a" :weight bold))
+ "Face for `ivy' minibuffer matches modulo 3.")
(defface ivy-confirm-face
'((t :foreground "ForestGreen" :inherit minibuffer-prompt))
- "Face used by Ivy to issue a confirmation prompt.")
+ "Face used by Ivy for a confirmation prompt.")
(defface ivy-match-required-face
'((t :foreground "red" :inherit minibuffer-prompt))
- "Face used by Ivy to issue a match required prompt.")
+ "Face used by Ivy for a match required prompt.")
+
+(setcdr (assoc load-file-name custom-current-group-alist) 'ivy)
(defface ivy-subdir
'((t (:inherit 'dired-directory)))
'((t (:foreground "#110099")))
"Face used by Ivy for highlighting remotes in the alternatives.")
+(defface ivy-virtual
+ '((t :inherit font-lock-builtin-face))
+ "Face used by Ivy for matching virtual buffer names.")
+
(defcustom ivy-height 10
"Number of lines for the minibuffer window."
:type 'integer)
(defcustom ivy-count-format "%-4d "
- "The style of showing the current candidate count for `ivy-read'.
-Set this to \"\" if you don't want the count. You can also set
-it to e.g. \"(%d/%d) \" if you want to see both the candidate
-index and the candidate count."
+ "The style to use for displaying the current candidate count for `ivy-read'.
+Set this to \"\" to suppress the count visibility.
+Set this to \"(%d/%d) \" to display both the index and the count."
:type '(choice
(const :tag "Count disabled" "")
(const :tag "Count matches" "%-4d ")
string))
(defcustom ivy-wrap nil
- "Whether to wrap around after the first and last candidate."
+ "When non-nil, wrap around after the first and the last candidate."
:type 'boolean)
(defcustom ivy-display-style (unless (version< emacs-version "24.5") 'fancy)
"The style for formatting the minibuffer.
-By default, the matched strings will be copied as they are.
+By default, the matched strings are copied as is.
-With the fancy method, the matching parts of the regexp will be
-additionally highlighted, just like `swiper' does it.
+The fancy display style highlights matching parts of the regexp,
+a behavior similar to `swiper'.
This setting depends on `add-face-text-property' - a C function
-available as of 24.5. It will behave poorly in earlier Emacs
-versions."
+available as of Emacs 24.5. Fancy style will render poorly in
+earlier versions of Emacs."
:type '(choice
(const :tag "Plain" nil)
(const :tag "Fancy" fancy)))
(defcustom ivy-on-del-error-function 'minibuffer-keyboard-quit
"The handler for when `ivy-backward-delete-char' throws.
-This is usually meant as a quick exit out of the minibuffer."
+Usually a quick exit out of the minibuffer."
:type 'function)
(defcustom ivy-extra-directories '("../" "./")
"History list of candidates entered in the minibuffer.
Maximum length of the history list is determined by the value
-of `history-length', which see.")
+of `history-length'.")
(defvar ivy--directory nil
"Current directory when completing file names.")
(defvar ivy--prompt nil
"Store the format-style prompt.
-When non-nil, it should contain one %d.")
+When non-nil, it should contain at least one %d.")
(defvar ivy--prompt-extra ""
"Temporary modifications to the prompt.")
"Quit the minibuffer and call ACTION afterwards."
(ivy-set-action
`(lambda (x)
- (funcall ,action x)
+ (funcall ',action x)
(ivy-set-action ',(ivy-state-action ivy-last))))
(setq ivy-exit 'done)
(exit-minibuffer))
"Exit the minibuffer with the selected candidate.
When ARG is t, exit with current text, ignoring the candidates."
(interactive "P")
+ (cond (arg
+ (ivy-immediate-done))
+ (ivy--directory
+ (ivy--directory-done))
+ ((eq (ivy-state-collection ivy-last) 'Info-read-node-name-1)
+ (if (or (equal ivy--current "(./)")
+ (equal ivy--current "(../)"))
+ (ivy-quit-and-run
+ (ivy-read "Go to file: " 'read-file-name-internal
+ :action (lambda (x)
+ (Info-find-node
+ (expand-file-name x ivy--directory)
+ "Top"))))
+ (ivy-done)))
+ (t
+ (ivy-done))))
+
+(defun ivy--directory-done ()
+ "Handle exit from the minibuffer when completing file names."
(let (dir)
- (cond (arg
- (ivy-immediate-done))
- ((and ivy--directory
- (equal ivy-text "/sudo::"))
- (setq dir (concat ivy-text ivy--directory))
- (ivy--cd dir)
- (ivy--exhibit))
- ((and ivy--directory
- (or
- (and
- (not (equal ivy-text ""))
- (ignore-errors
- (file-directory-p
- (setq dir
- (file-name-as-directory
- (expand-file-name
- ivy-text ivy--directory))))))
- (and
- (not (string= ivy--current "./"))
- (cl-plusp ivy--length)
- (file-directory-p
- (setq dir (expand-file-name
- ivy--current ivy--directory))))))
- (ivy--cd dir)
- (ivy--exhibit))
- ((eq (ivy-state-collection ivy-last) 'Info-read-node-name-1)
- (if (or (equal ivy--current "(./)")
- (equal ivy--current "(../)"))
- (ivy-quit-and-run
- (ivy-read "Go to file: " 'read-file-name-internal
- :action (lambda (x)
- (Info-find-node
- (expand-file-name x ivy--directory)
- "Top"))))
- (ivy-done)))
- ((and ivy--directory
- (string-match "\\`/[^/]+:.*:.*\\'" ivy-text))
- (ivy-done))
- ((and ivy--directory
- (string-match
- "\\`/\\([^/]+?\\):\\(?:\\(.*\\)@\\)?\\(.*\\)\\'"
- ivy-text))
- (let ((method (match-string 1 ivy-text))
- (user (match-string 2 ivy-text))
- (rest (match-string 3 ivy-text))
- res)
- (require 'tramp)
- (dolist (x (tramp-get-completion-function method))
- (setq res (append res (funcall (car x) (cadr x)))))
- (setq res (delq nil res))
- (when user
- (dolist (x res)
- (setcar x user)))
- (setq res (cl-delete-duplicates res :test #'equal))
- (let* ((old-ivy-last ivy-last)
- (enable-recursive-minibuffers t)
- (host (ivy-read "Find File: "
- (mapcar #'ivy-build-tramp-name res)
- :initial-input rest)))
- (setq ivy-last old-ivy-last)
- (when host
- (setq ivy--directory "/")
- (ivy--cd (concat "/" method ":" host ":"))))))
- (t
- (ivy-done)))))
+ (cond
+ ((equal ivy-text "/sudo::")
+ (setq dir (concat ivy-text ivy--directory))
+ (ivy--cd dir)
+ (ivy--exhibit))
+ ((or
+ (and
+ (not (equal ivy-text ""))
+ (ignore-errors
+ (file-directory-p
+ (setq dir
+ (file-name-as-directory
+ (expand-file-name
+ ivy-text ivy--directory))))))
+ (and
+ (not (string= ivy--current "./"))
+ (cl-plusp ivy--length)
+ (ignore-errors
+ (file-directory-p
+ (setq dir (file-name-as-directory
+ (expand-file-name
+ ivy--current ivy--directory)))))))
+ (ivy--cd dir)
+ (ivy--exhibit))
+ ((or (and (equal ivy--directory "/")
+ (string-match "\\`[^/]+:.*:.*\\'" ivy-text))
+ (string-match "\\`/[^/]+:.*:.*\\'" ivy-text))
+ (ivy-done))
+ ((or (and (equal ivy--directory "/")
+ (cond ((string-match
+ "\\`\\([^/]+?\\):\\(?:\\(.*\\)@\\)?\\(.*\\)\\'"
+ ivy-text))
+ ((string-match
+ "\\`\\([^/]+?\\):\\(?:\\(.*\\)@\\)?\\(.*\\)\\'"
+ ivy--current)
+ (setq ivy-text ivy--current))))
+ (string-match
+ "\\`/\\([^/]+?\\):\\(?:\\(.*\\)@\\)?\\(.*\\)\\'"
+ ivy-text))
+ (let ((method (match-string 1 ivy-text))
+ (user (match-string 2 ivy-text))
+ (rest (match-string 3 ivy-text))
+ res)
+ (require 'tramp)
+ (dolist (x (tramp-get-completion-function method))
+ (setq res (append res (funcall (car x) (cadr x)))))
+ (setq res (delq nil res))
+ (when user
+ (dolist (x res)
+ (setcar x user)))
+ (setq res (cl-delete-duplicates res :test #'equal))
+ (let* ((old-ivy-last ivy-last)
+ (enable-recursive-minibuffers t)
+ (host (ivy-read "user@host: "
+ (mapcar #'ivy-build-tramp-name res)
+ :initial-input rest)))
+ (setq ivy-last old-ivy-last)
+ (when host
+ (setq ivy--directory "/")
+ (ivy--cd (concat "/" method ":" host ":"))))))
+ (t
+ (ivy-done)))))
(defcustom ivy-tab-space nil
"When non-nil, `ivy-partial-or-done' should insert a space."
If the text hasn't changed as a result, forward to `ivy-alt-done'."
(interactive)
(if (and (eq (ivy-state-collection ivy-last) #'read-file-name-internal)
- (string-match "\\`/" ivy-text))
+ (or (and (equal ivy--directory "/")
+ (string-match "\\`[^/]+:.*\\'" ivy-text))
+ (string-match "\\`/" ivy-text)))
(let ((default-directory ivy--directory))
(minibuffer-complete)
(setq ivy-text (ivy--input))
- (when (and (file-directory-p ivy-text)
- (= ivy--length 1))
- (ivy--cd (expand-file-name ivy-text))))
+ (when (file-directory-p
+ (expand-file-name ivy-text ivy--directory))
+ (ivy--cd (file-name-as-directory
+ (expand-file-name ivy-text ivy--directory)))))
(or (ivy-partial)
(when (or (eq this-command last-command)
(eq ivy--length 1))
(defun ivy-resume ()
"Resume the last completion session."
(interactive)
+ (when (eq (ivy-state-caller ivy-last) 'swiper)
+ (switch-to-buffer (ivy-state-buffer ivy-last)))
(with-current-buffer (ivy-state-buffer ivy-last)
(ivy-read
(ivy-state-prompt ivy-last)
(defun ivy--get-window (state)
"Get the window from STATE."
- (let ((window (ivy-state-window state)))
- (if (window-live-p window)
- window
- (if (= (length (window-list)) 1)
- (selected-window)
- (next-window)))))
+ (if (ivy-state-p state)
+ (let ((window (ivy-state-window state)))
+ (if (window-live-p window)
+ window
+ (if (= (length (window-list)) 1)
+ (selected-window)
+ (next-window))))
+ (selected-window)))
(defun ivy--actionp (x)
"Return non-nil when X is a list of actions."
(let ((input (ivy--input))
url)
(if (setq url (ffap-url-p input))
- (progn
- (ivy-set-action
- (lambda (_)
- (funcall ffap-url-fetcher url)))
- (setq ivy-exit 'done)
- (exit-minibuffer))
+ (ivy-exit-with-action
+ (lambda (_)
+ (funcall ffap-url-fetcher url)))
(setq input (expand-file-name input))
(let ((file (file-name-nondirectory input))
(dir (expand-file-name (file-name-directory input))))
"An alist of sorting functions for each collection function.
Interactive functions that call completion fit in here as well.
-For each entry, nil means no sorting. It's very useful to turn
-off the sorting for functions that have candidates in the natural
-buffer order, like `org-refile' or `Man-goto-section'.
+Nil means no sorting, which is useful to turn off the sorting for
+functions that have candidates in the natural buffer order, like
+`org-refile' or `Man-goto-section'.
+
+The entry associated with t is used for all fall-through cases.
-The entry associated to t is used for all fall-through cases."
+See also `ivy-sort-max-size'."
:type
'(alist
:key-type (choice
'((swiper . ivy-recompute-index-swiper)
(swiper-multi . ivy-recompute-index-swiper)
(counsel-git-grep . ivy-recompute-index-swiper)
+ (counsel-grep . ivy-recompute-index-swiper-async)
(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.")
+When the input changes, the appropriate function returns 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.
-Each function should take a string and return a valid regex or a
-regex sequence (see below).
-The entry associated to t is used for all fall-through cases.
-Possible choices: `ivy--regex', `regexp-quote', `ivy--regex-plus'.
+Each key is (in order of priority):
+1. The actual collection function, e.g. `read-file-name-internal'.
+2. The symbol passed by :caller into `ivy-read'.
+3. `this-command'.
+4. t.
+
+Each value is a function that should take a string and return a
+valid regex or a regex sequence (see below).
+
+Possible choices: `ivy--regex', `regexp-quote',
+`ivy--regex-plus', `ivy--regex-fuzzy'.
-In case a function returns a list, it should look like this:
+If a function returns a list, it should format like this:
'((\"matching-regexp\" . t) (\"non-matching-regexp\") ...).
The matches will be filtered in a sequence, you can mix the
(push dir seq))
seq)))
+(defvar ivy-recursive-restore t
+ "When non-nil, restore the above state when exiting the minibuffer.
+This variable is let-bound to nil by functions that take care of
+the restoring themselves.")
+
;;** Entry Point
(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 caller)
+ &key predicate require-match initial-input
+ history preselect keymap update-fn sort
+ 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
-and a space. When PROMPT contains %d, it will be updated with
-the current number of matching candidates. If % appears elsewhere
-in the PROMPT it should be quoted as %%.
-See also `ivy-count-format'.
+PROMPT is a format string, normally ending in a colon and a
+space; %d anywhere in the string is replaced by the current
+number of matching candidates. For the literal % character,
+escape it with %%. See also `ivy-count-format'.
-COLLECTION is a list of strings, or a function, or an alist, or a
-hash table.
+COLLECTION is either a list of strings, a function, an alist, or
+a hash table.
-If INITIAL-INPUT is non-nil, insert it in the minibuffer initially.
+If INITIAL-INPUT is not nil, then insert that input in the
+minibuffer initially.
-KEYMAP is composed together with `ivy-minibuffer-map'.
+KEYMAP is composed with `ivy-minibuffer-map'.
-If PRESELECT is non-nil select the corresponding candidate out of
-the ones that match INITIAL-INPUT.
+If PRESELECT is not nil, then select the corresponding candidate
+out of the ones that match the INITIAL-INPUT.
UPDATE-FN is called each time the current candidate(s) is changed.
-When SORT is t, refer to `ivy-sort-functions-alist' for sorting.
+When SORT is t, use `ivy-sort-functions-alist' for sorting.
-ACTION is a lambda to call after a result was selected. It should
-take a single argument, usually a string.
+ACTION is a lambda function to call after selecting a result. It
+takes a single string argument.
-UNWIND is a lambda to call before exiting.
+UNWIND is a lambda function to call before exiting.
-RE-BUILDER is a lambda that transforms text into a regex.
+RE-BUILDER is a lambda function to call to transform text into a
+regex pattern.
-MATCHER can completely override matching.
+MATCHER is to override matching.
-DYNAMIC-COLLECTION is a boolean that determines whether to update
-the list of candidates with each input by calling COLLECTION for
-the current input.
+DYNAMIC-COLLECTION is a boolean to specify if the list of
+candidates is updated after each input by calling COLLECTION.
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)))
+It is used, along with COLLECTION, to determine which
+customizations apply to the current completion session."
+ (let ((extra-actions (append (plist-get ivy--actions-list t)
+ (plist-get ivy--actions-list this-command))))
(when extra-actions
(setq action
(if (functionp action)
("o" ,action "default")
,@extra-actions)
(delete-dups (append action extra-actions))))))
- (let ((recursive-ivy-last (and (window-minibuffer-p) ivy-last)))
+ (let ((recursive-ivy-last (and (active-minibuffer-window) ivy-last)))
(setq ivy-last
(make-ivy-state
:prompt prompt
(ivy--reset-state ivy-last)
(prog1
(unwind-protect
- (minibuffer-with-setup-hook
- #'ivy--minibuffer-setup
- (let* ((hist (or history 'ivy-history))
- (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)))
- (res (read-from-minibuffer
- prompt
- (ivy-state-initial-input ivy-last)
- (make-composed-keymap keymap ivy-minibuffer-map)
- nil
- hist)))
- (when (eq ivy-exit 'done)
- (let ((item (if ivy--directory
- ivy--current
- ivy-text)))
- (unless (equal item "")
- (set hist (cons (propertize item 'ivy-index ivy--index)
- (delete item
- (cdr (symbol-value hist)))))))
- res)))
+ (minibuffer-with-setup-hook
+ #'ivy--minibuffer-setup
+ (let* ((hist (or history 'ivy-history))
+ (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))))
+ (read-from-minibuffer
+ prompt
+ (ivy-state-initial-input ivy-last)
+ (make-composed-keymap keymap ivy-minibuffer-map)
+ nil
+ hist)
+ (when (eq ivy-exit 'done)
+ (let ((item (if ivy--directory
+ ivy--current
+ ivy-text)))
+ (unless (equal item "")
+ (set hist (cons (propertize item 'ivy-index ivy--index)
+ (delete item
+ (cdr (symbol-value hist))))))))
+ ivy--current))
(remove-hook 'post-command-hook #'ivy--exhibit)
(when (setq unwind (ivy-state-unwind ivy-last))
- (funcall unwind)))
+ (funcall unwind))
+ (unless (eq ivy-exit 'done)
+ (when recursive-ivy-last
+ (ivy--reset-state (setq ivy-last recursive-ivy-last)))))
(ivy-call)
- (when recursive-ivy-last
+ (when (and recursive-ivy-last
+ ivy-recursive-restore)
(ivy--reset-state (setq ivy-last recursive-ivy-last))))))
(defun ivy--reset-state (state)
"Reset the ivy to STATE.
This is useful for recursive `ivy-read'."
- (let ((prompt (ivy-state-prompt state))
+ (let ((prompt (or (ivy-state-prompt state) ""))
(collection (ivy-state-collection state))
(predicate (ivy-state-predicate state))
(history (ivy-state-history state))
(dynamic-collection (ivy-state-dynamic-collection state))
(initial-input (ivy-state-initial-input state))
(require-match (ivy-state-require-match state))
- (matcher (ivy-state-matcher state)))
+ (caller (ivy-state-caller state)))
(unless initial-input
(setq initial-input (cdr (assoc this-command
ivy-initial-inputs-alist))))
(or re-builder
(and (functionp collection)
(cdr (assoc collection ivy-re-builders-alist)))
+ (and caller
+ (cdr (assoc caller ivy-re-builders-alist)))
+ (cdr (assoc this-command ivy-re-builders-alist))
(cdr (assoc t ivy-re-builders-alist))
'ivy--regex))
(setq ivy--subexps 0)
(equal initial-input default-directory)
(equal initial-input ""))
(setq coll (cons initial-input coll)))
- (setq initial-input nil)))
+ (unless (ivy-state-action ivy-last)
+ (setq initial-input nil))))
((eq collection 'internal-complete-buffer)
(setq coll (ivy--buffer-list "" ivy-use-virtual-buffers)))
((or (functionp collection)
(when preselect
(unless (or (and require-match
(not (eq collection 'internal-complete-buffer)))
+ dynamic-collection
(let ((re (regexp-quote preselect)))
(cl-find-if (lambda (x) (string-match re x))
coll)))
(setq coll (cons preselect coll))))
- (setq ivy--index (or
- (and dynamic-collection
- ivy--index)
- (and preselect
- (ivy--preselect-index
- coll initial-input preselect matcher))
- 0))
(setq ivy--old-re nil)
(setq ivy--old-cands nil)
- (setq ivy--all-candidates coll))
+ (when (integerp preselect)
+ (setq ivy--old-re "")
+ (setq ivy--index preselect))
+ (when initial-input
+ ;; Needed for anchor to work
+ (setq ivy--old-cands coll)
+ (setq ivy--old-cands (ivy--filter initial-input coll)))
+ (setq ivy--all-candidates coll)
+ (unless (integerp preselect)
+ (setq ivy--index (or
+ (and dynamic-collection
+ ivy--index)
+ (and preselect
+ (ivy--preselect-index
+ preselect
+ (if initial-input
+ ivy--old-cands
+ coll)))
+ 0))))
(setq ivy-exit nil)
(setq ivy--default (or
(thing-at-point 'url)
;;;###autoload
(defun ivy-completing-read (prompt collection
&optional predicate require-match initial-input
- history def _inherit-input-method)
+ history def inherit-input-method)
"Read a string in the minibuffer, with completion.
-This is an interface that conforms to `completing-read', so that
-it can be used for `completing-read-function'.
+This interface conforms to `completing-read' and can be used for
+`completing-read-function'.
PROMPT is a string to prompt with; normally it ends in a colon and a space.
COLLECTION can be a list of strings, an alist, an obarray or a hash table.
PREDICATE limits completion to a subset of COLLECTION.
-REQUIRE-MATCH is considered boolean. See `completing-read'.
+REQUIRE-MATCH is specified with a boolean value. See `completing-read'.
INITIAL-INPUT is a string that can be inserted into the minibuffer initially.
-_HISTORY is ignored for now.
+HISTORY is a list of previously selected inputs.
DEF is the default value.
-_INHERIT-INPUT-METHOD is ignored for now.
-
-The history, defaults and input-method arguments are ignored for now."
- (ivy-read (replace-regexp-in-string "%" "%%" prompt)
- collection
- :predicate predicate
- :require-match require-match
- :initial-input (if (consp initial-input)
- (car initial-input)
- (if (and (stringp initial-input)
- (string-match "\\+" initial-input))
- (replace-regexp-in-string
- "\\+" "\\\\+" initial-input)
- initial-input))
- :preselect (if (listp def) (car def) def)
- :history history
- :keymap nil
- :sort
- (let ((sort (assoc this-command ivy-sort-functions-alist)))
- (if sort
- (cdr sort)
- t))))
+INHERIT-INPUT-METHOD is currently ignored."
+ (if (memq this-command '(tmm-menubar tmm-shortcut))
+ (completing-read-default prompt collection
+ predicate require-match
+ initial-input history
+ def inherit-input-method)
+ ;; See the doc of `completing-read'.
+ (when (consp history)
+ (when (numberp (cdr history))
+ (setq initial-input (nth (1- (cdr history))
+ (symbol-value (car history)))))
+ (setq history (car history)))
+ (ivy-read (replace-regexp-in-string "%" "%%" prompt)
+ collection
+ :predicate predicate
+ :require-match require-match
+ :initial-input (if (consp initial-input)
+ (car initial-input)
+ (if (and (stringp initial-input)
+ (string-match "\\+" initial-input))
+ (replace-regexp-in-string
+ "\\+" "\\\\+" initial-input)
+ initial-input))
+ :preselect (if (listp def) (car def) def)
+ :history history
+ :keymap nil
+ :sort
+ (let ((sort (assoc this-command ivy-sort-functions-alist)))
+ (if sort
+ (cdr sort)
+ t)))))
+
+(defvar ivy-completion-beg nil
+ "Completion bounds start.")
+
+(defvar ivy-completion-end nil
+ "Completion bounds end.")
+
+(defun ivy-completion-in-region-action (str)
+ "Insert STR, erasing the previous one.
+The previous string is between `ivy-completion-beg' and `ivy-completion-end'."
+ (when (stringp str)
+ (with-ivy-window
+ (when ivy-completion-beg
+ (delete-region
+ ivy-completion-beg
+ ivy-completion-end))
+ (setq ivy-completion-beg
+ (move-marker (make-marker) (point)))
+ (insert str)
+ (setq ivy-completion-end
+ (move-marker (make-marker) (point))))))
+
+(defun ivy-completion-common-length (str)
+ "Return the length of the first 'completions-common-part face in STR."
+ (let ((pos 0)
+ (len (length str)))
+ (while (and (<= pos len)
+ (let ((prop (get-text-property pos 'face str)))
+ (not (eq 'completions-common-part
+ (if (listp prop) (car prop) prop)))))
+ (setq pos (1+ pos)))
+ (if (< pos len)
+ (or (next-single-property-change pos 'face str) len)
+ 0)))
+
+(defun ivy-completion-in-region (start end collection &optional predicate)
+ "An Ivy function suitable for `completion-in-region-function'."
+ (let* ((enable-recursive-minibuffers t)
+ (str (buffer-substring-no-properties start end))
+ (comps
+ (completion-all-completions str collection predicate (- end start))))
+ (if (null comps)
+ (message "No matches")
+ (nconc comps nil)
+ (setq ivy-completion-beg (- end (ivy-completion-common-length (car comps))))
+ (setq ivy-completion-end end)
+ (if (null (cdr comps))
+ (progn
+ (setf (ivy-state-window ivy-last) (selected-window))
+ (ivy-completion-in-region-action
+ (substring-no-properties
+ (car comps))))
+ (let* ((w (1+ (floor (log (length comps) 10))))
+ (ivy-count-format (and ivy-count-format
+ (format "%%-%dd " w))))
+ (and
+ (ivy-read (format "(%s): " str)
+ ;; remove 'completions-first-difference face
+ (mapcar #'substring-no-properties comps)
+ :predicate predicate
+ :action #'ivy-completion-in-region-action
+ :require-match t)
+ t))))))
;;;###autoload
(define-minor-mode ivy-mode
"Toggle Ivy mode on or off.
-With ARG, turn Ivy mode on if arg is positive, off otherwise.
-Turning on Ivy mode will set `completing-read-function' to
+Turn Ivy mode on if ARG is positive, off otherwise.
+Turning on Ivy mode sets `completing-read-function' to
`ivy-completing-read'.
Global bindings:
:keymap ivy-mode-map
:lighter " ivy"
(if ivy-mode
- (setq completing-read-function 'ivy-completing-read)
- (setq completing-read-function 'completing-read-default)))
-
-(defun ivy--preselect-index (candidates initial-input preselect matcher)
- "Return the index in CANDIDATES filtered by INITIAL-INPUT for PRESELECT.
-When MATCHER is non-nil it's used instead of `cl-remove-if-not'."
- (if initial-input
(progn
- (setq initial-input (ivy--regex-plus initial-input))
- (setq candidates
- (if matcher
- (funcall matcher initial-input candidates)
- (cl-remove-if-not
- (lambda (x)
- (string-match initial-input x))
- candidates))))
- (when matcher
- (setq candidates (funcall matcher "" candidates))))
- (or (cl-position preselect candidates :test #'equal)
- (and (stringp preselect)
- (let ((re (regexp-quote preselect)))
- (cl-position-if
- (lambda (x)
- (string-match re x))
- candidates)))))
+ (setq completing-read-function 'ivy-completing-read)
+ (setq completion-in-region-function 'ivy-completion-in-region))
+ (setq completing-read-function 'completing-read-default)
+ (setq completion-in-region-function 'completion--in-region)))
+
+(defun ivy--preselect-index (preselect candidates)
+ "Return the index of PRESELECT in CANDIDATES."
+ (cond ((integerp preselect)
+ preselect)
+ ((cl-position preselect candidates :test #'equal))
+ ((stringp preselect)
+ (let ((re (regexp-quote preselect)))
+ (cl-position-if
+ (lambda (x)
+ (string-match re x))
+ candidates)))))
;;* Implementation
;;** Regex
(nreverse res)))
(defun ivy--regex (str &optional greedy)
- "Re-build regex from STR in case it has a space.
+ "Re-build regex pattern from STR in case it has a space.
When GREEDY is non-nil, join words in a greedy way."
(let ((hashed (unless greedy
(gethash str ivy--regex-hash))))
".*?")))))
ivy--regex-hash)))))
-(defun ivy--regex-ignore-order (str)
- "Re-build regex from STR by splitting it on spaces.
+(defun ivy--regex-ignore-order--part (str &optional discard)
+ "Re-build regex from STR by splitting at spaces.
Ignore the order of each group."
(let* ((subs (split-string str " +" t))
(len (length subs)))
(cl-case len
- (1
- (setq ivy--subexps 0)
- (car subs))
+ (0
+ "")
(t
- (setq ivy--subexps len)
- (let ((all (mapconcat #'identity subs "\\|")))
- (mapconcat
- (lambda (x)
- (if (string-match "\\`\\\\(.*\\\\)\\'" x)
- x
- (format "\\(%s\\)" x)))
- (make-list len all)
- ".*?"))))))
+ (mapcar (lambda (x) (cons x (not discard)))
+ subs)))))
+
+(defun ivy--regex-ignore-order (str)
+ "Re-build regex from STR by splitting at spaces.
+Ignore the order of each group. Everything before \"!\" should
+match. Everything after \"!\" should not match."
+ (let ((parts (split-string str "!" t)))
+ (cl-case (length parts)
+ (0
+ "")
+ (1
+ (if (string= (substring str 0 1) "!")
+ (list (cons "" t)
+ (ivy--regex-ignore-order--part (car parts) t))
+ (ivy--regex-ignore-order--part (car parts))))
+ (2
+ (append
+ (ivy--regex-ignore-order--part (car parts))
+ (ivy--regex-ignore-order--part (cadr parts) t)))
+ (t (error "Unexpected: use only one !")))))
(defun ivy--regex-plus (str)
"Build a regex sequence from STR.
-Spaces are wild, everything before \"!\" should match.
-Everything after \"!\" should not match."
+Spaces are wild card characters, everything before \"!\" should
+match. Everything after \"!\" should not match."
(let ((parts (split-string str "!" t)))
(cl-case (length parts)
(0
"")
(1
- (ivy--regex (car parts)))
+ (if (string= (substring str 0 1) "!")
+ (list (cons "" t)
+ (list (ivy--regex (car parts))))
+ (ivy--regex (car parts))))
(2
- (let ((res
- (mapcar #'list
- (split-string (cadr parts) " " t))))
- (cons (cons (ivy--regex (car parts)) t)
- res)))
+ (cons
+ (cons (ivy--regex (car parts)) t)
+ (mapcar #'list (split-string (cadr parts) " " t))))
(t (error "Unexpected: use only one !")))))
(defun ivy--regex-fuzzy (str)
"Update the prompt according to `ivy--prompt'."
(when ivy--prompt
(unless (memq this-command '(ivy-done ivy-alt-done ivy-partial-or-done
- counsel-find-symbol))
+ counsel-find-symbol))
(setq ivy--prompt-extra ""))
(let (head tail)
(if (string-match "\\(.*\\): \\'" ivy--prompt)
(constrain-to-field nil (point-max))))))
(defun ivy--set-match-props (str match props)
- "Set STR text proprties that match MATCH to PROPS."
+ "Set STR text properties that match MATCH to PROPS."
(when (string-match match str)
(set-text-properties
(match-beginning 0)
(ivy--buffer-list "" ivy-use-virtual-buffers)))
(setq ivy--old-re nil))))
(ivy--insert-minibuffer
- (ivy--format
- (ivy--filter ivy-text ivy--all-candidates)))
+ (with-current-buffer (ivy-state-buffer ivy-last)
+ (ivy--format
+ (ivy--filter ivy-text ivy--all-candidates))))
(setq ivy--old-text ivy-text))))
(defun ivy--insert-minibuffer (text)
(ivy--resize-minibuffer-to-fit))))
(defun ivy--resize-minibuffer-to-fit ()
- "Resize the minibuffer window so it has enough space to display
-all of the text contained in the minibuffer."
+ "Resize the minibuffer window size to fit the text in the minibuffer."
(with-selected-window (minibuffer-window)
(if (fboundp 'window-text-pixel-size)
(let ((text-height (cdr (window-text-pixel-size)))
(body-height (window-body-height nil t)))
(when (> text-height body-height)
- (window-resize nil (- text-height body-height) nil t t)))
- (let ((text-height (count-screen-lines))
- (body-height (window-body-height)))
- (when (> text-height body-height)
- (window-resize nil (- text-height body-height) nil t))))))
+ ;; Note: the size increment needs to be at least frame-char-height,
+ ;; otherwise resizing won't do anything.
+ (let ((delta (max (- text-height body-height) (frame-char-height))))
+ (window-resize nil delta nil t t))))
+ (let ((text-height (count-screen-lines))
+ (body-height (window-body-height)))
+ (when (> text-height body-height)
+ (window-resize nil (- text-height body-height) nil t))))))
(declare-function colir-blend-face-background "ext:colir")
In any completion session, the case folding starts in auto:
- when the input is all lower case, `case-fold-search' is t
-- otherwise it's nil.
+- otherwise nil.
You can toggle this to make `case-fold-search' nil regardless of input."
(interactive)
;; reset cache so that the candidate list updates
(setq ivy--old-re nil))
+(defun ivy--re-filter (re candidates)
+ "Return all RE matching CANDIDATES.
+RE is a list of cons cells, with a regexp car and a boolean cdr.
+When the cdr is t, the car must match.
+Otherwise, the car must not match."
+ (let ((re-list (if (stringp re) (list (cons re t)) re))
+ (res candidates))
+ (dolist (re re-list)
+ (setq res
+ (ignore-errors
+ (funcall
+ (if (cdr re)
+ #'cl-remove-if-not
+ #'cl-remove-if)
+ (let ((re-str (car re)))
+ (lambda (x) (string-match re-str x)))
+ res))))
+ res))
+
(defun ivy--filter (name candidates)
"Return all items that match NAME in CANDIDATES.
CANDIDATES are assumed to be static."
(lambda (x) (string-match re x))
ivy--old-cands)))
(t
- (let ((re-list (if (stringp re) (list (cons re t)) re))
- (res candidates))
- (dolist (re re-list)
- (setq res
- (ignore-errors
- (funcall
- (if (cdr re)
- #'cl-remove-if-not
- #'cl-remove-if)
- (let ((re-str (car re)))
- (lambda (x) (string-match re-str x)))
- res))))
- res)))))
+ (ivy--re-filter re candidates)))))
(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)))))
+ (setq ivy--old-re
+ (if (eq ivy--regex-function 'ivy--regex-ignore-order)
+ re
+ (if cands
+ re-str
+ "")))
+ (setq ivy--old-cands (ivy--sort name cands))))))
+
+(defcustom ivy-sort-matches-functions-alist '((t . nil))
+ "An alist of functions used to sort the matching candidates.
+
+This is different from `ivy-sort-functions-alist', which is used
+to sort the whole collection only once. The functions taken from
+here are instead used on each input change, but they are used
+only on already matching candidates, not on all of them.
+
+The alist KEY is a collection function or t to match previously
+not matched collection functions.
+
+The alist VAL is a sorting function with the signature of
+`ivy--prefix-sort'.")
+
+(defun ivy--sort-files-by-date (_name candidates)
+ "Re-soft CANDIDATES according to file modification date."
+ (let ((default-directory ivy--directory))
+ (cl-sort (copy-sequence candidates)
+ (lambda (f1 f2)
+ (time-less-p
+ (nth 5 (file-attributes f2))
+ (nth 5 (file-attributes f1)))))))
+
+(defun ivy--sort (name candidates)
+ "Re-sort CANDIDATES by NAME.
+All CANDIDATES are assumed to match NAME."
+ (let ((key (or (ivy-state-caller ivy-last)
+ (when (functionp (ivy-state-collection ivy-last))
+ (ivy-state-collection ivy-last))))
+ fun)
+ (cond ((and (require 'flx nil 'noerror)
+ (eq ivy--regex-function 'ivy--regex-fuzzy))
+ (ivy--flx-sort name candidates))
+ ((setq fun (cdr (or (assoc key ivy-sort-matches-functions-alist)
+ (assoc t ivy-sort-matches-functions-alist))))
+ (funcall fun name candidates))
+ (t
+ candidates))))
+
+(defun ivy--prefix-sort (name candidates)
+ "Re-sort CANDIDATES.
+Prefix matches to NAME are put ahead of the list."
+ (if (or (string-match "^\\^" name) (string= name ""))
+ candidates
+ (let ((re-prefix (concat "^" (funcall ivy--regex-function name)))
+ res-prefix
+ res-noprefix)
+ (dolist (s candidates)
+ (if (string-match re-prefix s)
+ (push s res-prefix)
+ (push s res-noprefix)))
+ (nconc
+ (nreverse res-prefix)
+ (nreverse res-noprefix)))))
(defun ivy--recompute-index (name re-str cands)
(let* ((caller (ivy-state-caller ivy-last))
(not (equal ivy--old-re "")))
(setq ivy--index
(or (ivy--preselect-index
- cands
- nil
(ivy-state-preselect ivy-last)
- nil)
+ cands)
ivy--index)))))
(defun ivy-recompute-index-swiper (_re-str cands)
;; Compare with eq to handle equal duplicates in cands
(setq idx (cl-position (pop tail) cands)))
(or idx 0))
+ (if ivy--old-cands
+ ivy--index
+ ;; already in ivy-state-buffer
+ (let ((n (line-number-at-pos))
+ (res 0)
+ (i 0))
+ (dolist (c cands)
+ (when (eq n (read (get-text-property 0 'display c)))
+ (setq res i))
+ (cl-incf i))
+ res)))))
+
+(defun ivy-recompute-index-swiper-async (_re-str cands)
+ (let ((tail (nthcdr ivy--index ivy--old-cands))
+ idx)
+ (if (and tail ivy--old-cands (not (equal "^" ivy--old-re)))
+ (progn
+ (while (and tail (null idx))
+ ;; Compare with `equal', since the collection is re-created
+ ;; each time with `split-string'
+ (setq idx (cl-position (pop tail) cands :test #'equal)))
+ (or idx 0))
ivy--index)))
(defun ivy-recompute-index-zero (_re-str _cands)
0)
+(defcustom ivy-minibuffer-faces
+ '(ivy-minibuffer-match-face-1
+ ivy-minibuffer-match-face-2
+ ivy-minibuffer-match-face-3
+ ivy-minibuffer-match-face-4)
+ "List of `ivy' faces for minibuffer group matches.")
+
(defun ivy--flx-sort (name cands)
"Sort according to closeness to string NAME the string list CANDS."
(condition-case nil
(delq nil
(mapcar
(lambda (x)
- (let ((score (car (flx-score x flx-name ivy--flx-cache))))
+ (let ((score (flx-score x flx-name ivy--flx-cache)))
(and score
(cons score x))))
cands))))
(if cands-with-score
- (mapcar #'cdr
+ (mapcar (lambda (x)
+ (let ((str (copy-sequence (cdr x)))
+ (i 0)
+ (last-j -2))
+ (dolist (j (cdar x))
+ (unless (eq j (1+ last-j))
+ (cl-incf i))
+ (setq last-j j)
+ (ivy-add-face-text-property
+ j (1+ j)
+ (nth (1+ (mod (+ i 2) (1- (length ivy-minibuffer-faces))))
+ ivy-minibuffer-faces)
+ str))
+ str))
(sort cands-with-score
(lambda (x y)
- (> (car x) (car y)))))
+ (> (caar x) (caar y)))))
cands))
cands)
(error
cands)))
-(defvar ivy-format-function 'ivy-format-function-default
+(defcustom ivy-format-function 'ivy-format-function-default
"Function to transform the list of candidates into a string.
-This string will be inserted into the minibuffer.")
-
-(defun ivy-format-function-default (cands)
- "Transform CANDS into a string for minibuffer."
- (if (bound-and-true-p truncate-lines)
- (mapconcat #'identity cands "\n")
- (let ((ww (- (window-width)
- (if (and (boundp 'fringe-mode) (eq fringe-mode 0)) 1 0))))
- (mapconcat
- (if truncate-lines
- (lambda (s)
- (if (> (length s) ww)
- (concat (substring s 0 (- ww 3)) "...")
- s))
- #'identity)
- cands "\n"))))
-
-(defun ivy-format-function-arrow (cands)
- "Transform CANDS into a string for minibuffer."
+This string is inserted into the minibuffer."
+ :type '(choice
+ (const :tag "Default" ivy-format-function-default)
+ (const :tag "Arrow prefix" ivy-format-function-arrow)
+ (const :tag "Full line" ivy-format-function-line)))
+
+(defun ivy--truncate-string (str width)
+ "Truncate STR to WIDTH."
+ (if (> (string-width str) width)
+ (concat (substring str 0 (min (- width 3)
+ (- (length str) 3))) "...")
+ str))
+
+(defun ivy--format-function-generic (selected-fn other-fn cand-pairs separator)
+ "Transform CAND-PAIRS into a string for minibuffer.
+SELECTED-FN and OTHER-FN each take two string arguments.
+SEPARATOR is used to join the candidates."
(let ((i -1))
(mapconcat
- (lambda (s)
- (concat (if (eq (cl-incf i) ivy--index)
- "> "
- " ")
- s))
- cands "\n")))
-
-(defface ivy-minibuffer-match-face-1
- '((((class color) (background light))
- :background "#d3d3d3")
- (((class color) (background dark))
- :background "#555555"))
- "The background face for `ivy' minibuffer matches.")
-
-(defface ivy-minibuffer-match-face-2
- '((((class color) (background light))
- :background "#e99ce8" :weight bold)
- (((class color) (background dark))
- :background "#777777" :weight bold))
- "Face for `ivy' minibuffer matches modulo 1.")
-
-(defface ivy-minibuffer-match-face-3
- '((((class color) (background light))
- :background "#bbbbff" :weight bold)
- (((class color) (background dark))
- :background "#7777ff" :weight bold))
- "Face for `ivy' minibuffer matches modulo 2.")
-
-(defface ivy-minibuffer-match-face-4
- '((((class color) (background light))
- :background "#ffbbff" :weight bold)
- (((class color) (background dark))
- :background "#8a498a" :weight bold))
- "Face for `ivy' minibuffer matches modulo 3.")
-
-(defcustom ivy-minibuffer-faces
- '(ivy-minibuffer-match-face-1
- ivy-minibuffer-match-face-2
- ivy-minibuffer-match-face-3
- ivy-minibuffer-match-face-4)
- "List of `ivy' faces for minibuffer group matches.")
+ (lambda (pair)
+ (let ((str (car pair))
+ (extra (cdr pair))
+ (curr (eq (cl-incf i) ivy--index)))
+ (if curr
+ (funcall selected-fn str extra)
+ (funcall other-fn str extra))))
+ cand-pairs
+ separator)))
+
+(defun ivy-format-function-default (cand-pairs)
+ "Transform CAND-PAIRS into a string for minibuffer."
+ (ivy--format-function-generic
+ (lambda (str extra)
+ (concat (ivy--add-face str 'ivy-current-match) extra))
+ #'concat
+ cand-pairs
+ "\n"))
+
+(defun ivy-format-function-arrow (cand-pairs)
+ "Transform CAND-PAIRS into a string for minibuffer."
+ (ivy--format-function-generic
+ (lambda (str extra)
+ (concat "> " (ivy--add-face str 'ivy-current-match) extra))
+ (lambda (str extra)
+ (concat " " str extra))
+ cand-pairs
+ "\n"))
+
+(defun ivy-format-function-line (cand-pairs)
+ "Transform CAND-PAIRS into a string for minibuffer."
+ (ivy--format-function-generic
+ (lambda (str extra)
+ (ivy--add-face (concat str extra "\n") 'ivy-current-match))
+ (lambda (str extra)
+ (concat str extra "\n"))
+ cand-pairs
+ ""))
+
+(defun ivy-add-face-text-property (start end face str)
+ (if (fboundp 'add-face-text-property)
+ (add-face-text-property
+ start end face nil str)
+ (font-lock-append-text-property
+ start end 'face face str)))
(defun ivy--format-minibuffer-line (str)
(let ((start 0)
(str (copy-sequence str)))
- (when (eq ivy-display-style 'fancy)
- (unless ivy--old-re
- (setq ivy--old-re (funcall ivy--regex-function ivy-text)))
- (while (and (string-match ivy--old-re str start)
- (> (- (match-end 0) (match-beginning 0)) 0))
- (setq start (match-end 0))
- (let ((i 0))
- (while (<= i ivy--subexps)
- (let ((face
- (cond ((zerop ivy--subexps)
- (cadr ivy-minibuffer-faces))
- ((zerop i)
- (car ivy-minibuffer-faces))
- (t
- (nth (1+ (mod (+ i 2) (1- (length ivy-minibuffer-faces))))
- ivy-minibuffer-faces)))))
- (if (fboundp 'add-face-text-property)
- (add-face-text-property
- (match-beginning i)
- (match-end i)
- face
- nil
- str)
- (font-lock-append-text-property
- (match-beginning i)
- (match-end i)
- 'face
- face
- str)))
- (cl-incf i)))))
+ (cond ((eq ivy--regex-function 'ivy--regex-ignore-order)
+ (when (consp ivy--old-re)
+ (let ((i 1))
+ (dolist (re ivy--old-re)
+ (when (string-match (car re) str)
+ (ivy-add-face-text-property
+ (match-beginning 0) (match-end 0)
+ (nth (1+ (mod (+ i 2) (1- (length ivy-minibuffer-faces))))
+ ivy-minibuffer-faces)
+ str))
+ (cl-incf i)))))
+ ((and (eq ivy-display-style 'fancy)
+ (not (eq ivy--regex-function 'ivy--regex-fuzzy)))
+ (unless ivy--old-re
+ (setq ivy--old-re (funcall ivy--regex-function ivy-text)))
+ (while (and (string-match ivy--old-re str start)
+ (> (- (match-end 0) (match-beginning 0)) 0))
+ (setq start (match-end 0))
+ (let ((i 0))
+ (while (<= i ivy--subexps)
+ (let ((face
+ (cond ((zerop ivy--subexps)
+ (cadr ivy-minibuffer-faces))
+ ((zerop i)
+ (car ivy-minibuffer-faces))
+ (t
+ (nth (1+ (mod (+ i 2) (1- (length ivy-minibuffer-faces))))
+ ivy-minibuffer-faces)))))
+ (ivy-add-face-text-property
+ (match-beginning i) (match-end i)
+ face str))
+ (cl-incf i))))))
str))
(defun ivy--format (cands)
x)))
cands))))
(setq ivy--current (copy-sequence (nth index cands)))
- (setq cands (mapcar
- #'ivy--format-minibuffer-line
- cands))
- (setf (nth index cands)
- (ivy--add-face (nth index cands) 'ivy-current-match))
(let* ((ivy--index index)
- (res (concat "\n" (funcall ivy-format-function cands))))
+ (cand-pairs (mapcar
+ (lambda (cand)
+ (cons (ivy--format-minibuffer-line cand) nil)) cands))
+ (res (concat "\n" (funcall ivy-format-function cand-pairs))))
(put-text-property 0 (length res) 'read-only nil res)
res))))
(defvar recentf-list)
-(defface ivy-virtual '((t :inherit font-lock-builtin-face))
- "Face used by Ivy for matching virtual buffer names.")
-
(defcustom ivy-virtual-abbreviate 'name
"The mode of abbreviation for virtual buffer names."
:type '(choice
(defun ivy-reverse-i-search ()
"Enter a recursive `ivy-read' session using the current history.
-The selected history element will be inserted into the minibufer."
+The selected history element will be inserted into the minibuffer."
(interactive)
(let ((enable-recursive-minibuffers t)
(history (symbol-value (ivy-state-history ivy-last)))
- (old-last ivy-last))
+ (old-last ivy-last)
+ (ivy-recursive-restore nil))
(ivy-read "Reverse-i-search: "
history
:action (lambda (x)
(defun ivy-occur ()
"Stop completion and put the current matches into a new buffer.
-The new buffer will also remember the current action(s).
+The new buffer remembers current action(s).
-While in the *ivy-occur* buffer, selecting a cadidate with RET or
+While in the *ivy-occur* buffer, selecting a candidate with RET or
a mouse click will call the appropriate action for that candidate.
-It's possible to have an unlimited amount of *ivy-occur* buffers."
+There is no limit on the number of *ivy-occur* buffers."
(interactive)
(let ((buffer
(generate-new-buffer
(setq ivy--old-cands
(split-string
(shell-command-to-string
- (format counsel-git-grep-cmd ivy--old-re))
+ (format counsel-git-grep-cmd
+ (if (stringp ivy--old-re)
+ ivy--old-re
+ (caar ivy--old-re))))
"\n"
t))
(ivy-occur-grep-mode))
(goto-char pos)
(ivy-occur-press))))
+(declare-function swiper--cleanup "swiper")
+(declare-function swiper--add-overlays "swiper")
+
(defun ivy-occur-press ()
"Execute action for the current candidate."
(interactive)