1 ;;; counsel.el --- Various completion functions using Ivy -*- lexical-binding: t -*-
3 ;; Copyright (C) 2015 Free Software Foundation, Inc.
5 ;; Author: Oleh Krehel <ohwoeowho@gmail.com>
6 ;; URL: https://github.com/abo-abo/swiper
8 ;; Package-Requires: ((emacs "24.1") (swiper "0.4.0"))
9 ;; Keywords: completion, matching
11 ;; This file is part of GNU Emacs.
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)
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.
23 ;; For a full copy of the GNU General Public License
24 ;; see <http://www.gnu.org/licenses/>.
28 ;; Just call one of the interactive functions in this file to complete
29 ;; the corresponding thing using `ivy'.
31 ;; Currently available: Elisp symbols, Clojure symbols, Git files.
38 (defvar counsel-completion-beg nil
39 "Completion bounds start.")
41 (defvar counsel-completion-end nil
42 "Completion bounds end.")
46 "Elisp completion at point."
48 (let* ((bnd (unless (and (looking-at ")")
49 (eq (char-before) ?\())
50 (bounds-of-thing-at-point
53 (buffer-substring-no-properties
58 (funp (eq (char-before (car bnd)) ?\())
62 (setq counsel-completion-beg
63 (move-marker (make-marker) (car bnd)))
64 (setq counsel-completion-end
65 (move-marker (make-marker) (cdr bnd))))
66 (setq counsel-completion-beg nil)
67 (setq counsel-completion-end nil))
72 (push (symbol-name x) symbol-names))))
74 (all-completions str obarray
79 (special-form-p x)))))))
80 (ivy-read "Symbol name: " symbol-names
81 :predicate (and funp #'functionp)
83 :action #'counsel--el-action)))
85 (declare-function slime-symbol-start-pos "ext:slime")
86 (declare-function slime-symbol-end-pos "ext:slime")
87 (declare-function slime-contextual-completions "ext:slime-c-p-c")
91 "Common Lisp completion at point."
93 (setq counsel-completion-beg (slime-symbol-start-pos))
94 (setq counsel-completion-end (slime-symbol-end-pos))
95 (ivy-read "Symbol name: "
96 (car (slime-contextual-completions
97 counsel-completion-beg
98 counsel-completion-end))
99 :action #'counsel--el-action))
101 (defun counsel--el-action (symbol)
102 "Insert SYMBOL, erasing the previous one."
103 (when (stringp symbol)
105 (when counsel-completion-beg
107 counsel-completion-beg
108 counsel-completion-end))
109 (setq counsel-completion-beg
110 (move-marker (make-marker) (point)))
112 (setq counsel-completion-end
113 (move-marker (make-marker) (point))))))
115 (declare-function deferred:sync! "ext:deferred")
116 (declare-function jedi:complete-request "ext:jedi-core")
117 (declare-function jedi:ac-direct-matches "ext:jedi")
119 (defun counsel-jedi ()
120 "Python completion at point."
122 (let ((bnd (bounds-of-thing-at-point 'symbol)))
125 (setq counsel-completion-beg (car bnd))
126 (setq counsel-completion-end (cdr bnd)))
127 (setq counsel-completion-beg nil)
128 (setq counsel-completion-end nil)))
130 (jedi:complete-request))
131 (ivy-read "Symbol name: " (jedi:ac-direct-matches)
132 :action #'counsel--py-action))
134 (defun counsel--py-action (symbol)
135 "Insert SYMBOL, erasing the previous one."
136 (when (stringp symbol)
138 (when counsel-completion-beg
140 counsel-completion-beg
141 counsel-completion-end))
142 (setq counsel-completion-beg
143 (move-marker (make-marker) (point)))
145 (setq counsel-completion-end
146 (move-marker (make-marker) (point)))
147 (when (equal (get-text-property 0 'symbol symbol) "f")
149 (setq counsel-completion-end
150 (move-marker (make-marker) (point)))
151 (backward-char 1)))))
153 (defvar counsel-describe-map
154 (let ((map (make-sparse-keymap)))
155 (define-key map (kbd "C-.") #'counsel-find-symbol)
156 (define-key map (kbd "C-,") #'counsel--info-lookup-symbol)
159 (defun counsel-find-symbol ()
160 "Jump to the definition of the current symbol."
162 (ivy-set-action #'counsel--find-symbol)
165 (defun counsel--info-lookup-symbol ()
166 "Lookup the current symbol in the info docs."
168 (ivy-set-action #'counsel-info-lookup-symbol)
171 (defun counsel--find-symbol (x)
172 "Find symbol definition that corresponds to string X."
173 (ring-insert find-tag-marker-ring (point-marker))
174 (let ((full-name (get-text-property 0 'full-name x)))
176 (find-library full-name)
177 (let ((sym (read x)))
178 (cond ((and (eq (ivy-state-caller ivy-last)
179 'counsel-describe-variable)
188 (prin1-to-string sym)))
190 (prin1-to-string sym)))
192 (error "Couldn't fild definition of %s"
195 (defvar counsel-describe-symbol-history nil
196 "History for `counsel-describe-variable' and `counsel-describe-function'.")
198 (defun counsel-symbol-at-point ()
199 "Return current symbol at point as a string."
200 (let ((s (thing-at-point 'symbol)))
202 (if (string-match "\\`[`']?\\(.*?\\)'?\\'" s)
206 (defun counsel-variable-list ()
207 "Return the list of all currently bound variables."
211 (when (or (get vv 'variable-documentation)
212 (and (boundp vv) (not (keywordp vv))))
213 (push (symbol-name vv) cands))))
217 (defun counsel-describe-variable ()
218 "Forward to `describe-variable'."
220 (let ((enable-recursive-minibuffers t))
222 "Describe variable: "
223 (counsel-variable-list)
224 :keymap counsel-describe-map
225 :preselect (counsel-symbol-at-point)
226 :history 'counsel-describe-symbol-history
232 :caller 'counsel-describe-variable)))
235 'counsel-describe-variable
236 '(("i" counsel-info-lookup-symbol "info")
237 ("d" counsel--find-symbol "definition")))
240 'counsel-describe-function
241 '(("i" counsel-info-lookup-symbol "info")
242 ("d" counsel--find-symbol "definition")))
245 (defun counsel-describe-function ()
246 "Forward to `describe-function'."
248 (let ((enable-recursive-minibuffers t))
249 (ivy-read "Describe function: "
254 (push (symbol-name x) cands))))
256 :keymap counsel-describe-map
257 :preselect (counsel-symbol-at-point)
258 :history 'counsel-describe-symbol-history
264 :caller 'counsel-describe-function)))
266 (defvar info-lookup-mode)
267 (declare-function info-lookup->completions "info-look")
268 (declare-function info-lookup->mode-value "info-look")
269 (declare-function info-lookup-select-mode "info-look")
270 (declare-function info-lookup-change-mode "info-look")
271 (declare-function info-lookup "info-look")
274 (defun counsel-info-lookup-symbol (symbol &optional mode)
275 "Forward to (`info-describe-symbol' SYMBOL MODE) with ivy completion."
279 (let* ((topic 'symbol)
280 (mode (cond (current-prefix-arg
281 (info-lookup-change-mode topic))
282 ((info-lookup->mode-value
283 topic (info-lookup-select-mode))
285 ((info-lookup-change-mode topic))))
286 (completions (info-lookup->completions topic mode))
287 (enable-recursive-minibuffers t)
290 (mapcar #'car completions)
292 (list value info-lookup-mode))))
294 (info-lookup 'symbol symbol mode))
296 (defvar counsel-unicode-char-history nil
297 "History for `counsel-unicode-char'.")
300 (defun counsel-unicode-char ()
301 "Insert a Unicode character at point."
303 (let ((minibuffer-allow-text-properties t))
304 (setq counsel-completion-beg (point))
305 (setq counsel-completion-end (point))
306 (ivy-read "Unicode name: "
309 (format "% -60s%c" (car x) (cdr x))
312 :action (lambda (char)
314 (delete-region counsel-completion-beg counsel-completion-end)
315 (setq counsel-completion-beg (point))
316 (insert-char (get-text-property 0 'result char))
317 (setq counsel-completion-end (point))))
318 :history 'counsel-unicode-char-history)))
320 (declare-function cider-sync-request:complete "ext:cider-client")
322 (defun counsel-clj ()
323 "Clojure completion at point."
329 (cider-sync-request:complete str ":same")))))
332 (defun counsel-git ()
333 "Find file in the current Git repository."
335 (let* ((default-directory (locate-dominating-file
336 default-directory ".git"))
338 (shell-command-to-string
339 "git ls-files --full-name --")
343 (let ((default-directory ,default-directory))
345 (ivy-read "Find file: " cands
348 (defvar counsel--git-grep-dir nil
349 "Store the base git directory.")
351 (defvar counsel--git-grep-count nil
352 "Store the line count in current repository.")
354 (defun counsel-more-chars (n)
355 "Return two fake candidates prompting for at least N input."
357 (format "%d chars more" (- n (length ivy-text)))))
359 (defvar counsel-git-grep-cmd "git --no-pager grep --full-name -n --no-color -i -e %S"
360 "Store the command for `counsel-git-grep'.")
362 (defun counsel-git-grep-function (string &optional _pred &rest _unused)
363 "Grep in the current git repository for STRING."
364 (if (and (> counsel--git-grep-count 20000)
365 (< (length string) 3))
366 (counsel-more-chars 3)
367 (let* ((default-directory counsel--git-grep-dir)
368 (cmd (format counsel-git-grep-cmd
369 (setq ivy--old-re (ivy--regex string t)))))
370 (if (<= counsel--git-grep-count 20000)
371 (split-string (shell-command-to-string cmd) "\n" t)
372 (counsel--gg-candidates (ivy--regex string))
375 (defvar counsel-git-grep-map
376 (let ((map (make-sparse-keymap)))
377 (define-key map (kbd "C-l") 'counsel-git-grep-recenter)
378 (define-key map (kbd "M-q") 'counsel-git-grep-query-replace)
381 (defun counsel-git-grep-query-replace ()
382 "Start `query-replace' with string to replace from last search string."
384 (if (null (window-minibuffer-p))
386 "Should only be called in the minibuffer through `counsel-git-grep-map'")
387 (let* ((enable-recursive-minibuffers t)
388 (from (ivy--regex ivy-text))
389 (to (query-replace-read-to from "Query replace" t)))
391 (dolist (cand ivy--old-cands)
392 (when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" cand)
394 (let ((file-name (match-string-no-properties 1 cand)))
395 (setq file-name (expand-file-name file-name counsel--git-grep-dir))
396 (unless (member file-name done-buffers)
397 (push file-name done-buffers)
398 (find-file file-name)
399 (goto-char (point-min)))
400 (perform-replace from to t t nil))))))
401 (delete-minibuffer-contents)
405 (defun counsel-git-grep-recenter ()
408 (counsel-git-grep-action ivy--current)
409 (recenter-top-bottom)))
411 (defun counsel-git-grep-action (x)
412 (when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" x)
414 (let ((file-name (match-string-no-properties 1 x))
415 (line-number (match-string-no-properties 2 x)))
416 (find-file (expand-file-name file-name counsel--git-grep-dir))
417 (goto-char (point-min))
418 (forward-line (1- (string-to-number line-number)))
419 (re-search-forward (ivy--regex ivy-text t) (line-end-position) t)
420 (unless (eq ivy-exit 'done)
422 (swiper--add-overlays (ivy--regex ivy-text)))))))
424 (defvar counsel-git-grep-history nil
425 "History for `counsel-git-grep'.")
427 (defvar counsel-git-grep-cmd-history
428 '("git --no-pager grep --full-name -n --no-color -i -e %S")
429 "History for `counsel-git-grep' shell commands.")
432 (defun counsel-git-grep (&optional cmd initial-input)
433 "Grep for a string in the current git repository.
434 When CMD is a string, use it as a \"git grep\" command.
435 When CMD is non-nil, prompt for a specific \"git grep\" command.
436 INITIAL-INPUT can be given as the initial minibuffer input."
440 (setq counsel-git-grep-cmd cmd))
442 (setq counsel-git-grep-cmd
443 (ivy-read "cmd: " counsel-git-grep-cmd-history
444 :history 'counsel-git-grep-cmd-history))
445 (setq counsel-git-grep-cmd-history
446 (delete-dups counsel-git-grep-cmd-history)))
448 (setq counsel-git-grep-cmd "git --no-pager grep --full-name -n --no-color -i -e %S")))
449 (setq counsel--git-grep-dir
450 (locate-dominating-file default-directory ".git"))
451 (if (null counsel--git-grep-dir)
452 (error "Not in a git repository")
453 (setq counsel--git-grep-count (counsel--gg-count "" t))
454 (ivy-read "git grep: " 'counsel-git-grep-function
455 :initial-input initial-input
456 :matcher #'counsel-git-grep-matcher
457 :dynamic-collection (> counsel--git-grep-count 20000)
458 :keymap counsel-git-grep-map
459 :action #'counsel-git-grep-action
460 :unwind #'swiper--cleanup
461 :history 'counsel-git-grep-history
462 :caller 'counsel-git-grep)))
464 (defcustom counsel-find-file-at-point nil
465 "When non-nil, add file-at-point to the list of candidates."
469 (declare-function ffap-guesser "ffap")
471 (defvar counsel-find-file-map (make-sparse-keymap))
474 (defun counsel-find-file ()
475 "Forward to `find-file'."
477 (ivy-read "Find file: " 'read-file-name-internal
478 :matcher #'counsel--find-file-matcher
482 (find-file (expand-file-name x ivy--directory))))
483 :preselect (when counsel-find-file-at-point
486 :require-match 'confirm-after-completion
487 :history 'file-name-history
488 :keymap counsel-find-file-map))
490 (defcustom counsel-find-file-ignore-regexp nil
491 "A regexp of files to ignore while in `counsel-find-file'.
492 These files are un-ignored if `ivy-text' matches them.
493 The common way to show all files is to start `ivy-text' with a dot.
494 Possible value: \"\\(?:\\`[#.]\\)\\|\\(?:[#~]\\'\\)\"."
497 (defun counsel--find-file-matcher (regexp candidates)
498 "Return REGEXP-matching CANDIDATES.
499 Skip some dotfiles unless `ivy-text' requires them."
500 (let ((res (cl-remove-if-not
502 (string-match regexp x))
504 (if (or (null counsel-find-file-ignore-regexp)
505 (string-match counsel-find-file-ignore-regexp ivy-text))
509 (string-match counsel-find-file-ignore-regexp x))
512 (defun counsel-git-grep-matcher (regexp candidates)
513 (or (and (equal regexp ivy--old-re)
520 (when (string-match "^[^:]+:[^:]+:" x)
521 (setq x (substring x (match-end 0)))
523 (string-match regexp x)
530 (string-match (car re) x)
531 (not (string-match (car re) x)))))))
534 (setq ivy--old-re regexp))))
536 (defvar counsel--async-time nil
537 "Store the time when a new process was started.
538 Or the time of the last minibuffer update.")
540 (defun counsel--async-command (cmd)
541 (let* ((counsel--process " *counsel*")
542 (proc (get-process counsel--process))
543 (buff (get-buffer counsel--process)))
545 (delete-process proc))
548 (setq proc (start-process-shell-command
552 (setq counsel--async-time (current-time))
553 (set-process-sentinel proc #'counsel--async-sentinel)
554 (set-process-filter proc #'counsel--async-filter)))
556 (defun counsel--async-sentinel (process event)
557 (if (string= event "finished\n")
559 (with-current-buffer (process-buffer process)
560 (setq ivy--all-candidates
562 (split-string (buffer-string) "\n" t)))
563 (setq ivy--old-cands ivy--all-candidates))
565 (if (string= event "exited abnormally with code 1\n")
567 (setq ivy--all-candidates '("Error"))
568 (setq ivy--old-cands ivy--all-candidates)
571 (defun counsel--async-filter (process str)
572 "Receive from PROCESS the output STR.
573 Update the minibuffer with the amount of lines collected every
574 0.5 seconds since the last update."
575 (with-current-buffer (process-buffer process)
581 (time-since counsel--async-time))
582 (with-current-buffer (process-buffer process)
583 (goto-char (point-min))
584 (setq size (- (buffer-size) (forward-line (buffer-size)))))
585 (ivy--insert-minibuffer
586 (format "\ncollected: %d" size))
587 (setq counsel--async-time (current-time)))))
589 (defun counsel-locate-action-extern (x)
590 "Use xdg-open shell command on X."
591 (call-process shell-file-name nil
595 (if (eq system-type 'darwin)
598 (shell-quote-argument x))))
600 (declare-function dired-jump "dired-x")
601 (defun counsel-locate-action-dired (x)
602 "Use `dired-jump' on X."
605 (defvar counsel-locate-history nil
606 "History for `counsel-locate'.")
608 (defcustom counsel-locate-options (if (eq system-type 'darwin)
611 "Command line options for `locate`."
613 :type '(repeat string))
617 '(("x" counsel-locate-action-extern "xdg-open")
618 ("d" counsel-locate-action-dired "dired")))
620 (defun counsel-unquote-regex-parens (str)
621 (replace-regexp-in-string
623 (replace-regexp-in-string
627 (defun counsel-locate-function (str &rest _u)
628 (if (< (length str) 3)
629 (counsel-more-chars 3)
630 (counsel--async-command
631 (format "locate %s '%s'"
632 (mapconcat #'identity counsel-locate-options " ")
633 (counsel-unquote-regex-parens
637 (defun counsel-delete-process ()
638 (let ((process (get-process " *counsel*")))
640 (delete-process process))))
643 (defun counsel-locate ()
644 "Call locate shell command."
646 (ivy-read "Locate: " #'counsel-locate-function
647 :dynamic-collection t
648 :history 'counsel-locate-history
649 :action (lambda (file)
653 :unwind #'counsel-delete-process))
655 (defun counsel--generic (completion-fn)
656 "Complete thing at point with COMPLETION-FN."
657 (let* ((bnd (bounds-of-thing-at-point 'symbol))
659 (buffer-substring-no-properties
662 (candidates (funcall completion-fn str))
664 (res (ivy-read (format "pattern (%s): " str)
668 (delete-region (car bnd) (cdr bnd)))
671 (defun counsel-directory-parent (dir)
672 "Return the directory parent of directory DIR."
673 (concat (file-name-nondirectory
674 (directory-file-name dir)) "/"))
676 (defun counsel-string-compose (prefix str)
677 "Make PREFIX the display prefix of STR though text properties."
678 (let ((str (copy-sequence str)))
681 (concat prefix (substring str 0 1))
686 (defun counsel-load-library ()
687 "Load a selected the Emacs Lisp library.
688 The libraries are offered from `load-path'."
690 (let ((dirs load-path)
691 (suffix (concat (regexp-opt '(".el" ".el.gz") t) "\\'"))
692 (cands (make-hash-table :test #'equal))
698 (when (file-directory-p dir)
699 (dolist (file (file-name-all-completions "" dir))
700 (when (string-match suffix file)
701 (unless (string-match "pkg.elc?$" file)
702 (setq short-name (substring file 0 (match-beginning 0)))
703 (if (setq old-val (gethash short-name cands))
705 ;; assume going up directory once will resolve name clash
706 (setq dir-parent (counsel-directory-parent (cdr old-val)))
709 (counsel-string-compose dir-parent (car old-val))
712 (setq dir-parent (counsel-directory-parent dir))
713 (puthash (concat dir-parent short-name)
716 (counsel-string-compose
717 dir-parent short-name)
718 'full-name (expand-file-name file dir))
724 'full-name (expand-file-name file dir))
726 (maphash (lambda (_k v) (push (car v) res)) cands)
727 (ivy-read "Load library: " (nreverse res)
730 (get-text-property 0 'full-name x)))
731 :keymap counsel-describe-map)))
733 (defvar counsel-gg-state nil
734 "The current state of candidates / count sync.")
736 (defun counsel--gg-candidates (regex)
737 "Return git grep candidates for REGEX."
738 (setq counsel-gg-state -2)
739 (counsel--gg-count regex)
740 (let* ((default-directory counsel--git-grep-dir)
741 (counsel-gg-process " *counsel-gg*")
742 (proc (get-process counsel-gg-process))
743 (buff (get-buffer counsel-gg-process)))
745 (delete-process proc))
748 (setq proc (start-process-shell-command
752 (format counsel-git-grep-cmd regex)
754 (set-process-sentinel
756 #'counsel--gg-sentinel)))
758 (defun counsel--gg-sentinel (process event)
759 (if (string= event "finished\n")
761 (with-current-buffer (process-buffer process)
762 (setq ivy--all-candidates
763 (or (split-string (buffer-string) "\n" t)
765 (setq ivy--old-cands ivy--all-candidates))
766 (when (= 0 (cl-incf counsel-gg-state))
768 (if (string= event "exited abnormally with code 1\n")
770 (setq ivy--all-candidates '("Error"))
771 (setq ivy--old-cands ivy--all-candidates)
774 (defun counsel--gg-count (regex &optional no-async)
775 "Quickly and asynchronously count the amount of git grep REGEX matches.
776 When NO-ASYNC is non-nil, do it synchronously."
777 (let ((default-directory counsel--git-grep-dir)
781 (replace-regexp-in-string
783 counsel-git-grep-cmd)
784 ;; "git grep -i -c '%s'"
785 (replace-regexp-in-string
787 (replace-regexp-in-string "'" "''" regex)))
788 " | sed 's/.*:\\(.*\\)/\\1/g' | awk '{s+=$1} END {print s}'"))
789 (counsel-ggc-process " *counsel-gg-count*"))
791 (string-to-number (shell-command-to-string cmd))
792 (let ((proc (get-process counsel-ggc-process))
793 (buff (get-buffer counsel-ggc-process)))
795 (delete-process proc))
798 (setq proc (start-process-shell-command
802 (set-process-sentinel
804 #'(lambda (process event)
805 (when (string= event "finished\n")
806 (with-current-buffer (process-buffer process)
807 (setq ivy--full-length (string-to-number (buffer-string))))
808 (when (= 0 (cl-incf counsel-gg-state))
809 (ivy--exhibit)))))))))
811 (defun counsel--M-x-transformer (cmd)
812 "Add a binding to CMD if it's bound in the current window.
813 CMD is a command name."
814 (let ((binding (substitute-command-keys (format "\\[%s]" cmd))))
815 (setq binding (replace-regexp-in-string "C-x 6" "<f2>" binding))
816 (if (string-match "^M-x" binding)
818 (format "%s (%s)" cmd
819 (propertize binding 'face 'font-lock-keyword-face)))))
821 (defvar smex-initialized-p)
822 (defvar smex-ido-cache)
823 (declare-function smex-initialize "ext:smex")
824 (declare-function smex-detect-new-commands "ext:smex")
825 (declare-function smex-update "ext:smex")
826 (declare-function smex-rank "ext:smex")
829 (defun counsel-M-x (&optional initial-input)
830 "Ivy version of `execute-extended-command'.
831 Optional INITIAL-INPUT is the initial input in the minibuffer."
833 (unless initial-input
834 (setq initial-input (cdr (assoc this-command
835 ivy-initial-inputs-alist))))
836 (let* ((store ivy-format-function)
842 (mapcar #'counsel--M-x-transformer cands)))))
846 (when (require 'smex nil 'noerror)
847 (unless smex-initialized-p
849 (smex-detect-new-commands)
851 (setq cands smex-ido-cache)
854 (ivy-read "M-x " cands
857 :history 'extended-command-history
860 (when (featurep 'smex)
861 (smex-rank (intern cmd)))
862 (let ((prefix-arg current-prefix-arg))
863 (command-execute (intern cmd) 'record)))
865 :keymap counsel-describe-map
866 :initial-input initial-input)))
868 (declare-function powerline-reset "ext:powerline")
870 (defun counsel--load-theme-action (x)
871 "Disable current themes and load theme X."
874 (mapc #'disable-theme custom-enabled-themes)
875 (load-theme (intern x))
876 (when (fboundp 'powerline-reset)
878 (error "Problem loading theme %s" x)))
881 (defun counsel-load-theme ()
882 "Forward to `load-theme'.
883 Usable with `ivy-resume', `ivy-next-line-and-call' and
884 `ivy-previous-line-and-call'."
886 (ivy-read "Load custom theme: "
888 (custom-available-themes))
889 :action #'counsel--load-theme-action))
891 (defvar rhythmbox-library)
892 (declare-function rhythmbox-load-library "ext:helm-rhythmbox")
893 (declare-function dbus-call-method "dbus")
894 (declare-function rhythmbox-song-uri "ext:helm-rhythmbox")
895 (declare-function helm-rhythmbox-candidates "ext:helm-rhythmbox")
897 (defun counsel-rhythmbox-enqueue-song (song)
898 "Let Rhythmbox enqueue SONG."
899 (let ((service "org.gnome.Rhythmbox3")
900 (path "/org/gnome/Rhythmbox3/PlayQueue")
901 (interface "org.gnome.Rhythmbox3.PlayQueue"))
902 (dbus-call-method :session service path interface
903 "AddToQueue" (rhythmbox-song-uri song))))
905 (defvar counsel-rhythmbox-history nil
906 "History for `counsel-rhythmbox'.")
909 (defun counsel-rhythmbox ()
910 "Choose a song from the Rhythmbox library to play or enqueue."
912 (unless (require 'helm-rhythmbox nil t)
913 (error "Please install `helm-rhythmbox'"))
914 (unless rhythmbox-library
915 (rhythmbox-load-library)
916 (while (null rhythmbox-library)
918 (ivy-read "Rhythmbox: "
919 (helm-rhythmbox-candidates)
920 :history 'counsel-rhythmbox-history
923 ("p" helm-rhythmbox-play-song "Play song")
924 ("e" counsel-rhythmbox-enqueue-song "Enqueue song"))))
926 (defvar counsel-org-tags nil
927 "Store the current list of tags.")
929 (defvar org-outline-regexp)
930 (defvar org-indent-mode)
931 (defvar org-indent-indentation-per-level)
932 (defvar org-tags-column)
933 (declare-function org-get-tags-string "org")
934 (declare-function org-move-to-column "org")
936 (defun counsel-org-change-tags (tags)
937 (let ((current (org-get-tags-string))
938 (col (current-column))
940 ;; Insert new tags at the correct column
941 (beginning-of-line 1)
942 (setq level (or (and (looking-at org-outline-regexp)
943 (- (match-end 0) (point) 1))
946 ((and (equal current "") (equal tags "")))
948 (concat "\\([ \t]*" (regexp-quote current) "\\)[ \t]*$")
954 (goto-char (match-beginning 0))
955 (let* ((c0 (current-column))
956 ;; compute offset for the case of org-indent-mode active
957 (di (if (bound-and-true-p org-indent-mode)
958 (* (1- org-indent-indentation-per-level) (1- level))
960 (p0 (if (equal (char-before) ?*) (1+ (point)) (point)))
961 (tc (+ org-tags-column (if (> org-tags-column 0) (- di) di)))
962 (c1 (max (1+ c0) (if (> tc 0) tc (- (- tc) (string-width tags)))))
963 (rpl (concat (make-string (max 0 (- c1 c0)) ?\ ) tags)))
964 (replace-match rpl t t)
965 (and c0 indent-tabs-mode (tabify p0 (point)))
967 (t (error "Tags alignment failed")))
968 (org-move-to-column col)))
970 (defun counsel-org--set-tags ()
971 (counsel-org-change-tags
974 (mapconcat #'identity counsel-org-tags ":"))
977 (defvar org-agenda-bulk-marked-entries)
979 (declare-function org-get-at-bol "org")
980 (declare-function org-agenda-error "org-agenda")
982 (defun counsel-org-tag-action (x)
983 (if (member x counsel-org-tags)
985 (setq counsel-org-tags (delete x counsel-org-tags)))
987 (setq counsel-org-tags (append counsel-org-tags (list x)))
988 (unless (member x ivy--all-candidates)
989 (setq ivy--all-candidates (append ivy--all-candidates (list x))))))
990 (let ((prompt (counsel-org-tag-prompt)))
991 (setf (ivy-state-prompt ivy-last) prompt)
992 (setq ivy--prompt (concat "%-4d " prompt)))
993 (cond ((memq this-command '(ivy-done
996 (if (eq major-mode 'org-agenda-mode)
997 (if (null org-agenda-bulk-marked-entries)
998 (let ((hdmarker (or (org-get-at-bol 'org-hd-marker)
999 (org-agenda-error))))
1000 (with-current-buffer (marker-buffer hdmarker)
1001 (goto-char hdmarker)
1002 (counsel-org--set-tags)))
1003 (let ((add-tags (copy-sequence counsel-org-tags)))
1004 (dolist (m org-agenda-bulk-marked-entries)
1005 (with-current-buffer (marker-buffer m)
1008 (setq counsel-org-tags
1010 (append (split-string (org-get-tags-string) ":" t)
1012 (counsel-org--set-tags))))))
1013 (counsel-org--set-tags)))
1014 ((eq this-command 'ivy-call)
1015 (delete-minibuffer-contents))))
1017 (defun counsel-org-tag-prompt ()
1018 (format "Tags (%s): "
1019 (mapconcat #'identity counsel-org-tags ", ")))
1021 (defvar org-setting-tags)
1022 (defvar org-last-tags-completion-table)
1023 (defvar org-tag-persistent-alist)
1024 (defvar org-tag-alist)
1025 (defvar org-complete-tags-always-offer-all-agenda-tags)
1027 (declare-function org-at-heading-p "org")
1028 (declare-function org-back-to-heading "org")
1029 (declare-function org-get-buffer-tags "org")
1030 (declare-function org-global-tags-completion-table "org")
1031 (declare-function org-agenda-files "org")
1032 (declare-function org-agenda-set-tags "org-agenda")
1035 (defun counsel-org-tag ()
1036 "Add or remove tags in org-mode."
1039 (if (eq major-mode 'org-agenda-mode)
1040 (if org-agenda-bulk-marked-entries
1041 (setq counsel-org-tags nil)
1042 (let ((hdmarker (or (org-get-at-bol 'org-hd-marker)
1043 (org-agenda-error))))
1044 (with-current-buffer (marker-buffer hdmarker)
1045 (goto-char hdmarker)
1046 (setq counsel-org-tags
1047 (split-string (org-get-tags-string) ":" t)))))
1048 (unless (org-at-heading-p)
1049 (org-back-to-heading t))
1050 (setq counsel-org-tags (split-string (org-get-tags-string) ":" t)))
1051 (let ((org-setting-tags t)
1052 (org-last-tags-completion-table
1053 (append org-tag-persistent-alist
1054 (or org-tag-alist (org-get-buffer-tags))
1056 (or org-complete-tags-always-offer-all-agenda-tags
1057 (eq major-mode 'org-agenda-mode))
1058 (org-global-tags-completion-table
1059 (org-agenda-files))))))
1060 (ivy-read (counsel-org-tag-prompt)
1061 (lambda (str &rest _unused)
1063 (all-completions str 'org-tags-completion-function)))
1064 :history 'org-tags-history
1065 :action 'counsel-org-tag-action))))
1068 (defun counsel-org-tag-agenda ()
1069 "Set tags for the current agenda item."
1071 (let ((store (symbol-function 'org-set-tags)))
1075 (symbol-function 'counsel-org-tag))
1076 (org-agenda-set-tags nil nil))
1077 (fset 'org-set-tags store))))
1079 (defun counsel-ag-function (string &optional _pred &rest _unused)
1080 "Grep in the current directory for STRING."
1081 (if (< (length string) 3)
1082 (counsel-more-chars 3)
1083 (let ((default-directory counsel--git-grep-dir)
1084 (regex (counsel-unquote-regex-parens
1086 (ivy--regex string)))))
1087 (counsel--async-command
1088 (format "ag --noheading --nocolor %S" regex))
1091 (defun counsel-ag (&optional initial-input initial-directory)
1092 "Grep for a string in the current directory using ag.
1093 INITIAL-INPUT can be given as the initial minibuffer input."
1095 (setq counsel--git-grep-dir (or initial-directory default-directory))
1096 (ivy-read "ag: " 'counsel-ag-function
1097 :initial-input initial-input
1098 :dynamic-collection t
1099 :history 'counsel-git-grep-history
1100 :action #'counsel-git-grep-action
1102 (counsel-delete-process)
1103 (swiper--cleanup))))
1105 (defun counsel-recoll-function (string &optional _pred &rest _unused)
1106 "Grep in the current directory for STRING."
1107 (if (< (length string) 3)
1108 (counsel-more-chars 3)
1109 (counsel--async-command
1110 (format "recoll -t -b '%s'" string))
1113 ;; This command uses the recollq command line tool that comes together
1114 ;; with the recoll (the document indexing database) source:
1115 ;; http://www.lesbonscomptes.com/recoll/download.html
1116 ;; You need to build it yourself (together with recoll):
1117 ;; cd ./query && make && sudo cp recollq /usr/local/bin
1118 ;; You can try the GUI version of recoll with:
1119 ;; sudo apt-get install recoll
1120 ;; Unfortunately, that does not install recollq.
1121 (defun counsel-recoll (&optional initial-input)
1122 "Search for a string in the recoll database.
1123 You'll be given a list of files that match.
1124 Selecting a file will launch `swiper' for that file.
1125 INITIAL-INPUT can be given as the initial minibuffer input."
1127 (ivy-read "recoll: " 'counsel-recoll-function
1128 :initial-input initial-input
1129 :dynamic-collection t
1130 :history 'counsel-git-grep-history
1132 (when (string-match "file://\\(.*\\)\\'" x)
1133 (let ((file-name (match-string 1 x)))
1134 (find-file file-name)
1135 (unless (string-match "pdf$" x)
1136 (swiper ivy-text)))))))
1138 (defcustom counsel-yank-pop-truncate nil
1139 "When non-nil, truncate the display of long strings."
1143 (defun counsel-yank-pop ()
1144 "Ivy replacement for `yank-pop'."
1146 (if (eq last-command 'yank)
1148 (setq counsel-completion-end (point))
1149 (setq counsel-completion-beg
1151 (search-backward (car kill-ring))
1153 (setq counsel-completion-beg (point))
1154 (setq counsel-completion-end (point)))
1155 (let ((candidates (cl-remove-if
1157 (or (< (length s) 3)
1158 (string-match "\\`[\n[:blank:]]+\\'" s)))
1159 (delete-dups kill-ring))))
1160 (when counsel-yank-pop-truncate
1163 (if (string-match "\\`\\(.*\n.*\n.*\n.*\\)\n" s)
1165 (let ((s (copy-sequence s)))
1175 (ivy-read "kill-ring: " candidates
1176 :action 'counsel-yank-pop-action)))
1178 (defun counsel-yank-pop-action (s)
1179 "Insert S into the buffer, overwriting the previous yank."
1181 (delete-region counsel-completion-beg
1182 counsel-completion-end)
1183 (insert (substring-no-properties s))
1184 (setq counsel-completion-end (point))))
1188 ;;; counsel.el ends here