]> code.delx.au - gnu-emacs-elpa/blob - packages/swiper/counsel.el
Merge commit 'b45fc3f6c0802be172c4e887d1cea268ce7866e0' from swiper
[gnu-emacs-elpa] / packages / swiper / counsel.el
1 ;;; counsel.el --- Various completion functions using Ivy -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2015 Free Software Foundation, Inc.
4
5 ;; Author: Oleh Krehel <ohwoeowho@gmail.com>
6 ;; URL: https://github.com/abo-abo/swiper
7 ;; Version: 0.1.0
8 ;; Package-Requires: ((emacs "24.1") (swiper "0.4.0"))
9 ;; Keywords: completion, matching
10
11 ;; This file is part of GNU Emacs.
12
13 ;; This file is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 3, or (at your option)
16 ;; any later version.
17
18 ;; This program is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22
23 ;; For a full copy of the GNU General Public License
24 ;; see <http://www.gnu.org/licenses/>.
25
26 ;;; Commentary:
27 ;;
28 ;; Just call one of the interactive functions in this file to complete
29 ;; the corresponding thing using `ivy'.
30 ;;
31 ;; Currently available: Elisp symbols, Clojure symbols, Git files.
32
33 ;;; Code:
34
35 (require 'swiper)
36 (require 'etags)
37
38 (defvar counsel-completion-beg nil
39 "Completion bounds start.")
40
41 (defvar counsel-completion-end nil
42 "Completion bounds end.")
43
44 ;;;###autoload
45 (defun counsel-el ()
46 "Elisp completion at point."
47 (interactive)
48 (let* ((bnd (unless (and (looking-at ")")
49 (eq (char-before) ?\())
50 (bounds-of-thing-at-point
51 'symbol)))
52 (str (if bnd
53 (buffer-substring-no-properties
54 (car bnd)
55 (cdr bnd))
56 ""))
57 (ivy-height 7)
58 (funp (eq (char-before (car bnd)) ?\())
59 symbol-names)
60 (if bnd
61 (progn
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))
68 (if (string= str "")
69 (mapatoms
70 (lambda (x)
71 (when (symbolp x)
72 (push (symbol-name x) symbol-names))))
73 (setq symbol-names
74 (all-completions str obarray
75 (and funp
76 (lambda (x)
77 (or (functionp x)
78 (macrop x)
79 (special-form-p x)))))))
80 (ivy-read "Symbol name: " symbol-names
81 :predicate (and funp #'functionp)
82 :initial-input str
83 :action #'counsel--el-action)))
84
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")
88
89 ;;;###autoload
90 (defun counsel-cl ()
91 "Common Lisp completion at point."
92 (interactive)
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))
100
101 (defun counsel--el-action (symbol)
102 "Insert SYMBOL, erasing the previous one."
103 (when (stringp symbol)
104 (with-ivy-window
105 (when counsel-completion-beg
106 (delete-region
107 counsel-completion-beg
108 counsel-completion-end))
109 (setq counsel-completion-beg
110 (move-marker (make-marker) (point)))
111 (insert symbol)
112 (setq counsel-completion-end
113 (move-marker (make-marker) (point))))))
114
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")
118
119 (defun counsel-jedi ()
120 "Python completion at point."
121 (interactive)
122 (let ((bnd (bounds-of-thing-at-point 'symbol)))
123 (if bnd
124 (progn
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)))
129 (deferred:sync!
130 (jedi:complete-request))
131 (ivy-read "Symbol name: " (jedi:ac-direct-matches)
132 :action #'counsel--py-action))
133
134 (defun counsel--py-action (symbol)
135 "Insert SYMBOL, erasing the previous one."
136 (when (stringp symbol)
137 (with-ivy-window
138 (when counsel-completion-beg
139 (delete-region
140 counsel-completion-beg
141 counsel-completion-end))
142 (setq counsel-completion-beg
143 (move-marker (make-marker) (point)))
144 (insert symbol)
145 (setq counsel-completion-end
146 (move-marker (make-marker) (point)))
147 (when (equal (get-text-property 0 'symbol symbol) "f")
148 (insert "()")
149 (setq counsel-completion-end
150 (move-marker (make-marker) (point)))
151 (backward-char 1)))))
152
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)
157 map))
158
159 (defun counsel-find-symbol ()
160 "Jump to the definition of the current symbol."
161 (interactive)
162 (ivy-set-action #'counsel--find-symbol)
163 (ivy-done))
164
165 (defun counsel--info-lookup-symbol ()
166 "Lookup the current symbol in the info docs."
167 (interactive)
168 (ivy-set-action #'counsel-info-lookup-symbol)
169 (ivy-done))
170
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)))
175 (if full-name
176 (find-library full-name)
177 (let ((sym (read x)))
178 (cond ((boundp sym)
179 (find-variable sym))
180 ((fboundp sym)
181 (find-function sym))
182 ((or (featurep sym)
183 (locate-library
184 (prin1-to-string sym)))
185 (find-library
186 (prin1-to-string sym)))
187 (t
188 (error "Couldn't fild definition of %s"
189 sym)))))))
190
191 (defvar counsel-describe-symbol-history nil
192 "History for `counsel-describe-variable' and `counsel-describe-function'.")
193
194 (defun counsel-symbol-at-point ()
195 "Return current symbol at point as a string."
196 (let ((s (thing-at-point 'symbol)))
197 (and (stringp s)
198 (if (string-match "\\`[`']?\\(.*?\\)'?\\'" s)
199 (match-string 1 s)
200 s))))
201
202 (defun counsel-variable-list ()
203 "Return the list of all currently bound variables."
204 (let (cands)
205 (mapatoms
206 (lambda (vv)
207 (when (or (get vv 'variable-documentation)
208 (and (boundp vv) (not (keywordp vv))))
209 (push (symbol-name vv) cands))))
210 cands))
211
212 ;;;###autoload
213 (defun counsel-describe-variable ()
214 "Forward to `describe-variable'."
215 (interactive)
216 (let ((enable-recursive-minibuffers t))
217 (ivy-read
218 "Describe variable: "
219 (counsel-variable-list)
220 :keymap counsel-describe-map
221 :preselect (counsel-symbol-at-point)
222 :history 'counsel-describe-symbol-history
223 :require-match t
224 :sort t
225 :action (lambda (x)
226 (describe-variable
227 (intern x))))))
228
229 (ivy-set-actions
230 'counsel-describe-variable
231 '(("i" counsel-info-lookup-symbol "info")
232 ("d" counsel--find-symbol "definition")))
233
234 (ivy-set-actions
235 'counsel-describe-function
236 '(("i" counsel-info-lookup-symbol "info")
237 ("d" counsel--find-symbol "definition")))
238
239 ;;;###autoload
240 (defun counsel-describe-function ()
241 "Forward to `describe-function'."
242 (interactive)
243 (let ((enable-recursive-minibuffers t))
244 (ivy-read "Describe function: "
245 (let (cands)
246 (mapatoms
247 (lambda (x)
248 (when (fboundp x)
249 (push (symbol-name x) cands))))
250 cands)
251 :keymap counsel-describe-map
252 :preselect (counsel-symbol-at-point)
253 :history 'counsel-describe-symbol-history
254 :require-match t
255 :sort t
256 :action (lambda (x)
257 (describe-function
258 (intern x))))))
259
260 (defvar info-lookup-mode)
261 (declare-function info-lookup->completions "info-look")
262 (declare-function info-lookup->mode-value "info-look")
263 (declare-function info-lookup-select-mode "info-look")
264 (declare-function info-lookup-change-mode "info-look")
265 (declare-function info-lookup "info-look")
266
267 ;;;###autoload
268 (defun counsel-info-lookup-symbol (symbol &optional mode)
269 "Forward to (`info-describe-symbol' SYMBOL MODE) with ivy completion."
270 (interactive
271 (progn
272 (require 'info-look)
273 (let* ((topic 'symbol)
274 (mode (cond (current-prefix-arg
275 (info-lookup-change-mode topic))
276 ((info-lookup->mode-value
277 topic (info-lookup-select-mode))
278 info-lookup-mode)
279 ((info-lookup-change-mode topic))))
280 (completions (info-lookup->completions topic mode))
281 (enable-recursive-minibuffers t)
282 (value (ivy-read
283 "Describe symbol: "
284 (mapcar #'car completions)
285 :sort t)))
286 (list value info-lookup-mode))))
287 (require 'info-look)
288 (info-lookup 'symbol symbol mode))
289
290 (defvar counsel-unicode-char-history nil
291 "History for `counsel-unicode-char'.")
292
293 ;;;###autoload
294 (defun counsel-unicode-char ()
295 "Insert a Unicode character at point."
296 (interactive)
297 (let ((minibuffer-allow-text-properties t))
298 (setq counsel-completion-beg (point))
299 (setq counsel-completion-end (point))
300 (ivy-read "Unicode name: "
301 (mapcar (lambda (x)
302 (propertize
303 (format "% -60s%c" (car x) (cdr x))
304 'result (cdr x)))
305 (ucs-names))
306 :action (lambda (char)
307 (with-ivy-window
308 (delete-region counsel-completion-beg counsel-completion-end)
309 (setq counsel-completion-beg (point))
310 (insert-char (get-text-property 0 'result char))
311 (setq counsel-completion-end (point))))
312 :history 'counsel-unicode-char-history)))
313
314 (declare-function cider-sync-request:complete "ext:cider-client")
315 ;;;###autoload
316 (defun counsel-clj ()
317 "Clojure completion at point."
318 (interactive)
319 (counsel--generic
320 (lambda (str)
321 (mapcar
322 #'cl-caddr
323 (cider-sync-request:complete str ":same")))))
324
325 ;;;###autoload
326 (defun counsel-git ()
327 "Find file in the current Git repository."
328 (interactive)
329 (let* ((default-directory (locate-dominating-file
330 default-directory ".git"))
331 (cands (split-string
332 (shell-command-to-string
333 "git ls-files --full-name --")
334 "\n"
335 t))
336 (action (lambda (x) (find-file x))))
337 (ivy-read "Find file: " cands
338 :action action)))
339
340 (defvar counsel--git-grep-dir nil
341 "Store the base git directory.")
342
343 (defvar counsel--git-grep-count nil
344 "Store the line count in current repository.")
345
346 (defun counsel-more-chars (n)
347 "Return two fake candidates prompting for at least N input."
348 (list ""
349 (format "%d chars more" (- n (length ivy-text)))))
350
351 (defun counsel-git-grep-function (string &optional _pred &rest _unused)
352 "Grep in the current git repository for STRING."
353 (if (and (> counsel--git-grep-count 20000)
354 (< (length string) 3))
355 (counsel-more-chars 3)
356 (let* ((default-directory counsel--git-grep-dir)
357 (cmd (format "git --no-pager grep --full-name -n --no-color -i -e %S"
358 (setq ivy--old-re (ivy--regex string t)))))
359 (if (<= counsel--git-grep-count 20000)
360 (split-string (shell-command-to-string cmd) "\n" t)
361 (counsel--gg-candidates (ivy--regex string))
362 nil))))
363
364 (defvar counsel-git-grep-map
365 (let ((map (make-sparse-keymap)))
366 (define-key map (kbd "C-l") 'counsel-git-grep-recenter)
367 map))
368
369 (defun counsel-git-grep-recenter ()
370 (interactive)
371 (with-ivy-window
372 (counsel-git-grep-action ivy--current)
373 (recenter-top-bottom)))
374
375 (defun counsel-git-grep-action (x)
376 (when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" x)
377 (with-ivy-window
378 (let ((file-name (match-string-no-properties 1 x))
379 (line-number (match-string-no-properties 2 x)))
380 (find-file (expand-file-name file-name counsel--git-grep-dir))
381 (goto-char (point-min))
382 (forward-line (1- (string-to-number line-number)))
383 (re-search-forward (ivy--regex ivy-text t) (line-end-position) t)
384 (unless (eq ivy-exit 'done)
385 (swiper--cleanup)
386 (swiper--add-overlays (ivy--regex ivy-text)))))))
387
388 (defvar counsel-git-grep-history nil
389 "History for `counsel-git-grep'.")
390
391 ;;;###autoload
392 (defun counsel-git-grep (&optional initial-input)
393 "Grep for a string in the current git repository.
394 INITIAL-INPUT can be given as the initial minibuffer input."
395 (interactive)
396 (setq counsel--git-grep-dir
397 (locate-dominating-file default-directory ".git"))
398 (if (null counsel--git-grep-dir)
399 (error "Not in a git repository")
400 (setq counsel--git-grep-count (counsel--gg-count "" t))
401 (ivy-read "git grep: " 'counsel-git-grep-function
402 :initial-input initial-input
403 :matcher #'counsel-git-grep-matcher
404 :dynamic-collection (> counsel--git-grep-count 20000)
405 :keymap counsel-git-grep-map
406 :action #'counsel-git-grep-action
407 :unwind #'swiper--cleanup
408 :history 'counsel-git-grep-history)))
409
410 (defcustom counsel-find-file-at-point nil
411 "When non-nil, add file-at-point to the list of candidates."
412 :type 'boolean
413 :group 'ivy)
414
415 (declare-function ffap-guesser "ffap")
416
417 (defvar counsel-find-file-map (make-sparse-keymap))
418
419 ;;;###autoload
420 (defun counsel-find-file ()
421 "Forward to `find-file'."
422 (interactive)
423 (ivy-read "Find file: " 'read-file-name-internal
424 :matcher #'counsel--find-file-matcher
425 :action
426 (lambda (x)
427 (with-ivy-window
428 (find-file (expand-file-name x ivy--directory))))
429 :preselect (when counsel-find-file-at-point
430 (require 'ffap)
431 (ffap-guesser))
432 :require-match 'confirm-after-completion
433 :history 'file-name-history
434 :keymap counsel-find-file-map))
435
436 (defcustom counsel-find-file-ignore-regexp nil
437 "A regexp of files to ignore while in `counsel-find-file'.
438 These files are un-ignored if `ivy-text' matches them.
439 The common way to show all files is to start `ivy-text' with a dot.
440 Possible value: \"\\(?:\\`[#.]\\)\\|\\(?:[#~]\\'\\)\"."
441 :group 'ivy)
442
443 (defun counsel--find-file-matcher (regexp candidates)
444 "Return REGEXP-matching CANDIDATES.
445 Skip some dotfiles unless `ivy-text' requires them."
446 (let ((res (cl-remove-if-not
447 (lambda (x)
448 (string-match regexp x))
449 candidates)))
450 (if (or (null counsel-find-file-ignore-regexp)
451 (string-match counsel-find-file-ignore-regexp ivy-text))
452 res
453 (cl-remove-if
454 (lambda (x)
455 (string-match counsel-find-file-ignore-regexp x))
456 res))))
457
458 (defun counsel-git-grep-matcher (regexp candidates)
459 (or (and (equal regexp ivy--old-re)
460 ivy--old-cands)
461 (prog1
462 (setq ivy--old-cands
463 (cl-remove-if-not
464 (lambda (x)
465 (ignore-errors
466 (when (string-match "^[^:]+:[^:]+:" x)
467 (setq x (substring x (match-end 0)))
468 (if (stringp regexp)
469 (string-match regexp x)
470 (let ((res t))
471 (dolist (re regexp)
472 (setq res
473 (and res
474 (ignore-errors
475 (if (cdr re)
476 (string-match (car re) x)
477 (not (string-match (car re) x)))))))
478 res)))))
479 candidates))
480 (setq ivy--old-re regexp))))
481
482 (defun counsel--async-command (cmd)
483 (let* ((counsel--process " *counsel*")
484 (proc (get-process counsel--process))
485 (buff (get-buffer counsel--process)))
486 (when proc
487 (delete-process proc))
488 (when buff
489 (kill-buffer buff))
490 (setq proc (start-process-shell-command
491 counsel--process
492 counsel--process
493 cmd))
494 (set-process-sentinel proc #'counsel--async-sentinel)))
495
496 (defun counsel--async-sentinel (process event)
497 (if (string= event "finished\n")
498 (progn
499 (with-current-buffer (process-buffer process)
500 (setq ivy--all-candidates
501 (ivy--sort-maybe
502 (split-string (buffer-string) "\n" t)))
503 (setq ivy--old-cands ivy--all-candidates))
504 (ivy--exhibit))
505 (if (string= event "exited abnormally with code 1\n")
506 (progn
507 (setq ivy--all-candidates '("Error"))
508 (setq ivy--old-cands ivy--all-candidates)
509 (ivy--exhibit)))))
510
511 (defun counsel-locate-action-extern (x)
512 "Use xdg-open shell command on X."
513 (call-process shell-file-name nil
514 nil nil
515 shell-command-switch
516 (format "%s %s"
517 (if (eq system-type 'darwin)
518 "open"
519 "xdg-open")
520 (shell-quote-argument x))))
521
522 (declare-function dired-jump "dired-x")
523 (defun counsel-locate-action-dired (x)
524 "Use `dired-jump' on X."
525 (dired-jump nil x))
526
527 (defvar counsel-locate-history nil
528 "History for `counsel-locate'.")
529
530 (defcustom counsel-locate-options (if (eq system-type 'darwin)
531 '("-i")
532 '("-i" "--regex"))
533 "Command line options for `locate`."
534 :group 'ivy
535 :type '(repeat string))
536
537 (ivy-set-actions
538 'counsel-locate
539 '(("x" counsel-locate-action-extern "xdg-open")
540 ("d" counsel-locate-action-dired "dired")))
541
542 (defun counsel-unquote-regex-parens (str)
543 (replace-regexp-in-string
544 "\\\\)" ")"
545 (replace-regexp-in-string
546 "\\\\(" "("
547 str)))
548
549 (defun counsel-locate-function (str &rest _u)
550 (if (< (length str) 3)
551 (counsel-more-chars 3)
552 (counsel--async-command
553 (format "locate %s '%s'"
554 (mapconcat #'identity counsel-locate-options " ")
555 (counsel-unquote-regex-parens
556 (ivy--regex str))))
557 '("" "working...")))
558
559 ;;;###autoload
560 (defun counsel-locate ()
561 "Call locate shell command."
562 (interactive)
563 (ivy-read "Locate: " #'counsel-locate-function
564 :dynamic-collection t
565 :history 'counsel-locate-history
566 :action (lambda (file)
567 (when file
568 (find-file file)))))
569
570 (defun counsel--generic (completion-fn)
571 "Complete thing at point with COMPLETION-FN."
572 (let* ((bnd (bounds-of-thing-at-point 'symbol))
573 (str (if bnd
574 (buffer-substring-no-properties
575 (car bnd) (cdr bnd))
576 ""))
577 (candidates (funcall completion-fn str))
578 (ivy-height 7)
579 (res (ivy-read (format "pattern (%s): " str)
580 candidates)))
581 (when (stringp res)
582 (when bnd
583 (delete-region (car bnd) (cdr bnd)))
584 (insert res))))
585
586 (defun counsel-directory-parent (dir)
587 "Return the directory parent of directory DIR."
588 (concat (file-name-nondirectory
589 (directory-file-name dir)) "/"))
590
591 (defun counsel-string-compose (prefix str)
592 "Make PREFIX the display prefix of STR though text properties."
593 (let ((str (copy-sequence str)))
594 (put-text-property
595 0 1 'display
596 (concat prefix (substring str 0 1))
597 str)
598 str))
599
600 ;;;###autoload
601 (defun counsel-load-library ()
602 "Load a selected the Emacs Lisp library.
603 The libraries are offered from `load-path'."
604 (interactive)
605 (let ((dirs load-path)
606 (suffix (concat (regexp-opt '(".el" ".el.gz") t) "\\'"))
607 (cands (make-hash-table :test #'equal))
608 short-name
609 old-val
610 dir-parent
611 res)
612 (dolist (dir dirs)
613 (when (file-directory-p dir)
614 (dolist (file (file-name-all-completions "" dir))
615 (when (string-match suffix file)
616 (unless (string-match "pkg.elc?$" file)
617 (setq short-name (substring file 0 (match-beginning 0)))
618 (if (setq old-val (gethash short-name cands))
619 (progn
620 ;; assume going up directory once will resolve name clash
621 (setq dir-parent (counsel-directory-parent (cdr old-val)))
622 (puthash short-name
623 (cons
624 (counsel-string-compose dir-parent (car old-val))
625 (cdr old-val))
626 cands)
627 (setq dir-parent (counsel-directory-parent dir))
628 (puthash (concat dir-parent short-name)
629 (cons
630 (propertize
631 (counsel-string-compose
632 dir-parent short-name)
633 'full-name (expand-file-name file dir))
634 dir)
635 cands))
636 (puthash short-name
637 (cons (propertize
638 short-name
639 'full-name (expand-file-name file dir))
640 dir) cands)))))))
641 (maphash (lambda (_k v) (push (car v) res)) cands)
642 (ivy-read "Load library: " (nreverse res)
643 :action (lambda (x)
644 (load-library
645 (get-text-property 0 'full-name x)))
646 :keymap counsel-describe-map)))
647
648 (defvar counsel-gg-state nil
649 "The current state of candidates / count sync.")
650
651 (defun counsel--gg-candidates (regex)
652 "Return git grep candidates for REGEX."
653 (setq counsel-gg-state -2)
654 (counsel--gg-count regex)
655 (let* ((default-directory counsel--git-grep-dir)
656 (counsel-gg-process " *counsel-gg*")
657 (proc (get-process counsel-gg-process))
658 (buff (get-buffer counsel-gg-process)))
659 (when proc
660 (delete-process proc))
661 (when buff
662 (kill-buffer buff))
663 (setq proc (start-process-shell-command
664 counsel-gg-process
665 counsel-gg-process
666 (format "git --no-pager grep --full-name -n --no-color -i -e %S | head -n 200"
667 regex)))
668 (set-process-sentinel
669 proc
670 #'counsel--gg-sentinel)))
671
672 (defun counsel--gg-sentinel (process event)
673 (if (string= event "finished\n")
674 (progn
675 (with-current-buffer (process-buffer process)
676 (setq ivy--all-candidates (split-string (buffer-string) "\n" t))
677 (setq ivy--old-cands ivy--all-candidates))
678 (when (= 0 (cl-incf counsel-gg-state))
679 (ivy--exhibit)))
680 (if (string= event "exited abnormally with code 1\n")
681 (progn
682 (setq ivy--all-candidates '("Error"))
683 (setq ivy--old-cands ivy--all-candidates)
684 (ivy--exhibit)))))
685
686 (defun counsel--gg-count (regex &optional no-async)
687 "Quickly and asynchronously count the amount of git grep REGEX matches.
688 When NO-ASYNC is non-nil, do it synchronously."
689 (let ((default-directory counsel--git-grep-dir)
690 (cmd (format "git grep -i -c '%s' | sed 's/.*:\\(.*\\)/\\1/g' | awk '{s+=$1} END {print s}'"
691 regex))
692 (counsel-ggc-process " *counsel-gg-count*"))
693 (if no-async
694 (string-to-number (shell-command-to-string cmd))
695 (let ((proc (get-process counsel-ggc-process))
696 (buff (get-buffer counsel-ggc-process)))
697 (when proc
698 (delete-process proc))
699 (when buff
700 (kill-buffer buff))
701 (setq proc (start-process-shell-command
702 counsel-ggc-process
703 counsel-ggc-process
704 cmd))
705 (set-process-sentinel
706 proc
707 #'(lambda (process event)
708 (when (string= event "finished\n")
709 (with-current-buffer (process-buffer process)
710 (setq ivy--full-length (string-to-number (buffer-string))))
711 (when (= 0 (cl-incf counsel-gg-state))
712 (ivy--exhibit)))))))))
713
714 (defun counsel--M-x-transformer (cmd)
715 "Add a binding to CMD if it's bound in the current window.
716 CMD is a command name."
717 (let ((binding (substitute-command-keys (format "\\[%s]" cmd))))
718 (setq binding (replace-regexp-in-string "C-x 6" "<f2>" binding))
719 (if (string-match "^M-x" binding)
720 cmd
721 (format "%s (%s)" cmd
722 (propertize binding 'face 'font-lock-keyword-face)))))
723
724 (defvar smex-initialized-p)
725 (defvar smex-ido-cache)
726 (declare-function smex-initialize "ext:smex")
727 (declare-function smex-detect-new-commands "ext:smex")
728 (declare-function smex-update "ext:smex")
729 (declare-function smex-rank "ext:smex")
730
731 ;;;###autoload
732 (defun counsel-M-x (&optional initial-input)
733 "Ivy version of `execute-extended-command'.
734 Optional INITIAL-INPUT is the initial input in the minibuffer."
735 (interactive)
736 (unless initial-input
737 (setq initial-input (cdr (assoc this-command
738 ivy-initial-inputs-alist))))
739 (let* ((store ivy-format-function)
740 (ivy-format-function
741 (lambda (cands)
742 (funcall
743 store
744 (with-ivy-window
745 (mapcar #'counsel--M-x-transformer cands)))))
746 (cands obarray)
747 (pred 'commandp)
748 (sort t))
749 (when (require 'smex nil 'noerror)
750 (unless smex-initialized-p
751 (smex-initialize))
752 (smex-detect-new-commands)
753 (smex-update)
754 (setq cands smex-ido-cache)
755 (setq pred nil)
756 (setq sort nil))
757 (ivy-read "M-x " cands
758 :predicate pred
759 :require-match t
760 :history 'extended-command-history
761 :action
762 (lambda (cmd)
763 (when (featurep 'smex)
764 (smex-rank (intern cmd)))
765 (let ((prefix-arg current-prefix-arg))
766 (command-execute (intern cmd) 'record)))
767 :sort sort
768 :keymap counsel-describe-map
769 :initial-input initial-input)))
770
771 (declare-function powerline-reset "ext:powerline")
772
773 (defun counsel--load-theme-action (x)
774 "Disable current themes and load theme X."
775 (condition-case nil
776 (progn
777 (mapc #'disable-theme custom-enabled-themes)
778 (load-theme (intern x))
779 (when (fboundp 'powerline-reset)
780 (powerline-reset)))
781 (error "Problem loading theme %s" x)))
782
783 ;;;###autoload
784 (defun counsel-load-theme ()
785 "Forward to `load-theme'.
786 Usable with `ivy-resume', `ivy-next-line-and-call' and
787 `ivy-previous-line-and-call'."
788 (interactive)
789 (ivy-read "Load custom theme: "
790 (mapcar 'symbol-name
791 (custom-available-themes))
792 :action #'counsel--load-theme-action))
793
794 (defvar rhythmbox-library)
795 (declare-function rhythmbox-load-library "ext:helm-rhythmbox")
796 (declare-function dbus-call-method "dbus")
797 (declare-function rhythmbox-song-uri "ext:helm-rhythmbox")
798 (declare-function helm-rhythmbox-candidates "ext:helm-rhythmbox")
799
800 (defun counsel-rhythmbox-enqueue-song (song)
801 "Let Rhythmbox enqueue SONG."
802 (let ((service "org.gnome.Rhythmbox3")
803 (path "/org/gnome/Rhythmbox3/PlayQueue")
804 (interface "org.gnome.Rhythmbox3.PlayQueue"))
805 (dbus-call-method :session service path interface
806 "AddToQueue" (rhythmbox-song-uri song))))
807
808 (defvar counsel-rhythmbox-history nil
809 "History for `counsel-rhythmbox'.")
810
811 ;;;###autoload
812 (defun counsel-rhythmbox ()
813 "Choose a song from the Rhythmbox library to play or enqueue."
814 (interactive)
815 (unless (require 'helm-rhythmbox nil t)
816 (error "Please install `helm-rhythmbox'"))
817 (unless rhythmbox-library
818 (rhythmbox-load-library)
819 (while (null rhythmbox-library)
820 (sit-for 0.1)))
821 (ivy-read "Rhythmbox: "
822 (helm-rhythmbox-candidates)
823 :history 'counsel-rhythmbox-history
824 :action
825 '(1
826 ("p" helm-rhythmbox-play-song "Play song")
827 ("e" counsel-rhythmbox-enqueue-song "Enqueue song"))))
828
829 (defvar counsel-org-tags nil
830 "Store the current list of tags.")
831
832 (defvar org-outline-regexp)
833 (defvar org-indent-mode)
834 (defvar org-indent-indentation-per-level)
835 (defvar org-tags-column)
836 (declare-function org-get-tags-string "org")
837 (declare-function org-move-to-column "org")
838
839 (defun counsel-org-change-tags (tags)
840 (let ((current (org-get-tags-string))
841 (col (current-column))
842 level)
843 ;; Insert new tags at the correct column
844 (beginning-of-line 1)
845 (setq level (or (and (looking-at org-outline-regexp)
846 (- (match-end 0) (point) 1))
847 1))
848 (cond
849 ((and (equal current "") (equal tags "")))
850 ((re-search-forward
851 (concat "\\([ \t]*" (regexp-quote current) "\\)[ \t]*$")
852 (point-at-eol) t)
853 (if (equal tags "")
854 (delete-region
855 (match-beginning 0)
856 (match-end 0))
857 (goto-char (match-beginning 0))
858 (let* ((c0 (current-column))
859 ;; compute offset for the case of org-indent-mode active
860 (di (if (bound-and-true-p org-indent-mode)
861 (* (1- org-indent-indentation-per-level) (1- level))
862 0))
863 (p0 (if (equal (char-before) ?*) (1+ (point)) (point)))
864 (tc (+ org-tags-column (if (> org-tags-column 0) (- di) di)))
865 (c1 (max (1+ c0) (if (> tc 0) tc (- (- tc) (string-width tags)))))
866 (rpl (concat (make-string (max 0 (- c1 c0)) ?\ ) tags)))
867 (replace-match rpl t t)
868 (and c0 indent-tabs-mode (tabify p0 (point)))
869 tags)))
870 (t (error "Tags alignment failed")))
871 (org-move-to-column col)))
872
873 (defun counsel-org--set-tags ()
874 (counsel-org-change-tags
875 (if counsel-org-tags
876 (format ":%s:"
877 (mapconcat #'identity counsel-org-tags ":"))
878 "")))
879
880 (defvar org-agenda-bulk-marked-entries)
881
882 (declare-function org-get-at-bol "org")
883 (declare-function org-agenda-error "org-agenda")
884
885 (defun counsel-org-tag-action (x)
886 (if (member x counsel-org-tags)
887 (progn
888 (setq counsel-org-tags (delete x counsel-org-tags)))
889 (unless (equal x "")
890 (setq counsel-org-tags (append counsel-org-tags (list x)))
891 (unless (member x ivy--all-candidates)
892 (setq ivy--all-candidates (append ivy--all-candidates (list x))))))
893 (let ((prompt (counsel-org-tag-prompt)))
894 (setf (ivy-state-prompt ivy-last) prompt)
895 (setq ivy--prompt (concat "%-4d " prompt)))
896 (cond ((memq this-command '(ivy-done
897 ivy-alt-done
898 ivy-immediate-done))
899 (if (eq major-mode 'org-agenda-mode)
900 (if (null org-agenda-bulk-marked-entries)
901 (let ((hdmarker (or (org-get-at-bol 'org-hd-marker)
902 (org-agenda-error))))
903 (with-current-buffer (marker-buffer hdmarker)
904 (goto-char hdmarker)
905 (counsel-org--set-tags)))
906 (let ((add-tags (copy-sequence counsel-org-tags)))
907 (dolist (m org-agenda-bulk-marked-entries)
908 (with-current-buffer (marker-buffer m)
909 (save-excursion
910 (goto-char m)
911 (setq counsel-org-tags
912 (delete-dups
913 (append (split-string (org-get-tags-string) ":" t)
914 add-tags)))
915 (counsel-org--set-tags))))))
916 (counsel-org--set-tags)))
917 ((eq this-command 'ivy-call)
918 (delete-minibuffer-contents))))
919
920 (defun counsel-org-tag-prompt ()
921 (format "Tags (%s): "
922 (mapconcat #'identity counsel-org-tags ", ")))
923
924 (defvar org-setting-tags)
925 (defvar org-last-tags-completion-table)
926 (defvar org-tag-persistent-alist)
927 (defvar org-tag-alist)
928 (defvar org-complete-tags-always-offer-all-agenda-tags)
929
930 (declare-function org-at-heading-p "org")
931 (declare-function org-back-to-heading "org")
932 (declare-function org-get-buffer-tags "org")
933 (declare-function org-global-tags-completion-table "org")
934 (declare-function org-agenda-files "org")
935 (declare-function org-agenda-set-tags "org-agenda")
936
937 ;;;###autoload
938 (defun counsel-org-tag ()
939 "Add or remove tags in org-mode."
940 (interactive)
941 (save-excursion
942 (if (eq major-mode 'org-agenda-mode)
943 (if org-agenda-bulk-marked-entries
944 (setq counsel-org-tags nil)
945 (let ((hdmarker (or (org-get-at-bol 'org-hd-marker)
946 (org-agenda-error))))
947 (with-current-buffer (marker-buffer hdmarker)
948 (goto-char hdmarker)
949 (setq counsel-org-tags
950 (split-string (org-get-tags-string) ":" t)))))
951 (unless (org-at-heading-p)
952 (org-back-to-heading t))
953 (setq counsel-org-tags (split-string (org-get-tags-string) ":" t)))
954 (let ((org-setting-tags t)
955 (org-last-tags-completion-table
956 (append org-tag-persistent-alist
957 (or org-tag-alist (org-get-buffer-tags))
958 (and
959 (or org-complete-tags-always-offer-all-agenda-tags
960 (eq major-mode 'org-agenda-mode))
961 (org-global-tags-completion-table
962 (org-agenda-files))))))
963 (ivy-read (counsel-org-tag-prompt)
964 (lambda (str &rest _unused)
965 (delete-dups
966 (all-completions str 'org-tags-completion-function)))
967 :history 'org-tags-history
968 :action 'counsel-org-tag-action))))
969
970 ;;;###autoload
971 (defun counsel-org-tag-agenda ()
972 "Set tags for the current agenda item."
973 (interactive)
974 (let ((store (symbol-function 'org-set-tags)))
975 (unwind-protect
976 (progn
977 (fset 'org-set-tags
978 (symbol-function 'counsel-org-tag))
979 (org-agenda-set-tags nil nil))
980 (fset 'org-set-tags store))))
981
982 (defun counsel-ag-function (string &optional _pred &rest _unused)
983 "Grep in the current directory for STRING."
984 (if (< (length string) 3)
985 (counsel-more-chars 3)
986 (let ((regex (counsel-unquote-regex-parens
987 (setq ivy--old-re
988 (ivy--regex string)))))
989 (counsel--async-command
990 (format "ag --noheading --nocolor %S" regex))
991 nil)))
992
993 (defun counsel-ag (&optional initial-input)
994 "Grep for a string in the current directory using ag.
995 INITIAL-INPUT can be given as the initial minibuffer input."
996 (interactive)
997 (setq counsel--git-grep-dir default-directory)
998 (ivy-read "ag: " 'counsel-ag-function
999 :initial-input initial-input
1000 :dynamic-collection t
1001 :history 'counsel-git-grep-history
1002 :action #'counsel-git-grep-action
1003 :unwind #'swiper--cleanup))
1004
1005 (defun counsel-recoll-function (string &optional _pred &rest _unused)
1006 "Grep in the current directory for STRING."
1007 (if (< (length string) 3)
1008 (counsel-more-chars 3)
1009 (counsel--async-command
1010 (format "recoll -t -b '%s'" string))
1011 nil))
1012
1013 ;; This command uses the recollq command line tool that comes together
1014 ;; with the recoll (the document indexing database) source:
1015 ;; http://www.lesbonscomptes.com/recoll/download.html
1016 ;; You need to build it yourself (together with recoll):
1017 ;; cd ./query && make && sudo cp recollq /usr/local/bin
1018 ;; You can try the GUI version of recoll with:
1019 ;; sudo apt-get install recoll
1020 ;; Unfortunately, that does not install recollq.
1021 (defun counsel-recoll (&optional initial-input)
1022 "Search for a string in the recoll database.
1023 You'll be given a list of files that match.
1024 Selecting a file will launch `swiper' for that file.
1025 INITIAL-INPUT can be given as the initial minibuffer input."
1026 (interactive)
1027 (ivy-read "recoll: " 'counsel-recoll-function
1028 :initial-input initial-input
1029 :dynamic-collection t
1030 :history 'counsel-git-grep-history
1031 :action (lambda (x)
1032 (when (string-match "file://\\(.*\\)\\'" x)
1033 (let ((file-name (match-string 1 x)))
1034 (find-file file-name)
1035 (unless (string-match "pdf$" x)
1036 (swiper ivy-text)))))))
1037
1038 (defcustom counsel-yank-pop-truncate nil
1039 "When non-nil, truncate the display of long strings."
1040 :group 'ivy)
1041
1042 ;;;###autoload
1043 (defun counsel-yank-pop ()
1044 "Ivy replacement for `yank-pop'."
1045 (interactive)
1046 (if (eq last-command 'yank)
1047 (progn
1048 (setq counsel-completion-end (point))
1049 (setq counsel-completion-beg
1050 (save-excursion
1051 (search-backward (car kill-ring))
1052 (point))))
1053 (setq counsel-completion-beg (point))
1054 (setq counsel-completion-end (point)))
1055 (let ((candidates (cl-remove-if
1056 (lambda (s)
1057 (or (< (length s) 3)
1058 (string-match "\\`[\n[:blank:]]+\\'" s)))
1059 (delete-dups kill-ring))))
1060 (when counsel-yank-pop-truncate
1061 (setq candidates
1062 (mapcar (lambda (s)
1063 (if (string-match "\\`\\(.*\n.*\n.*\n.*\\)\n" s)
1064 (progn
1065 (let ((s (copy-sequence s)))
1066 (put-text-property
1067 (match-end 1)
1068 (length s)
1069 'display
1070 " [...]"
1071 s)
1072 s))
1073 s))
1074 candidates)))
1075 (ivy-read "kill-ring: " candidates
1076 :action 'counsel-yank-pop-action)))
1077
1078 (defun counsel-yank-pop-action (s)
1079 "Insert S into the buffer, overwriting the previous yank."
1080 (with-ivy-window
1081 (delete-region counsel-completion-beg
1082 counsel-completion-end)
1083 (insert (substring-no-properties s))
1084 (setq counsel-completion-end (point))))
1085
1086 (provide 'counsel)
1087
1088 ;;; counsel.el ends here