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