"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))
(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)
+ (ivy--directory
+ (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 (expand-file-name
- ivy--current ivy--directory))))))
- (ivy--cd dir)
- (ivy--exhibit))
+ (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))))
((eq (ivy-state-collection ivy-last) 'Info-read-node-name-1)
(if (or (equal ivy--current "(./)")
(equal ivy--current "(../)"))
(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)))))
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))
(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))))
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 to t is used for all fall-through cases.
+
+See also `ivy-sort-max-size'."
:type
'(alist
:key-type (choice
_INHERIT-INPUT-METHOD is ignored for now.
The history, defaults and input-method arguments are ignored for now."
+ ;; 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
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)))))
+ (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
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 according to 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))
"Function to transform the list of candidates into a string.
This string will be inserted into the minibuffer.")
+(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-default (cands)
"Transform CANDS into a string for minibuffer."
(if (bound-and-true-p truncate-lines)
(mapconcat
(if truncate-lines
(lambda (s)
- (if (> (length s) ww)
- (concat (substring s 0 (- ww 3)) "...")
- s))
+ (ivy--truncate-string s ww))
#'identity)
cands "\n"))))
\\{ivy-occur-grep-mode-map}")
+(defvar counsel-git-grep-cmd)
+
(defun ivy-occur ()
"Stop completion and put the current matches into a 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)