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