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