]> code.delx.au - gnu-emacs-elpa/blob - packages/swiper/ivy.el
Merge commit '1380f8c1a51f31514125a08f632d017cdfb8e26e' from hydra
[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-subdir
54 '((t (:weight bold)))
55 "Face used by Ivy for highlighting subdirs in the alternatives.")
56
57 (defcustom ivy-height 10
58 "Number of lines for the minibuffer window."
59 :type 'integer)
60
61 (defcustom ivy-count-format "%-4d "
62 "The style of showing the current candidate count for `ivy-read'.
63 Set this to nil if you don't want the count."
64 :type 'string)
65
66 (defcustom ivy-wrap nil
67 "Whether to wrap around after the first and last candidate."
68 :type 'boolean)
69
70 (defcustom ivy-on-del-error-function 'minibuffer-keyboard-quit
71 "The handler for when `ivy-backward-delete-char' throws.
72 This is usually meant as a quick exit out of the minibuffer."
73 :type 'function)
74
75 (defcustom ivy-extra-directories '("../" "./")
76 "Add this to the front of the list when completing file names.
77 Only \"./\" and \"../\" apply here. They appear in reverse order."
78 :type 'list)
79
80 ;;* User Visible
81 ;;** Keymap
82 (require 'delsel)
83 (defvar ivy-minibuffer-map
84 (let ((map (make-sparse-keymap)))
85 (define-key map (kbd "C-m") 'ivy-done)
86 (define-key map (kbd "C-j") 'ivy-alt-done)
87 (define-key map (kbd "C-n") 'ivy-next-line)
88 (define-key map (kbd "C-p") 'ivy-previous-line)
89 (define-key map (kbd "<down>") 'ivy-next-line)
90 (define-key map (kbd "<up>") 'ivy-previous-line)
91 (define-key map (kbd "C-s") 'ivy-next-line-or-history)
92 (define-key map (kbd "C-r") 'ivy-previous-line-or-history)
93 (define-key map (kbd "SPC") 'self-insert-command)
94 (define-key map (kbd "DEL") 'ivy-backward-delete-char)
95 (define-key map (kbd "M-<") 'ivy-beginning-of-buffer)
96 (define-key map (kbd "M->") 'ivy-end-of-buffer)
97 (define-key map (kbd "<left>") 'ivy-beginning-of-buffer)
98 (define-key map (kbd "<right>") 'ivy-end-of-buffer)
99 (define-key map (kbd "M-n") 'ivy-next-history-element)
100 (define-key map (kbd "M-p") 'ivy-previous-history-element)
101 (define-key map (kbd "C-g") 'minibuffer-keyboard-quit)
102 (define-key map (kbd "C-v") 'ivy-scroll-up-command)
103 (define-key map (kbd "M-v") 'ivy-scroll-down-command)
104 (define-key map (kbd "C-M-n") 'ivy-next-line-and-call)
105 (define-key map (kbd "C-M-p") 'ivy-previous-line-and-call)
106 (define-key map (kbd "M-q") 'ivy-toggle-regexp-quote)
107 map)
108 "Keymap used in the minibuffer.")
109
110 (defvar ivy-history nil
111 "History list of candidates entered in the minibuffer.
112
113 Maximum length of the history list is determined by the value
114 of `history-length', which see.")
115
116 (defvar ivy-require-match t
117 "Store require-match. See `completing-read'.")
118
119 (defvar ivy--directory nil
120 "Current directory when completing file names.")
121
122 (defvar ivy--length 0
123 "Store the amount of viable candidates.")
124
125 (defvar ivy-text ""
126 "Store the user's string as it is typed in.")
127
128 (defvar ivy-window nil
129 "Store the window in which `ivy-read' was called.")
130
131 (defvar ivy--current ""
132 "Current candidate.")
133
134 (defvar ivy--index 0
135 "Store the index of the current candidate.")
136
137 (defvar ivy-exit nil
138 "Store 'done if the completion was successfully selected.
139 Otherwise, store nil.")
140
141 (defvar ivy--action nil
142 "Store a function to call at the end of `ivy--read'.")
143
144 (defvar ivy--persistent-action nil
145 "Store a function to call for current candidate without exiting.")
146
147 (defvar ivy--all-candidates nil
148 "Store the candidates passed to `ivy-read'.")
149
150 (defvar ivy--default nil
151 "Default initial input.")
152
153 (defvar ivy--update-fn nil
154 "Current function to call when current candidate(s) update.")
155
156 (defvar ivy--prompt nil
157 "Store the format-style prompt.
158 When non-nil, it should contain one %d.")
159
160 (defvar ivy--old-re nil
161 "Store the old regexp.")
162
163 (defvar ivy--old-cands nil
164 "Store the candidates matched by `ivy--old-re'.")
165
166 (defvar ivy--regex-function 'ivy--regex
167 "Current function for building a regex.")
168
169 (defvar Info-current-file)
170
171 ;;** Commands
172 (defun ivy-done ()
173 "Exit the minibuffer with the selected candidate."
174 (interactive)
175 (delete-minibuffer-contents)
176 (when (cond (ivy--directory
177 (insert
178 (if (zerop ivy--length)
179 (expand-file-name ivy-text ivy--directory)
180 (expand-file-name ivy--current ivy--directory)))
181 (setq ivy-exit 'done))
182 ((zerop ivy--length)
183 (if (memq ivy-require-match
184 '(nil confirm confirm-after-completion))
185 (progn
186 (insert ivy-text)
187 (setq ivy-exit 'done))
188 (unless (string-match "match required" ivy--prompt)
189 (setq ivy--prompt
190 (if (string-match ": $" ivy--prompt)
191 (concat
192 (substring ivy--prompt 0 -2)
193 " (match required): ")
194 (concat
195 ivy--prompt
196 "(match required) "))))
197 (insert ivy-text)
198 (ivy--exhibit)
199 nil))
200 (t
201 (insert ivy--current)
202 (setq ivy-exit 'done)))
203 (exit-minibuffer)))
204
205 (defun ivy-alt-done (&optional arg)
206 "Exit the minibuffer with the selected candidate.
207 When ARG is t, exit with current text, ignoring the candidates."
208 (interactive "P")
209 (if arg
210 (ivy-immediate-done)
211 (let (dir)
212 (if (and ivy--directory
213 (not (string= ivy--current "./"))
214 (cl-plusp ivy--length)
215 (file-directory-p
216 (setq dir (expand-file-name
217 ivy--current ivy--directory))))
218 (progn
219 (ivy--cd dir)
220 (ivy--exhibit))
221 (ivy-done)))))
222
223 (defun ivy-immediate-done ()
224 "Exit the minibuffer with the current input."
225 (interactive)
226 (delete-minibuffer-contents)
227 (insert ivy-text)
228 (setq ivy-exit 'done)
229 (exit-minibuffer))
230
231 (defun ivy-beginning-of-buffer ()
232 "Select the first completion candidate."
233 (interactive)
234 (setq ivy--index 0))
235
236 (defun ivy-end-of-buffer ()
237 "Select the last completion candidate."
238 (interactive)
239 (setq ivy--index (1- ivy--length)))
240
241 (defun ivy-scroll-up-command ()
242 "Scroll the candidates upward by the minibuffer height."
243 (interactive)
244 (setq ivy--index (min (+ ivy--index ivy-height)
245 (1- ivy--length))))
246
247 (defun ivy-scroll-down-command ()
248 "Scroll the candidates downward by the minibuffer height."
249 (interactive)
250 (setq ivy--index (max (- ivy--index ivy-height)
251 0)))
252
253 (defun ivy-next-line (&optional arg)
254 "Move cursor vertically down ARG candidates."
255 (interactive "p")
256 (setq arg (or arg 1))
257 (cl-incf ivy--index arg)
258 (when (>= ivy--index (1- ivy--length))
259 (if ivy-wrap
260 (ivy-beginning-of-buffer)
261 (setq ivy--index (1- ivy--length)))))
262
263 (defun ivy-next-line-or-history (&optional arg)
264 "Move cursor vertically down ARG candidates.
265 If the input is empty, select the previous history element instead."
266 (interactive "p")
267 (when (string= ivy-text "")
268 (ivy-previous-history-element 1))
269 (ivy-next-line arg))
270
271 (defun ivy-previous-line (&optional arg)
272 "Move cursor vertically up ARG candidates."
273 (interactive "p")
274 (setq arg (or arg 1))
275 (cl-decf ivy--index arg)
276 (when (< ivy--index 0)
277 (if ivy-wrap
278 (ivy-end-of-buffer)
279 (setq ivy--index 0))))
280
281 (defun ivy-previous-line-or-history (arg)
282 "Move cursor vertically up ARG candidates.
283 If the input is empty, select the previous history element instead."
284 (interactive "p")
285 (when (string= ivy-text "")
286 (ivy-previous-history-element 1))
287 (ivy-previous-line arg))
288
289 (defun ivy-next-line-and-call (&optional arg)
290 "Move cursor vertically down ARG candidates."
291 (interactive "p")
292 (ivy-next-line arg)
293 (ivy--exhibit)
294 (when ivy--persistent-action
295 (with-selected-window ivy-window
296 (funcall ivy--persistent-action ivy--current))))
297
298 (defun ivy-previous-line-and-call (&optional arg)
299 "Move cursor vertically down ARG candidates."
300 (interactive "p")
301 (ivy-previous-line arg)
302 (ivy--exhibit)
303 (when ivy--persistent-action
304 (with-selected-window ivy-window
305 (funcall ivy--persistent-action ivy--current))))
306
307 (defun ivy-previous-history-element (arg)
308 "Forward to `previous-history-element' with ARG."
309 (interactive "p")
310 (previous-history-element arg)
311 (move-end-of-line 1))
312
313 (defun ivy-next-history-element (arg)
314 "Forward to `next-history-element' with ARG."
315 (interactive "p")
316 (next-history-element arg)
317 (move-end-of-line 1))
318
319 (defun ivy--cd (dir)
320 "When completing file names, move to directory DIR."
321 (if (null ivy--directory)
322 (error "Unexpected")
323 (setq ivy--old-cands nil)
324 (setq ivy--all-candidates
325 (ivy--sorted-files (setq ivy--directory dir)))
326 (setq ivy-text "")
327 (delete-minibuffer-contents)))
328
329 (defun ivy-backward-delete-char ()
330 "Forward to `backward-delete-char'.
331 On error (read-only), call `ivy-on-del-error-function'."
332 (interactive)
333 (if (and ivy--directory (= (minibuffer-prompt-end) (point)))
334 (progn
335 (ivy--cd (file-name-directory
336 (directory-file-name
337 (expand-file-name
338 ivy--directory))))
339 (ivy--exhibit))
340 (condition-case nil
341 (backward-delete-char 1)
342 (error
343 (when ivy-on-del-error-function
344 (funcall ivy-on-del-error-function))))))
345
346 (defvar ivy--regexp-quote 'regexp-quote
347 "Store the regexp quoting state.")
348
349 (defun ivy-toggle-regexp-quote ()
350 "Toggle the regexp quoting."
351 (interactive)
352 (setq ivy--old-re nil)
353 (cl-rotatef ivy--regex-function ivy--regexp-quote))
354
355 (defun ivy-sort-file-function-default (x y)
356 "Compare two files X and Y.
357 Prioritize directories."
358 (if (get-text-property 0 'dirp x)
359 (if (get-text-property 0 'dirp y)
360 (string< x y)
361 t)
362 (if (get-text-property 0 'dirp y)
363 nil
364 (string< x y))))
365
366 (defvar ivy-sort-functions-alist
367 '((read-file-name-internal . ivy-sort-file-function-default)
368 (internal-complete-buffer . nil)
369 (counsel-git-grep-function . nil)
370 (t . string-lessp))
371 "An alist of sorting functions for each collection function.
372 For each entry, nil means no sorting.
373 The entry associated to t is used for all fall-through cases.")
374
375 (defvar ivy-re-builders-alist
376 '((t . ivy--regex))
377 "An alist of regex building functions for each collection function.
378 Each function should take a string and return a valid regex.
379 The entry associated to t is used for all fall-through cases.
380 Possible choices: `ivy--regex', `regexp-quote'.")
381
382 (defcustom ivy-sort-max-size 30000
383 "Sorting won't be done for collections larger than this."
384 :type 'integer)
385
386 (defun ivy--sorted-files (dir)
387 "Return the list of files in DIR.
388 Directories come first."
389 (let* ((default-directory dir)
390 (seq (all-completions "" 'read-file-name-internal))
391 sort-fn)
392 (if (equal dir "/")
393 seq
394 (setq seq (delete "./" (delete "../" seq)))
395 (when (eq (setq sort-fn (cdr (assoc 'read-file-name-internal
396 ivy-sort-functions-alist)))
397 'ivy-sort-file-function-default)
398 (setq seq (mapcar (lambda (x)
399 (propertize x 'dirp (string-match-p "/$" x)))
400 seq)))
401 (when sort-fn
402 (setq seq (cl-sort seq sort-fn)))
403 (dolist (dir ivy-extra-directories)
404 (push dir seq))
405 seq)))
406
407 ;;** Entry Point
408 (cl-defun ivy-read (prompt collection
409 &key predicate require-match initial-input
410 history preselect keymap update-fn sort)
411 "Read a string in the minibuffer, with completion.
412
413 PROMPT is a string to prompt with; normally it ends in a colon
414 and a space. When PROMPT contains %d, it will be updated with
415 the current number of matching candidates.
416 See also `ivy-count-format'.
417
418 COLLECTION is a list of strings.
419
420 If INITIAL-INPUT is non-nil, insert it in the minibuffer initially.
421
422 KEYMAP is composed together with `ivy-minibuffer-map'.
423
424 If PRESELECT is non-nil select the corresponding candidate out of
425 the ones that match INITIAL-INPUT.
426
427 UPDATE-FN is called each time the current candidate(s) is changed.
428
429 When SORT is t, refer to `ivy-sort-functions-alist' for sorting."
430 (setq ivy--directory nil)
431 (setq ivy-require-match require-match)
432 (setq ivy-window (selected-window))
433 (setq ivy--regex-function
434 (or (and (functionp collection)
435 (cdr (assoc collection ivy-re-builders-alist)))
436 (cdr (assoc t ivy-re-builders-alist))
437 'ivy--regex))
438 (setq ivy--subexps 0)
439 (setq ivy--regexp-quote 'regexp-quote)
440 (let (coll sort-fn)
441 (cond ((eq collection 'Info-read-node-name-1)
442 (if (equal Info-current-file "dir")
443 (setq coll
444 (mapcar (lambda (x) (format "(%s)" x))
445 (cl-delete-duplicates
446 (all-completions "(" collection predicate)
447 :test 'equal)))
448 (setq coll (all-completions "" collection predicate))))
449 ((eq collection 'read-file-name-internal)
450 (setq ivy--directory default-directory)
451 (setq coll
452 (ivy--sorted-files default-directory))
453 (when initial-input
454 (unless (or require-match
455 (equal initial-input default-directory))
456 (setq coll (cons initial-input coll)))
457 (setq initial-input nil)))
458 ((or (functionp collection)
459 (vectorp collection)
460 (listp (car collection)))
461 (setq coll (all-completions "" collection predicate)))
462 ((hash-table-p collection)
463 (error "Hash table as a collection unsupported"))
464 (t
465 (setq coll collection)))
466 (when sort
467 (if (and (functionp collection)
468 (setq sort-fn (assoc collection ivy-sort-functions-alist)))
469 (when (and (setq sort-fn (cdr sort-fn))
470 (not (eq collection 'read-file-name-internal)))
471 (setq coll (cl-sort coll sort-fn)))
472 (unless (eq history 'org-refile-history)
473 (if (and (setq sort-fn (cdr (assoc t ivy-sort-functions-alist)))
474 (<= (length coll) ivy-sort-max-size))
475 (setq coll (cl-sort (copy-sequence coll) sort-fn))))))
476 (when preselect
477 (unless (or require-match
478 (member preselect coll))
479 (setq coll (cons preselect coll))))
480 (setq ivy--index (or
481 (and preselect
482 (ivy--preselect-index
483 coll initial-input preselect))
484 0))
485 (setq ivy--old-re nil)
486 (setq ivy--old-cands nil)
487 (setq ivy-text "")
488 (setq ivy--all-candidates coll)
489 (setq ivy--update-fn update-fn)
490 (setq ivy-exit nil)
491 (setq ivy--default (or (thing-at-point 'symbol) ""))
492 (setq ivy--prompt
493 (cond ((string-match "%.*d" prompt)
494 prompt)
495 ((string-match "%.*d" ivy-count-format)
496 (concat ivy-count-format prompt))
497 (ivy--directory
498 prompt)
499 (t
500 nil)))
501 (setq ivy--action nil)
502 (prog1
503 (unwind-protect
504 (minibuffer-with-setup-hook
505 #'ivy--minibuffer-setup
506 (let* ((hist (or history 'ivy-history))
507 (res (read-from-minibuffer
508 prompt
509 initial-input
510 (make-composed-keymap keymap ivy-minibuffer-map)
511 nil
512 hist)))
513 (when (eq ivy-exit 'done)
514 (set hist (cons ivy-text
515 (delete ivy-text
516 (cdr (symbol-value hist)))))
517 res)))
518 (remove-hook 'post-command-hook #'ivy--exhibit))
519 (when ivy--action
520 (funcall ivy--action)))))
521
522 (defun ivy-completing-read (prompt collection
523 &optional predicate require-match initial-input
524 history def _inherit-input-method)
525 "Read a string in the minibuffer, with completion.
526
527 This is an interface that conforms to `completing-read', so that
528 it can be used for `completing-read-function'.
529
530 PROMPT is a string to prompt with; normally it ends in a colon and a space.
531 COLLECTION can be a list of strings, an alist, an obarray or a hash table.
532 PREDICATE limits completion to a subset of COLLECTION.
533
534 REQUIRE-MATCH is stored into `ivy-require-match'. See `completing-read'.
535 INITIAL-INPUT is a string that can be inserted into the minibuffer initially.
536 _HISTORY is ignored for now.
537 DEF is the default value.
538 _INHERIT-INPUT-METHOD is ignored for now.
539
540 The history, defaults and input-method arguments are ignored for now."
541 (when (listp def)
542 (setq def (car def)))
543 (ivy-read prompt collection
544 :predicate predicate
545 :require-match require-match
546 :initial-input initial-input
547 :preselect def
548 :history history
549 :keymap nil
550 :sort t))
551
552 ;;;###autoload
553 (define-minor-mode ivy-mode
554 "Toggle Ivy mode on or off.
555 With ARG, turn Ivy mode on if arg is positive, off otherwise.
556 Turning on Ivy mode will set `completing-read-function' to
557 `ivy-completing-read'.
558
559 \\{ivy-minibuffer-map}"
560 :group 'ivy
561 :global t
562 :lighter " ivy"
563 (if ivy-mode
564 (setq completing-read-function 'ivy-completing-read)
565 (setq completing-read-function 'completing-read-default)))
566
567 (defun ivy--preselect-index (candidates initial-input preselect)
568 "Return the index in CANDIDATES filtered by INITIAL-INPUT for PRESELECT."
569 (when initial-input
570 (setq candidates
571 (cl-remove-if-not
572 (lambda (x)
573 (string-match initial-input x))
574 candidates)))
575 (or (cl-position preselect candidates :test 'equal)
576 (cl-position-if
577 (lambda (x)
578 (string-match preselect x))
579 candidates)))
580
581 ;;* Implementation
582 ;;** Regex
583 (defvar ivy--subexps 0
584 "Number of groups in the current `ivy--regex'.")
585
586 (defvar ivy--regex-hash
587 (make-hash-table :test 'equal)
588 "Store pre-computed regex.")
589
590 (defun ivy--regex (str &optional greedy)
591 "Re-build regex from STR in case it has a space.
592 When GREEDY is non-nil, join words in a greedy way."
593 (let ((hashed (unless greedy
594 (gethash str ivy--regex-hash))))
595 (if hashed
596 (prog1 (cdr hashed)
597 (setq ivy--subexps (car hashed)))
598 (cdr (puthash str
599 (let ((subs (split-string str " +" t)))
600 (if (= (length subs) 1)
601 (cons
602 (setq ivy--subexps 0)
603 (car subs))
604 (cons
605 (setq ivy--subexps (length subs))
606 (mapconcat
607 (lambda (x)
608 (if (string-match "^\\\\(.*\\\\)$" x)
609 x
610 (format "\\(%s\\)" x)))
611 subs
612 (if greedy
613 ".*"
614 ".*?")))))
615 ivy--regex-hash)))))
616
617 ;;** Rest
618 (defun ivy--minibuffer-setup ()
619 "Setup ivy completion in the minibuffer."
620 (set (make-local-variable 'completion-show-inline-help) nil)
621 (set (make-local-variable 'minibuffer-default-add-function)
622 (lambda ()
623 (list ivy--default)))
624 (setq-local max-mini-window-height ivy-height)
625 (add-hook 'post-command-hook #'ivy--exhibit nil t)
626 ;; show completions with empty input
627 (ivy--exhibit))
628
629 (defun ivy--input ()
630 "Return the current minibuffer input."
631 ;; assume one-line minibuffer input
632 (buffer-substring-no-properties
633 (minibuffer-prompt-end)
634 (line-end-position)))
635
636 (defun ivy--cleanup ()
637 "Delete the displayed completion candidates."
638 (save-excursion
639 (goto-char (minibuffer-prompt-end))
640 (delete-region (line-end-position) (point-max))))
641
642 (defvar ivy--dynamic-function nil
643 "When this is non-nil, call it for each input change to get new candidates.")
644
645 (defvar ivy--full-length nil
646 "When `ivy--dynamic-function' is non-nil, this can be the total amount of candidates.")
647
648 (defvar ivy--old-text nil
649 "Store old `ivy-text' for dynamic completion.")
650
651 (defun ivy--insert-prompt ()
652 "Update the prompt according to `ivy--prompt'."
653 (when ivy--prompt
654 (let ((inhibit-read-only t)
655 (n-str
656 (format
657 (if ivy--directory
658 (concat ivy--prompt (abbreviate-file-name ivy--directory))
659 ivy--prompt)
660 (or (and ivy--dynamic-function
661 ivy--full-length)
662 ivy--length))))
663 (save-excursion
664 (goto-char (point-min))
665 (delete-region (point-min) (minibuffer-prompt-end))
666 (set-text-properties
667 0 (length n-str)
668 '(front-sticky t rear-nonsticky t field t read-only t face minibuffer-prompt)
669 n-str)
670 (insert n-str))
671 ;; get out of the prompt area
672 (constrain-to-field nil (point-max)))))
673
674 (defvar inhibit-message)
675
676 (defun ivy--exhibit ()
677 "Insert Ivy completions display.
678 Should be run via minibuffer `post-command-hook'."
679 (setq ivy-text (ivy--input))
680 (if ivy--dynamic-function
681 ;; while-no-input would cause annoying
682 ;; "Waiting for process to die...done" message interruptions
683 (let ((inhibit-message t))
684 (while-no-input
685 (unless (equal ivy--old-text ivy-text)
686 (let ((store ivy--dynamic-function)
687 (ivy--dynamic-function nil))
688 (setq ivy--all-candidates (funcall store ivy-text)))
689 (setq ivy--old-text ivy-text))
690 (ivy--insert-minibuffer (ivy--format ivy--all-candidates))))
691 (when ivy--directory
692 (if (string-match "/$" ivy-text)
693 (if (member ivy-text ivy--all-candidates)
694 (ivy--cd (expand-file-name ivy-text ivy--directory))
695 (when (string-match "//$" ivy-text)
696 (ivy--cd "/")))
697 (if (string-match "~$" ivy-text)
698 (ivy--cd (expand-file-name "~/")))))
699 (ivy--insert-minibuffer
700 (ivy--format
701 (ivy--filter ivy-text ivy--all-candidates)))))
702
703 (defun ivy--insert-minibuffer (text)
704 (ivy--cleanup)
705 (let ((buffer-undo-list t)
706 deactivate-mark)
707 (when ivy--update-fn
708 (funcall ivy--update-fn))
709 (ivy--insert-prompt)
710 ;; Do nothing if while-no-input was aborted.
711 (when (stringp text)
712 (save-excursion
713 (forward-line 1)
714 (insert text)))))
715
716 (defun ivy--add-face (str face)
717 "Propertize STR with FACE.
718 `font-lock-append-text-property' is used, since it's better than
719 `propertize' or `add-face-text-property' in this case."
720 (ignore-errors
721 (font-lock-append-text-property 0 (length str) 'face face str))
722 str)
723
724 (defun ivy--filter (name candidates)
725 "Return the matches for NAME for CANDIDATES.
726 CANDIDATES are assumed to be static."
727 (let* ((re (funcall ivy--regex-function name))
728 (cands (cond ((and (equal re ivy--old-re)
729 ivy--old-cands)
730 ivy--old-cands)
731 ((and ivy--old-re
732 (not (string-match "\\\\" ivy--old-re))
733 (not (equal ivy--old-re ""))
734 (memq (cl-search
735 (if (string-match "\\\\)$" ivy--old-re)
736 (substring ivy--old-re 0 -2)
737 ivy--old-re)
738 re) '(0 2)))
739 (ignore-errors
740 (cl-remove-if-not
741 (lambda (x) (string-match re x))
742 ivy--old-cands)))
743 (t
744 (ignore-errors
745 (cl-remove-if-not
746 (lambda (x) (string-match re x))
747 candidates)))))
748 (tail (nthcdr ivy--index ivy--old-cands))
749 idx)
750 (when (and tail ivy--old-cands)
751 (unless (and (not (equal re ivy--old-re))
752 (or (setq ivy--index
753 (or
754 (cl-position re cands
755 :test 'equal)
756 (and ivy--directory
757 (cl-position
758 (concat re "/") cands
759 :test 'equal))))))
760 (while (and tail (null idx))
761 ;; Compare with eq to handle equal duplicates in cands
762 (setq idx (cl-position (pop tail) cands)))
763 (setq ivy--index (or idx 0))))
764 (setq ivy--old-re re)
765 (setq ivy--old-cands cands)))
766
767 (defun ivy--format (cands)
768 "Return a string for CANDS suitable for display in the minibuffer.
769 CANDS is a list of strings."
770 (setq ivy--length (length cands))
771 (when (>= ivy--index ivy--length)
772 (setq ivy--index (max (1- ivy--length) 0)))
773 (if (null cands)
774 ""
775 (let* ((half-height (/ ivy-height 2))
776 (start (max 0 (- ivy--index half-height)))
777 (end (min (+ start (1- ivy-height)) ivy--length))
778 (cands (cl-subseq cands start end))
779 (index (min ivy--index half-height (1- (length cands)))))
780 (when ivy--directory
781 (setq cands (mapcar (lambda (x)
782 (if (string-match-p "/$" x)
783 (propertize x 'face 'ivy-subdir)
784 x))
785 cands)))
786 (setq ivy--current (copy-sequence (nth index cands)))
787 (setf (nth index cands)
788 (ivy--add-face ivy--current 'ivy-current-match))
789 (let* ((ww (window-width))
790 (res (concat "\n" (mapconcat
791 (lambda (s)
792 (if (> (length s) ww)
793 (concat (substring s 0 (- ww 3)) "...")
794 s))
795 cands "\n"))))
796 (put-text-property 0 (length res) 'read-only nil res)
797 res))))
798
799 (provide 'ivy)
800
801 ;;; ivy.el ends here