:group 'convenience)
(defface ivy-current-match
- '((t (:inherit highlight)))
+ '((((class color) (background light))
+ :background "#1a4b77" :foreground "white")
+ (((class color) (background dark))
+ :background "#65a7e2" :foreground "black"))
"Face used by Ivy for highlighting first match.")
(defface ivy-confirm-face
(defcustom ivy-count-format "%-4d "
"The style of showing the current candidate count for `ivy-read'.
-Set this to nil if you don't want the count."
- :type 'string)
+Set this to nil if you don't want the count. You can also set it
+to e.g. \"(%d/%d) \" if you want to see both the candidate index
+and the candidate count."
+ :type '(choice (const :tag "Count disabled" nil) string))
(defcustom ivy-wrap nil
"Whether to wrap around after the first and last candidate."
:type 'boolean)
+(defcustom ivy-display-style nil
+ "The style for formatting the minibuffer.
+
+By default, the matched strings will be copied as they are.
+
+With the fancy method, the matching parts of the regexp will be
+additionally highlighted, just like `swiper' does it."
+ :type '(choice
+ (const :tag "Plain" nil)
+ (const :tag "Fancy" fancy)))
+
(defcustom ivy-on-del-error-function 'minibuffer-keyboard-quit
"The handler for when `ivy-backward-delete-char' throws.
This is usually meant as a quick exit out of the minibuffer."
"When non-nil, add `recentf-mode' and bookmarks to the list of buffers."
:type 'boolean)
+(defvar ivy--actions-list nil
+ "A list of extra actions per command.")
+
+(defun ivy-set-actions (cmd actions)
+ "Set CMD extra exit points to ACTIONS."
+ (setq ivy--actions-list
+ (plist-put ivy--actions-list cmd actions)))
+
;;* Keymap
(require 'delsel)
(defvar ivy-minibuffer-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-m") 'ivy-done)
+ (define-key map (kbd "C-M-m") 'ivy-call)
(define-key map (kbd "C-j") 'ivy-alt-done)
+ (define-key map (kbd "C-M-j") 'ivy-immediate-done)
(define-key map (kbd "TAB") 'ivy-partial-or-done)
(define-key map (kbd "C-n") 'ivy-next-line)
(define-key map (kbd "C-p") 'ivy-previous-line)
(define-key map (kbd "M-d") 'ivy-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)
- (define-key map (kbd "<right>") 'ivy-end-of-buffer)
(define-key map (kbd "M-n") 'ivy-next-history-element)
(define-key map (kbd "M-p") 'ivy-previous-history-element)
(define-key map (kbd "C-g") 'minibuffer-keyboard-quit)
(define-key map (kbd "M-j") 'ivy-yank-word)
(define-key map (kbd "M-i") 'ivy-insert-current)
(define-key map (kbd "C-o") 'hydra-ivy/body)
+ (define-key map (kbd "M-o") 'ivy-dispatching-done)
(define-key map (kbd "C-k") 'ivy-kill-line)
+ (define-key map (kbd "S-SPC") 'ivy-restrict-to-matches)
+ (define-key map (kbd "M-w") 'ivy-kill-ring-save)
map)
"Keymap used in the minibuffer.")
(autoload 'hydra-ivy/body "ivy-hydra" "" t)
,@body))
(minibuffer-keyboard-quit)))
+(defmacro with-ivy-window (&rest body)
+ "Execute BODY in the window from which `ivy-read' was called."
+ (declare (indent 0)
+ (debug t))
+ `(with-selected-window (ivy-state-window ivy-last)
+ ,@body))
+
(defun ivy--done (text)
"Insert TEXT and exit minibuffer."
(if (and ivy--directory
(insert ivy-text)
(ivy--exhibit))))
+(defun ivy-dispatching-done ()
+ "Select one of the available actions and call `ivy-done'."
+ (interactive)
+ (let ((actions (ivy-state-action ivy-last)))
+ (if (null (ivy--actionp actions))
+ (ivy-done)
+ (let* ((hint (concat ivy--current
+ "\n"
+ (mapconcat
+ (lambda (x)
+ (format "%s: %s"
+ (propertize
+ (car x)
+ 'face 'font-lock-builtin-face)
+ (nth 2 x)))
+ (cdr actions)
+ "\n")
+ "\n"))
+ (key (string (read-key hint)))
+ (action (assoc key (cdr actions))))
+ (cond ((string= key "\a"))
+ ((null action)
+ (error "%s is not bound" key))
+ (t
+ (message "")
+ (ivy-set-action (nth 1 action))
+ (ivy-done)))))))
+
(defun ivy-build-tramp-name (x)
"Reconstruct X into a path.
Is is a cons cell, related to `tramp-get-completion-function'."
(setq ivy-exit 'done)
(exit-minibuffer))
+;;;###autoload
(defun ivy-resume ()
"Resume the last completion session."
(interactive)
(interactive)
(ivy-set-index (max (- ivy--index ivy-height)
0)))
+
(defun ivy-minibuffer-grow ()
- "Grow the minibuffer window by 1 line"
+ "Grow the minibuffer window by 1 line."
(interactive)
(setq-local max-mini-window-height
(cl-incf ivy-height)))
(ivy-previous-line arg))
(defun ivy-toggle-calling ()
- "Flip `ivy-calling'"
+ "Flip `ivy-calling'."
(interactive)
(when (setq ivy-calling (not ivy-calling))
(ivy-call)))
+(defun ivy--get-action (state)
+ "Get the action function from STATE."
+ (let ((action (ivy-state-action state)))
+ (when action
+ (if (functionp action)
+ action
+ (cadr (nth (car action) action))))))
+
+(defun ivy--actionp (x)
+ "Return non-nil when X is a list of actions."
+ (and x (listp x) (not (eq (car x) 'closure))))
+
+(defun ivy-next-action ()
+ "When the current action is a list, scroll it forwards."
+ (interactive)
+ (let ((action (ivy-state-action ivy-last)))
+ (when (ivy--actionp action)
+ (unless (>= (car action) (1- (length action)))
+ (cl-incf (car action))))))
+
+(defun ivy-prev-action ()
+ "When the current action is a list, scroll it backwards."
+ (interactive)
+ (let ((action (ivy-state-action ivy-last)))
+ (when (ivy--actionp action)
+ (unless (<= (car action) 1)
+ (cl-decf (car action))))))
+
+(defun ivy-action-name ()
+ "Return the name associated with the current action."
+ (let ((action (ivy-state-action ivy-last)))
+ (if (ivy--actionp action)
+ (format "[%d/%d] %s"
+ (car action)
+ (1- (length action))
+ (nth 2 (nth (car action) action)))
+ "[1/1] default")))
+
(defun ivy-call ()
"Call the current action without exiting completion."
- (when (ivy-state-action ivy-last)
- (with-selected-window (ivy-state-window ivy-last)
- (funcall (ivy-state-action ivy-last) ivy--current))))
+ (interactive)
+ (let ((action (ivy--get-action ivy-last)))
+ (when action
+ (let* ((collection (ivy-state-collection ivy-last))
+ (x (if (and (consp collection)
+ (consp (car collection)))
+ (cdr (assoc ivy--current collection))
+ (if (equal ivy--current "")
+ ivy-text
+ ivy--current))))
+ (funcall action x)))))
(defun ivy-next-line-and-call (&optional arg)
"Move cursor vertically down ARG candidates.
ivy--directory))))
(ivy--exhibit))
(ignore-errors
- (backward-kill-word 1))))
+ (let ((pt (point)))
+ (forward-word -1)
+ (delete-region (point) pt)))))
(defvar ivy--regexp-quote 'regexp-quote
"Store the regexp quoting state.")
"An alist of sorting functions for each collection function.
Interactive functions that call completion fit in here as well.
-For each entry, nil means no sorting. It's very useful to turn
+For each entry, nil means no sorting. It's very useful to turn
off the sorting for functions that have candidates in the natural
buffer order, like `org-refile' or `Man-goto-section'.
PROMPT is a string to prompt with; normally it ends in a colon
and a space. When PROMPT contains %d, it will be updated with
-the current number of matching candidates.
+the current number of matching candidates. If % appears elsewhere
+in the PROMPT it should be quoted as %%.
See also `ivy-count-format'.
COLLECTION is a list of strings.
DYNAMIC-COLLECTION is a function to call to update the list of
candidates with each input."
+ (let ((extra-actions (plist-get ivy--actions-list this-command)))
+ (when extra-actions
+ (setq action
+ (if (functionp action)
+ `(1
+ ("o" ,action "default")
+ ,@extra-actions)
+ (delete-dups (append action extra-actions))))))
(setq ivy-last
(make-ivy-state
:prompt prompt
(let* ((hist (or history 'ivy-history))
(minibuffer-completion-table collection)
(minibuffer-completion-predicate predicate)
+ (resize-mini-windows (cond
+ ((display-graphic-p) nil)
+ ((null resize-mini-windows) 'grow-only)
+ (t resize-mini-windows)))
(res (read-from-minibuffer
prompt
(ivy-state-initial-input ivy-last)
(let ((item (if ivy--directory
ivy--current
ivy-text)))
- (set hist (cons (propertize item 'ivy-index ivy--index)
- (delete item
- (cdr (symbol-value hist))))))
+ (unless (equal item "")
+ (set hist (cons (propertize item 'ivy-index ivy--index)
+ (delete item
+ (cdr (symbol-value hist)))))))
res)))
(remove-hook 'post-command-hook #'ivy--exhibit)
(when (setq unwind (ivy-state-unwind ivy-last))
(funcall unwind)))
- (when (setq action (ivy-state-action ivy-last))
- (funcall action ivy--current))))
+ (ivy-call)))
(defun ivy--reset-state (state)
"Reset the ivy to STATE.
(re-builder (ivy-state-re-builder state))
(dynamic-collection (ivy-state-dynamic-collection state))
(initial-input (ivy-state-initial-input state))
- (require-match (ivy-state-require-match state)))
+ (require-match (ivy-state-require-match state))
+ (matcher (ivy-state-matcher state)))
(unless initial-input
(setq initial-input (cdr (assoc this-command
ivy-initial-inputs-alist))))
((eq collection 'read-file-name-internal)
(setq ivy--directory default-directory)
(require 'dired)
- (setq coll
- (ivy--sorted-files default-directory))
+ (when preselect
+ (let ((preselect-directory (file-name-directory preselect)))
+ (unless (or (null preselect-directory)
+ (string= preselect-directory
+ default-directory))
+ (setq ivy--directory preselect-directory))
+ (setq preselect (file-name-nondirectory preselect))))
+ (setq coll (ivy--sorted-files ivy--directory))
(when initial-input
(unless (or require-match
(equal initial-input default-directory)
((eq collection 'internal-complete-buffer)
(setq coll (ivy--buffer-list "" ivy-use-virtual-buffers)))
((or (functionp collection)
+ (byte-code-function-p collection)
(vectorp collection)
(listp (car collection)))
(setq coll (all-completions "" collection predicate)))
ivy--index)
(and preselect
(ivy--preselect-index
- coll initial-input preselect))
+ coll initial-input preselect matcher))
0))
(setq ivy--old-re nil)
(setq ivy--old-cands nil)
(setq ivy--prompt
(cond ((string-match "%.*d" prompt)
prompt)
+ ((null ivy-count-format)
+ nil)
+ ((string-match "%d.*%d" ivy-count-format)
+ (let ((w (length (number-to-string
+ (length ivy--all-candidates))))
+ (s (copy-sequence ivy-count-format)))
+ (string-match "%d" s)
+ (match-end 0)
+ (string-match "%d" s (match-end 0))
+ (setq s (replace-match (format "%%-%dd" w) nil nil s))
+ (string-match "%d" s)
+ (concat (replace-match (format "%%%dd" w) nil nil s)
+ prompt)))
((string-match "%.*d" ivy-count-format)
(concat ivy-count-format prompt))
(ivy--directory
nil)))
(setf (ivy-state-initial-input ivy-last) initial-input)))
+;;;###autoload
(defun ivy-completing-read (prompt collection
&optional predicate require-match initial-input
history def _inherit-input-method)
_INHERIT-INPUT-METHOD is ignored for now.
The history, defaults and input-method arguments are ignored for now."
- (ivy-read prompt collection
+ (ivy-read (replace-regexp-in-string "%" "%%" prompt)
+ collection
:predicate predicate
:require-match require-match
:initial-input (if (consp initial-input)
(car initial-input)
- initial-input)
+ (if (and (stringp initial-input)
+ (string-match "\\+" initial-input))
+ (replace-regexp-in-string
+ "\\+" "\\\\+" initial-input)
+ initial-input))
:preselect (if (listp def) (car def) def)
:history history
:keymap nil
(setq completing-read-function 'ivy-completing-read)
(setq completing-read-function 'completing-read-default)))
-(defun ivy--preselect-index (candidates initial-input preselect)
- "Return the index in CANDIDATES filtered by INITIAL-INPUT for PRESELECT."
- (when initial-input
- (setq initial-input (ivy--regex-plus initial-input))
- (setq candidates
- (cl-remove-if-not
- (lambda (x)
- (string-match initial-input x))
- candidates)))
+(defun ivy--preselect-index (candidates initial-input preselect matcher)
+ "Return the index in CANDIDATES filtered by INITIAL-INPUT for PRESELECT.
+When MATCHER is non-nil it's used instead of `cl-remove-if-not'."
+ (if initial-input
+ (progn
+ (setq initial-input (ivy--regex-plus initial-input))
+ (setq candidates
+ (if matcher
+ (funcall matcher initial-input candidates)
+ (cl-remove-if-not
+ (lambda (x)
+ (string-match initial-input x))
+ candidates))))
+ (when matcher
+ (setq candidates (funcall matcher "" candidates))))
(or (cl-position preselect candidates :test #'equal)
(cl-position-if
(lambda (x)
(if hashed
(prog1 (cdr hashed)
(setq ivy--subexps (car hashed)))
+ (when (string-match "\\([^\\]\\|^\\)\\\\$" str)
+ (setq str (substring str 0 -1)))
(cdr (puthash str
(let ((subs (ivy--split str)))
(if (= (length subs) 1)
(set (make-local-variable 'minibuffer-default-add-function)
(lambda ()
(list ivy--default)))
+ (when (display-graphic-p)
+ (setq truncate-lines t))
(setq-local max-mini-window-height ivy-height)
(add-hook 'post-command-hook #'ivy--exhibit nil t)
;; show completions with empty input
(let ((inhibit-read-only t)
(std-props '(front-sticky t rear-nonsticky t field t read-only t))
(n-str
- (format
+ (concat
+ (if (and (bound-and-true-p minibuffer-depth-indicate-mode)
+ (> (minibuffer-depth) 1))
+ (format "[%d] " (minibuffer-depth))
+ "")
(concat
- (if (and (bound-and-true-p minibuffer-depth-indicate-mode)
- (> (minibuffer-depth) 1))
- (format "[%d] " (minibuffer-depth))
- "")
- head
+ (if (string-match "%d.*%d" ivy-count-format)
+ (format head
+ (1+ ivy--index)
+ (or (and (ivy-state-dynamic-collection ivy-last)
+ ivy--full-length)
+ ivy--length))
+ (format head
+ (or (and (ivy-state-dynamic-collection ivy-last)
+ ivy--full-length)
+ ivy--length)))
ivy--prompt-extra
- tail
- (if ivy--directory
- (abbreviate-file-name ivy--directory)
- ""))
- (or (and (ivy-state-dynamic-collection ivy-last)
- ivy--full-length)
- ivy--length))))
+ tail)
+ (if ivy--directory
+ (abbreviate-file-name ivy--directory)
+ ""))))
(save-excursion
(goto-char (point-min))
(delete-region (point-min) (minibuffer-prompt-end))
+ (when (> (length n-str) (window-width))
+ (setq n-str (concat (substring n-str 0
+ (max (- (window-width) 30)
+ 10)) "... ")))
(set-text-properties 0 (length n-str)
`(face minibuffer-prompt ,@std-props)
n-str)
(defvar inhibit-message)
+(defun ivy--sort-maybe (collection)
+ "Sort COLLECTION if needed."
+ (let ((sort (ivy-state-sort ivy-last))
+ entry)
+ (if (null sort)
+ collection
+ (let ((sort-fn (cond ((functionp sort)
+ sort)
+ ((setq entry (assoc (ivy-state-collection ivy-last)
+ ivy-sort-functions-alist))
+ (cdr entry))
+ (t
+ (cdr (assoc t ivy-sort-functions-alist))))))
+ (if (functionp sort-fn)
+ (cl-sort (copy-sequence collection) sort-fn)
+ collection)))))
+
(defun ivy--exhibit ()
"Insert Ivy completions display.
Should be run via minibuffer `post-command-hook'."
- (setq ivy-text (ivy--input))
- (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))
- (unless (equal ivy--old-text ivy-text)
- (while-no-input
- ;; dynamic collection should take care of everything
- (funcall (ivy-state-dynamic-collection ivy-last) ivy-text)
- (setq ivy--old-text ivy-text)))
- (unless (eq ivy--full-length -1)
- (ivy--insert-minibuffer
- (ivy--format ivy--all-candidates))))
- (cond (ivy--directory
- (if (string-match "/\\'" ivy-text)
- (if (member ivy-text ivy--all-candidates)
- (ivy--cd (expand-file-name ivy-text ivy--directory))
- (when (string-match "//\\'" ivy-text)
- (if (and default-directory
- (string-match "\\`[[:alpha:]]:/" default-directory))
- (ivy--cd (match-string 0 default-directory))
- (ivy--cd "/")))
- (when (string-match "[[:alpha:]]:/" ivy-text)
- (let ((drive-root (match-string 0 ivy-text)))
- (when (file-exists-p drive-root)
- (ivy--cd drive-root)))))
- (if (string-match "\\`~\\'" ivy-text)
- (ivy--cd (expand-file-name "~/")))))
- ((eq (ivy-state-collection ivy-last) 'internal-complete-buffer)
- (when (or (and (string-match "\\` " ivy-text)
- (not (string-match "\\` " ivy--old-text)))
- (and (string-match "\\` " ivy--old-text)
- (not (string-match "\\` " ivy-text))))
- (setq ivy--all-candidates
- (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
- (ivy--filter ivy-text ivy--all-candidates)))
- (setq ivy--old-text ivy-text)))
+ (when (memq 'ivy--exhibit post-command-hook)
+ (setq ivy-text (ivy--input))
+ (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))
+ (unless (equal ivy--old-text ivy-text)
+ (while-no-input
+ (setq ivy--all-candidates
+ (ivy--sort-maybe
+ (funcall (ivy-state-collection ivy-last) ivy-text)))
+ (setq ivy--old-text ivy-text)))
+ (when ivy--all-candidates
+ (ivy--insert-minibuffer
+ (ivy--format ivy--all-candidates))))
+ (cond (ivy--directory
+ (if (string-match "/\\'" ivy-text)
+ (if (member ivy-text ivy--all-candidates)
+ (ivy--cd (expand-file-name ivy-text ivy--directory))
+ (when (string-match "//\\'" ivy-text)
+ (if (and default-directory
+ (string-match "\\`[[:alpha:]]:/" default-directory))
+ (ivy--cd (match-string 0 default-directory))
+ (ivy--cd "/")))
+ (when (string-match "[[:alpha:]]:/" ivy-text)
+ (let ((drive-root (match-string 0 ivy-text)))
+ (when (file-exists-p drive-root)
+ (ivy--cd drive-root)))))
+ (if (string-match "\\`~\\'" ivy-text)
+ (ivy--cd (expand-file-name "~/")))))
+ ((eq (ivy-state-collection ivy-last) 'internal-complete-buffer)
+ (when (or (and (string-match "\\` " ivy-text)
+ (not (string-match "\\` " ivy--old-text)))
+ (and (string-match "\\` " ivy--old-text)
+ (not (string-match "\\` " ivy-text))))
+ (setq ivy--all-candidates
+ (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
+ (ivy--filter ivy-text ivy--all-candidates)))
+ (setq ivy--old-text ivy-text))))
(defun ivy--insert-minibuffer (text)
"Insert TEXT into minibuffer with appropriate cleanup."
(let ((buffer-undo-list t))
(save-excursion
(forward-line 1)
- (insert text))))))
+ (insert text))))
+ (when (display-graphic-p)
+ (ivy--resize-minibuffer-to-fit))))
+
+(defun ivy--resize-minibuffer-to-fit ()
+ "Resize the minibuffer window so it has enough space to display
+all of the text contained in the minibuffer."
+ (with-selected-window (minibuffer-window)
+ (if (fboundp 'window-text-pixel-size)
+ (let ((text-height (cdr (window-text-pixel-size)))
+ (body-height (window-body-height nil t)))
+ (when (> text-height body-height)
+ (window-resize nil (- text-height body-height) nil t t)))
+ (let ((text-height (count-screen-lines))
+ (body-height (window-body-height)))
+ (when (> text-height body-height)
+ (window-resize nil (- text-height body-height) nil t))))))
(declare-function colir-blend-face-background "ext:colir")
`propertize' or `add-face-text-property' in this case."
(require 'colir)
(condition-case nil
- (colir-blend-face-background 0 (length str) face str)
+ (progn
+ (colir-blend-face-background 0 (length str) face str)
+ (let ((foreground (face-foreground face)))
+ (when foreground
+ (add-face-text-property
+ 0 (length str)
+ `(:foreground ,foreground)
+ nil
+ str))))
(error
(ignore-errors
(font-lock-append-text-property 0 (length str) 'face face str))))
"Return all items that match NAME in CANDIDATES.
CANDIDATES are assumed to be static."
(let* ((re (funcall ivy--regex-function name))
+ (re-str (if (listp re) (caar re) re))
(matcher (ivy-state-matcher ivy-last))
+ (case-fold-search (string= name (downcase name)))
(cands (cond
(matcher
(funcall matcher re candidates))
(tail (nthcdr ivy--index ivy--old-cands))
idx)
(when (and tail ivy--old-cands (not (equal "^" ivy--old-re)))
- (unless (and (not (equal re ivy--old-re))
+ (unless (and (not (equal re-str ivy--old-re))
(or (setq ivy--index
(or
- (cl-position re cands
- :test #'equal)
+ (cl-position (if (and (> (length re-str) 0)
+ (eq ?^ (aref re-str 0)))
+ (substring re-str 1)
+ re-str) cands
+ :test #'equal)
(and ivy--directory
(cl-position
- (concat re "/") cands
+ (concat re-str "/") cands
:test #'equal))))))
(while (and tail (null idx))
;; Compare with eq to handle equal duplicates in cands
(or (cl-position (ivy-state-preselect ivy-last)
cands :test #'equal)
ivy--index)))
- (setq ivy--old-re (if cands re ""))
+ (setq ivy--old-re (if cands re-str ""))
(setq ivy--old-cands cands)))
(defvar ivy-format-function 'ivy-format-function-default
(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")))
+ (if (bound-and-true-p truncate-lines)
+ (mapconcat #'identity cands "\n")
+ (let ((ww (- (window-width)
+ (if (and (boundp 'fringe-mode) (eq fringe-mode 0)) 1 0))))
+ (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."
s))
cands "\n")))
+(defcustom swiper-minibuffer-faces
+ '(swiper-minibuffer-match-face-1
+ swiper-minibuffer-match-face-2
+ swiper-minibuffer-match-face-3
+ swiper-minibuffer-match-face-4)
+ "List of `swiper' faces for minibuffer group matches.")
+
+(defun ivy--format-minibuffer-line (str)
+ (let ((start 0)
+ (str (copy-sequence str)))
+ (when (eq ivy-display-style 'fancy)
+ (unless ivy--old-re
+ (setq ivy--old-re (funcall ivy--regex-function ivy-text)))
+ (while (and (string-match ivy--old-re str start)
+ (> (- (match-end 0) (match-beginning 0)) 0))
+ (setq start (match-end 0))
+ (let ((i 0))
+ (while (<= i ivy--subexps)
+ (let ((face
+ (cond ((zerop ivy--subexps)
+ (cadr swiper-minibuffer-faces))
+ ((zerop i)
+ (car swiper-minibuffer-faces))
+ (t
+ (nth (1+ (mod (+ i 2) (1- (length swiper-minibuffer-faces))))
+ swiper-minibuffer-faces)))))
+ (if (fboundp 'add-face-text-property)
+ (add-face-text-property
+ (match-beginning i)
+ (match-end i)
+ face
+ nil
+ str)
+ (font-lock-append-text-property
+ (match-beginning i)
+ (match-end i)
+ 'face
+ face
+ str)))
+ (cl-incf i)))))
+ str))
+
(defun ivy--format (cands)
"Return a string for CANDS suitable for display in the minibuffer.
CANDS is a list of strings."
x))
cands)))
(setq ivy--current (copy-sequence (nth index cands)))
- (setf (nth index cands)
- (ivy--add-face ivy--current 'ivy-current-match))
(setq cands (mapcar
- (lambda (s)
- (let ((s (copy-sequence s)))
- (when (fboundp 'add-face-text-property)
- (add-face-text-property
- 0 (length s)
- `(:height ,(face-attribute 'default :height)
- :overline nil) nil s))
- s))
+ #'ivy--format-minibuffer-line
cands))
+ (setf (nth index cands)
+ (ivy--add-face (nth index cands) 'ivy-current-match))
(let* ((ivy--index index)
(res (concat "\n" (funcall ivy-format-function cands))))
(put-text-property 0 (length res) 'read-only nil res)
(defun ivy--switch-buffer-action (buffer)
"Switch to BUFFER.
+BUFFER may be a string or nil."
+ (with-ivy-window
+ (if (zerop (length buffer))
+ (switch-to-buffer
+ ivy-text nil 'force-same-window)
+ (let ((virtual (assoc buffer ivy--virtual-buffers)))
+ (if (and virtual
+ (not (get-buffer buffer)))
+ (find-file (cdr virtual))
+ (switch-to-buffer
+ buffer nil 'force-same-window))))))
+
+(defun ivy--switch-buffer-other-window-action (buffer)
+ "Switch to BUFFER in other window.
BUFFER may be a string or nil."
(if (zerop (length buffer))
- (switch-to-buffer
- ivy-text nil 'force-same-window)
+ (switch-to-buffer-other-window ivy-text)
(let ((virtual (assoc buffer ivy--virtual-buffers)))
(if (and virtual
(not (get-buffer buffer)))
- (find-file (cdr virtual))
- (switch-to-buffer
- buffer nil 'force-same-window)))))
+ (find-file-other-window (cdr virtual))
+ (switch-to-buffer-other-window buffer)))))
+
+(defun ivy--rename-buffer-action (buffer)
+ "Rename BUFFER."
+ (let ((new-name (read-string "Rename buffer (to new name): ")))
+ (with-current-buffer buffer
+ (rename-buffer new-name))))
+
+(defvar ivy-switch-buffer-map (make-sparse-keymap))
+
+(ivy-set-actions
+ 'ivy-switch-buffer
+ '(("k"
+ (lambda (x)
+ (kill-buffer x)
+ (ivy--reset-state ivy-last))
+ "kill")
+ ("j"
+ ivy--switch-buffer-other-window-action
+ "other")
+ ("r"
+ ivy--rename-buffer-action
+ "rename")))
+;;;###autoload
(defun ivy-switch-buffer ()
"Switch to another buffer."
(interactive)
(if (not ivy-mode)
(call-interactively 'switch-to-buffer)
- (ivy-read "Switch to buffer: " 'internal-complete-buffer
- :preselect (buffer-name (other-buffer (current-buffer)))
- :action #'ivy--switch-buffer-action)))
+ (let ((this-command 'ivy-switch-buffer))
+ (ivy-read "Switch to buffer: " 'internal-complete-buffer
+ :preselect (buffer-name (other-buffer (current-buffer)))
+ :action #'ivy--switch-buffer-action
+ :keymap ivy-switch-buffer-map))))
+;;;###autoload
(defun ivy-recentf ()
"Find a file on `recentf-list'."
(interactive)
(ivy-read "Recentf: " recentf-list
- :action #'find-file))
+ :action
+ (lambda (f)
+ (with-ivy-window
+ (find-file f)))))
(defun ivy-yank-word ()
"Pull next word from buffer into search string."
(interactive)
(let (amend)
- (with-selected-window (ivy-state-window ivy-last)
+ (with-ivy-window
(let ((pt (point))
(le (line-end-position)))
(forward-word 1)
(when amend
(insert amend))))
+(defun ivy-kill-ring-save ()
+ "Store the current candidates into the kill ring.
+If the region is active, forward to `kill-ring-save' instead."
+ (interactive)
+ (if (region-active-p)
+ (call-interactively 'kill-ring-save)
+ (kill-new
+ (mapconcat
+ #'identity
+ ivy--old-cands
+ "\n"))))
+
(defun ivy-insert-current ()
"Make the current candidate into current input.
Don't finish completion."
(insert (substring-no-properties x))
(ivy--cd-maybe)))))
+(defun ivy-restrict-to-matches ()
+ "Restrict candidates to current matches and erase input."
+ (interactive)
+ (delete-minibuffer-contents)
+ (setq ivy--all-candidates
+ (ivy--filter ivy-text ivy--all-candidates)))
+
(provide 'ivy)
;;; ivy.el ends here