]> code.delx.au - gnu-emacs-elpa/blob - packages/swiper/counsel.el
Update packages/yasnippet by subtree-merging from its github-based upstream
[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-exit-with-action #'counsel--find-symbol))
163
164 (defun counsel--info-lookup-symbol ()
165 "Lookup the current symbol in the info docs."
166 (interactive)
167 (ivy-exit-with-action #'counsel-info-lookup-symbol))
168
169 (defun counsel--find-symbol (x)
170 "Find symbol definition that corresponds to string X."
171 (with-no-warnings
172 (ring-insert find-tag-marker-ring (point-marker)))
173 (let ((full-name (get-text-property 0 'full-name x)))
174 (if full-name
175 (find-library full-name)
176 (let ((sym (read x)))
177 (cond ((and (eq (ivy-state-caller ivy-last)
178 'counsel-describe-variable)
179 (boundp sym))
180 (find-variable sym))
181 ((fboundp sym)
182 (find-function sym))
183 ((boundp sym)
184 (find-variable sym))
185 ((or (featurep sym)
186 (locate-library
187 (prin1-to-string sym)))
188 (find-library
189 (prin1-to-string sym)))
190 (t
191 (error "Couldn't fild definition of %s"
192 sym)))))))
193
194 (defvar counsel-describe-symbol-history nil
195 "History for `counsel-describe-variable' and `counsel-describe-function'.")
196
197 (defun counsel-symbol-at-point ()
198 "Return current symbol at point as a string."
199 (let ((s (thing-at-point 'symbol)))
200 (and (stringp s)
201 (if (string-match "\\`[`']?\\(.*?\\)'?\\'" s)
202 (match-string 1 s)
203 s))))
204
205 (defun counsel-variable-list ()
206 "Return the list of all currently bound variables."
207 (let (cands)
208 (mapatoms
209 (lambda (vv)
210 (when (or (get vv 'variable-documentation)
211 (and (boundp vv) (not (keywordp vv))))
212 (push (symbol-name vv) cands))))
213 cands))
214
215 ;;;###autoload
216 (defun counsel-describe-variable ()
217 "Forward to `describe-variable'."
218 (interactive)
219 (let ((enable-recursive-minibuffers t))
220 (ivy-read
221 "Describe variable: "
222 (counsel-variable-list)
223 :keymap counsel-describe-map
224 :preselect (counsel-symbol-at-point)
225 :history 'counsel-describe-symbol-history
226 :require-match t
227 :sort t
228 :action (lambda (x)
229 (describe-variable
230 (intern x)))
231 :caller 'counsel-describe-variable)))
232
233 (ivy-set-actions
234 'counsel-describe-variable
235 '(("i" counsel-info-lookup-symbol "info")
236 ("d" counsel--find-symbol "definition")))
237
238 (ivy-set-actions
239 'counsel-describe-function
240 '(("i" counsel-info-lookup-symbol "info")
241 ("d" counsel--find-symbol "definition")))
242
243 (ivy-set-actions
244 'counsel-M-x
245 '(("d" counsel--find-symbol "definition")))
246
247 ;;;###autoload
248 (defun counsel-describe-function ()
249 "Forward to `describe-function'."
250 (interactive)
251 (let ((enable-recursive-minibuffers t))
252 (ivy-read "Describe function: "
253 (let (cands)
254 (mapatoms
255 (lambda (x)
256 (when (fboundp x)
257 (push (symbol-name x) cands))))
258 cands)
259 :keymap counsel-describe-map
260 :preselect (counsel-symbol-at-point)
261 :history 'counsel-describe-symbol-history
262 :require-match t
263 :sort t
264 :action (lambda (x)
265 (describe-function
266 (intern x)))
267 :caller 'counsel-describe-function)))
268
269 (defvar info-lookup-mode)
270 (declare-function info-lookup->completions "info-look")
271 (declare-function info-lookup->mode-value "info-look")
272 (declare-function info-lookup-select-mode "info-look")
273 (declare-function info-lookup-change-mode "info-look")
274 (declare-function info-lookup "info-look")
275
276 ;;;###autoload
277 (defun counsel-info-lookup-symbol (symbol &optional mode)
278 "Forward to (`info-describe-symbol' SYMBOL MODE) with ivy completion."
279 (interactive
280 (progn
281 (require 'info-look)
282 (let* ((topic 'symbol)
283 (mode (cond (current-prefix-arg
284 (info-lookup-change-mode topic))
285 ((info-lookup->mode-value
286 topic (info-lookup-select-mode))
287 info-lookup-mode)
288 ((info-lookup-change-mode topic))))
289 (completions (info-lookup->completions topic mode))
290 (enable-recursive-minibuffers t)
291 (value (ivy-read
292 "Describe symbol: "
293 (mapcar #'car completions)
294 :sort t)))
295 (list value info-lookup-mode))))
296 (require 'info-look)
297 (info-lookup 'symbol symbol mode))
298
299 (defvar counsel-unicode-char-history nil
300 "History for `counsel-unicode-char'.")
301
302 ;;;###autoload
303 (defun counsel-unicode-char ()
304 "Insert a Unicode character at point."
305 (interactive)
306 (let ((minibuffer-allow-text-properties t))
307 (setq counsel-completion-beg (point))
308 (setq counsel-completion-end (point))
309 (ivy-read "Unicode name: "
310 (mapcar (lambda (x)
311 (propertize
312 (format "% -60s%c" (car x) (cdr x))
313 'result (cdr x)))
314 (ucs-names))
315 :action (lambda (char)
316 (with-ivy-window
317 (delete-region counsel-completion-beg counsel-completion-end)
318 (setq counsel-completion-beg (point))
319 (insert-char (get-text-property 0 'result char))
320 (setq counsel-completion-end (point))))
321 :history 'counsel-unicode-char-history)))
322
323 (declare-function cider-sync-request:complete "ext:cider-client")
324 ;;;###autoload
325 (defun counsel-clj ()
326 "Clojure completion at point."
327 (interactive)
328 (counsel--generic
329 (lambda (str)
330 (mapcar
331 #'cl-caddr
332 (cider-sync-request:complete str ":same")))))
333
334 ;;;###autoload
335 (defun counsel-git ()
336 "Find file in the current Git repository."
337 (interactive)
338 (let* ((default-directory (locate-dominating-file
339 default-directory ".git"))
340 (cands (split-string
341 (shell-command-to-string
342 "git ls-files --full-name --")
343 "\n"
344 t))
345 (action `(lambda (x)
346 (let ((default-directory ,default-directory))
347 (find-file x)))))
348 (ivy-read "Find file: " cands
349 :action action)))
350
351 (defvar counsel--git-grep-dir nil
352 "Store the base git directory.")
353
354 (defvar counsel--git-grep-count nil
355 "Store the line count in current repository.")
356
357 (defun counsel-more-chars (n)
358 "Return two fake candidates prompting for at least N input."
359 (list ""
360 (format "%d chars more" (- n (length ivy-text)))))
361
362 (defvar counsel-git-grep-cmd "git --no-pager grep --full-name -n --no-color -i -e %S"
363 "Store the command for `counsel-git-grep'.")
364
365 (defun counsel-git-grep-function (string &optional _pred &rest _unused)
366 "Grep in the current git repository for STRING."
367 (if (and (> counsel--git-grep-count 20000)
368 (< (length string) 3))
369 (counsel-more-chars 3)
370 (let* ((default-directory counsel--git-grep-dir)
371 (cmd (format counsel-git-grep-cmd
372 (setq ivy--old-re (ivy--regex string t)))))
373 (if (<= counsel--git-grep-count 20000)
374 (split-string (shell-command-to-string cmd) "\n" t)
375 (counsel--gg-candidates (ivy--regex string))
376 nil))))
377
378 (defvar counsel-git-grep-map
379 (let ((map (make-sparse-keymap)))
380 (define-key map (kbd "C-l") 'counsel-git-grep-recenter)
381 (define-key map (kbd "M-q") 'counsel-git-grep-query-replace)
382 map))
383
384 (defun counsel-git-grep-query-replace ()
385 "Start `query-replace' with string to replace from last search string."
386 (interactive)
387 (if (null (window-minibuffer-p))
388 (user-error
389 "Should only be called in the minibuffer through `counsel-git-grep-map'")
390 (let* ((enable-recursive-minibuffers t)
391 (from (ivy--regex ivy-text))
392 (to (query-replace-read-to from "Query replace" t)))
393 (ivy-exit-with-action
394 (lambda (_)
395 (let (done-buffers)
396 (dolist (cand ivy--old-cands)
397 (when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" cand)
398 (with-ivy-window
399 (let ((file-name (match-string-no-properties 1 cand)))
400 (setq file-name (expand-file-name file-name counsel--git-grep-dir))
401 (unless (member file-name done-buffers)
402 (push file-name done-buffers)
403 (find-file file-name)
404 (goto-char (point-min)))
405 (perform-replace from to t t nil)))))))))))
406
407 (defun counsel-git-grep-recenter ()
408 (interactive)
409 (with-ivy-window
410 (counsel-git-grep-action ivy--current)
411 (recenter-top-bottom)))
412
413 (defun counsel-git-grep-action (x)
414 (when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" x)
415 (with-ivy-window
416 (let ((file-name (match-string-no-properties 1 x))
417 (line-number (match-string-no-properties 2 x)))
418 (find-file (expand-file-name file-name counsel--git-grep-dir))
419 (goto-char (point-min))
420 (forward-line (1- (string-to-number line-number)))
421 (re-search-forward (ivy--regex ivy-text t) (line-end-position) t)
422 (unless (eq ivy-exit 'done)
423 (swiper--cleanup)
424 (swiper--add-overlays (ivy--regex ivy-text)))))))
425
426 (defvar counsel-git-grep-history nil
427 "History for `counsel-git-grep'.")
428
429 (defvar counsel-git-grep-cmd-history
430 '("git --no-pager grep --full-name -n --no-color -i -e %S")
431 "History for `counsel-git-grep' shell commands.")
432
433 ;;;###autoload
434 (defun counsel-git-grep (&optional cmd initial-input)
435 "Grep for a string in the current git repository.
436 When CMD is a string, use it as a \"git grep\" command.
437 When CMD is non-nil, prompt for a specific \"git grep\" command.
438 INITIAL-INPUT can be given as the initial minibuffer input."
439 (interactive "P")
440 (cond
441 ((stringp cmd)
442 (setq counsel-git-grep-cmd cmd))
443 (cmd
444 (setq counsel-git-grep-cmd
445 (ivy-read "cmd: " counsel-git-grep-cmd-history
446 :history 'counsel-git-grep-cmd-history))
447 (setq counsel-git-grep-cmd-history
448 (delete-dups counsel-git-grep-cmd-history)))
449 (t
450 (setq counsel-git-grep-cmd "git --no-pager grep --full-name -n --no-color -i -e %S")))
451 (setq counsel--git-grep-dir
452 (locate-dominating-file default-directory ".git"))
453 (if (null counsel--git-grep-dir)
454 (error "Not in a git repository")
455 (setq counsel--git-grep-count (counsel--gg-count "" t))
456 (ivy-read "git grep: " 'counsel-git-grep-function
457 :initial-input initial-input
458 :matcher #'counsel-git-grep-matcher
459 :dynamic-collection (> counsel--git-grep-count 20000)
460 :keymap counsel-git-grep-map
461 :action #'counsel-git-grep-action
462 :unwind #'swiper--cleanup
463 :history 'counsel-git-grep-history
464 :caller 'counsel-git-grep)))
465
466 (defcustom counsel-find-file-at-point nil
467 "When non-nil, add file-at-point to the list of candidates."
468 :type 'boolean
469 :group 'ivy)
470
471 (declare-function ffap-guesser "ffap")
472
473 (defvar counsel-find-file-map (make-sparse-keymap))
474
475 ;;;###autoload
476 (defun counsel-find-file ()
477 "Forward to `find-file'."
478 (interactive)
479 (ivy-read "Find file: " 'read-file-name-internal
480 :matcher #'counsel--find-file-matcher
481 :action
482 (lambda (x)
483 (with-ivy-window
484 (find-file (expand-file-name x ivy--directory))))
485 :preselect (when counsel-find-file-at-point
486 (require 'ffap)
487 (ffap-guesser))
488 :require-match 'confirm-after-completion
489 :history 'file-name-history
490 :keymap counsel-find-file-map))
491
492 (defcustom counsel-find-file-ignore-regexp nil
493 "A regexp of files to ignore while in `counsel-find-file'.
494 These files are un-ignored if `ivy-text' matches them.
495 The common way to show all files is to start `ivy-text' with a dot.
496 Possible value: \"\\(?:\\`[#.]\\)\\|\\(?:[#~]\\'\\)\"."
497 :group 'ivy)
498
499 (defun counsel--find-file-matcher (regexp candidates)
500 "Return REGEXP-matching CANDIDATES.
501 Skip some dotfiles unless `ivy-text' requires them."
502 (let ((res (cl-remove-if-not
503 (lambda (x)
504 (string-match regexp x))
505 candidates)))
506 (if (or (null counsel-find-file-ignore-regexp)
507 (string-match counsel-find-file-ignore-regexp ivy-text))
508 res
509 (cl-remove-if
510 (lambda (x)
511 (string-match counsel-find-file-ignore-regexp x))
512 res))))
513
514 (defun counsel-git-grep-matcher (regexp candidates)
515 (or (and (equal regexp ivy--old-re)
516 ivy--old-cands)
517 (prog1
518 (setq ivy--old-cands
519 (cl-remove-if-not
520 (lambda (x)
521 (ignore-errors
522 (when (string-match "^[^:]+:[^:]+:" x)
523 (setq x (substring x (match-end 0)))
524 (if (stringp regexp)
525 (string-match regexp x)
526 (let ((res t))
527 (dolist (re regexp)
528 (setq res
529 (and res
530 (ignore-errors
531 (if (cdr re)
532 (string-match (car re) x)
533 (not (string-match (car re) x)))))))
534 res)))))
535 candidates))
536 (setq ivy--old-re regexp))))
537
538 (defvar counsel--async-time nil
539 "Store the time when a new process was started.
540 Or the time of the last minibuffer update.")
541
542 (defun counsel--async-command (cmd)
543 (let* ((counsel--process " *counsel*")
544 (proc (get-process counsel--process))
545 (buff (get-buffer counsel--process)))
546 (when proc
547 (delete-process proc))
548 (when buff
549 (kill-buffer buff))
550 (setq proc (start-process-shell-command
551 counsel--process
552 counsel--process
553 cmd))
554 (setq counsel--async-time (current-time))
555 (set-process-sentinel proc #'counsel--async-sentinel)
556 (set-process-filter proc #'counsel--async-filter)))
557
558 (defun counsel--async-sentinel (process event)
559 (if (string= event "finished\n")
560 (progn
561 (with-current-buffer (process-buffer process)
562 (setq ivy--all-candidates
563 (ivy--sort-maybe
564 (split-string (buffer-string) "\n" t)))
565 (if (null ivy--old-cands)
566 (setq ivy--index
567 (or (ivy--preselect-index
568 (ivy-state-preselect ivy-last)
569 ivy--all-candidates)
570 0))
571 (ivy--recompute-index
572 ivy-text
573 (funcall ivy--regex-function ivy-text)
574 ivy--all-candidates))
575 (setq ivy--old-cands ivy--all-candidates))
576 (ivy--exhibit))
577 (if (string= event "exited abnormally with code 1\n")
578 (progn
579 (setq ivy--all-candidates '("Error"))
580 (setq ivy--old-cands ivy--all-candidates)
581 (ivy--exhibit)))))
582
583 (defun counsel--async-filter (process str)
584 "Receive from PROCESS the output STR.
585 Update the minibuffer with the amount of lines collected every
586 0.5 seconds since the last update."
587 (with-current-buffer (process-buffer process)
588 (insert str))
589 (let (size)
590 (when (time-less-p
591 ;; 0.5s
592 '(0 0 500000 0)
593 (time-since counsel--async-time))
594 (with-current-buffer (process-buffer process)
595 (goto-char (point-min))
596 (setq size (- (buffer-size) (forward-line (buffer-size)))))
597 (ivy--insert-minibuffer
598 (format "\ncollected: %d" size))
599 (setq counsel--async-time (current-time)))))
600
601 (defun counsel-locate-action-extern (x)
602 "Use xdg-open shell command on X."
603 (call-process shell-file-name nil
604 nil nil
605 shell-command-switch
606 (format "%s %s"
607 (if (eq system-type 'darwin)
608 "open"
609 "xdg-open")
610 (shell-quote-argument x))))
611
612 (declare-function dired-jump "dired-x")
613 (defun counsel-locate-action-dired (x)
614 "Use `dired-jump' on X."
615 (dired-jump nil x))
616
617 (defvar counsel-locate-history nil
618 "History for `counsel-locate'.")
619
620 (defcustom counsel-locate-options (if (eq system-type 'darwin)
621 '("-i")
622 '("-i" "--regex"))
623 "Command line options for `locate`."
624 :group 'ivy
625 :type '(repeat string))
626
627 (ivy-set-actions
628 'counsel-locate
629 '(("x" counsel-locate-action-extern "xdg-open")
630 ("d" counsel-locate-action-dired "dired")))
631
632 (defun counsel-unquote-regex-parens (str)
633 (replace-regexp-in-string
634 "\\\\)" ")"
635 (replace-regexp-in-string
636 "\\\\(" "("
637 str)))
638
639 (defun counsel-locate-function (str &rest _u)
640 (if (< (length str) 3)
641 (counsel-more-chars 3)
642 (counsel--async-command
643 (format "locate %s '%s'"
644 (mapconcat #'identity counsel-locate-options " ")
645 (counsel-unquote-regex-parens
646 (ivy--regex str))))
647 '("" "working...")))
648
649 (defun counsel-delete-process ()
650 (let ((process (get-process " *counsel*")))
651 (when process
652 (delete-process process))))
653
654 ;;;###autoload
655 (defun counsel-locate (&optional initial-input)
656 "Call the \"locate\" shell command.
657 INITIAL-INPUT can be given as the initial minibuffer input."
658 (interactive)
659 (ivy-read "Locate: " #'counsel-locate-function
660 :initial-input initial-input
661 :dynamic-collection t
662 :history 'counsel-locate-history
663 :action (lambda (file)
664 (with-ivy-window
665 (when file
666 (find-file file))))
667 :unwind #'counsel-delete-process))
668
669 (defun counsel--generic (completion-fn)
670 "Complete thing at point with COMPLETION-FN."
671 (let* ((bnd (bounds-of-thing-at-point 'symbol))
672 (str (if bnd
673 (buffer-substring-no-properties
674 (car bnd) (cdr bnd))
675 ""))
676 (candidates (funcall completion-fn str))
677 (ivy-height 7)
678 (res (ivy-read (format "pattern (%s): " str)
679 candidates)))
680 (when (stringp res)
681 (when bnd
682 (delete-region (car bnd) (cdr bnd)))
683 (insert res))))
684
685 (defun counsel-directory-parent (dir)
686 "Return the directory parent of directory DIR."
687 (concat (file-name-nondirectory
688 (directory-file-name dir)) "/"))
689
690 (defun counsel-string-compose (prefix str)
691 "Make PREFIX the display prefix of STR though text properties."
692 (let ((str (copy-sequence str)))
693 (put-text-property
694 0 1 'display
695 (concat prefix (substring str 0 1))
696 str)
697 str))
698
699 ;;;###autoload
700 (defun counsel-load-library ()
701 "Load a selected the Emacs Lisp library.
702 The libraries are offered from `load-path'."
703 (interactive)
704 (let ((dirs load-path)
705 (suffix (concat (regexp-opt '(".el" ".el.gz") t) "\\'"))
706 (cands (make-hash-table :test #'equal))
707 short-name
708 old-val
709 dir-parent
710 res)
711 (dolist (dir dirs)
712 (when (file-directory-p dir)
713 (dolist (file (file-name-all-completions "" dir))
714 (when (string-match suffix file)
715 (unless (string-match "pkg.elc?$" file)
716 (setq short-name (substring file 0 (match-beginning 0)))
717 (if (setq old-val (gethash short-name cands))
718 (progn
719 ;; assume going up directory once will resolve name clash
720 (setq dir-parent (counsel-directory-parent (cdr old-val)))
721 (puthash short-name
722 (cons
723 (counsel-string-compose dir-parent (car old-val))
724 (cdr old-val))
725 cands)
726 (setq dir-parent (counsel-directory-parent dir))
727 (puthash (concat dir-parent short-name)
728 (cons
729 (propertize
730 (counsel-string-compose
731 dir-parent short-name)
732 'full-name (expand-file-name file dir))
733 dir)
734 cands))
735 (puthash short-name
736 (cons (propertize
737 short-name
738 'full-name (expand-file-name file dir))
739 dir) cands)))))))
740 (maphash (lambda (_k v) (push (car v) res)) cands)
741 (ivy-read "Load library: " (nreverse res)
742 :action (lambda (x)
743 (load-library
744 (get-text-property 0 'full-name x)))
745 :keymap counsel-describe-map)))
746
747 (defvar counsel-gg-state nil
748 "The current state of candidates / count sync.")
749
750 (defun counsel--gg-candidates (regex)
751 "Return git grep candidates for REGEX."
752 (setq counsel-gg-state -2)
753 (counsel--gg-count regex)
754 (let* ((default-directory counsel--git-grep-dir)
755 (counsel-gg-process " *counsel-gg*")
756 (proc (get-process counsel-gg-process))
757 (buff (get-buffer counsel-gg-process)))
758 (when proc
759 (delete-process proc))
760 (when buff
761 (kill-buffer buff))
762 (setq proc (start-process-shell-command
763 counsel-gg-process
764 counsel-gg-process
765 (concat
766 (format counsel-git-grep-cmd regex)
767 " | head -n 200")))
768 (set-process-sentinel
769 proc
770 #'counsel--gg-sentinel)))
771
772 (defun counsel--gg-sentinel (process event)
773 (if (string= event "finished\n")
774 (progn
775 (with-current-buffer (process-buffer process)
776 (setq ivy--all-candidates
777 (or (split-string (buffer-string) "\n" t)
778 '("")))
779 (setq ivy--old-cands ivy--all-candidates))
780 (when (= 0 (cl-incf counsel-gg-state))
781 (ivy--exhibit)))
782 (if (string= event "exited abnormally with code 1\n")
783 (progn
784 (setq ivy--all-candidates '("Error"))
785 (setq ivy--old-cands ivy--all-candidates)
786 (ivy--exhibit)))))
787
788 (defun counsel--gg-count (regex &optional no-async)
789 "Quickly and asynchronously count the amount of git grep REGEX matches.
790 When NO-ASYNC is non-nil, do it synchronously."
791 (let ((default-directory counsel--git-grep-dir)
792 (cmd
793 (concat
794 (format
795 (replace-regexp-in-string
796 "--full-name" "-c"
797 counsel-git-grep-cmd)
798 ;; "git grep -i -c '%s'"
799 (replace-regexp-in-string
800 "-" "\\\\-"
801 (replace-regexp-in-string "'" "''" regex)))
802 " | sed 's/.*:\\(.*\\)/\\1/g' | awk '{s+=$1} END {print s}'"))
803 (counsel-ggc-process " *counsel-gg-count*"))
804 (if no-async
805 (string-to-number (shell-command-to-string cmd))
806 (let ((proc (get-process counsel-ggc-process))
807 (buff (get-buffer counsel-ggc-process)))
808 (when proc
809 (delete-process proc))
810 (when buff
811 (kill-buffer buff))
812 (setq proc (start-process-shell-command
813 counsel-ggc-process
814 counsel-ggc-process
815 cmd))
816 (set-process-sentinel
817 proc
818 #'(lambda (process event)
819 (when (string= event "finished\n")
820 (with-current-buffer (process-buffer process)
821 (setq ivy--full-length (string-to-number (buffer-string))))
822 (when (= 0 (cl-incf counsel-gg-state))
823 (ivy--exhibit)))))))))
824
825 (defun counsel--M-x-transformer (cand-pair)
826 "Add a binding to CAND-PAIR cdr if the car is bound in the current window.
827 CAND-PAIR is (command-name . extra-info)."
828 (let* ((command-name (car cand-pair))
829 (extra-info (cdr cand-pair))
830 (binding (substitute-command-keys (format "\\[%s]" command-name))))
831 (setq binding (replace-regexp-in-string "C-x 6" "<f2>" binding))
832 (if (string-match "^M-x" binding)
833 cand-pair
834 (cons command-name
835 (if extra-info
836 (format " %s (%s)" extra-info (propertize binding 'face 'font-lock-keyword-face))
837 (format " (%s)" (propertize binding 'face 'font-lock-keyword-face)))))))
838
839 (defvar smex-initialized-p)
840 (defvar smex-ido-cache)
841 (declare-function smex-initialize "ext:smex")
842 (declare-function smex-detect-new-commands "ext:smex")
843 (declare-function smex-update "ext:smex")
844 (declare-function smex-rank "ext:smex")
845
846 (defun counsel--M-x-prompt ()
847 "M-x plus the string representation of `current-prefix-arg'."
848 (if (not current-prefix-arg)
849 "M-x "
850 (concat
851 (if (eq current-prefix-arg '-)
852 "- "
853 (if (integerp current-prefix-arg)
854 (format "%d " current-prefix-arg)
855 (if (= (car current-prefix-arg) 4)
856 "C-u "
857 (format "%d " (car current-prefix-arg)))))
858 "M-x ")))
859
860 ;;;###autoload
861 (defun counsel-M-x (&optional initial-input)
862 "Ivy version of `execute-extended-command'.
863 Optional INITIAL-INPUT is the initial input in the minibuffer."
864 (interactive)
865 (unless initial-input
866 (setq initial-input (cdr (assoc this-command
867 ivy-initial-inputs-alist))))
868 (let* ((store ivy-format-function)
869 (ivy-format-function
870 (lambda (cand-pairs)
871 (funcall
872 store
873 (with-ivy-window
874 (mapcar #'counsel--M-x-transformer cand-pairs)))))
875 (cands obarray)
876 (pred 'commandp)
877 (sort t))
878 (when (require 'smex nil 'noerror)
879 (unless smex-initialized-p
880 (smex-initialize))
881 (smex-detect-new-commands)
882 (smex-update)
883 (setq cands smex-ido-cache)
884 (setq pred nil)
885 (setq sort nil))
886 (ivy-read (counsel--M-x-prompt) cands
887 :predicate pred
888 :require-match t
889 :history 'extended-command-history
890 :action
891 (lambda (cmd)
892 (when (featurep 'smex)
893 (smex-rank (intern cmd)))
894 (let ((prefix-arg current-prefix-arg)
895 (ivy-format-function store))
896 (command-execute (intern cmd) 'record)))
897 :sort sort
898 :keymap counsel-describe-map
899 :initial-input initial-input
900 :caller 'counsel-M-x)))
901
902 (declare-function powerline-reset "ext:powerline")
903
904 (defun counsel--load-theme-action (x)
905 "Disable current themes and load theme X."
906 (condition-case nil
907 (progn
908 (mapc #'disable-theme custom-enabled-themes)
909 (load-theme (intern x))
910 (when (fboundp 'powerline-reset)
911 (powerline-reset)))
912 (error "Problem loading theme %s" x)))
913
914 ;;;###autoload
915 (defun counsel-load-theme ()
916 "Forward to `load-theme'.
917 Usable with `ivy-resume', `ivy-next-line-and-call' and
918 `ivy-previous-line-and-call'."
919 (interactive)
920 (ivy-read "Load custom theme: "
921 (mapcar 'symbol-name
922 (custom-available-themes))
923 :action #'counsel--load-theme-action))
924
925 (defvar rhythmbox-library)
926 (declare-function rhythmbox-load-library "ext:helm-rhythmbox")
927 (declare-function dbus-call-method "dbus")
928 (declare-function rhythmbox-song-uri "ext:helm-rhythmbox")
929 (declare-function helm-rhythmbox-candidates "ext:helm-rhythmbox")
930
931 (defun counsel-rhythmbox-enqueue-song (song)
932 "Let Rhythmbox enqueue SONG."
933 (let ((service "org.gnome.Rhythmbox3")
934 (path "/org/gnome/Rhythmbox3/PlayQueue")
935 (interface "org.gnome.Rhythmbox3.PlayQueue"))
936 (dbus-call-method :session service path interface
937 "AddToQueue" (rhythmbox-song-uri song))))
938
939 (defvar counsel-rhythmbox-history nil
940 "History for `counsel-rhythmbox'.")
941
942 ;;;###autoload
943 (defun counsel-rhythmbox ()
944 "Choose a song from the Rhythmbox library to play or enqueue."
945 (interactive)
946 (unless (require 'helm-rhythmbox nil t)
947 (error "Please install `helm-rhythmbox'"))
948 (unless rhythmbox-library
949 (rhythmbox-load-library)
950 (while (null rhythmbox-library)
951 (sit-for 0.1)))
952 (ivy-read "Rhythmbox: "
953 (helm-rhythmbox-candidates)
954 :history 'counsel-rhythmbox-history
955 :action
956 '(1
957 ("p" helm-rhythmbox-play-song "Play song")
958 ("e" counsel-rhythmbox-enqueue-song "Enqueue song"))
959 :caller 'counsel-rhythmbox))
960
961 (defvar counsel-org-tags nil
962 "Store the current list of tags.")
963
964 (defvar org-outline-regexp)
965 (defvar org-indent-mode)
966 (defvar org-indent-indentation-per-level)
967 (defvar org-tags-column)
968 (declare-function org-get-tags-string "org")
969 (declare-function org-move-to-column "org-compat")
970
971 (defun counsel-org-change-tags (tags)
972 (let ((current (org-get-tags-string))
973 (col (current-column))
974 level)
975 ;; Insert new tags at the correct column
976 (beginning-of-line 1)
977 (setq level (or (and (looking-at org-outline-regexp)
978 (- (match-end 0) (point) 1))
979 1))
980 (cond
981 ((and (equal current "") (equal tags "")))
982 ((re-search-forward
983 (concat "\\([ \t]*" (regexp-quote current) "\\)[ \t]*$")
984 (point-at-eol) t)
985 (if (equal tags "")
986 (delete-region
987 (match-beginning 0)
988 (match-end 0))
989 (goto-char (match-beginning 0))
990 (let* ((c0 (current-column))
991 ;; compute offset for the case of org-indent-mode active
992 (di (if (bound-and-true-p org-indent-mode)
993 (* (1- org-indent-indentation-per-level) (1- level))
994 0))
995 (p0 (if (equal (char-before) ?*) (1+ (point)) (point)))
996 (tc (+ org-tags-column (if (> org-tags-column 0) (- di) di)))
997 (c1 (max (1+ c0) (if (> tc 0) tc (- (- tc) (string-width tags)))))
998 (rpl (concat (make-string (max 0 (- c1 c0)) ?\ ) tags)))
999 (replace-match rpl t t)
1000 (and c0 indent-tabs-mode (tabify p0 (point)))
1001 tags)))
1002 (t (error "Tags alignment failed")))
1003 (org-move-to-column col)))
1004
1005 (defun counsel-org--set-tags ()
1006 (counsel-org-change-tags
1007 (if counsel-org-tags
1008 (format ":%s:"
1009 (mapconcat #'identity counsel-org-tags ":"))
1010 "")))
1011
1012 (defvar org-agenda-bulk-marked-entries)
1013
1014 (declare-function org-get-at-bol "org")
1015 (declare-function org-agenda-error "org-agenda")
1016
1017 (defun counsel-org-tag-action (x)
1018 (if (member x counsel-org-tags)
1019 (progn
1020 (setq counsel-org-tags (delete x counsel-org-tags)))
1021 (unless (equal x "")
1022 (setq counsel-org-tags (append counsel-org-tags (list x)))
1023 (unless (member x ivy--all-candidates)
1024 (setq ivy--all-candidates (append ivy--all-candidates (list x))))))
1025 (let ((prompt (counsel-org-tag-prompt)))
1026 (setf (ivy-state-prompt ivy-last) prompt)
1027 (setq ivy--prompt (concat "%-4d " prompt)))
1028 (cond ((memq this-command '(ivy-done
1029 ivy-alt-done
1030 ivy-immediate-done))
1031 (if (eq major-mode 'org-agenda-mode)
1032 (if (null org-agenda-bulk-marked-entries)
1033 (let ((hdmarker (or (org-get-at-bol 'org-hd-marker)
1034 (org-agenda-error))))
1035 (with-current-buffer (marker-buffer hdmarker)
1036 (goto-char hdmarker)
1037 (counsel-org--set-tags)))
1038 (let ((add-tags (copy-sequence counsel-org-tags)))
1039 (dolist (m org-agenda-bulk-marked-entries)
1040 (with-current-buffer (marker-buffer m)
1041 (save-excursion
1042 (goto-char m)
1043 (setq counsel-org-tags
1044 (delete-dups
1045 (append (split-string (org-get-tags-string) ":" t)
1046 add-tags)))
1047 (counsel-org--set-tags))))))
1048 (counsel-org--set-tags)))
1049 ((eq this-command 'ivy-call)
1050 (delete-minibuffer-contents))))
1051
1052 (defun counsel-org-tag-prompt ()
1053 (format "Tags (%s): "
1054 (mapconcat #'identity counsel-org-tags ", ")))
1055
1056 (defvar org-setting-tags)
1057 (defvar org-last-tags-completion-table)
1058 (defvar org-tag-persistent-alist)
1059 (defvar org-tag-alist)
1060 (defvar org-complete-tags-always-offer-all-agenda-tags)
1061
1062 (declare-function org-at-heading-p "org")
1063 (declare-function org-back-to-heading "org")
1064 (declare-function org-get-buffer-tags "org")
1065 (declare-function org-global-tags-completion-table "org")
1066 (declare-function org-agenda-files "org")
1067 (declare-function org-agenda-set-tags "org-agenda")
1068
1069 ;;;###autoload
1070 (defun counsel-org-tag ()
1071 "Add or remove tags in org-mode."
1072 (interactive)
1073 (save-excursion
1074 (if (eq major-mode 'org-agenda-mode)
1075 (if org-agenda-bulk-marked-entries
1076 (setq counsel-org-tags nil)
1077 (let ((hdmarker (or (org-get-at-bol 'org-hd-marker)
1078 (org-agenda-error))))
1079 (with-current-buffer (marker-buffer hdmarker)
1080 (goto-char hdmarker)
1081 (setq counsel-org-tags
1082 (split-string (org-get-tags-string) ":" t)))))
1083 (unless (org-at-heading-p)
1084 (org-back-to-heading t))
1085 (setq counsel-org-tags (split-string (org-get-tags-string) ":" t)))
1086 (let ((org-setting-tags t)
1087 (org-last-tags-completion-table
1088 (append org-tag-persistent-alist
1089 (or org-tag-alist (org-get-buffer-tags))
1090 (and
1091 (or org-complete-tags-always-offer-all-agenda-tags
1092 (eq major-mode 'org-agenda-mode))
1093 (org-global-tags-completion-table
1094 (org-agenda-files))))))
1095 (ivy-read (counsel-org-tag-prompt)
1096 (lambda (str &rest _unused)
1097 (delete-dups
1098 (all-completions str 'org-tags-completion-function)))
1099 :history 'org-tags-history
1100 :action 'counsel-org-tag-action))))
1101
1102 ;;;###autoload
1103 (defun counsel-org-tag-agenda ()
1104 "Set tags for the current agenda item."
1105 (interactive)
1106 (let ((store (symbol-function 'org-set-tags)))
1107 (unwind-protect
1108 (progn
1109 (fset 'org-set-tags
1110 (symbol-function 'counsel-org-tag))
1111 (org-agenda-set-tags nil nil))
1112 (fset 'org-set-tags store))))
1113
1114 (defun counsel-ag-function (string &optional _pred &rest _unused)
1115 "Grep in the current directory for STRING."
1116 (if (< (length string) 3)
1117 (counsel-more-chars 3)
1118 (let ((default-directory counsel--git-grep-dir)
1119 (regex (counsel-unquote-regex-parens
1120 (setq ivy--old-re
1121 (ivy--regex string)))))
1122 (counsel--async-command
1123 (format "ag --vimgrep %S" regex))
1124 nil)))
1125
1126 ;;;###autoload
1127 (defun counsel-ag (&optional initial-input initial-directory)
1128 "Grep for a string in the current directory using ag.
1129 INITIAL-INPUT can be given as the initial minibuffer input."
1130 (interactive)
1131 (setq counsel--git-grep-dir (or initial-directory default-directory))
1132 (ivy-read "ag: " 'counsel-ag-function
1133 :initial-input initial-input
1134 :dynamic-collection t
1135 :history 'counsel-git-grep-history
1136 :action #'counsel-git-grep-action
1137 :unwind (lambda ()
1138 (counsel-delete-process)
1139 (swiper--cleanup))))
1140
1141 ;;;###autoload
1142 (defun counsel-grep ()
1143 "Grep for a string in the current file."
1144 (interactive)
1145 (setq counsel--git-grep-dir (buffer-file-name))
1146 (ivy-read "grep: " 'counsel-grep-function
1147 :dynamic-collection t
1148 :preselect (format "%d:%s"
1149 (line-number-at-pos)
1150 (buffer-substring-no-properties
1151 (line-beginning-position)
1152 (line-end-position)))
1153 :history 'counsel-git-grep-history
1154 :update-fn (lambda ()
1155 (counsel-grep-action ivy--current))
1156 :action #'counsel-grep-action
1157 :unwind (lambda ()
1158 (counsel-delete-process)
1159 (swiper--cleanup))
1160 :caller 'counsel-grep))
1161
1162 (defun counsel-grep-function (string &optional _pred &rest _unused)
1163 "Grep in the current directory for STRING."
1164 (if (< (length string) 3)
1165 (counsel-more-chars 3)
1166 (let ((regex (counsel-unquote-regex-parens
1167 (setq ivy--old-re
1168 (ivy--regex string)))))
1169 (counsel--async-command
1170 (format "grep -nP --ignore-case '%s' %s" regex counsel--git-grep-dir))
1171 nil)))
1172
1173 (defun counsel-grep-action (x)
1174 (when (string-match "\\`\\([0-9]+\\):\\(.*\\)\\'" x)
1175 (with-ivy-window
1176 (let ((file-name counsel--git-grep-dir)
1177 (line-number (match-string-no-properties 1 x)))
1178 (find-file file-name)
1179 (goto-char (point-min))
1180 (forward-line (1- (string-to-number line-number)))
1181 (re-search-forward (ivy--regex ivy-text t) (line-end-position) t)
1182 (unless (eq ivy-exit 'done)
1183 (swiper--cleanup)
1184 (swiper--add-overlays (ivy--regex ivy-text)))))))
1185
1186 (defun counsel-recoll-function (string &optional _pred &rest _unused)
1187 "Grep in the current directory for STRING."
1188 (if (< (length string) 3)
1189 (counsel-more-chars 3)
1190 (counsel--async-command
1191 (format "recoll -t -b '%s'" string))
1192 nil))
1193
1194 ;; This command uses the recollq command line tool that comes together
1195 ;; with the recoll (the document indexing database) source:
1196 ;; http://www.lesbonscomptes.com/recoll/download.html
1197 ;; You need to build it yourself (together with recoll):
1198 ;; cd ./query && make && sudo cp recollq /usr/local/bin
1199 ;; You can try the GUI version of recoll with:
1200 ;; sudo apt-get install recoll
1201 ;; Unfortunately, that does not install recollq.
1202 (defun counsel-recoll (&optional initial-input)
1203 "Search for a string in the recoll database.
1204 You'll be given a list of files that match.
1205 Selecting a file will launch `swiper' for that file.
1206 INITIAL-INPUT can be given as the initial minibuffer input."
1207 (interactive)
1208 (ivy-read "recoll: " 'counsel-recoll-function
1209 :initial-input initial-input
1210 :dynamic-collection t
1211 :history 'counsel-git-grep-history
1212 :action (lambda (x)
1213 (when (string-match "file://\\(.*\\)\\'" x)
1214 (let ((file-name (match-string 1 x)))
1215 (find-file file-name)
1216 (unless (string-match "pdf$" x)
1217 (swiper ivy-text)))))))
1218
1219 (defvar tmm-km-list nil)
1220 (declare-function tmm-get-keymap "tmm")
1221 (declare-function tmm--completion-table "tmm")
1222 (declare-function tmm-get-keybind "tmm")
1223
1224 (defun counsel-tmm-prompt (menu)
1225 "Select and call an item from the MENU keymap."
1226 (let (out
1227 choice
1228 chosen-string)
1229 (setq tmm-km-list nil)
1230 (map-keymap (lambda (k v) (tmm-get-keymap (cons k v))) menu)
1231 (setq tmm-km-list (nreverse tmm-km-list))
1232 (setq out (ivy-read "Menu bar: " (tmm--completion-table tmm-km-list)
1233 :require-match t
1234 :sort nil))
1235 (setq choice (cdr (assoc out tmm-km-list)))
1236 (setq chosen-string (car choice))
1237 (setq choice (cdr choice))
1238 (cond ((keymapp choice)
1239 (counsel-tmm-prompt choice))
1240 ((and choice chosen-string)
1241 (setq last-command-event chosen-string)
1242 (call-interactively choice)))))
1243
1244 (defun counsel-tmm ()
1245 "Text-mode emulation of looking and choosing from a menubar."
1246 (interactive)
1247 (require 'tmm)
1248 (run-hooks 'menu-bar-update-hook)
1249 (counsel-tmm-prompt (tmm-get-keybind [menu-bar])))
1250
1251 (defcustom counsel-yank-pop-truncate nil
1252 "When non-nil, truncate the display of long strings."
1253 :group 'ivy)
1254
1255 ;;;###autoload
1256 (defun counsel-yank-pop ()
1257 "Ivy replacement for `yank-pop'."
1258 (interactive)
1259 (if (eq last-command 'yank)
1260 (progn
1261 (setq counsel-completion-end (point))
1262 (setq counsel-completion-beg
1263 (save-excursion
1264 (search-backward (car kill-ring))
1265 (point))))
1266 (setq counsel-completion-beg (point))
1267 (setq counsel-completion-end (point)))
1268 (let ((candidates (cl-remove-if
1269 (lambda (s)
1270 (or (< (length s) 3)
1271 (string-match "\\`[\n[:blank:]]+\\'" s)))
1272 (delete-dups kill-ring))))
1273 (when counsel-yank-pop-truncate
1274 (setq candidates
1275 (mapcar (lambda (s)
1276 (if (string-match "\\`\\(.*\n.*\n.*\n.*\\)\n" s)
1277 (progn
1278 (let ((s (copy-sequence s)))
1279 (put-text-property
1280 (match-end 1)
1281 (length s)
1282 'display
1283 " [...]"
1284 s)
1285 s))
1286 s))
1287 candidates)))
1288 (ivy-read "kill-ring: " candidates
1289 :action 'counsel-yank-pop-action)))
1290
1291 (defun counsel-yank-pop-action (s)
1292 "Insert S into the buffer, overwriting the previous yank."
1293 (with-ivy-window
1294 (delete-region counsel-completion-beg
1295 counsel-completion-end)
1296 (insert (substring-no-properties s))
1297 (setq counsel-completion-end (point))))
1298
1299 (provide 'counsel)
1300
1301 ;;; counsel.el ends here