"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 "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)))))
(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)
(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 to t is used for all fall-through cases."
+The entry associated with 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))
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.
+The entry associated with t is used for all fall-through cases.
Possible choices: `ivy--regex', `regexp-quote', `ivy--regex-plus'.
-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
;;** 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."
+It is used, along with COLLECTION, to determine which
+customizations apply to the current completion session."
(let ((extra-actions (plist-get ivy--actions-list this-command)))
(when extra-actions
(setq 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)))
+ (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)))
(remove-hook 'post-command-hook #'ivy--exhibit)
(when (setq unwind (ivy-state-unwind ivy-last))
(funcall unwind)))
(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))
(re-builder (ivy-state-re-builder 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)))
+ (require-match (ivy-state-require-match state)))
(unless initial-input
(setq initial-input (cdr (assoc this-command
ivy-initial-inputs-alist))))
(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)
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."
+_INHERIT-INPUT-METHOD is currently ignored."
+
+ ;; 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
;;;###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:
(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)))))
+(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.
-Ignore the order of each group."
+ "Re-build regex from STR by splitting at spaces.
+Ignore the order of each group.
+
+ATTENTION: This is just a proof of concept and may not work as
+expected. Besides ignoring the order of the tokens where 'foo'
+and 'bar', 'bar' and 'foo' are matched, it also matches multiple
+occurrences of 'foo' and 'bar'. To ignore the sort order and avoid
+multiple matches, use `ivy-restrict-to-matches' instead.
+"
(let* ((subs (split-string str " +" t))
(len (length subs)))
(cl-case len
(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
"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))))))
+ (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)
(and ivy-case-fold-search
(string= name (downcase name))))
(cands (cond
- (matcher
- (funcall matcher re candidates))
- ((and ivy--old-re
- (stringp re)
- (stringp ivy--old-re)
- (not (string-match "\\\\" ivy--old-re))
- (not (equal ivy--old-re ""))
- (memq (cl-search
- (if (string-match "\\\\)\\'" ivy--old-re)
+ (matcher
+ (funcall matcher re candidates))
+ ((and ivy--old-re
+ (stringp re)
+ (stringp ivy--old-re)
+ (not (string-match "\\\\" ivy--old-re))
+ (not (equal ivy--old-re ""))
+ (memq (cl-search
+ (if (string-match "\\\\)\\'" ivy--old-re)
(substring ivy--old-re 0 -2)
ivy--old-re)
re)
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)))))
+ (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)
(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.")
+ (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
+ ""))
(defcustom ivy-minibuffer-faces
'(ivy-minibuffer-match-face-1
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)))
(define-key map (kbd "g") 'ivy-occur-press)
(define-key map (kbd "a") 'ivy-occur-read-action)
(define-key map (kbd "o") 'ivy-occur-dispatch)
+ (define-key map (kbd "q") 'quit-window)
map)
"Keymap for Ivy Occur mode.")
\\{ivy-occur-grep-mode-map}")
+(defvar counsel-git-grep-cmd)
+
(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
(do-grep (eq (ivy-state-caller ivy-last) 'counsel-git-grep)))
(with-current-buffer buffer
(if do-grep
- (ivy-occur-grep-mode)
+ (progn
+ (setq ivy--old-cands
+ (split-string
+ (shell-command-to-string
+ (format counsel-git-grep-cmd ivy--old-re))
+ "\n"
+ t))
+ (ivy-occur-grep-mode))
(ivy-occur-mode))
(setf (ivy-state-text ivy-last) ivy-text)
(setq ivy-occur-last ivy-last)