(require 'swiper)
+(defvar counsel-completion-beg nil
+ "Completion bounds start.")
+
+(defvar counsel-completion-end nil
+ "Completion bounds end.")
+
;;;###autoload
(defun counsel-el ()
"Elisp completion at point."
:initial-input str
:action #'counsel--el-action)))
-(defvar counsel-completion-beg nil
- "Completion bounds start.")
-
-(defvar counsel-completion-end nil
- "Completion bounds end.")
-
(defun counsel--el-action (symbol)
"Insert SYMBOL, erasing the previous one."
(when (stringp symbol)
(match-string 1 s)
s))))
+(defun counsel-variable-list ()
+ "Return the list of all currently bound variables."
+ (let (cands)
+ (mapatoms
+ (lambda (vv)
+ (when (or (get vv 'variable-documentation)
+ (and (boundp vv) (not (keywordp vv))))
+ (push (symbol-name vv) cands))))
+ cands))
+
;;;###autoload
(defun counsel-describe-variable ()
"Forward to `describe-variable'."
(let ((enable-recursive-minibuffers t))
(ivy-read
"Describe variable: "
- (let (cands)
- (mapatoms
- (lambda (vv)
- (when (or (get vv 'variable-documentation)
- (and (boundp vv) (not (keywordp vv))))
- (push (symbol-name vv) cands))))
- cands)
+ (counsel-variable-list)
:keymap counsel-describe-map
:preselect (counsel-symbol-at-point)
:history 'counsel-describe-symbol-history
(describe-variable
(intern x))))))
+(ivy-set-actions
+ 'counsel-describe-variable
+ '(("i" counsel-info-lookup-symbol "info")
+ ("d" counsel--find-symbol "definition")))
+
+(ivy-set-actions
+ 'counsel-describe-function
+ '(("i" counsel-info-lookup-symbol "info")
+ ("d" counsel--find-symbol "definition")))
+
;;;###autoload
(defun counsel-describe-function ()
"Forward to `describe-function'."
(defvar counsel--git-grep-count nil
"Store the line count in current repository.")
+(defun counsel-more-chars (n)
+ "Return two fake candidates prompting for at least N input."
+ (list ""
+ (format "%d chars more" (- n (length ivy-text)))))
+
(defun counsel-git-grep-function (string &optional _pred &rest _unused)
"Grep in the current git repository for STRING."
(if (and (> counsel--git-grep-count 20000)
(< (length string) 3))
- (progn
- (setq ivy--full-length counsel--git-grep-count)
- (list ""
- (format "%d chars more" (- 3 (length ivy-text)))))
+ (counsel-more-chars 3)
(let* ((default-directory counsel--git-grep-dir)
(cmd (format "git --no-pager grep --full-name -n --no-color -i -e %S"
- (ivy--regex string t)))
- res)
+ (ivy--regex string t))))
(if (<= counsel--git-grep-count 20000)
- (progn
- (setq res (shell-command-to-string cmd))
- (setq ivy--full-length nil)
- (split-string res "\n" t))
- (setq ivy--full-length -1)
+ (split-string (shell-command-to-string cmd) "\n" t)
(counsel--gg-candidates (ivy--regex string))
nil))))
(defun counsel-git-grep-recenter ()
(interactive)
- (with-selected-window (ivy-state-window ivy-last)
+ (with-ivy-window
(counsel-git-grep-action ivy--current)
(recenter-top-bottom)))
(defun counsel-git-grep-action (x)
(when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" x)
- (let ((file-name (match-string-no-properties 1 x))
- (line-number (match-string-no-properties 2 x)))
- (find-file (expand-file-name file-name counsel--git-grep-dir))
- (goto-char (point-min))
- (forward-line (1- (string-to-number line-number)))
- (re-search-forward (ivy--regex ivy-text t) (line-end-position) t)
- (unless (eq ivy-exit 'done)
- (setq swiper--window (selected-window))
- (swiper--cleanup)
- (swiper--add-overlays (ivy--regex ivy-text))))))
+ (with-ivy-window
+ (let ((file-name (match-string-no-properties 1 x))
+ (line-number (match-string-no-properties 2 x)))
+ (find-file (expand-file-name file-name counsel--git-grep-dir))
+ (goto-char (point-min))
+ (forward-line (1- (string-to-number line-number)))
+ (re-search-forward (ivy--regex ivy-text t) (line-end-position) t)
+ (unless (eq ivy-exit 'done)
+ (swiper--cleanup)
+ (swiper--add-overlays (ivy--regex ivy-text)))))))
(defvar counsel-git-grep-history nil
"History for `counsel-git-grep'.")
;;;###autoload
(defun counsel-git-grep (&optional initial-input)
- "Grep for a string in the current git repository."
+ "Grep for a string in the current git repository.
+INITIAL-INPUT can be given as the initial minibuffer input."
(interactive)
(setq counsel--git-grep-dir
(locate-dominating-file default-directory ".git"))
(if (null counsel--git-grep-dir)
(error "Not in a git repository")
(setq counsel--git-grep-count (counsel--gg-count "" t))
- (ivy-read "pattern: " 'counsel-git-grep-function
+ (ivy-read "git grep: " 'counsel-git-grep-function
:initial-input initial-input
:matcher #'counsel-git-grep-matcher
- :dynamic-collection (when (> counsel--git-grep-count 20000)
- 'counsel-git-grep-function)
+ :dynamic-collection (> counsel--git-grep-count 20000)
:keymap counsel-git-grep-map
:action #'counsel-git-grep-action
:unwind #'swiper--cleanup
(declare-function ffap-guesser "ffap")
+(defvar counsel-find-file-map (make-sparse-keymap))
+
;;;###autoload
(defun counsel-find-file ()
"Forward to `find-file'."
:matcher #'counsel--find-file-matcher
:action
(lambda (x)
- (find-file (expand-file-name x ivy--directory)))
+ (with-ivy-window
+ (find-file (expand-file-name x ivy--directory))))
:preselect (when counsel-find-file-at-point
(require 'ffap)
(ffap-guesser))
:require-match 'confirm-after-completion
- :history 'file-name-history))
+ :history 'file-name-history
+ :keymap counsel-find-file-map))
(defcustom counsel-find-file-ignore-regexp nil
"A regexp of files to ignore while in `counsel-find-file'.
candidates))
(setq ivy--old-re regexp))))
-(defun counsel-locate-function (str &rest _u)
- (if (< (length str) 3)
- (list ""
- (format "%d chars more" (- 3 (length ivy-text))))
- (counsel--async-command
- (concat "locate -i --regex " (ivy--regex str)))))
-
(defun counsel--async-command (cmd)
(let* ((counsel--process " *counsel*")
(proc (get-process counsel--process))
(with-current-buffer (process-buffer process)
(setq ivy--all-candidates (split-string (buffer-string) "\n" t))
(setq ivy--old-cands ivy--all-candidates))
- (ivy--insert-minibuffer
- (ivy--format ivy--all-candidates)))
+ (ivy--exhibit))
(if (string= event "exited abnormally with code 1\n")
- (message "Error"))))
+ (progn
+ (setq ivy--all-candidates '("Error"))
+ (setq ivy--old-cands ivy--all-candidates)
+ (ivy--exhibit)))))
+
+(defun counsel-locate-action-extern (x)
+ "Use xdg-open shell command on X."
+ (call-process shell-file-name nil
+ nil nil
+ shell-command-switch
+ (format "xdg-open %s" (shell-quote-argument x))))
+
+(declare-function dired-jump "dired-x")
+(defun counsel-locate-action-dired (x)
+ "Use `dired-jump' on X."
+ (dired-jump nil x))
+
+(defvar counsel-locate-history nil
+ "History for `counsel-locate'.")
+
+(ivy-set-actions
+ 'counsel-locate
+ '(("x" counsel-locate-action-extern "xdg-open")
+ ("d" counsel-locate-action-dired "dired")))
+
+(defun counsel-locate-function (str &rest _u)
+ (if (< (length str) 3)
+ (counsel-more-chars 3)
+ (counsel--async-command
+ (concat "locate -i --regex " (ivy--regex str)))
+ '("" "working...")))
;;;###autoload
(defun counsel-locate ()
- "Call locate."
+ "Call locate shell command."
(interactive)
- (ivy-read "pattern: " nil
- :dynamic-collection #'counsel-locate-function
- :action (lambda (val)
- (when val
- (find-file val)))))
+ (ivy-read "Locate: " #'counsel-locate-function
+ :dynamic-collection t
+ :history 'counsel-locate-history
+ :action (lambda (file)
+ (when file
+ (find-file file)))))
(defun counsel--generic (completion-fn)
"Complete thing at point with COMPLETION-FN."
(get-text-property 0 'full-name x)))
:keymap counsel-describe-map)))
+(defvar counsel-gg-state nil
+ "The current state of candidates / count sync.")
+
(defun counsel--gg-candidates (regex)
"Return git grep candidates for REGEX."
+ (setq counsel-gg-state -2)
(counsel--gg-count regex)
(let* ((default-directory counsel--git-grep-dir)
(counsel-gg-process " *counsel-gg*")
(with-current-buffer (process-buffer process)
(setq ivy--all-candidates (split-string (buffer-string) "\n" t))
(setq ivy--old-cands ivy--all-candidates))
- (unless (eq ivy--full-length -1)
- (ivy--insert-minibuffer
- (ivy--format ivy--all-candidates))))
+ (when (= 0 (cl-incf counsel-gg-state))
+ (ivy--exhibit)))
(if (string= event "exited abnormally with code 1\n")
- (message "Error"))))
+ (progn
+ (setq ivy--all-candidates '("Error"))
+ (setq ivy--old-cands ivy--all-candidates)
+ (ivy--exhibit)))))
(defun counsel--gg-count (regex &optional no-async)
"Quickly and asynchronously count the amount of git grep REGEX matches.
(when (string= event "finished\n")
(with-current-buffer (process-buffer process)
(setq ivy--full-length (string-to-number (buffer-string))))
- (ivy--insert-minibuffer
- (ivy--format ivy--all-candidates)))))))))
+ (when (= 0 (cl-incf counsel-gg-state))
+ (ivy--exhibit)))))))))
(defun counsel--M-x-transformer (cmd)
"Add a binding to CMD if it's bound in the current window.
(lambda (cands)
(funcall
store
- (with-selected-window (ivy-state-window ivy-last)
+ (with-ivy-window
(mapcar #'counsel--M-x-transformer cands)))))
(cands obarray)
(pred 'commandp)
(custom-available-themes))
:action #'counsel--load-theme-action))
+(defvar rhythmbox-library)
+(declare-function rhythmbox-load-library "ext:helm-rhythmbox")
+(declare-function dbus-call-method "dbus")
+(declare-function rhythmbox-song-uri "ext:helm-rhythmbox")
+(declare-function helm-rhythmbox-candidates "ext:helm-rhythmbox")
+
+(defun counsel-rhythmbox-enqueue-song (song)
+ "Let Rhythmbox enqueue SONG."
+ (let ((service "org.gnome.Rhythmbox3")
+ (path "/org/gnome/Rhythmbox3/PlayQueue")
+ (interface "org.gnome.Rhythmbox3.PlayQueue"))
+ (dbus-call-method :session service path interface
+ "AddToQueue" (rhythmbox-song-uri song))))
+
+(defvar counsel-rhythmbox-history nil
+ "History for `counsel-rhythmbox'.")
+
+;;;###autoload
+(defun counsel-rhythmbox ()
+ "Choose a song from the Rhythmbox library to play or enqueue."
+ (interactive)
+ (unless (require 'helm-rhythmbox nil t)
+ (error "Please install `helm-rhythmbox'"))
+ (unless rhythmbox-library
+ (rhythmbox-load-library)
+ (while (null rhythmbox-library)
+ (sit-for 0.1)))
+ (ivy-read "Rhythmbox: "
+ (helm-rhythmbox-candidates)
+ :history 'counsel-rhythmbox-history
+ :action
+ '(1
+ ("p" helm-rhythmbox-play-song "Play song")
+ ("e" counsel-rhythmbox-enqueue-song "Enqueue song"))))
+
+(defvar counsel-org-tags nil
+ "Store the current list of tags.")
+
+(defvar org-outline-regexp)
+(defvar org-indent-mode)
+(defvar org-indent-indentation-per-level)
+(defvar org-tags-column)
+(declare-function org-get-tags-string "org")
+(declare-function org-move-to-column "org")
+
+(defun counsel-org-change-tags (tags)
+ (let ((current (org-get-tags-string))
+ (col (current-column))
+ level)
+ ;; Insert new tags at the correct column
+ (beginning-of-line 1)
+ (setq level (or (and (looking-at org-outline-regexp)
+ (- (match-end 0) (point) 1))
+ 1))
+ (cond
+ ((and (equal current "") (equal tags "")))
+ ((re-search-forward
+ (concat "\\([ \t]*" (regexp-quote current) "\\)[ \t]*$")
+ (point-at-eol) t)
+ (if (equal tags "")
+ (delete-region
+ (match-beginning 0)
+ (match-end 0))
+ (goto-char (match-beginning 0))
+ (let* ((c0 (current-column))
+ ;; compute offset for the case of org-indent-mode active
+ (di (if (bound-and-true-p org-indent-mode)
+ (* (1- org-indent-indentation-per-level) (1- level))
+ 0))
+ (p0 (if (equal (char-before) ?*) (1+ (point)) (point)))
+ (tc (+ org-tags-column (if (> org-tags-column 0) (- di) di)))
+ (c1 (max (1+ c0) (if (> tc 0) tc (- (- tc) (string-width tags)))))
+ (rpl (concat (make-string (max 0 (- c1 c0)) ?\ ) tags)))
+ (replace-match rpl t t)
+ (and c0 indent-tabs-mode (tabify p0 (point)))
+ tags)))
+ (t (error "Tags alignment failed")))
+ (org-move-to-column col)))
+
+(defun counsel-org-tag-action (x)
+ (if (member x counsel-org-tags)
+ (progn
+ (setq counsel-org-tags (delete x counsel-org-tags)))
+ (setq counsel-org-tags (append counsel-org-tags (list x)))
+ (unless (member x ivy--all-candidates)
+ (setq ivy--all-candidates (append ivy--all-candidates (list x)))))
+ (let ((prompt (counsel-org-tag-prompt)))
+ (setf (ivy-state-prompt ivy-last) prompt)
+ (setq ivy--prompt (concat "%-4d " prompt)))
+ (cond ((memq this-command '(ivy-done ivy-alt-done))
+ (counsel-org-change-tags
+ (if counsel-org-tags
+ (format ":%s:"
+ (mapconcat #'identity counsel-org-tags ":"))
+ "")))
+ ((eq this-command 'ivy-call)
+ (delete-minibuffer-contents))))
+
+(defun counsel-org-tag-prompt ()
+ (format "Tags (%s): "
+ (mapconcat #'identity counsel-org-tags ", ")))
+
+(defvar org-setting-tags)
+(defvar org-last-tags-completion-table)
+(defvar org-tag-persistent-alist)
+(defvar org-tag-alist)
+(defvar org-complete-tags-always-offer-all-agenda-tags)
+
+(declare-function org-at-heading-p "org")
+(declare-function org-back-to-heading "org")
+(declare-function org-get-buffer-tags "org")
+(declare-function org-global-tags-completion-table "org")
+(declare-function org-agenda-files "org")
+(declare-function org-agenda-set-tags "org-agenda")
+
+;;;###autoload
+(defun counsel-org-tag ()
+ "Add or remove tags in org-mode."
+ (interactive)
+ (save-excursion
+ (unless (org-at-heading-p)
+ (org-back-to-heading t))
+ (setq counsel-org-tags (split-string (org-get-tags-string) ":" t))
+ (let ((org-setting-tags t)
+ (org-last-tags-completion-table
+ (append org-tag-persistent-alist
+ (or org-tag-alist (org-get-buffer-tags))
+ (and
+ org-complete-tags-always-offer-all-agenda-tags
+ (org-global-tags-completion-table
+ (org-agenda-files))))))
+ (ivy-read (counsel-org-tag-prompt)
+ (lambda (str &rest _unused)
+ (delete-dups
+ (all-completions str 'org-tags-completion-function)))
+ :history 'org-tags-history
+ :action 'counsel-org-tag-action))))
+
+;;;###autoload
+(defun counsel-org-tag-agenda ()
+ "Set tags for the current agenda item."
+ (interactive)
+ (let ((store (symbol-function 'org-set-tags)))
+ (unwind-protect
+ (progn
+ (fset 'org-set-tags
+ (symbol-function 'counsel-org-tag))
+ (org-agenda-set-tags nil nil))
+ (fset 'org-set-tags store))))
+
+(defun counsel-ag-function (string &optional _pred &rest _unused)
+ "Grep in the current directory for STRING."
+ (if (< (length string) 3)
+ (counsel-more-chars 3)
+ (let ((regex (replace-regexp-in-string
+ "\\\\)" ")"
+ (replace-regexp-in-string
+ "\\\\(" "("
+ (ivy--regex string)))))
+ (counsel--async-command
+ (format "ag --noheading --nocolor %S" regex))
+ nil)))
+
+(defun counsel-ag (&optional initial-input)
+ "Grep for a string in the current directory using ag.
+INITIAL-INPUT can be given as the initial minibuffer input."
+ (interactive)
+ (setq counsel--git-grep-dir default-directory)
+ (ivy-read "ag: " 'counsel-ag-function
+ :initial-input initial-input
+ :dynamic-collection t
+ :history 'counsel-git-grep-history
+ :action #'counsel-git-grep-action
+ :unwind #'swiper--cleanup))
+
+(defun counsel-recoll-function (string &optional _pred &rest _unused)
+ "Grep in the current directory for STRING."
+ (if (< (length string) 3)
+ (counsel-more-chars 3)
+ (counsel--async-command
+ (format "recoll -t -b '%s'" string))
+ nil))
+
+;; This command uses the recollq command line tool that comes together
+;; with the recoll (the document indexing database) source:
+;; http://www.lesbonscomptes.com/recoll/download.html
+;; You need to build it yourself (together with recoll):
+;; cd ./query && make && sudo cp recollq /usr/local/bin
+;; You can try the GUI version of recoll with:
+;; sudo apt-get install recoll
+;; Unfortunately, that does not install recollq.
+(defun counsel-recoll (&optional initial-input)
+ "Search for a string in the recoll database.
+You'll be given a list of files that match.
+Selecting a file will launch `swiper' for that file.
+INITIAL-INPUT can be given as the initial minibuffer input."
+ (interactive)
+ (ivy-read "recoll: " 'counsel-recoll-function
+ :initial-input initial-input
+ :dynamic-collection t
+ :history 'counsel-git-grep-history
+ :action (lambda (x)
+ (when (string-match "file://\\(.*\\)\\'" x)
+ (let ((file-name (match-string 1 x)))
+ (find-file file-name)
+ (unless (string-match "pdf$" x)
+ (swiper ivy-text)))))))
(provide 'counsel)
--- /dev/null
+#+OPTIONS: toc:nil
+* 0.6.0
+** Fixes
+*** =swiper-avy= should use only the current window
+Not all windows. See [[https://github.com/abo-abo/swiper/issues/117][#117]].
+*** fix wrap-around for =ivy-next-line=
+See [[https://github.com/abo-abo/swiper/issues/118][#118]].
+*** =swiper-avy= should do nothing for empty input
+See [[https://github.com/abo-abo/avy/issues/50][#50]].
+*** =ivy-alt-done= should require TRAMP if necessary
+See [[https://github.com/abo-abo/swiper/pull/145][#145]].
+*** =swiper-query-replace= shouldn't miss the first occurrence
+See [[https://github.com/abo-abo/swiper/pull/144][#144]].
+*** =swiper= should not deactivate mark
+*** =ivy-mode= should not switch to TRAMP for certain input
+See [[https://github.com/abo-abo/swiper/pull/145][#145]].
+*** =counsel-find-file= should work better with TRAMP
+"/ssh:foo" should not be cut off
+See [[https://github.com/abo-abo/swiper/pull/145][#145]].
+*** =counsel-find-file= supports Windows drive letters
+See [[https://github.com/abo-abo/swiper/pull/155][#155]].
+*** =counsel-file-file= should work better with files that contain "~"
+See [[https://github.com/abo-abo/swiper/pull/157][#157]].
+*** =counsel-M-x= should respect =ivy-format-function=
+See [[https://github.com/abo-abo/swiper/pull/150][#150]].
+*** =counsel-git-grep= should position better on exit
+See [[https://github.com/abo-abo/swiper/pull/153][#153]].
+*** =ivy-mode= should re-scale text to minibuffer height
+See [[https://github.com/abo-abo/swiper/pull/151][#151]].
+*** =counsel-unicode-char= should use action-style call
+See [[https://github.com/abo-abo/swiper/pull/160][#160]].
+*** =ivy-read= should allow % in prompt string
+See [[https://github.com/abo-abo/swiper/pull/171][#171]].
+*** =ivy-call= should execute in proper window
+See [[https://github.com/abo-abo/swiper/pull/176][#176]].
+** New Features
+*** =ivy-mode=
+**** Open an Info file on the file system
+When in =Info-mode=, press ~g~ and select either "(./)" or "(../)" to
+switch to file name completion. That file will be opened with Info.
+**** Account for =minibuffer-depth-indication-mode=
+If you have =minibuffer-depth-indication-mode= on, the minibuffer
+prompt will indicate the current depth.
+See [[https://github.com/abo-abo/swiper/pull/134][#134]].
+**** Add fuzzy matching function
+To enable fuzzy matching, set your =ivy-re-builders-alist= accordingly:
+#+begin_src elisp
+(setq ivy-re-builders-alist
+ '((t . ivy--regex-fuzzy)))
+#+end_src
+See [[https://github.com/abo-abo/swiper/pull/136][#136]].
+
+See also [[https://github.com/abo-abo/swiper/pull/142][#142]] for toggling fuzzy matching with ~C-o m~.
+**** =case-fold-search= optimization
+Bind case-fold-search to t when the input is all lower-case:
+
+- input "the" matches both "the" and "The".
+- input "The" matches only "The".
+
+See [[https://github.com/abo-abo/swiper/pull/166][#166]].
+**** Allow to see the candidate index a la =anzu= via =ivy-count-format=
+To have this feature, use something like this:
+#+begin_src elisp
+(setq ivy-count-format "(%d/%d) ")
+#+end_src
+See [[https://github.com/abo-abo/swiper/pull/167][#167]].
+
+You can also set this to nil, if you don't want any count, see [[https://github.com/abo-abo/swiper/pull/188][#188]].
+**** Allow to add additional exit points for any command
+Example for =ivy-switch-to-buffer=:
+#+begin_src elisp
+(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")))
+#+end_src
+
+After this:
+
+- use ~M-o k~ to kill a buffer
+- use ~M-o j~ to switch to a buffer in other window
+
+You can always use ~M-o o~ to access the default action. When there is
+only one action, ~M-o~ does the same as ~C-m~.
+
+See [[https://github.com/abo-abo/swiper/pull/164][#164]].
+
+
+
+
+
+
+
+
+*** =counsel-describe-function= and =counsel-decribe-variable=
+**** Add a binding to look up the symbol in info
+Press ~C-,~ to look up the symbol in info, instead of the default
+describe action.
+See [[https://github.com/abo-abo/swiper/pull/121][#121]].
+**** Handle symbol-at-point better in non-Elisp buffers
+See [[https://github.com/abo-abo/swiper/pull/126][#126]].
+*** =ivy-switch-buffer=
+**** New face =ivy-virtual=
+See [[https://github.com/abo-abo/swiper/pull/129][#129]].
+**** Deal better with invisible buffers
+See [[https://github.com/abo-abo/swiper/pull/135][#135]].
+**** Add custom keymap
+You can customize =ivy-switch-buffer-map=.
+
+See [[https://github.com/abo-abo/swiper/pull/164][#164]].
+**** Add extra actions
+Add a =kill-buffer= action, and =switch-to-buffer-other-window= action.
+*** =counsel-git-grep=
+**** Add Async
+Make it fully async: the process =git grep= will be killed and
+restarted on new input. This results in almost no keyboard delay.
+**** Own history variable
+*** =swiper=
+**** Own history variable
+Having own history variable allows to get more use of ~M-p~, ~M-n~ and ~C-r~.
+*** =counsel-el=
+**** Switch to action-style call
+This allows to make use of ~C-M-n~ and ~C-M-p~.
+*** =counsel-locate=
+**** Add Async
+**** Add extra actions
+In addition to the default action of opening a file add:
+
+- =xdg-open= action
+- =dired= action
+
+Press ~M-o~ or ~C-o~ to access these actions.
+**** Add own history
+
+*** API
+**** Add :matcher
+A matcher is a function that accepts a regexp and a list of candidates
+and returns the filtered list of candidates.
+
+The default matcher is basically =cl-remove-if-not= + =string-match=.
+If you'd like to customize this, pass your own matcher.
+
+See =counsel-git-grep-matcher= for an example.
+**** Allow to customize the initial input for all commands
+Customize =ivy-initial-inputs-alist= for this.
+See [[https://github.com/abo-abo/swiper/pull/140][#140]].
+**** =ivy-sort-functions-alist= should also examine =this-command=
+**** :dynamic-collection is now a boolean
+Pass the collection function as the second var instead.
+
+** New Commands
+*** =ivy-call=
+Execute the current action for the current candidate without exiting
+the minibuffer. Bound to ~C-M-m~ or ~M-RET~ or ~C-o g~.
+
+
+*** =counsel-find-file=
+Forward to =find-file= with Ivy completion.
+
+=ivy-next-line-and-call= as well as =ivy-resume= should work for this command.
+
+The variable =counsel-find-file-ignore-regexp= allows to ignore
+certain files, like dot files. Input a leading dot to see all files.
+
+The variable =counsel-find-file-at-point= allows to automatically use
+=ffap=. You also can do it manually with ~M-n~ when the point is on a file name.
+
+The variable =counsel-find-file-map= allows to customize the
+minibuffer key bindings for this command.
+
+Recommended binding:
+
+#+begin_src elisp
+(global-set-key (kbd "C-x C-f") 'counsel-find-file)
+#+end_src
+
+You can peek at files with ~C-M-n~ and ~C-M-p~.
+
+See [[https://github.com/abo-abo/swiper/issues/122][#122]] and [[https://github.com/abo-abo/swiper/issues/123][#123]].
+
+See [[https://github.com/abo-abo/swiper/pull/152][#152]] about ~M-n~, ~M-p~ and ~M-i~ switching directories when necessary.
+
+*** =ivy-recentf=
+Find a file on =recentf-list=.
+
+Note that if your set =ivy-use-virtual-buffers=, =recentf-list= is
+merged into candidates list for =ivy-switch-buffer=. But if you want
+it separately, you can use this command.
+
+See [[https://github.com/abo-abo/swiper/issues/124][#124]].
+*** =ivy-yank-word=
+Add word at point to minibuffer input.
+
+This is similar to what ~C-w~ does for =isearch=. However it's bound
+to ~M-j~ instead of ~C-w~, since ~C-w~ is bound to =kill-region= - a
+useful command.
+
+See [[https://github.com/abo-abo/swiper/issues/125][#125]].
+*** =counsel-M-x=
+Forward to =execute-extended-command= with Ivy completion.
+The candidate list will also display the key binding for each bound command.
+
+This command will piggyback on =smex= for sorting, if =smex= is installed.
+
+Use =counsel-M-x-initial-input= to customize the initial input for
+this command. By default, it's "^" - the regex character that
+indicates beginning of string. This results in much faster matching,
+since you usually type the command name from the start.
+
+See [[https://github.com/abo-abo/swiper/pull/136][#136]] and [[https://github.com/abo-abo/swiper/pull/138][#138]].
+
+*** =hydra-ivy=
+Press ~C-o~ to toggle the Hydra for Ivy.
+It gives access to shorter bindings and many customizable options.
+
+Use ~C-o >~ to grow the minibuffer.
+Use ~C-o <~ to shrink the minibuffer.
+
+See [[https://github.com/abo-abo/swiper/pull/151][#151]].
+
+*** =ivy-toggle-calling=
+Toggle executing the current action each time a new candidate is selected.
+
+This command is bound to ~C-o c~.
+
+To explain how this is useful: ~C-M-m C-M-f C-M-f C-M-f~ is equivalent to ~C-o cjjj~.
+
+*** =ivy-insert-current=
+Inserts the current candidate into the minibuffer.
+
+Press ~M-i~ if you want something close to the current candidate. You
+can follow up with an edit and select.
+
+I find this very useful when creating new files with a similar name to
+the existing file: ~C-x C-f M-i~ + a bit of editing is very fast.
+
+See [[https://github.com/abo-abo/swiper/pull/141][#141]].
+
+*** =counsel-load-theme=
+Forward to =load-theme= with Ivy completion. Allows to rapidly try themes (e.g. with ~C-M-n~).
+
+*** =ivy-reverse-i-search=
+Allow to recursively match history with ~C-r~.
+
+I like this command from bash shell. The usual way to search through
+history is with ~M-p~ and ~M-n~. Using =ivy-reverse-i-search= will
+open a recursive completion session with the current history as the
+candidates.
+*** =counsel-rhythmbox=
+[[http://oremacs.com/2015/07/09/counsel-rhythmbox/][Control Rhythmbox from Emacs.]]
+*** =ivy-dispatching-done=
+Select an action for the current candidate and execute it. Bound to ~M-o~.
+
+Some commands that support ~M-o~:
+
+- =counsel-rhythmbox=
+- =counsel-describe-function=
+- =counsel-describe-variable=
+- =ivy-switch-buffer=
+- =counsel-locate=
+
+*** =counsel-org-tag=
+Forward to =org-set-tags= with Ivy completion.
+
+Selecting any tag each time will toggle it on/off.
+The current list of selected tags will be displayed in the prompt.
+
+See [[https://github.com/abo-abo/swiper/pull/177][#177]] and [[https://github.com/abo-abo/swiper/pull/91][#91]].
+
+*** =counsel-org-tag-agenda=
+Forward to =org-agenda-set-tags= with Ivy completion.
+See [[https://github.com/abo-abo/swiper/pull/177][#177]].
+
+*** =counsel-ag=
+Interactively =ag= using Ivy completion.
+
+*** =counsel-recoll=
+Use =recoll= with Ivy completion.
+See [[http://oremacs.com/2015/07/27/counsel-recoll/][Using Recoll desktop search database with Emacs]].
+
+Install recoll with =sudo apt-get install recoll=.
+
+*** =swiper-from-isearch=
+Start =swiper= from the current =isearch= input.
+
+*** =ivy-immediate-done=
+Use this command to exit the minibuffer choosing not the current
+candidate, but the current text. Bound to ~C-M-j~ or ~C-u C-j~.
+
+See [[https://github.com/abo-abo/swiper/pull/183][#183]].
(require 'ivy)
(eval-when-compile
- (unless (package-installed-p 'hydra)
+ (unless (or (featurep 'hydra) (package-installed-p 'hydra))
(defmacro defhydra (name &rest _)
"This is a stub for the uninstalled `hydra' package."
`(defun ,(intern (format "%S/body" name)) ()
(defhydra hydra-ivy (:hint nil
:color pink)
"
-^^^^^^ ^Yes^ ^No^ ^Maybe^
-^^^^^^^^^^^^^^---------------------------------------
-^ ^ _k_ ^ ^ _f_ollow _i_nsert _c_: calling %s(if ivy-calling \"on\" \"off\")
+^^^^^^ ^Yes^ ^No^ ^Maybe^ ^Action^
+^^^^^^^^^^^^^^---------------------------------------------------
+^ ^ _k_ ^ ^ _f_ollow _i_nsert _c_: calling %s(if ivy-calling \"on\" \"off\") _w_/_s_: %s(ivy-action-name)
_h_ ^+^ _l_ _d_one _o_ops _m_: matcher %s(if (eq ivy--regex-function 'ivy--regex-fuzzy) \"fuzzy\" \"ivy\")
-^ ^ _j_ ^ ^ ^ ^ ^ ^ _<_/_>_: shrink/grow window
+^ ^ _j_ ^ ^ _g_o ^ ^ _<_/_>_: shrink/grow window
"
;; arrows
("h" ivy-beginning-of-buffer)
("f" ivy-alt-done :exit nil)
("C-j" ivy-alt-done :exit nil)
("d" ivy-done :exit t)
+ ("g" ivy-call)
("C-m" ivy-done :exit t)
("c" ivy-toggle-calling)
("m" ivy-toggle-fuzzy)
(">" ivy-minibuffer-grow)
- ("<" ivy-minibuffer-shrink))
+ ("<" ivy-minibuffer-shrink)
+ ("w" ivy-prev-action)
+ ("s" ivy-next-action))
(provide 'ivy-hydra)
#("\nDESCRIPTION\nFUNCTION LETTERS\nSWITCHES\nDIAGNOSTICS\nEXAMPLE 1\nEXAMPLE 2\nEXAMPLE 3\nSEE ALSO\nAUTHOR"
0 90 (read-only nil)
90 96 (face ivy-current-match read-only nil)))))
+
+(ert-deftest ivy--filter ()
+ (setq ivy-last (make-ivy-state))
+ (should (equal (ivy--filter "the" '("foo" "the" "The"))
+ '("the" "The")))
+ (should (equal (ivy--filter "The" '("foo" "the" "The"))
+ '("The"))))
+
(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."
"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-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)
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'."
(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 ((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)
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
_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)
(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)
(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))
(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
+ (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."
CANDIDATES are assumed to be static."
(let* ((re (funcall ivy--regex-function name))
(matcher (ivy-state-matcher ivy-last))
+ (case-fold-search (string= name (downcase name)))
(cands (cond
(matcher
(funcall matcher re candidates))
(unless (and (not (equal re ivy--old-re))
(or (setq ivy--index
(or
- (cl-position re cands
- :test #'equal)
+ (cl-position (if (and (> (length re) 0)
+ (eq ?^ (aref re 0)))
+ (substring re 1)
+ re) cands
+ :test #'equal)
(and ivy--directory
(cl-position
(concat re "/") cands
(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-other-window ivy-text)
+ (let ((virtual (assoc buffer ivy--virtual-buffers)))
+ (if (and virtual
+ (not (get-buffer buffer)))
+ (find-file-other-window (cdr virtual))
+ (switch-to-buffer-other-window buffer)))))
+
+(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")))
+
(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))))
(defun ivy-recentf ()
"Find a file on `recentf-list'."
"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)
(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
map)
"Keymap for swiper.")
-(defvar swiper--window nil
- "Store the current window.")
-
(defun swiper-query-replace ()
"Start `query-replace' with string to replace from last search string."
(interactive)
(to (query-replace-read-to from "Query replace" t)))
(delete-minibuffer-contents)
(ivy-set-action (lambda (_)
- (with-selected-window swiper--window
+ (with-ivy-window
(move-beginning-of-line 1)
(perform-replace from to
t t nil))))
(declare-function avy--regex-candidates "ext:avy")
(declare-function avy--process "ext:avy")
(declare-function avy--overlay-post "ext:avy")
-(declare-function avy--goto "ext:avy")
+(declare-function avy-action-goto "ext:avy")
;;;###autoload
(defun swiper-avy ()
"Jump to one of the current swiper candidates."
(interactive)
(unless (string= ivy-text "")
- (with-selected-window (ivy-state-window ivy-last)
+ (with-ivy-window
(let* ((avy-all-windows nil)
(candidates
(avy--regex-candidates
(candidate
(avy--process candidates #'avy--overlay-post)))
(ivy-quit-and-run
- (avy--goto candidate))))))
+ (avy-action-goto candidate))))))
(defun swiper-recenter-top-bottom (&optional arg)
- "Call (`recenter-top-bottom' ARG) in `swiper--window'."
+ "Call (`recenter-top-bottom' ARG)."
(interactive "P")
- (with-selected-window swiper--window
+ (with-ivy-window
(recenter-top-bottom arg)))
(defun swiper-font-lock-ensure ()
dired-mode
jabber-chat-mode
elfeed-search-mode
- fundamental-mode)))
+ fundamental-mode
+ Man-mode
+ woman-mode)))
(unless (> (buffer-size) 100000)
(if (fboundp 'font-lock-ensure)
(font-lock-ensure)
"Perform initialization common to both completion methods."
(setq swiper--opoint (point))
(setq swiper--len 0)
- (setq swiper--anchor (line-number-at-pos))
- (setq swiper--window (selected-window)))
+ (setq swiper--anchor (line-number-at-pos)))
(defun swiper--re-builder (str)
"Transform STR into a swiper regex.
res)
(unwind-protect
(setq res (ivy-read
- (replace-regexp-in-string
- "%s" "pattern: " swiper--format-spec)
+ "Swiper: "
candidates
:initial-input initial-input
:keymap swiper-map
(defun swiper--update-input-ivy ()
"Called when `ivy' input is updated."
- (swiper--cleanup)
- (let* ((re (ivy--regex ivy-text))
- (str ivy--current)
- (num (if (string-match "^[0-9]+" str)
- (string-to-number (match-string 0 str))
- 0)))
- (with-selected-window swiper--window
+ (with-ivy-window
+ (swiper--cleanup)
+ (let* ((re (ivy--regex ivy-text))
+ (str ivy--current)
+ (num (if (string-match "^[0-9]+" str)
+ (string-to-number (match-string 0 str))
+ 0)))
(goto-char (point-min))
(when (cl-plusp num)
(goto-char (point-min))
(isearch-range-invisible (line-beginning-position)
(line-end-position))
(unless (and (>= (point) (window-start))
- (<= (point) (window-end swiper--window t)))
+ (<= (point) (window-end (ivy-state-window ivy-last) t)))
(recenter)))
(swiper--add-overlays re))))
(line-beginning-position)
(1+ (line-end-position)))))
(overlay-put ov 'face 'swiper-line-face)
- (overlay-put ov 'window swiper--window)
- (push ov swiper--overlays))
- (let* ((wh (window-height))
- (beg (or beg (save-excursion
- (forward-line (- wh))
- (point))))
- (end (or end (save-excursion
- (forward-line wh)
- (point)))))
- (when (>= (length re) swiper-min-highlight)
- (save-excursion
- (goto-char beg)
- ;; RE can become an invalid regexp
- (while (and (ignore-errors (re-search-forward re end t))
- (> (- (match-end 0) (match-beginning 0)) 0))
- (let ((i 0))
- (while (<= i ivy--subexps)
- (when (match-beginning i)
- (let ((overlay (make-overlay (match-beginning i)
- (match-end i)))
- (face
- (cond ((zerop ivy--subexps)
- (cadr swiper-faces))
- ((zerop i)
- (car swiper-faces))
- (t
- (nth (1+ (mod (+ i 2) (1- (length swiper-faces))))
- swiper-faces)))))
- (push overlay swiper--overlays)
- (overlay-put overlay 'face face)
- (overlay-put overlay 'window swiper--window)
- (overlay-put overlay 'priority i)))
- (cl-incf i))))))))
+ (overlay-put ov 'window (ivy-state-window ivy-last))
+ (push ov swiper--overlays)
+ (let* ((wh (window-height))
+ (beg (or beg (save-excursion
+ (forward-line (- wh))
+ (point))))
+ (end (or end (save-excursion
+ (forward-line wh)
+ (point)))))
+ (when (>= (length re) swiper-min-highlight)
+ (save-excursion
+ (goto-char beg)
+ ;; RE can become an invalid regexp
+ (while (and (ignore-errors (re-search-forward re end t))
+ (> (- (match-end 0) (match-beginning 0)) 0))
+ (let ((i 0))
+ (while (<= i ivy--subexps)
+ (when (match-beginning i)
+ (let ((overlay (make-overlay (match-beginning i)
+ (match-end i)))
+ (face
+ (cond ((zerop ivy--subexps)
+ (cadr swiper-faces))
+ ((zerop i)
+ (car swiper-faces))
+ (t
+ (nth (1+ (mod (+ i 2) (1- (length swiper-faces))))
+ swiper-faces)))))
+ (push overlay swiper--overlays)
+ (overlay-put overlay 'face face)
+ (overlay-put overlay 'window (ivy-state-window ivy-last))
+ (overlay-put overlay 'priority i)))
+ (cl-incf i)))))))))
(defun swiper--action (x input)
"Goto line X and search for INPUT."
(push-mark swiper--opoint t)
(message "Mark saved where search started")))))
+;; (define-key isearch-mode-map (kbd "C-o") 'swiper-from-isearch)
+(defun swiper-from-isearch ()
+ "Invoke `swiper' from isearch."
+ (interactive)
+ (let ((query (if isearch-regexp
+ isearch-string
+ (regexp-quote isearch-string))))
+ (isearch-exit)
+ (swiper query)))
+
(provide 'swiper)
;;; swiper.el ends here