]> code.delx.au - gnu-emacs-elpa/blob - ivy.el
9870032bf9d349fc8aed9710b12b4134654d4c69
[gnu-emacs-elpa] / ivy.el
1 ;;; ivy.el --- Incremental Vertical completYon -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2015 Free Software Foundation, Inc.
4
5 ;; Author: Oleh Krehel <ohwoeowho@gmail.com>
6 ;; URL: https://github.com/abo-abo/swiper
7 ;; Version: 0.2.3
8 ;; Package-Requires: ((emacs "24.1"))
9 ;; Keywords: matching
10
11 ;; This file is part of GNU Emacs.
12
13 ;; This file is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 3, or (at your option)
16 ;; any later version.
17
18 ;; This program is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22
23 ;; For a full copy of the GNU General Public License
24 ;; see <http://www.gnu.org/licenses/>.
25
26 ;;; Commentary:
27 ;;
28 ;; This package provides `ivy-read' as an alternative to
29 ;; `completing-read' and similar functions.
30 ;;
31 ;; There's no intricate code to determine the best candidate.
32 ;; Instead, the user can navigate to it with `ivy-next-line' and
33 ;; `ivy-previous-line'.
34 ;;
35 ;; The matching is done by splitting the input text by spaces and
36 ;; re-building it into a regex.
37 ;; So "for example" is transformed into "\\(for\\).*\\(example\\)".
38
39 ;;; Code:
40 (require 'cl-lib)
41
42 ;;* Customization
43 (defgroup ivy nil
44 "Incremental vertical completion."
45 :group 'convenience)
46
47 (defface ivy-current-match
48 '((t (:inherit highlight)))
49 "Face used by Ivy for highlighting first match.")
50
51 (defface ivy-confirm-face
52 '((t :foreground "ForestGreen" :inherit minibuffer-prompt))
53 "Face used by Ivy to issue a confirmation prompt.")
54
55 (defface ivy-match-required-face
56 '((t :foreground "red" :inherit minibuffer-prompt))
57 "Face used by Ivy to issue a match required prompt.")
58
59 (defface ivy-subdir
60 '((t (:inherit 'dired-directory)))
61 "Face used by Ivy for highlighting subdirs in the alternatives.")
62
63 (defface ivy-remote
64 '((t (:foreground "#110099")))
65 "Face used by Ivy for highlighting remotes in the alternatives.")
66
67 (defcustom ivy-height 10
68 "Number of lines for the minibuffer window."
69 :type 'integer)
70
71 (defcustom ivy-count-format "%-4d "
72 "The style of showing the current candidate count for `ivy-read'.
73 Set this to nil if you don't want the count."
74 :type 'string)
75
76 (defcustom ivy-wrap nil
77 "Whether to wrap around after the first and last candidate."
78 :type 'boolean)
79
80 (defcustom ivy-on-del-error-function 'minibuffer-keyboard-quit
81 "The handler for when `ivy-backward-delete-char' throws.
82 This is usually meant as a quick exit out of the minibuffer."
83 :type 'function)
84
85 (defcustom ivy-extra-directories '("../" "./")
86 "Add this to the front of the list when completing file names.
87 Only \"./\" and \"../\" apply here. They appear in reverse order."
88 :type 'list)
89
90 (defcustom ivy-use-virtual-buffers nil
91 "When non-nil, add `recentf-mode' and bookmarks to the list of buffers."
92 :type 'boolean)
93
94 ;;* Keymap
95 (require 'delsel)
96 (defvar ivy-minibuffer-map
97 (let ((map (make-sparse-keymap)))
98 (define-key map (kbd "C-m") 'ivy-done)
99 (define-key map (kbd "C-j") 'ivy-alt-done)
100 (define-key map (kbd "TAB") 'ivy-partial-or-done)
101 (define-key map (kbd "C-n") 'ivy-next-line)
102 (define-key map (kbd "C-p") 'ivy-previous-line)
103 (define-key map (kbd "<down>") 'ivy-next-line)
104 (define-key map (kbd "<up>") 'ivy-previous-line)
105 (define-key map (kbd "C-s") 'ivy-next-line-or-history)
106 (define-key map (kbd "C-r") 'ivy-previous-line-or-history)
107 (define-key map (kbd "SPC") 'self-insert-command)
108 (define-key map (kbd "DEL") 'ivy-backward-delete-char)
109 (define-key map (kbd "M-DEL") 'ivy-backward-kill-word)
110 (define-key map (kbd "C-d") 'ivy-delete-char)
111 (define-key map (kbd "C-f") 'ivy-forward-char)
112 (define-key map (kbd "M-d") 'ivy-kill-word)
113 (define-key map (kbd "M-<") 'ivy-beginning-of-buffer)
114 (define-key map (kbd "M->") 'ivy-end-of-buffer)
115 (define-key map (kbd "<left>") 'ivy-beginning-of-buffer)
116 (define-key map (kbd "<right>") 'ivy-end-of-buffer)
117 (define-key map (kbd "M-n") 'ivy-next-history-element)
118 (define-key map (kbd "M-p") 'ivy-previous-history-element)
119 (define-key map (kbd "C-g") 'minibuffer-keyboard-quit)
120 (define-key map (kbd "C-v") 'ivy-scroll-up-command)
121 (define-key map (kbd "M-v") 'ivy-scroll-down-command)
122 (define-key map (kbd "C-M-n") 'ivy-next-line-and-call)
123 (define-key map (kbd "C-M-p") 'ivy-previous-line-and-call)
124 (define-key map (kbd "M-q") 'ivy-toggle-regexp-quote)
125 map)
126 "Keymap used in the minibuffer.")
127
128 (defvar ivy-mode-map
129 (let ((map (make-sparse-keymap)))
130 (define-key map [remap switch-to-buffer] 'ivy-switch-buffer)
131 map)
132 "Keymap for `ivy-mode'.")
133
134 ;;* Globals
135 (cl-defstruct ivy-state
136 prompt collection
137 predicate require-match initial-input
138 history preselect keymap update-fn sort
139 ;; The window in which `ivy-read' was called
140 window
141 action
142 unwind
143 re-builder
144 matcher
145 ;; When this is non-nil, call it for each input change to get new candidates
146 dynamic-collection)
147
148 (defvar ivy-last nil
149 "The last parameters passed to `ivy-read'.")
150
151 (defsubst ivy-set-action (action)
152 (setf (ivy-state-action ivy-last) action))
153
154 (defvar ivy-history nil
155 "History list of candidates entered in the minibuffer.
156
157 Maximum length of the history list is determined by the value
158 of `history-length', which see.")
159
160 (defvar ivy--directory nil
161 "Current directory when completing file names.")
162
163 (defvar ivy--length 0
164 "Store the amount of viable candidates.")
165
166 (defvar ivy-text ""
167 "Store the user's string as it is typed in.")
168
169 (defvar ivy--current ""
170 "Current candidate.")
171
172 (defvar ivy--index 0
173 "Store the index of the current candidate.")
174
175 (defvar ivy-exit nil
176 "Store 'done if the completion was successfully selected.
177 Otherwise, store nil.")
178
179 (defvar ivy--all-candidates nil
180 "Store the candidates passed to `ivy-read'.")
181
182 (defvar ivy--default nil
183 "Default initial input.")
184
185 (defvar ivy--prompt nil
186 "Store the format-style prompt.
187 When non-nil, it should contain one %d.")
188
189 (defvar ivy--prompt-extra ""
190 "Temporary modifications to the prompt.")
191
192 (defvar ivy--old-re nil
193 "Store the old regexp.")
194
195 (defvar ivy--old-cands nil
196 "Store the candidates matched by `ivy--old-re'.")
197
198 (defvar ivy--regex-function 'ivy--regex
199 "Current function for building a regex.")
200
201 (defvar Info-current-file)
202
203 (defmacro ivy-quit-and-run (&rest body)
204 "Quit the minibuffer and run BODY afterwards."
205 `(progn
206 (put 'quit 'error-message "")
207 (run-at-time nil nil
208 (lambda ()
209 (put 'quit 'error-message "Quit")
210 ,@body))
211 (minibuffer-keyboard-quit)))
212
213 (defun ivy--done (text)
214 "Insert TEXT and exit minibuffer."
215 (if (and ivy--directory
216 (not (eq (ivy-state-history ivy-last) 'grep-files-history)))
217 (insert (expand-file-name
218 text ivy--directory))
219 (insert text))
220 (setq ivy-exit 'done)
221 (exit-minibuffer))
222
223 ;;* Commands
224 (defun ivy-done ()
225 "Exit the minibuffer with the selected candidate."
226 (interactive)
227 (delete-minibuffer-contents)
228 (cond ((> ivy--length 0)
229 (ivy--done ivy--current))
230 ((memq (ivy-state-collection ivy-last)
231 '(read-file-name-internal internal-complete-buffer))
232 (if (or (not (eq confirm-nonexistent-file-or-buffer t))
233 (equal " (confirm)" ivy--prompt-extra))
234 (ivy--done ivy-text)
235 (setq ivy--prompt-extra " (confirm)")
236 (insert ivy-text)
237 (ivy--exhibit)))
238 ((memq (ivy-state-require-match ivy-last)
239 '(nil confirm confirm-after-completion))
240 (ivy--done ivy-text))
241 (t
242 (setq ivy--prompt-extra " (match required)")
243 (insert ivy-text)
244 (ivy--exhibit))))
245
246 (defun ivy-build-tramp-name (x)
247 "Reconstruct X into a path.
248 Is is a cons cell, related to `tramp-get-completion-function'."
249 (let ((user (car x))
250 (domain (cadr x)))
251 (if user
252 (concat user "@" domain)
253 domain)))
254
255 (declare-function tramp-get-completion-function "tramp")
256
257 (defun ivy-alt-done (&optional arg)
258 "Exit the minibuffer with the selected candidate.
259 When ARG is t, exit with current text, ignoring the candidates."
260 (interactive "P")
261 (if arg
262 (ivy-immediate-done)
263 (let (dir)
264 (cond ((and ivy--directory
265 (or
266 (and
267 (not (string= ivy--current "./"))
268 (cl-plusp ivy--length)
269 (file-directory-p
270 (setq dir (expand-file-name
271 ivy--current ivy--directory))))))
272 (ivy--cd dir)
273 (ivy--exhibit))
274 ((string-match "^/\\([^/]+?\\):\\(?:\\(.*\\)@\\)?" ivy-text)
275 (let ((method (match-string 1 ivy-text))
276 (user (match-string 2 ivy-text))
277 res)
278 (dolist (x (tramp-get-completion-function method))
279 (setq res (append res (funcall (car x) (cadr x)))))
280 (setq res (delq nil res))
281 (when user
282 (dolist (x res)
283 (setcar x user)))
284 (setq res (cl-delete-duplicates res :test 'equal))
285 (let ((host (ivy-read "Find File: "
286 (mapcar #'ivy-build-tramp-name res))))
287 (when host
288 (setq ivy--directory "/")
289 (ivy--cd (concat "/" method ":" host ":"))))))
290 (t
291 (ivy-done))))))
292
293 (defcustom ivy-tab-space nil
294 "When non-nil, `ivy-partial-or-done' should insert a space."
295 :type 'boolean)
296
297 (defun ivy-partial-or-done ()
298 "Complete the minibuffer text as much as possible.
299 If the text hasn't changed as a result, forward to `ivy-alt-done'."
300 (interactive)
301 (if (and (eq (ivy-state-collection ivy-last) 'read-file-name-internal)
302 (string-match "^/" ivy-text))
303 (let ((default-directory ivy--directory))
304 (minibuffer-complete)
305 (setq ivy-text (ivy--input))
306 (when (and (file-directory-p ivy-text)
307 (= ivy--length 1))
308 (ivy--cd (expand-file-name ivy-text))))
309 (or (ivy-partial)
310 (when (or (eq this-command last-command)
311 (eq ivy--length 1))
312 (ivy-alt-done)))))
313
314 (defun ivy-partial ()
315 "Complete the minibuffer text as much as possible."
316 (interactive)
317 (let* ((parts (or (split-string ivy-text " " t) (list "")))
318 (postfix (car (last parts)))
319 (completion-ignore-case t)
320 (new (try-completion postfix
321 (mapcar (lambda (str) (substring str (string-match postfix str)))
322 ivy--old-cands))))
323 (cond ((eq new t) nil)
324 ((string= new ivy-text) nil)
325 (new
326 (delete-region (minibuffer-prompt-end) (point-max))
327 (setcar (last parts) new)
328 (insert (mapconcat #'identity parts " ")
329 (if ivy-tab-space " " ""))
330 t))))
331
332 (defun ivy-immediate-done ()
333 "Exit the minibuffer with the current input."
334 (interactive)
335 (delete-minibuffer-contents)
336 (insert ivy-text)
337 (setq ivy-exit 'done)
338 (exit-minibuffer))
339
340 (defun ivy-resume ()
341 "Resume the last completion session."
342 (interactive)
343 (ivy-read
344 (ivy-state-prompt ivy-last)
345 (ivy-state-collection ivy-last)
346 :predicate (ivy-state-predicate ivy-last)
347 :require-match (ivy-state-require-match ivy-last)
348 :initial-input ivy-text
349 :history (ivy-state-history ivy-last)
350 :preselect (regexp-quote ivy--current)
351 :keymap (ivy-state-keymap ivy-last)
352 :update-fn (ivy-state-update-fn ivy-last)
353 :sort (ivy-state-sort ivy-last)
354 :action (ivy-state-action ivy-last)
355 :unwind (ivy-state-unwind ivy-last)
356 :re-builder (ivy-state-re-builder ivy-last)
357 :matcher (ivy-state-matcher ivy-last)
358 :dynamic-collection (ivy-state-dynamic-collection ivy-last)))
359
360 (defun ivy-beginning-of-buffer ()
361 "Select the first completion candidate."
362 (interactive)
363 (setq ivy--index 0))
364
365 (defun ivy-end-of-buffer ()
366 "Select the last completion candidate."
367 (interactive)
368 (setq ivy--index (1- ivy--length)))
369
370 (defun ivy-scroll-up-command ()
371 "Scroll the candidates upward by the minibuffer height."
372 (interactive)
373 (setq ivy--index (min (+ ivy--index ivy-height)
374 (1- ivy--length))))
375
376 (defun ivy-scroll-down-command ()
377 "Scroll the candidates downward by the minibuffer height."
378 (interactive)
379 (setq ivy--index (max (- ivy--index ivy-height)
380 0)))
381
382 (defun ivy-next-line (&optional arg)
383 "Move cursor vertically down ARG candidates."
384 (interactive "p")
385 (setq arg (or arg 1))
386 (cl-incf ivy--index arg)
387 (when (>= ivy--index (1- ivy--length))
388 (if ivy-wrap
389 (ivy-beginning-of-buffer)
390 (setq ivy--index (1- ivy--length)))))
391
392 (defun ivy-next-line-or-history (&optional arg)
393 "Move cursor vertically down ARG candidates.
394 If the input is empty, select the previous history element instead."
395 (interactive "p")
396 (when (string= ivy-text "")
397 (ivy-previous-history-element 1))
398 (ivy-next-line arg))
399
400 (defun ivy-previous-line (&optional arg)
401 "Move cursor vertically up ARG candidates."
402 (interactive "p")
403 (setq arg (or arg 1))
404 (cl-decf ivy--index arg)
405 (when (< ivy--index 0)
406 (if ivy-wrap
407 (ivy-end-of-buffer)
408 (setq ivy--index 0))))
409
410 (defun ivy-previous-line-or-history (arg)
411 "Move cursor vertically up ARG candidates.
412 If the input is empty, select the previous history element instead."
413 (interactive "p")
414 (when (string= ivy-text "")
415 (ivy-previous-history-element 1))
416 (ivy-previous-line arg))
417
418 (defun ivy-next-line-and-call (&optional arg)
419 "Move cursor vertically down ARG candidates.
420 Call the permanent action if possible."
421 (interactive "p")
422 (ivy-next-line arg)
423 (ivy--exhibit)
424 (when (ivy-state-action ivy-last)
425 (with-selected-window (ivy-state-window ivy-last)
426 (funcall (ivy-state-action ivy-last)))))
427
428 (defun ivy-previous-line-and-call (&optional arg)
429 "Move cursor vertically down ARG candidates.
430 Call the permanent action if possible."
431 (interactive "p")
432 (ivy-previous-line arg)
433 (ivy--exhibit)
434 (when (ivy-state-action ivy-last)
435 (with-selected-window (ivy-state-window ivy-last)
436 (funcall (ivy-state-action ivy-last)))))
437
438 (defun ivy-previous-history-element (arg)
439 "Forward to `previous-history-element' with ARG."
440 (interactive "p")
441 (previous-history-element arg)
442 (move-end-of-line 1)
443 (ivy--maybe-scroll-history))
444
445 (defun ivy-next-history-element (arg)
446 "Forward to `next-history-element' with ARG."
447 (interactive "p")
448 (next-history-element arg)
449 (move-end-of-line 1)
450 (ivy--maybe-scroll-history))
451
452 (defun ivy--maybe-scroll-history ()
453 "If the selected history element has an index, scroll there."
454 (let ((idx (ignore-errors
455 (get-text-property
456 (minibuffer-prompt-end)
457 'ivy-index))))
458 (when idx
459 (ivy--exhibit)
460 (setq ivy--index idx))))
461
462 (defun ivy--cd (dir)
463 "When completing file names, move to directory DIR."
464 (if (null ivy--directory)
465 (error "Unexpected")
466 (setq ivy--old-cands nil)
467 (setq ivy--old-re nil)
468 (setq ivy--index 0)
469 (setq ivy--all-candidates
470 (ivy--sorted-files (setq ivy--directory dir)))
471 (setq ivy-text "")
472 (delete-minibuffer-contents)))
473
474 (defun ivy-backward-delete-char ()
475 "Forward to `backward-delete-char'.
476 On error (read-only), call `ivy-on-del-error-function'."
477 (interactive)
478 (if (and ivy--directory (= (minibuffer-prompt-end) (point)))
479 (progn
480 (ivy--cd (file-name-directory
481 (directory-file-name
482 (expand-file-name
483 ivy--directory))))
484 (ivy--exhibit))
485 (condition-case nil
486 (backward-delete-char 1)
487 (error
488 (when ivy-on-del-error-function
489 (funcall ivy-on-del-error-function))))))
490
491 (defun ivy-delete-char (arg)
492 "Forward to `delete-char' ARG."
493 (interactive "p")
494 (unless (= (point) (line-end-position))
495 (delete-char arg)))
496
497 (defun ivy-forward-char (arg)
498 "Forward to `forward-char' ARG."
499 (interactive "p")
500 (unless (= (point) (line-end-position))
501 (forward-char arg)))
502
503 (defun ivy-kill-word (arg)
504 "Forward to `kill-word' ARG."
505 (interactive "p")
506 (unless (= (point) (line-end-position))
507 (kill-word arg)))
508
509 (defun ivy-backward-kill-word ()
510 "Forward to `backward-kill-word'."
511 (interactive)
512 (if (and ivy--directory (= (minibuffer-prompt-end) (point)))
513 (progn
514 (ivy--cd (file-name-directory
515 (directory-file-name
516 (expand-file-name
517 ivy--directory))))
518 (ivy--exhibit))
519 (ignore-errors
520 (backward-kill-word 1))))
521
522 (defvar ivy--regexp-quote 'regexp-quote
523 "Store the regexp quoting state.")
524
525 (defun ivy-toggle-regexp-quote ()
526 "Toggle the regexp quoting."
527 (interactive)
528 (setq ivy--old-re nil)
529 (cl-rotatef ivy--regex-function ivy--regexp-quote))
530
531 (defun ivy-sort-file-function-default (x y)
532 "Compare two files X and Y.
533 Prioritize directories."
534 (if (get-text-property 0 'dirp x)
535 (if (get-text-property 0 'dirp y)
536 (string< x y)
537 t)
538 (if (get-text-property 0 'dirp y)
539 nil
540 (string< x y))))
541
542 (defvar ivy-sort-functions-alist
543 '((read-file-name-internal . ivy-sort-file-function-default)
544 (internal-complete-buffer . nil)
545 (counsel-git-grep-function . nil)
546 (t . string-lessp))
547 "An alist of sorting functions for each collection function.
548 For each entry, nil means no sorting.
549 The entry associated to t is used for all fall-through cases.")
550
551 (defvar ivy-re-builders-alist
552 '((t . ivy--regex-plus))
553 "An alist of regex building functions for each collection function.
554 Each function should take a string and return a valid regex or a
555 regex sequence (see below).
556
557 The entry associated to t is used for all fall-through cases.
558 Possible choices: `ivy--regex', `regexp-quote', `ivy--regex-plus'.
559
560 In case a function returns a list, it should look like this:
561 '((\"matching-regexp\" . t) (\"non-matching-regexp\") ...).
562
563 The matches will be filtered in a sequence, you can mix the
564 regexps that should match and that should not match as you
565 like.")
566
567 (defcustom ivy-sort-max-size 30000
568 "Sorting won't be done for collections larger than this."
569 :type 'integer)
570
571 (defun ivy--sorted-files (dir)
572 "Return the list of files in DIR.
573 Directories come first."
574 (let* ((default-directory dir)
575 (seq (all-completions "" 'read-file-name-internal))
576 sort-fn)
577 (if (equal dir "/")
578 seq
579 (setq seq (delete "./" (delete "../" seq)))
580 (when (eq (setq sort-fn (cdr (assoc 'read-file-name-internal
581 ivy-sort-functions-alist)))
582 'ivy-sort-file-function-default)
583 (setq seq (mapcar (lambda (x)
584 (propertize x 'dirp (string-match-p "/$" x)))
585 seq)))
586 (when sort-fn
587 (setq seq (cl-sort seq sort-fn)))
588 (dolist (dir ivy-extra-directories)
589 (push dir seq))
590 seq)))
591
592 ;;** Entry Point
593 (cl-defun ivy-read (prompt collection
594 &key predicate require-match initial-input
595 history preselect keymap update-fn sort
596 action unwind re-builder matcher dynamic-collection)
597 "Read a string in the minibuffer, with completion.
598
599 PROMPT is a string to prompt with; normally it ends in a colon
600 and a space. When PROMPT contains %d, it will be updated with
601 the current number of matching candidates.
602 See also `ivy-count-format'.
603
604 COLLECTION is a list of strings.
605
606 If INITIAL-INPUT is non-nil, insert it in the minibuffer initially.
607
608 KEYMAP is composed together with `ivy-minibuffer-map'.
609
610 If PRESELECT is non-nil select the corresponding candidate out of
611 the ones that match INITIAL-INPUT.
612
613 UPDATE-FN is called each time the current candidate(s) is changed.
614
615 When SORT is t, refer to `ivy-sort-functions-alist' for sorting.
616
617 ACTION is a lambda to call after a result was selected.
618
619 UNWIND is a lambda to call before exiting.
620
621 RE-BUILDER is a lambda that transforms text into a regex.
622
623 MATCHER can completely override matching.
624
625 DYNAMIC-COLLECTION is a function to call to update the list of
626 candidates with each input."
627 (setq ivy-last
628 (make-ivy-state
629 :prompt prompt
630 :collection collection
631 :predicate predicate
632 :require-match require-match
633 :initial-input initial-input
634 :history history
635 :preselect preselect
636 :keymap keymap
637 :update-fn update-fn
638 :sort sort
639 :action action
640 :window (selected-window)
641 :unwind unwind
642 :re-builder re-builder
643 :matcher matcher
644 :dynamic-collection dynamic-collection))
645 (setq ivy--directory nil)
646 (setq ivy--regex-function
647 (or re-builder
648 (and (functionp collection)
649 (cdr (assoc collection ivy-re-builders-alist)))
650 (cdr (assoc t ivy-re-builders-alist))
651 'ivy--regex))
652 (setq ivy--subexps 0)
653 (setq ivy--regexp-quote 'regexp-quote)
654 (setq ivy--old-text "")
655 (setq ivy-text "")
656 (let (coll sort-fn)
657 (cond ((eq collection 'Info-read-node-name-1)
658 (if (equal Info-current-file "dir")
659 (setq coll
660 (mapcar (lambda (x) (format "(%s)" x))
661 (cl-delete-duplicates
662 (all-completions "(" collection predicate)
663 :test 'equal)))
664 (setq coll (all-completions "" collection predicate))))
665 ((eq collection 'read-file-name-internal)
666 (setq ivy--directory default-directory)
667 (require 'dired)
668 (setq coll
669 (ivy--sorted-files default-directory))
670 (when initial-input
671 (unless (or require-match
672 (equal initial-input default-directory))
673 (setq coll (cons initial-input coll)))
674 (setq initial-input nil)))
675 ((eq collection 'internal-complete-buffer)
676 (setq coll (ivy--buffer-list "" ivy-use-virtual-buffers)))
677 ((or (functionp collection)
678 (vectorp collection)
679 (listp (car collection)))
680 (setq coll (all-completions "" collection predicate)))
681 ((hash-table-p collection)
682 (error "Hash table as a collection unsupported"))
683 (t
684 (setq coll collection)))
685 (when sort
686 (if (and (functionp collection)
687 (setq sort-fn (assoc collection ivy-sort-functions-alist)))
688 (when (and (setq sort-fn (cdr sort-fn))
689 (not (eq collection 'read-file-name-internal)))
690 (setq coll (cl-sort coll sort-fn)))
691 (unless (eq history 'org-refile-history)
692 (if (and (setq sort-fn (cdr (assoc t ivy-sort-functions-alist)))
693 (<= (length coll) ivy-sort-max-size))
694 (setq coll (cl-sort (copy-sequence coll) sort-fn))))))
695 (when preselect
696 (unless (or require-match
697 (cl-find-if `(lambda (x)
698 (string-match ,(format "^%s" preselect) x))
699 coll))
700 (setq coll (cons preselect coll))))
701 (setq ivy--index (or
702 (and dynamic-collection
703 ivy--index)
704 (and preselect
705 (ivy--preselect-index
706 coll initial-input preselect))
707 0))
708 (setq ivy--old-re nil)
709 (setq ivy--old-cands nil)
710 (setq ivy--all-candidates coll)
711 (setq ivy-exit nil)
712 (setq ivy--default (or (thing-at-point 'symbol) ""))
713 (setq ivy--prompt
714 (cond ((string-match "%.*d" prompt)
715 prompt)
716 ((string-match "%.*d" ivy-count-format)
717 (concat ivy-count-format prompt))
718 (ivy--directory
719 prompt)
720 (t
721 nil)))
722 (prog1
723 (unwind-protect
724 (minibuffer-with-setup-hook
725 #'ivy--minibuffer-setup
726 (let* ((hist (or history 'ivy-history))
727 (minibuffer-completion-table collection)
728 (minibuffer-completion-predicate predicate)
729 (res (read-from-minibuffer
730 prompt
731 initial-input
732 (make-composed-keymap keymap ivy-minibuffer-map)
733 nil
734 hist)))
735 (when (eq ivy-exit 'done)
736 (set hist (cons (propertize ivy-text 'ivy-index ivy--index)
737 (delete ivy-text
738 (cdr (symbol-value hist)))))
739 res)))
740 (remove-hook 'post-command-hook #'ivy--exhibit)
741 (when (setq unwind (ivy-state-unwind ivy-last))
742 (funcall unwind)))
743 (when (setq action (ivy-state-action ivy-last))
744 (funcall action)))))
745
746 (defun ivy-completing-read (prompt collection
747 &optional predicate require-match initial-input
748 history def _inherit-input-method)
749 "Read a string in the minibuffer, with completion.
750
751 This is an interface that conforms to `completing-read', so that
752 it can be used for `completing-read-function'.
753
754 PROMPT is a string to prompt with; normally it ends in a colon and a space.
755 COLLECTION can be a list of strings, an alist, an obarray or a hash table.
756 PREDICATE limits completion to a subset of COLLECTION.
757 REQUIRE-MATCH is considered boolean. See `completing-read'.
758 INITIAL-INPUT is a string that can be inserted into the minibuffer initially.
759 _HISTORY is ignored for now.
760 DEF is the default value.
761 _INHERIT-INPUT-METHOD is ignored for now.
762
763 The history, defaults and input-method arguments are ignored for now."
764 (ivy-read prompt collection
765 :predicate predicate
766 :require-match require-match
767 :initial-input initial-input
768 :preselect (if (listp def) (car def) def)
769 :history history
770 :keymap nil
771 :sort t))
772
773 ;;;###autoload
774 (define-minor-mode ivy-mode
775 "Toggle Ivy mode on or off.
776 With ARG, turn Ivy mode on if arg is positive, off otherwise.
777 Turning on Ivy mode will set `completing-read-function' to
778 `ivy-completing-read'.
779
780 Global bindings:
781 \\{ivy-mode-map}
782
783 Minibuffer bindings:
784 \\{ivy-minibuffer-map}"
785 :group 'ivy
786 :global t
787 :keymap ivy-mode-map
788 :lighter " ivy"
789 (if ivy-mode
790 (setq completing-read-function 'ivy-completing-read)
791 (setq completing-read-function 'completing-read-default)))
792
793 (defun ivy--preselect-index (candidates initial-input preselect)
794 "Return the index in CANDIDATES filtered by INITIAL-INPUT for PRESELECT."
795 (when initial-input
796 (setq initial-input (ivy--regex-plus initial-input))
797 (setq candidates
798 (cl-remove-if-not
799 (lambda (x)
800 (string-match initial-input x))
801 candidates)))
802 (or (cl-position preselect candidates :test 'equal)
803 (cl-position-if
804 (lambda (x)
805 (string-match (regexp-quote preselect) x))
806 candidates)))
807
808 ;;* Implementation
809 ;;** Regex
810 (defvar ivy--subexps 0
811 "Number of groups in the current `ivy--regex'.")
812
813 (defvar ivy--regex-hash
814 (make-hash-table :test 'equal)
815 "Store pre-computed regex.")
816
817 (defun ivy--split (str)
818 "Split STR into a list by single spaces.
819 The remaining spaces stick to their left.
820 This allows to \"quote\" N spaces by inputting N+1 spaces."
821 (let ((len (length str))
822 start0
823 (start1 0)
824 res s)
825 (while (and (string-match " +" str start1)
826 (< start1 len))
827 (setq match-len (- (match-end 0) (match-beginning 0)))
828 (if (= match-len 1)
829 (progn
830 (when start0
831 (setq start1 start0)
832 (setq start0 nil))
833 (push (substring str start1 (match-beginning 0)) res)
834 (setq start1 (match-end 0)))
835 (setq str (replace-match
836 (make-string (1- match-len) ?\ )
837 nil nil str))
838 (setq start0 (or start0 start1))
839 (setq start1 (1- (match-end 0)))))
840 (if start0
841 (push (substring str start0) res)
842 (setq s (substring str start1))
843 (unless (= (length s) 0)
844 (push s res)))
845 (nreverse res)))
846
847 (defun ivy--regex (str &optional greedy)
848 "Re-build regex from STR in case it has a space.
849 When GREEDY is non-nil, join words in a greedy way."
850 (let ((hashed (unless greedy
851 (gethash str ivy--regex-hash))))
852 (if hashed
853 (prog1 (cdr hashed)
854 (setq ivy--subexps (car hashed)))
855 (cdr (puthash str
856 (let ((subs (ivy--split str)))
857 (if (= (length subs) 1)
858 (cons
859 (setq ivy--subexps 0)
860 (car subs))
861 (cons
862 (setq ivy--subexps (length subs))
863 (mapconcat
864 (lambda (x)
865 (if (string-match "^\\\\(.*\\\\)$" x)
866 x
867 (format "\\(%s\\)" x)))
868 subs
869 (if greedy
870 ".*"
871 ".*?")))))
872 ivy--regex-hash)))))
873
874 (defun ivy--regex-ignore-order (str)
875 "Re-build regex from STR by splitting it on spaces.
876 Ignore the order of each group."
877 (let* ((subs (split-string str " +" t))
878 (len (length subs)))
879 (cl-case len
880 (1
881 (setq ivy--subexps 0)
882 (car subs))
883 (t
884 (setq ivy--subexps len)
885 (let ((all (mapconcat #'identity subs "\\|")))
886 (mapconcat
887 (lambda (x)
888 (if (string-match "^\\\\(.*\\\\)$" x)
889 x
890 (format "\\(%s\\)" x)))
891 (make-list len all)
892 ".*?"))))))
893
894 (defun ivy--regex-plus (str)
895 "Build a regex sequence from STR.
896 Spaces are wild, everything before \"!\" should match.
897 Everything after \"!\" should not match."
898 (let ((parts (split-string str "!" t)))
899 (cl-case (length parts)
900 (0
901 "")
902 (1
903 (ivy--regex (car parts)))
904 (2
905 (let ((res
906 (mapcar #'list
907 (split-string (cadr parts) " " t))))
908 (cons (cons (ivy--regex (car parts)) t)
909 res)))
910 (t (error "Unexpected: use only one !")))))
911
912 ;;** Rest
913 (defun ivy--minibuffer-setup ()
914 "Setup ivy completion in the minibuffer."
915 (set (make-local-variable 'completion-show-inline-help) nil)
916 (set (make-local-variable 'minibuffer-default-add-function)
917 (lambda ()
918 (list ivy--default)))
919 (setq-local max-mini-window-height ivy-height)
920 (add-hook 'post-command-hook #'ivy--exhibit nil t)
921 ;; show completions with empty input
922 (ivy--exhibit))
923
924 (defun ivy--input ()
925 "Return the current minibuffer input."
926 ;; assume one-line minibuffer input
927 (buffer-substring-no-properties
928 (minibuffer-prompt-end)
929 (line-end-position)))
930
931 (defun ivy--cleanup ()
932 "Delete the displayed completion candidates."
933 (save-excursion
934 (goto-char (minibuffer-prompt-end))
935 (delete-region (line-end-position) (point-max))))
936
937 (defvar ivy--full-length nil
938 "When :dynamic-collection is non-nil, this can be the total amount of candidates.")
939
940 (defvar ivy--old-text ""
941 "Store old `ivy-text' for dynamic completion.")
942
943 (defun ivy--insert-prompt ()
944 "Update the prompt according to `ivy--prompt'."
945 (when ivy--prompt
946 (unless (memq this-command '(ivy-done ivy-alt-done ivy-partial-or-done
947 counsel-find-symbol))
948 (setq ivy--prompt-extra ""))
949 (let (head tail)
950 (if (string-match "\\(.*\\): $" ivy--prompt)
951 (progn
952 (setq head (match-string 1 ivy--prompt))
953 (setq tail ": "))
954 (setq head (substring ivy--prompt 0 -1))
955 (setq tail " "))
956 (let ((inhibit-read-only t)
957 (std-props '(front-sticky t rear-nonsticky t field t read-only t))
958 (n-str
959 (format
960 (concat head
961 ivy--prompt-extra
962 tail
963 (if ivy--directory
964 (abbreviate-file-name ivy--directory)
965 ""))
966 (or (and (ivy-state-dynamic-collection ivy-last)
967 ivy--full-length)
968 ivy--length))))
969 (save-excursion
970 (goto-char (point-min))
971 (delete-region (point-min) (minibuffer-prompt-end))
972 (set-text-properties 0 (length n-str)
973 `(face minibuffer-prompt ,@std-props)
974 n-str)
975 (ivy--set-match-props n-str "confirm"
976 `(face ivy-confirm-face ,@std-props))
977 (ivy--set-match-props n-str "match required"
978 `(face ivy-match-required-face ,@std-props))
979 (insert n-str))
980 ;; get out of the prompt area
981 (constrain-to-field nil (point-max))))))
982
983 (defun ivy--set-match-props (str match props)
984 "Set STR text proprties that match MATCH to PROPS."
985 (when (string-match match str)
986 (set-text-properties
987 (match-beginning 0)
988 (match-end 0)
989 props
990 str)))
991
992 (defvar inhibit-message)
993
994 (defun ivy--exhibit ()
995 "Insert Ivy completions display.
996 Should be run via minibuffer `post-command-hook'."
997 (setq ivy-text (ivy--input))
998 (if (ivy-state-dynamic-collection ivy-last)
999 ;; while-no-input would cause annoying
1000 ;; "Waiting for process to die...done" message interruptions
1001 (let ((inhibit-message t))
1002 (while-no-input
1003 (unless (equal ivy--old-text ivy-text)
1004 (cl-letf ((store (ivy-state-dynamic-collection ivy-last))
1005 ((ivy-state-dynamic-collection ivy-last) nil))
1006 (setq ivy--all-candidates (funcall store ivy-text))))
1007 (ivy--insert-minibuffer (ivy--format ivy--all-candidates))))
1008 (cond (ivy--directory
1009 (if (string-match "/$" ivy-text)
1010 (if (member ivy-text ivy--all-candidates)
1011 (ivy--cd (expand-file-name ivy-text ivy--directory))
1012 (when (string-match "//$" ivy-text)
1013 (ivy--cd "/")))
1014 (if (string-match "~$" ivy-text)
1015 (ivy--cd (expand-file-name "~/")))))
1016 ((eq (ivy-state-collection ivy-last) 'internal-complete-buffer)
1017 (when (or (and (string-match "^ " ivy-text)
1018 (not (string-match "^ " ivy--old-text)))
1019 (and (string-match "^ " ivy--old-text)
1020 (not (string-match "^ " ivy-text))))
1021 (setq ivy--all-candidates
1022 (if (and (> (length ivy-text) 0)
1023 (eq (aref ivy-text 0)
1024 ?\ ))
1025 (ivy--buffer-list " ")
1026 (ivy--buffer-list "" ivy-use-virtual-buffers)))
1027 (setq ivy--old-re nil))))
1028 (ivy--insert-minibuffer
1029 (ivy--format
1030 (ivy--filter ivy-text ivy--all-candidates))))
1031 (setq ivy--old-text ivy-text))
1032
1033 (defun ivy--insert-minibuffer (text)
1034 "Insert TEXT into minibuffer with appropriate cleanup."
1035 (let ((resize-mini-windows nil)
1036 (buffer-undo-list t)
1037 (update-fn (ivy-state-update-fn ivy-last))
1038 deactivate-mark)
1039 (ivy--cleanup)
1040 (when update-fn
1041 (funcall update-fn))
1042 (ivy--insert-prompt)
1043 ;; Do nothing if while-no-input was aborted.
1044 (when (stringp text)
1045 (save-excursion
1046 (forward-line 1)
1047 (insert text)))))
1048
1049 (defun ivy--add-face (str face)
1050 "Propertize STR with FACE.
1051 `font-lock-append-text-property' is used, since it's better than
1052 `propertize' or `add-face-text-property' in this case."
1053 (require 'colir)
1054 (condition-case nil
1055 (colir-blend-face-background 0 (length str) face str)
1056 (error
1057 (ignore-errors
1058 (font-lock-append-text-property 0 (length str) 'face face str))))
1059 str)
1060
1061 (defun ivy--filter (name candidates)
1062 "Return all items that match NAME in CANDIDATES.
1063 CANDIDATES are assumed to be static."
1064 (let* ((re (funcall ivy--regex-function name))
1065 (matcher (ivy-state-matcher ivy-last))
1066 (cands (cond
1067 (matcher
1068 (let ((ivy--old-re re))
1069 (cl-remove-if-not matcher candidates)))
1070 ((and (equal re ivy--old-re)
1071 ivy--old-cands)
1072 ivy--old-cands)
1073 ((and ivy--old-re
1074 (stringp re)
1075 (stringp ivy--old-re)
1076 (not (string-match "\\\\" ivy--old-re))
1077 (not (equal ivy--old-re ""))
1078 (memq (cl-search
1079 (if (string-match "\\\\)$" ivy--old-re)
1080 (substring ivy--old-re 0 -2)
1081 ivy--old-re)
1082 re) '(0 2)))
1083 (ignore-errors
1084 (cl-remove-if-not
1085 (lambda (x) (string-match re x))
1086 ivy--old-cands)))
1087 (t
1088 (let ((re-list (if (stringp re) (list (cons re t)) re))
1089 (res candidates))
1090 (dolist (re re-list)
1091 (setq res
1092 (ignore-errors
1093 (funcall
1094 (if (cdr re)
1095 #'cl-remove-if-not
1096 #'cl-remove-if)
1097 `(lambda (x) (string-match ,(car re) x))
1098 res))))
1099 res))))
1100 (tail (nthcdr ivy--index ivy--old-cands))
1101 idx)
1102 (when (and tail ivy--old-cands)
1103 (unless (and (not (equal re ivy--old-re))
1104 (or (setq ivy--index
1105 (or
1106 (cl-position re cands
1107 :test 'equal)
1108 (and ivy--directory
1109 (cl-position
1110 (concat re "/") cands
1111 :test 'equal))))))
1112 (while (and tail (null idx))
1113 ;; Compare with eq to handle equal duplicates in cands
1114 (setq idx (cl-position (pop tail) cands)))
1115 (setq ivy--index (or idx 0))))
1116 (when (and (string= name "") (not (equal ivy--old-re "")))
1117 (setq ivy--index
1118 (or (cl-position (ivy-state-preselect ivy-last)
1119 cands :test 'equal)
1120 ivy--index)))
1121 (setq ivy--old-re (if cands re ""))
1122 (setq ivy--old-cands cands)))
1123
1124 (defvar ivy-format-function 'ivy-format-function-default
1125 "Function to transform the list of candidates into a string.
1126 This string will be inserted into the minibuffer.")
1127
1128 (defun ivy-format-function-default (cands)
1129 "Transform CANDS into a string for minibuffer."
1130 (let ((ww (window-width)))
1131 (mapconcat
1132 (lambda (s)
1133 (if (> (length s) ww)
1134 (concat (substring s 0 (- ww 3)) "...")
1135 s))
1136 cands "\n")))
1137
1138 (defun ivy-format-function-arrow (cands)
1139 "Transform CANDS into a string for minibuffer."
1140 (let ((i -1))
1141 (mapconcat
1142 (lambda (s)
1143 (concat (if (eq (cl-incf i) ivy--index)
1144 "==> "
1145 " ")
1146 s))
1147 cands "\n")))
1148
1149 (defun ivy--format (cands)
1150 "Return a string for CANDS suitable for display in the minibuffer.
1151 CANDS is a list of strings."
1152 (setq ivy--length (length cands))
1153 (when (>= ivy--index ivy--length)
1154 (setq ivy--index (max (1- ivy--length) 0)))
1155 (if (null cands)
1156 (setq ivy--current "")
1157 (let* ((half-height (/ ivy-height 2))
1158 (start (max 0 (- ivy--index half-height)))
1159 (end (min (+ start (1- ivy-height)) ivy--length))
1160 (cands (cl-subseq cands start end))
1161 (index (min ivy--index half-height (1- (length cands)))))
1162 (when ivy--directory
1163 (setq cands (mapcar (lambda (x)
1164 (if (string-match-p "/$" x)
1165 (propertize x 'face 'ivy-subdir)
1166 x))
1167 cands)))
1168 (setq ivy--current (copy-sequence (nth index cands)))
1169 (setf (nth index cands)
1170 (ivy--add-face ivy--current 'ivy-current-match))
1171 (let* ((ivy--index index)
1172 (res (concat "\n" (funcall ivy-format-function cands))))
1173 (put-text-property 0 (length res) 'read-only nil res)
1174 res))))
1175
1176 (defvar ivy--virtual-buffers nil
1177 "Store the virtual buffers alist.")
1178
1179 (defvar recentf-list)
1180 (defvar ido-use-faces)
1181
1182 (defun ivy--virtual-buffers ()
1183 "Adapted from `ido-add-virtual-buffers-to-list'."
1184 (unless recentf-mode
1185 (recentf-mode 1))
1186 (let ((bookmarks (and (boundp 'bookmark-alist)
1187 bookmark-alist))
1188 virtual-buffers name)
1189 (dolist (head (append
1190 recentf-list
1191 (delete " - no file -"
1192 (delq nil (mapcar (lambda (bookmark)
1193 (cdr (assoc 'filename bookmark)))
1194 bookmarks)))))
1195 (setq name (file-name-nondirectory head))
1196 (when (equal name "")
1197 (setq name (file-name-nondirectory (directory-file-name head))))
1198 (when (equal name "")
1199 (setq name head))
1200 (and (not (equal name ""))
1201 (null (get-file-buffer head))
1202 (not (assoc name virtual-buffers))
1203 (push (cons name head) virtual-buffers)))
1204 (when virtual-buffers
1205 (if ido-use-faces
1206 (dolist (comp virtual-buffers)
1207 (put-text-property 0 (length (car comp))
1208 'face 'ido-virtual
1209 (car comp))))
1210 (setq ivy--virtual-buffers (nreverse virtual-buffers))
1211 (mapcar #'car ivy--virtual-buffers))))
1212
1213 (defun ivy--buffer-list (str &optional virtual)
1214 "Return the buffers that match STR.
1215 When VIRTUAL is non-nil, add virtual buffers."
1216 (delete-dups
1217 (append
1218 (mapcar
1219 (lambda (x)
1220 (if (with-current-buffer x
1221 (file-remote-p
1222 (abbreviate-file-name default-directory)))
1223 (propertize x 'face 'ivy-remote)
1224 x))
1225 (all-completions str 'internal-complete-buffer))
1226 (and virtual
1227 (ivy--virtual-buffers)))))
1228
1229 (defun ivy--switch-buffer-action ()
1230 "Finalizer for `ivy-switch-buffer'."
1231 (if (zerop (length ivy--current))
1232 (switch-to-buffer
1233 ivy-text nil 'force-same-window)
1234 (let ((virtual (assoc ivy--current ivy--virtual-buffers)))
1235 (if virtual
1236 (find-file (cdr virtual))
1237 (switch-to-buffer
1238 ivy--current nil 'force-same-window)))))
1239
1240 (defun ivy-switch-buffer ()
1241 "Switch to another buffer."
1242 (interactive)
1243 (if (not ivy-mode)
1244 (call-interactively 'switch-to-buffer)
1245 (ivy-read "Switch to buffer: " 'internal-complete-buffer
1246 :action #'ivy--switch-buffer-action
1247 :preselect (buffer-name (other-buffer (current-buffer))))))
1248
1249 (provide 'ivy)
1250
1251 ;;; ivy.el ends here