Only \"./\" and \"../\" apply here. They appear in reverse order."
:type 'list)
+(defcustom ivy-use-virtual-buffers nil
+ "When non-nil, add `recentf-mode' and bookmarks to the list of buffers."
+ :type 'boolean)
+
;;* Keymap
(require 'delsel)
(defvar ivy-minibuffer-map
(define-key map (kbd "C-r") 'ivy-previous-line-or-history)
(define-key map (kbd "SPC") 'self-insert-command)
(define-key map (kbd "DEL") 'ivy-backward-delete-char)
+ (define-key map (kbd "M-DEL") 'ivy-backward-kill-word)
(define-key map (kbd "M-<") 'ivy-beginning-of-buffer)
(define-key map (kbd "M->") 'ivy-end-of-buffer)
(define-key map (kbd "<left>") 'ivy-beginning-of-buffer)
;; The window in which `ivy-read' was called
window
action
- unwind)
+ unwind
+ re-builder
+ matcher
+ ;; When this is non-nil, call it for each input change to get new candidates
+ dynamic-collection)
(defvar ivy-last nil
"The last parameters passed to `ivy-read'.")
When called twice in a row, exit the minibuffer with the current
candidate."
(interactive)
- (or (ivy-partial)
- (if (eq this-command last-command)
- (ivy-done)
- (ivy-alt-done))))
+ (if (and (eq (ivy-state-collection ivy-last) 'read-file-name-internal)
+ (string-match "^/" ivy-text))
+ (let ((default-directory ivy--directory))
+ (minibuffer-complete)
+ (setq ivy-text (ivy--input))
+ (when (and (file-directory-p ivy-text)
+ (= ivy--length 1))
+ (ivy--cd (expand-file-name ivy-text))))
+ (or (ivy-partial)
+ (if (eq this-command last-command)
+ (ivy-done)
+ (ivy-alt-done)))))
(defun ivy-partial ()
"Complete the minibuffer text as much as possible."
:update-fn (ivy-state-update-fn ivy-last)
:sort (ivy-state-sort ivy-last)
:action (ivy-state-action ivy-last)
- :unwind (ivy-state-unwind ivy-last)))
+ :unwind (ivy-state-unwind ivy-last)
+ :re-builder (ivy-state-re-builder ivy-last)
+ :matcher (ivy-state-matcher ivy-last)
+ :dynamic-collection (ivy-state-dynamic-collection ivy-last)))
(defun ivy-beginning-of-buffer ()
"Select the first completion candidate."
(when ivy-on-del-error-function
(funcall ivy-on-del-error-function))))))
+(defun ivy-backward-kill-word ()
+ "Forward to `backward-kill-word'."
+ (interactive)
+ (if (and ivy--directory (= (minibuffer-prompt-end) (point)))
+ (progn
+ (ivy--cd (file-name-directory
+ (directory-file-name
+ (expand-file-name
+ ivy--directory))))
+ (ivy--exhibit))
+ (ignore-errors
+ (backward-kill-word 1))))
+
(defvar ivy--regexp-quote 'regexp-quote
"Store the regexp quoting state.")
(cl-defun ivy-read (prompt collection
&key predicate require-match initial-input
history preselect keymap update-fn sort
- action unwind)
+ action unwind re-builder matcher dynamic-collection)
"Read a string in the minibuffer, with completion.
PROMPT is a string to prompt with; normally it ends in a colon
ACTION is a lambda to call after a result was selected.
-UNWIND is a lambda to call before exiting."
+UNWIND is a lambda to call before exiting.
+
+RE-BUILDER is a lambda that transforms text into a regex.
+
+MATCHER can completely override matching.
+
+DYNAMIC-COLLECTION is a function to call to update the list of
+candidates with each input."
(setq ivy-last
(make-ivy-state
:prompt prompt
:sort sort
:action action
:window (selected-window)
- :unwind unwind))
+ :unwind unwind
+ :re-builder re-builder
+ :matcher matcher
+ :dynamic-collection dynamic-collection))
(setq ivy--directory nil)
(setq ivy--regex-function
- (or (and (functionp collection)
+ (or re-builder
+ (and (functionp collection)
(cdr (assoc collection ivy-re-builders-alist)))
(cdr (assoc t ivy-re-builders-alist))
'ivy--regex))
(setq coll (cons initial-input coll)))
(setq initial-input nil)))
((eq collection 'internal-complete-buffer)
- (setq coll
- (mapcar (lambda (x)
- (if (with-current-buffer x
- (file-remote-p
- (abbreviate-file-name default-directory)))
- (propertize x 'face 'ivy-remote)
- x))
- (all-completions "" collection predicate))))
+ (setq coll (ivy--buffer-list "" ivy-use-virtual-buffers)))
((or (functionp collection)
(vectorp collection)
(listp (car collection)))
coll))
(setq coll (cons preselect coll))))
(setq ivy--index (or
+ (and dynamic-collection
+ ivy--index)
(and preselect
(ivy--preselect-index
coll initial-input preselect))
(minibuffer-with-setup-hook
#'ivy--minibuffer-setup
(let* ((hist (or history 'ivy-history))
+ (minibuffer-completion-table collection)
+ (minibuffer-completion-predicate predicate)
(res (read-from-minibuffer
prompt
initial-input
(goto-char (minibuffer-prompt-end))
(delete-region (line-end-position) (point-max))))
-(defvar ivy--dynamic-function nil
- "When this is non-nil, call it for each input change to get new candidates.")
-
(defvar ivy--full-length nil
- "When `ivy--dynamic-function' is non-nil, this can be the total amount of candidates.")
+ "When :dynamic-collection is non-nil, this can be the total amount of candidates.")
(defvar ivy--old-text ""
"Store old `ivy-text' for dynamic completion.")
(if ivy--directory
(abbreviate-file-name ivy--directory)
""))
- (or (and ivy--dynamic-function
+ (or (and (ivy-state-dynamic-collection ivy-last)
ivy--full-length)
ivy--length))))
(save-excursion
"Insert Ivy completions display.
Should be run via minibuffer `post-command-hook'."
(setq ivy-text (ivy--input))
- (if ivy--dynamic-function
+ (if (ivy-state-dynamic-collection ivy-last)
;; while-no-input would cause annoying
;; "Waiting for process to die...done" message interruptions
(let ((inhibit-message t))
(while-no-input
(unless (equal ivy--old-text ivy-text)
- (let ((store ivy--dynamic-function)
- (ivy--dynamic-function nil))
+ (cl-letf ((store (ivy-state-dynamic-collection ivy-last))
+ ((ivy-state-dynamic-collection ivy-last) nil))
(setq ivy--all-candidates (funcall store ivy-text))))
(ivy--insert-minibuffer (ivy--format ivy--all-candidates))))
(cond (ivy--directory
(and (string-match "^ " ivy--old-text)
(not (string-match "^ " ivy-text))))
(setq ivy--all-candidates
- (all-completions
- (if (and (> (length ivy-text) 0)
- (eq (aref ivy-text 0)
- ?\ ))
- " "
- "")
- 'internal-complete-buffer))
+ (if (and (> (length ivy-text) 0)
+ (eq (aref ivy-text 0)
+ ?\ ))
+ (ivy--buffer-list " ")
+ (ivy--buffer-list "" ivy-use-virtual-buffers)))
(setq ivy--old-re nil))))
(ivy--insert-minibuffer
(ivy--format
(font-lock-append-text-property 0 (length str) 'face face str))))
str)
-(defun ivy--transform-re (regex)
- "Transform REGEX into another regex.
-This is a work-around for `swiper' and line starts."
- (if (equal (ivy-state-keymap ivy-last) swiper-map)
- (cond
- ((equal regex "^")
- ".")
- ((string-match "^\\(?:\\\\(\\)?\\(\\^\\)" regex)
- (setq ivy--old-re "")
- (replace-match (format "^[0-9 ]\\{%d\\}"
- (1+ swiper--width)) nil t regex 1))
- (t
- regex))
- regex))
-
(defun ivy--filter (name candidates)
"Return all items that match NAME in CANDIDATES.
CANDIDATES are assumed to be static."
- (let* ((re (ivy--transform-re
- (funcall ivy--regex-function name)))
- (cands (cond ((and (equal re ivy--old-re)
- ivy--old-cands)
- ivy--old-cands)
- ((and ivy--old-re
- (stringp re)
- (stringp ivy--old-re)
- (not (string-match "\\\\" ivy--old-re))
- (not (equal ivy--old-re ""))
- (memq (cl-search
- (if (string-match "\\\\)$" ivy--old-re)
- (substring ivy--old-re 0 -2)
- ivy--old-re)
- re) '(0 2)))
- (ignore-errors
- (cl-remove-if-not
- (lambda (x) (string-match re x))
- ivy--old-cands)))
- (t
- (let ((re-list (if (stringp re) (list (cons re t)) re))
- (res candidates))
- (dolist (re re-list)
- (setq res
- (ignore-errors
- (funcall
- (if (cdr re)
- #'cl-remove-if-not
- #'cl-remove-if)
- `(lambda (x) (string-match ,(car re) x))
- res))))
- res))))
+ (let* ((re (funcall ivy--regex-function name))
+ (matcher (ivy-state-matcher ivy-last))
+ (cands (cond
+ (matcher
+ (let ((ivy--old-re re))
+ (cl-remove-if-not matcher candidates)))
+ ((and (equal re ivy--old-re)
+ ivy--old-cands)
+ ivy--old-cands)
+ ((and ivy--old-re
+ (stringp re)
+ (stringp ivy--old-re)
+ (not (string-match "\\\\" ivy--old-re))
+ (not (equal ivy--old-re ""))
+ (memq (cl-search
+ (if (string-match "\\\\)$" ivy--old-re)
+ (substring ivy--old-re 0 -2)
+ ivy--old-re)
+ re) '(0 2)))
+ (ignore-errors
+ (cl-remove-if-not
+ (lambda (x) (string-match re x))
+ ivy--old-cands)))
+ (t
+ (let ((re-list (if (stringp re) (list (cons re t)) re))
+ (res candidates))
+ (dolist (re re-list)
+ (setq res
+ (ignore-errors
+ (funcall
+ (if (cdr re)
+ #'cl-remove-if-not
+ #'cl-remove-if)
+ `(lambda (x) (string-match ,(car re) x))
+ res))))
+ res))))
(tail (nthcdr ivy--index ivy--old-cands))
idx)
(when (and tail ivy--old-cands)
(or (cl-position (ivy-state-preselect ivy-last)
cands :test 'equal)
ivy--index)))
- (setq ivy--old-re re)
+ (setq ivy--old-re (if cands re ""))
(setq ivy--old-cands cands)))
+(defvar ivy-format-function 'ivy-format-function-default
+ "Function to transform the list of candidates into a string.
+This string will be inserted into the minibuffer.")
+
+(defun ivy-format-function-default (cands)
+ "Transform CANDS into a string for minibuffer."
+ (let ((ww (window-width)))
+ (mapconcat
+ (lambda (s)
+ (if (> (length s) ww)
+ (concat (substring s 0 (- ww 3)) "...")
+ s))
+ cands "\n")))
+
+(defun ivy-format-function-arrow (cands)
+ "Transform CANDS into a string for minibuffer."
+ (let ((i -1))
+ (mapconcat
+ (lambda (s)
+ (concat (if (eq (cl-incf i) ivy--index)
+ "==> "
+ " ")
+ s))
+ cands "\n")))
+
(defun ivy--format (cands)
"Return a string for CANDS suitable for display in the minibuffer.
CANDS is a list of strings."
(setq ivy--current (copy-sequence (nth index cands)))
(setf (nth index cands)
(ivy--add-face ivy--current 'ivy-current-match))
- (let* ((ww (window-width))
- (res (concat "\n" (mapconcat
- (lambda (s)
- (if (> (length s) ww)
- (concat (substring s 0 (- ww 3)) "...")
- s))
- cands "\n"))))
+ (let* ((ivy--index index)
+ (res (concat "\n" (funcall ivy-format-function cands))))
(put-text-property 0 (length res) 'read-only nil res)
res))))
(defvar recentf-list)
(defvar ido-use-faces)
-(defvar ido-process-ignore-lists)
-(defvar ido-ignored-list)
-(defvar ido-use-virtual-buffers)
-(declare-function ido-make-buffer-list "ido")
(defun ivy--virtual-buffers ()
"Adapted from `ido-add-virtual-buffers-to-list'."
(setq ivy--virtual-buffers (nreverse virtual-buffers))
(mapcar #'car ivy--virtual-buffers))))
-(defun ivy-buffer-list ()
- "Return the current list of buffers.
-See `ido-make-buffer-list'."
- (require 'ido)
- (setq ivy--virtual-buffers nil)
- (let ((ido-process-ignore-lists t)
- ido-ignored-list)
- (delete-dups
- (append (ido-make-buffer-list nil)
- (and
- ido-use-virtual-buffers
- (ivy--virtual-buffers))))))
+(defun ivy--buffer-list (str &optional virtual)
+ "Return the buffers that match STR.
+When VIRTUAL is non-nil, add virtual buffers."
+ (delete-dups
+ (append
+ (mapcar
+ (lambda (x)
+ (if (with-current-buffer x
+ (file-remote-p
+ (abbreviate-file-name default-directory)))
+ (propertize x 'face 'ivy-remote)
+ x))
+ (all-completions str 'internal-complete-buffer))
+ (and virtual
+ (ivy--virtual-buffers)))))
+
+(defun ivy--switch-buffer-action ()
+ "Finalizer for `ivy-switch-buffer'."
+ (if (zerop (length ivy--current))
+ (switch-to-buffer
+ ivy-text nil 'force-same-window)
+ (let ((virtual (assoc ivy--current ivy--virtual-buffers)))
+ (if virtual
+ (find-file (cdr virtual))
+ (switch-to-buffer
+ ivy--current nil 'force-same-window)))))
(defun ivy-switch-buffer ()
"Switch to another buffer."
(interactive)
(if (not ivy-mode)
(call-interactively 'switch-to-buffer)
- (let ((bl (ivy-buffer-list)))
- (ivy-read "Switch to buffer: " bl
- :action
- (lambda ()
- (let ((virtual (assoc ivy--current ivy--virtual-buffers)))
- (if virtual
- (find-file (cdr virtual))
- (switch-to-buffer
- ivy--current nil 'force-same-window))))))))
+ (ivy-read "Switch to buffer: " 'internal-complete-buffer
+ :action #'ivy--switch-buffer-action
+ :preselect (buffer-name (other-buffer (current-buffer))))))
(provide 'ivy)