Skip to content
Branch: master
Find file Copy path
Find file Copy path
111 contributors

Users who have contributed to this file

@abo-abo @basil-conto @justbur @mookid @ericdanan @stsquad @fabacino @Stebalien @DamienCassou @CeleritasCelery @kaushalmodi @tarsius @leungbk @Yevgnen @tumashu @joedicastro @jojojames @iquiw @edkolev @tsdh @purcell @raxod502 @manuel-uberti @errge @ejmr @cperl82
5934 lines (5270 sloc) 224 KB
;;; counsel.el --- Various completion functions using Ivy -*- lexical-binding: t -*-
;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
;; Author: Oleh Krehel <>
;; URL:
;; Version: 0.12.0
;; Package-Requires: ((emacs "24.3") (swiper "0.12.0"))
;; Keywords: convenience, matching, tools
;; This file is part of GNU Emacs.
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.
;; For a full copy of the GNU General Public License
;; see <>.
;;; Commentary:
;; Just call one of the interactive functions in this file to complete
;; the corresponding thing using `ivy'.
;; Currently available:
;; - Symbol completion for Elisp, Common Lisp, Python, Clojure, C, C++.
;; - Describe functions for Elisp: function, variable, library, command,
;; bindings, theme.
;; - Navigation functions: imenu, ace-line, semantic, outline.
;; - Git utilities: git-files, git-grep, git-log, git-stash, git-checkout.
;; - Grep utilities: grep, ag, pt, recoll, ack, rg.
;; - System utilities: process list, rhythmbox, linux-app.
;; - Many more.
;;; Code:
(require 'swiper)
(require 'compile)
(require 'dired)
(defgroup counsel nil
"Completion functions using Ivy."
:group 'matching
:prefix "counsel-")
;;* Utility
(define-obsolete-variable-alias 'counsel-more-chars-alist 'ivy-more-chars-alist "0.10.0")
(define-obsolete-function-alias 'counsel-more-chars 'ivy-more-chars "0.10.0")
(defun counsel--elisp-to-pcre (regex &optional look-around)
"Convert REGEX from Elisp format to PCRE format, on best-effort basis.
REGEX may be of any format returned by an Ivy regex function,
namely a string or a list. The return value is always a string.
Note that incorrect results may be returned for sufficiently
complex regexes."
(if (consp regex)
(if (and look-around
(or (cdr regex)
(not (cdar regex))))
(lambda (pair)
(let ((subexp (counsel--elisp-to-pcre (car pair))))
(format "(?%c.*%s)"
(if (cdr pair) ?= ?!)
(lambda (pair)
(let ((subexp (counsel--elisp-to-pcre (car pair))))
(if (string-match-p "|" subexp)
(format "(?:%s)" subexp)
(cl-remove-if-not #'cdr regex)
(lambda (s)
(or (cdr (assoc s '(("\\(" . "(")
("\\)" . ")")
("(" . "\\(")
(")" . "\\)")
("\\{" . "{")
("\\}" . "}")
("\\|" . "|")
("\\`" . "^")
("\\'" . "$"))))
"Unexpected error in `counsel--elisp-to-pcre' (got match %S)" s)))
regex t t)))
(defun counsel-directory-name (dir)
"Return the name of directory DIR with a slash."
(directory-file-name dir))))
(defun counsel-string-compose (prefix str)
"Make PREFIX the display prefix of STR through text properties."
(let ((str (copy-sequence str)))
0 1 'display
(concat prefix (substring str 0 1))
(defun counsel-require-program (cmd)
"Check system for program used in CMD, printing error if not found.
CMD is either a string or a list of strings.
To skip the `executable-find' check, start the string with a space."
(unless (and (stringp cmd) (string-match-p "^ " cmd))
(let ((program (if (listp cmd)
(car cmd)
(car (split-string cmd)))))
(or (and (stringp program)
(not (string= program ""))
(executable-find program))
(user-error "Required program \"%s\" not found in your path" program)))))
(defun counsel-prompt-function-default ()
"Return prompt appended with a semicolon."
(declare (obsolete ivy-set-prompt "0.10.0"))
(ivy-add-prompt-count (concat (ivy-state-prompt ivy-last) ": ")))
(declare-function eshell-split-path "esh-util")
(defun counsel-prompt-function-dir ()
"Return prompt appended with the parent directory."
(require 'esh-util)
(let* ((dir (ivy-state-directory ivy-last))
(parts (nthcdr 3 (eshell-split-path dir)))
(dir (format " [%s]: " (if parts (apply #'concat "..." parts) dir))))
(replace-regexp-in-string ; Insert dir before any trailing colon.
"\\(?:: ?\\)?\\'" dir (ivy-state-prompt ivy-last) t t))))
;;* Async Utility
(defvar counsel--async-time nil
"Store the time when a new process was started.
Or the time of the last minibuffer update.")
(defvar counsel--async-start nil
"Store the time when a new process was started.
Or the time of the last minibuffer update.")
(defvar counsel--async-duration nil
"Store the time a process takes to gather all its candidates.
The time is measured in seconds.")
(defvar counsel--async-exit-code-plist ()
"Associate commands with their exit code descriptions.
This plist maps commands to a plist mapping their exit codes to
(defvar counsel--async-last-error-string nil
"When the process returned non-0, store the output here.")
(defun counsel-set-async-exit-code (cmd number str)
"For CMD, associate NUMBER exit code with STR."
(let ((plist (plist-get counsel--async-exit-code-plist cmd)))
(setq counsel--async-exit-code-plist
(plist-put counsel--async-exit-code-plist
(plist-put plist number str)))))
(defvar counsel-async-split-string-re "\n"
"Store the regexp for splitting shell command output.")
'counsel-async-split-string-re 'counsel-async-split-string-re-alist "<2019-07-16 Tue>")
(defvar counsel-async-split-string-re-alist '((t . "\n"))
"Store the regexp for splitting shell command output.")
(defvar counsel-async-ignore-re nil
"Regexp matching candidates to ignore in `counsel--async-filter'.")
(make-obsolete-variable 'counsel-async-ignore-re 'counsel-async-ignore-re-alist "<2019-07-16 Tue>")
(defvar counsel-async-ignore-re-alist nil
"An alist of regexp matching candidates to ignore in `counsel--async-filter'.")
(defvar counsel--async-last-command nil
"Store the last command ran by `counsel--async-command'.")
(defun counsel--async-command (cmd &optional sentinel filter name)
"Start and return new counsel process by calling CMD.
CMD can be either a shell command as a string, or a list of the
program name to be called directly, followed by its arguments.
If the default counsel process or one with NAME already exists,
kill it and its associated buffer before starting a new one.
Give the process the functions SENTINEL and FILTER, which default
to `counsel--async-sentinel' and `counsel--async-filter',
(counsel-delete-process name)
(setq name (or name " *counsel*"))
(when (get-buffer name)
(kill-buffer name))
(setq counsel--async-last-command cmd)
(let* ((buf (get-buffer-create name))
(proc (if (listp cmd)
(apply #'start-file-process name buf cmd)
(start-file-process-shell-command name buf cmd))))
(setq counsel--async-time (current-time))
(setq counsel--async-start counsel--async-time)
(set-process-sentinel proc (or sentinel #'counsel--async-sentinel))
(set-process-filter proc (or filter #'counsel--async-filter))
(defvar counsel-grep-last-line nil)
(defun counsel--split-string (&optional str)
(or str (buffer-string))
(ivy-alist-setting counsel-async-split-string-re-alist)
(defun counsel--async-sentinel (process _msg)
"Sentinel function for an asynchronous counsel PROCESS."
(when (eq (process-status process) 'exit)
(if (zerop (process-exit-status process))
(with-current-buffer (process-buffer process)
(setq counsel-grep-last-line nil)
(when counsel--async-start
(setq counsel--async-duration
(time-to-seconds (time-since counsel--async-start))))
(let ((re (ivy-re-to-str (funcall ivy--regex-function ivy-text))))
(if ivy--old-cands
(if (eq (ivy-alist-setting ivy-index-functions-alist) 'ivy-recompute-index-zero)
(ivy-set-index 0)
(ivy--recompute-index ivy-text re ivy--all-candidates))
(unless (ivy-set-index
(ivy-state-preselect ivy-last)
(ivy--recompute-index ivy-text re ivy--all-candidates))))
(setq ivy--old-cands ivy--all-candidates)
(if ivy--all-candidates
(ivy--insert-minibuffer "")))
(setq counsel--async-last-error-string
(with-current-buffer (process-buffer process) (buffer-string)))
(setq ivy--all-candidates
(let ((status (process-exit-status process))
(plist (plist-get counsel--async-exit-code-plist
(ivy-state-caller ivy-last))))
(list (or (plist-get plist status)
(format "error code %d" status)))))
(setq ivy--old-cands ivy--all-candidates)
(defcustom counsel-async-filter-update-time 500000
"The amount of microseconds to wait until updating `counsel--async-filter'."
:type 'integer)
(defun counsel--async-filter (process str)
"Receive from PROCESS the output STR.
Update the minibuffer with the amount of lines collected every
`counsel-async-filter-update-time' microseconds since the last update."
(with-current-buffer (process-buffer process)
(insert str))
(when (time-less-p (list 0 0 counsel-async-filter-update-time)
(time-since counsel--async-time))
(let (numlines)
(with-current-buffer (process-buffer process)
(setq numlines (count-lines (point-min) (point-max)))
(let ((lines (counsel--split-string))
(ignore-re (ivy-alist-setting counsel-async-ignore-re-alist)))
(if (stringp ignore-re)
(cl-remove-if (lambda (line)
(string-match-p ignore-re line))
(let ((ivy--prompt (format "%d++ %s" numlines (ivy-state-prompt ivy-last))))
(ivy--insert-minibuffer (ivy--format ivy--all-candidates)))
(setq counsel--async-time (current-time)))))
(defun counsel-delete-process (&optional name)
"Delete current counsel process or that with NAME."
(let ((process (get-process (or name " *counsel*"))))
(when process
(delete-process process))))
;;* Completion at point
;;** `counsel-el'
(defun counsel-el ()
"Elisp completion at point."
(let* ((bnd (unless (and (looking-at ")")
(eq (char-before) ?\())
(bounds-of-thing-at-point 'symbol)))
(str (if bnd
(car bnd)
(cdr bnd))
(pred (and (eq (char-before (car bnd)) ?\()
(setq ivy-completion-beg (car bnd))
(setq ivy-completion-end (cdr bnd))
(if (string= str "")
(lambda (x)
(when (symbolp x)
(push (symbol-name x) symbol-names))))
(setq symbol-names (all-completions str obarray pred)))
(ivy-read "Symbol name: " symbol-names
:caller 'counsel-el
:predicate pred
:initial-input str
:action #'ivy-completion-in-region-action)))
(add-to-list 'ivy-height-alist '(counsel-el . 7))
;;** `counsel-cl'
(declare-function slime-symbol-start-pos "ext:slime")
(declare-function slime-symbol-end-pos "ext:slime")
(declare-function slime-contextual-completions "ext:slime-c-p-c")
(defun counsel-cl ()
"Common Lisp completion at point."
(setq ivy-completion-beg (slime-symbol-start-pos))
(setq ivy-completion-end (slime-symbol-end-pos))
(ivy-read "Symbol name: "
(car (slime-contextual-completions
:action #'ivy-completion-in-region-action))
;;** `counsel-jedi'
(declare-function deferred:sync! "ext:deferred")
(declare-function jedi:complete-request "ext:jedi-core")
(declare-function jedi:ac-direct-matches "ext:jedi")
(defun counsel-jedi ()
"Python completion at point."
(let ((bnd (bounds-of-thing-at-point 'symbol)))
(setq ivy-completion-beg (car bnd))
(setq ivy-completion-end (cdr bnd)))
(ivy-read "Symbol name: " (jedi:ac-direct-matches)
:action #'counsel--py-action))
(defun counsel--py-action (symbol-name)
"Insert SYMBOL-NAME, erasing the previous one."
(when (stringp symbol-name)
(when ivy-completion-beg
(setq ivy-completion-beg (point))
(insert symbol-name)
(setq ivy-completion-end (point))
(when (equal (get-text-property 0 'symbol symbol-name) "f")
(insert "()")
(setq ivy-completion-end (point))
;;** `counsel-clj'
(declare-function cider-sync-request:complete "ext:cider-client")
(defun counsel--generic (completion-fn)
"Complete thing at point with COMPLETION-FN."
(let* ((bnd (or (bounds-of-thing-at-point 'symbol)
(cons (point) (point))))
(str (buffer-substring-no-properties
(car bnd) (cdr bnd)))
(candidates (funcall completion-fn str))
(res (ivy-read (format "pattern (%s): " str)
:caller 'counsel--generic)))
(when (stringp res)
(when bnd
(delete-region (car bnd) (cdr bnd)))
(insert res))))
(add-to-list 'ivy-height-alist '(counsel--generic . 7))
(defun counsel-clj ()
"Clojure completion at point."
(lambda (str)
(cider-sync-request:complete str ":same")))))
;;** `counsel-company'
(defvar company-candidates)
(defvar company-common)
(defvar company-prefix)
(declare-function company-abort "ext:company")
(declare-function company-complete "ext:company")
(declare-function company-mode "ext:company")
(defun counsel-company ()
"Complete using `company-candidates'."
(company-mode 1)
(unless company-candidates
(let ((len (cond (company-common
(length company-common))
(length company-prefix)))))
(when len
(setq ivy-completion-beg (- (point) len))
(setq ivy-completion-end (point))
(ivy-read "Candidate: " company-candidates
:action #'ivy-completion-in-region-action
:unwind #'company-abort
:caller 'counsel-company))))
;;** `counsel-irony'
(declare-function irony-completion-candidates-async "ext:irony-completion")
(declare-function irony-completion-symbol-bounds "ext:irony-completion")
(declare-function irony-completion-annotation "ext:irony-completion")
(defun counsel-irony ()
"Inline C/C++ completion using Irony."
(irony-completion-candidates-async 'counsel-irony-callback))
(defun counsel-irony-callback (candidates)
"Callback function for Irony to search among CANDIDATES."
(let* ((symbol-bounds (irony-completion-symbol-bounds))
(beg (car symbol-bounds))
(end (cdr symbol-bounds))
(prefix (buffer-substring-no-properties beg end)))
(setq ivy-completion-beg beg
ivy-completion-end end)
(ivy-read "code: " (mapcar #'counsel-irony-annotate candidates)
:predicate (lambda (candidate)
(string-prefix-p prefix (car candidate)))
:caller 'counsel-irony
:action #'ivy-completion-in-region-action)))
(defun counsel-irony-annotate (x)
"Make Ivy candidate from Irony candidate X."
(cons (concat (car x) (irony-completion-annotation x))
(car x)))
(add-to-list 'ivy-display-functions-alist '(counsel-irony . ivy-display-function-overlay))
;;* Elisp symbols
;;** `counsel-describe-variable'
(defvar counsel-describe-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-.") #'counsel-find-symbol)
(define-key map (kbd "C-,") #'counsel--info-lookup-symbol)
'(("I" counsel-info-lookup-symbol "info")
("d" counsel--find-symbol "definition")))
(defvar counsel-describe-symbol-history ()
"History list for variable and function names.
Used by commands `counsel-describe-variable' and
(defun counsel-find-symbol ()
"Jump to the definition of the current symbol."
(ivy-exit-with-action #'counsel--find-symbol))
(defun counsel--info-lookup-symbol ()
"Lookup the current symbol in the info docs."
(ivy-exit-with-action #'counsel-info-lookup-symbol))
(defvar find-tag-marker-ring)
(declare-function xref-push-marker-stack "xref")
(defalias 'counsel--push-xref-marker
(if (require 'xref nil t)
(require 'etags)
(lambda (&optional m)
(ring-insert (with-no-warnings find-tag-marker-ring) (or m (point-marker)))))
"Compatibility shim for `xref-push-marker-stack'.")
(defun counsel--find-symbol (x)
"Find symbol definition that corresponds to string X."
(let ((full-name (get-text-property 0 'full-name x)))
(if full-name
(find-library full-name)
(let ((sym (read x)))
(cond ((and (eq (ivy-state-caller ivy-last)
(boundp sym))
(find-variable sym))
((fboundp sym)
(find-function sym))
((boundp sym)
(find-variable sym))
((or (featurep sym)
(prin1-to-string sym)))
(prin1-to-string sym)))
(error "Couldn't find definition of %s"
(defun counsel--variable-p (symbol)
"Return non-nil if SYMBOL is a bound or documented variable."
(or (and (boundp symbol)
(not (keywordp symbol)))
(get symbol 'variable-documentation)))
(defcustom counsel-describe-variable-function #'describe-variable
"Function to call to describe a variable passed as parameter."
:type 'function)
(defun counsel-describe-variable-transformer (var)
"Propertize VAR if it's a custom variable."
(if (custom-variable-p (intern var))
(ivy-append-face var 'ivy-highlight-face)
'counsel-describe-variable 'counsel-describe-variable-transformer)
(defun counsel-describe-variable ()
"Forward to `describe-variable'.
Variables declared using `defcustom' are highlighted according to
(let ((enable-recursive-minibuffers t))
(ivy-read "Describe variable: " obarray
:predicate #'counsel--variable-p
:require-match t
:history 'counsel-describe-symbol-history
:keymap counsel-describe-map
:preselect (ivy-thing-at-point)
:sort t
:action (lambda (x)
(funcall counsel-describe-variable-function (intern x)))
:caller 'counsel-describe-variable)))
;;** `counsel-describe-function'
'(("I" counsel-info-lookup-symbol "info")
("d" counsel--find-symbol "definition")))
(defcustom counsel-describe-function-function #'describe-function
"Function to call to describe a function passed as parameter."
:type 'function)
(defun counsel-describe-function-transformer (function-name)
"Propertize FUNCTION-NAME if it's an interactive function."
(if (commandp (intern function-name))
(ivy-append-face function-name 'ivy-highlight-face)
'counsel-describe-function 'counsel-describe-function-transformer)
(defun ivy-function-called-at-point ()
(let ((f (function-called-at-point)))
(and f (symbol-name f))))
(defcustom counsel-describe-function-preselect #'ivy-thing-at-point
"Determine what `counsel-describe-function' should preselect."
:type '(radio
(function-item ivy-thing-at-point)
(function-item ivy-function-called-at-point)))
(defun counsel-describe-function ()
"Forward to `describe-function'.
Interactive functions (i.e., commands) are highlighted according
to `ivy-highlight-face'."
(let ((enable-recursive-minibuffers t))
(ivy-read "Describe function: " obarray
:predicate (lambda (sym)
(or (fboundp sym)
(get sym 'function-documentation)))
:require-match t
:history 'counsel-describe-symbol-history
:keymap counsel-describe-map
:preselect (funcall counsel-describe-function-preselect)
:sort t
:action (lambda (x)
(funcall counsel-describe-function-function (intern x)))
:caller 'counsel-describe-function)))
;;** `counsel-set-variable'
(defvar counsel-set-variable-history nil
"Store history for `counsel-set-variable'.")
(defun counsel-read-setq-expression (sym)
"Read and eval a setq expression for SYM."
(setq this-command 'eval-expression)
(let* ((minibuffer-completing-symbol t)
(sym-value (symbol-value sym))
(expr (minibuffer-with-setup-hook
(lambda ()
(add-function :before-until (local 'eldoc-documentation-function)
(add-hook 'completion-at-point-functions #'elisp-completion-at-point nil t)
(run-hooks 'eval-expression-minibuffer-setup-hook)
(goto-char (minibuffer-prompt-end))
(forward-char 6)
(insert (format "%S " sym)))
(read-from-minibuffer "Eval: "
(if (and sym-value (consp sym-value))
"(setq '%S)"
"(setq %S)")
read-expression-map t
(eval-expression expr)))
(defun counsel--setq-doconst (x)
"Return a cons of description and value for X.
X is an item of a radio- or choice-type defcustom."
(when (listp x)
(let ((v (car-safe (last x)))
(tag (and (eq (car x) 'const)
(plist-get (cdr x) :tag))))
(when (and (or v tag) (not (eq v 'function)))
(when tag
(concat tag ": "))
(if (stringp v) v (prin1-to-string v)))
(if (symbolp v)
(list 'quote v)
(declare-function lv-message "ext:lv")
(declare-function lv-delete-window "ext:lv")
(declare-function custom-variable-documentation "cus-edit")
(defface counsel-variable-documentation
'((t :inherit font-lock-comment-face))
"Face for displaying Lisp documentation."
:group 'ivy-faces)
(defun counsel-set-variable (sym)
"Set a variable SYM, with completion.
When the selected variable is a `defcustom' with the type boolean
or radio, offer completion of all possible values.
Otherwise, offer a variant of `eval-expression', with the initial
input corresponding to the chosen variable.
With a prefix arg, restrict list to variables defined using
(interactive (list (intern
(ivy-read "Set variable: " obarray
:predicate (if current-prefix-arg
:history 'counsel-set-variable-history
:preselect (ivy-thing-at-point)))))
(let ((doc (and (require 'cus-edit)
(require 'lv nil t)
(not (string= "nil" (custom-variable-documentation sym)))
(propertize (custom-variable-documentation sym)
'face 'counsel-variable-documentation)))
(when doc
(lv-message (ivy--quote-format-string doc)))
(if (and (boundp sym)
(setq sym-type (get sym 'custom-type))
((and (consp sym-type)
(memq (car sym-type) '(choice radio)))
(setq cands (delq nil (mapcar #'counsel--setq-doconst
(cdr sym-type)))))
((eq sym-type 'boolean)
(setq cands '(("nil" . nil) ("t" . t))))
(t nil)))
(let* ((sym-val (symbol-value sym))
(res (ivy-read (format "Set (%S <%s>): " sym sym-val)
:preselect (prin1-to-string sym-val))))
(when res
(setq res
(if (assoc res cands)
(cdr (assoc res cands))
(read res)))
(set sym (if (and (listp res) (eq (car res) 'quote))
(cadr res)
(unless (boundp sym)
(set sym nil))
(counsel-read-setq-expression sym)))
(when doc
;;** `counsel-apropos'
(defun counsel-apropos ()
"Show all matching symbols.
See `apropos' for further information on what is considered
a symbol and how to search for them."
(ivy-read "Search for symbol (word list or regexp): " obarray
:predicate (lambda (sym)
(or (fboundp sym)
(boundp sym)
(facep sym)
(symbol-plist sym)))
:history 'counsel-apropos-history
:preselect (ivy-thing-at-point)
:sort t
:action (lambda (pattern)
(when (string= pattern "")
(user-error "Please specify a pattern"))
;; If the user selected a candidate form the list, we use
;; a pattern which matches only the selected symbol.
(if (memq this-command '(ivy-immediate-done ivy-alt-done))
;; Regexp pattern are passed verbatim, other input is
;; split into words.
(if (string= (regexp-quote pattern) pattern)
(apropos (split-string pattern "[ \t]+" t))
(apropos pattern))
(apropos (concat "\\`" pattern "\\'"))))
:caller 'counsel-apropos))
;;** `counsel-info-lookup-symbol'
(defvar info-lookup-mode)
(declare-function info-lookup-guess-default "info-look")
(declare-function info-lookup->completions "info-look")
(declare-function info-lookup->mode-value "info-look")
(declare-function info-lookup-select-mode "info-look")
(declare-function info-lookup-change-mode "info-look")
(declare-function info-lookup "info-look")
(defun counsel-info-lookup-symbol (symbol &optional mode)
"Forward SYMBOL to `info-lookup-symbol' with ivy completion.
With prefix arg MODE a query for the symbol help mode is offered."
(require 'info-look)
;; Courtesy of `info-lookup-interactive-arguments'
(let* ((topic 'symbol)
(mode (cond (current-prefix-arg
(info-lookup-change-mode topic))
topic (info-lookup-select-mode))
((info-lookup-change-mode topic))))
(enable-recursive-minibuffers t))
(list (ivy-read "Describe symbol: " (info-lookup->completions topic mode)
:history 'info-lookup-history
:preselect (info-lookup-guess-default topic mode)
:sort t
:caller 'counsel-info-lookup-symbol)
(info-lookup-symbol symbol mode))
;;** `counsel-M-x'
(defface counsel-key-binding
'((t :inherit font-lock-keyword-face))
"Face used by `counsel-M-x' for key bindings."
:group 'ivy-faces)
(defcustom counsel-alias-expand t
"When non-nil, show the expansion of aliases in `counsel-M-x'."
:type 'boolean
:group 'ivy)
(defun counsel-M-x-transformer (cmd)
"Return CMD annotated with its active key binding, if any."
(let ((alias (symbol-function (intern cmd)))
(key (where-is-internal (intern cmd) nil t)))
(concat cmd
(when (and (symbolp alias) counsel-alias-expand)
(format " (%s)" alias))
(when key
;; Prefer `<f2>' over `C-x 6' where applicable
(let ((i (cl-search [?\C-x ?6] key)))
(when i
(let ((dup (vconcat (substring key 0 i) [f2] (substring key (+ i 2))))
(map (current-global-map)))
(when (equal (lookup-key map key)
(lookup-key map dup))
(setq key dup)))))
(setq key (key-description key))
(put-text-property 0 (length key) 'face 'counsel-key-binding key)
(format " (%s)" key)))))
(defvar amx-initialized)
(defvar amx-cache)
(declare-function amx-initialize "ext:amx")
(declare-function amx-detect-new-commands "ext:amx")
(declare-function amx-update "ext:amx")
(declare-function amx-rank "ext:amx")
(defvar smex-initialized-p)
(defvar smex-ido-cache)
(declare-function smex-initialize "ext:smex")
(declare-function smex-detect-new-commands "ext:smex")
(declare-function smex-update "ext:smex")
(declare-function smex-rank "ext:smex")
(defun counsel--M-x-externs ()
"Return `counsel-M-x' candidates from external packages.
The return value is a list of strings. The currently supported
packages are, in order of precedence, `amx' and `smex'."
(cond ((require 'amx nil t)
(unless amx-initialized
(when (amx-detect-new-commands)
(mapcar (lambda (entry)
(symbol-name (car entry)))
((require 'smex nil t)
(unless smex-initialized-p
(when (smex-detect-new-commands)
(defun counsel--M-x-prompt ()
"String for `M-x' plus the string representation of `current-prefix-arg'."
(concat (cond ((null current-prefix-arg)
((eq current-prefix-arg '-)
"- ")
((integerp current-prefix-arg)
(format "%d " current-prefix-arg))
((= (car current-prefix-arg) 4)
"C-u ")
(format "%d " (car current-prefix-arg))))
"M-x "))
(defvar counsel-M-x-history nil
"History for `counsel-M-x'.")
(defun counsel-M-x-action (cmd)
"Execute CMD."
(setq cmd (intern cmd))
(cond ((bound-and-true-p amx-initialized)
(amx-rank cmd))
((bound-and-true-p smex-initialized-p)
(smex-rank cmd)))
(setq prefix-arg current-prefix-arg)
(setq this-command cmd)
(setq real-this-command cmd)
(command-execute cmd 'record))
(defun counsel-M-x (&optional initial-input)
"Ivy version of `execute-extended-command'.
Optional INITIAL-INPUT is the initial input in the minibuffer.
This function integrates with either the `amx' or `smex' package
when available, in that order of precedence."
;; When `counsel-M-x' returns, `last-command' would be set to
;; `counsel-M-x' because :action hasn't been invoked yet.
;; Instead, preserve the old value of `this-command'.
(setq this-command last-command)
(setq real-this-command real-last-command)
(let ((externs (counsel--M-x-externs)))
(ivy-read (counsel--M-x-prompt) (or externs obarray)
:predicate (and (not externs)
(lambda (sym)
(and (commandp sym)
(not (get sym 'byte-obsolete-info)))))
:require-match t
:history 'counsel-M-x-history
:action #'counsel-M-x-action
:sort (not externs)
:keymap counsel-describe-map
:initial-input initial-input
:caller 'counsel-M-x)))
`(("d" counsel--find-symbol "definition")
("h" ,(lambda (x) (describe-function (intern x))) "help")))
;;** `counsel-command-history'
(defun counsel-command-history-action-eval (cmd)
"Eval the command CMD."
(eval (read cmd)))
(defun counsel-command-history-action-edit-and-eval (cmd)
"Edit and eval the command CMD."
(edit-and-eval-command "Eval: " (read cmd)))
'(("r" counsel-command-history-action-eval "eval command")
("e" counsel-command-history-action-edit-and-eval "edit and eval command")))
(defun counsel-command-history ()
"Show the history of commands."
(ivy-read "Command: " (mapcar #'prin1-to-string command-history)
:require-match t
:action #'counsel-command-history-action-eval
:caller 'counsel-command-history))
;;** `counsel-load-library'
(defun counsel-library-candidates ()
"Return a list of completion candidates for `counsel-load-library'."
(let ((suffix (concat (regexp-opt '(".el" ".el.gz") t) "\\'"))
(cands (make-hash-table :test #'equal))
(dolist (dir load-path)
(setq dir (or dir default-directory)) ;; interpret nil in load-path as default-directory
(when (file-directory-p dir)
(dolist (file (file-name-all-completions "" dir))
(when (string-match suffix file)
(unless (string-match "pkg.elc?$" file)
(setq short-name (substring file 0 (match-beginning 0)))
(if (setq old-val (gethash short-name cands))
;; assume going up directory once will resolve name clash
(setq dir-parent (counsel-directory-name (cdr old-val)))
(puthash short-name
(counsel-string-compose dir-parent (car old-val))
(cdr old-val))
(setq dir-parent (counsel-directory-name dir))
(puthash (concat dir-parent short-name)
dir-parent short-name)
'full-name (expand-file-name file dir))
(puthash short-name
(cons (propertize
'full-name (expand-file-name file dir))
(maphash (lambda (_k v) (push (car v) res)) cands)
(nreverse res)))
(defun counsel-load-library ()
"Load a selected the Emacs Lisp library.
The libraries are offered from `load-path'."
(let ((cands (counsel-library-candidates)))
(ivy-read "Load library: " cands
:action (lambda (x)
(get-text-property 0 'full-name x)))
:keymap counsel-describe-map)))
'(("d" counsel--find-symbol "definition")))
;;** `counsel-find-library'
(declare-function find-library-name "find-func")
(defun counsel-find-library-other-window (library)
(let ((buf (find-file-noselect (find-library-name library))))
(pop-to-buffer buf 'other-window)))
(defun counsel-find-library-other-frame (library)
(let ((buf (find-file-noselect (find-library-name library))))
(condition-case nil
(switch-to-buffer-other-frame buf)
(error (pop-to-buffer buf)))))
'(("j" counsel-find-library-other-window "other window")
("f" counsel-find-library-other-frame "other frame")))
(defun counsel-find-library ()
"Visit a selected the Emacs Lisp library.
The libraries are offered from `load-path'."
(let ((cands (counsel-library-candidates)))
(ivy-read "Find library: " cands
:action #'counsel--find-symbol
:keymap counsel-describe-map
:caller 'counsel-find-library)))
;;** `counsel-load-theme'
(declare-function powerline-reset "ext:powerline")
(defun counsel-load-theme-action (x)
"Disable current themes and load theme X."
(condition-case nil
(mapc #'disable-theme custom-enabled-themes)
(load-theme (intern x) t)
(when (fboundp 'powerline-reset)
(error "Problem loading theme %s" x)))
(defun counsel-load-theme ()
"Forward to `load-theme'.
Usable with `ivy-resume', `ivy-next-line-and-call' and
(ivy-read "Load custom theme: "
(mapcar 'symbol-name
:action #'counsel-load-theme-action
:caller 'counsel-load-theme))
;;** `counsel-descbinds'
'(("d" counsel-descbinds-action-find "definition")
("I" counsel-descbinds-action-info "info")
("x" counsel-descbinds-action-exec "execute")))
(defvar counsel-descbinds-history nil
"History for `counsel-descbinds'.")
(defun counsel--descbinds-cands (&optional prefix buffer)
"Get key bindings starting with PREFIX in BUFFER.
See `describe-buffer-bindings' for further information."
(let ((buffer (or buffer (current-buffer)))
(re-exclude (regexp-opt
'("<vertical-line>" "<bottom-divider>" "<right-divider>"
"<mode-line>" "<C-down-mouse-2>" "<left-fringe>"
"<right-fringe>" "<header-line>"
"<vertical-scroll-bar>" "<horizontal-scroll-bar>")))
(let ((indent-tabs-mode t))
(describe-buffer-bindings buffer prefix))
(goto-char (point-min))
;; Skip the "Key translations" section
(re-search-forward " ")
(forward-char 1)
(while (not (eobp))
(when (looking-at "^\\([^\t\n]+\\)[\t ]*\\(.*\\)$")
(let ((key (match-string 1))
(fun (match-string 2))
(unless (or (member fun '("??" "self-insert-command"))
(string-match re-exclude key)
(not (or (commandp (setq cmd (intern-soft fun)))
(member fun '("Prefix Command")))))
(cons (format
"%-15s %s"
(propertize key 'face 'counsel-key-binding)
(cons key cmd))
(forward-line 1)))
(nreverse res)))
(defcustom counsel-descbinds-function #'describe-function
"Function to call to describe a function passed as parameter."
:type 'function)
(defun counsel-descbinds-action-describe (x)
"Describe function of candidate X.
See `describe-function' for further information."
(let ((cmd (cddr x)))
(funcall counsel-descbinds-function cmd)))
(defun counsel-descbinds-action-exec (x)
"Run candidate X.
See `execute-extended-command' for further information."
(let ((cmd (cddr x)))
(command-execute cmd 'record)))
(defun counsel-descbinds-action-find (x)
"Find symbol definition of candidate X.
See `counsel--find-symbol' for further information."
(let ((cmd (cddr x)))
(counsel--find-symbol (symbol-name cmd))))
(defun counsel-descbinds-action-info (x)
"Display symbol definition of candidate X, as found in the relevant manual.
See `info-lookup-symbol' for further information."
(let ((cmd (cddr x)))
(counsel-info-lookup-symbol (symbol-name cmd))))
(defun counsel-descbinds (&optional prefix buffer)
"Show a list of all defined keys and their definitions.
If non-nil, show only bindings that start with PREFIX.
BUFFER defaults to the current one."
(ivy-read "Bindings: " (counsel--descbinds-cands prefix buffer)
:action #'counsel-descbinds-action-describe
:history 'counsel-descbinds-history
:caller 'counsel-descbinds))
;;** `counsel-describe-face'
(defcustom counsel-describe-face-function #'describe-face
"Function to call to describe a face or face name argument."
:type 'function)
(defun counsel--face-at-point ()
"Return name of face around point.
Try detecting a face name in the text around point before falling
back to the face of the character after point, and finally the
`default' face."
(symbol-name (or (face-at-point t) 'default)))
(defun counsel-describe-face ()
"Completion for `describe-face'."
(ivy-read "Face: " (face-list)
:require-match t
:history 'face-name-history
:preselect (counsel--face-at-point)
:sort t
:action counsel-describe-face-function
:caller 'counsel-describe-face))
(defun counsel-customize-face (name)
"Customize face with NAME."
(customize-face (intern name)))
(defun counsel-customize-face-other-window (name)
"Customize face with NAME in another window."
(customize-face-other-window (intern name)))
'(("c" counsel-customize-face "customize")
("C" counsel-customize-face-other-window "customize other window")))
;;** `counsel-faces'
(defvar counsel--faces-format "%-40s %s")
(defun counsel--faces-format-function (names)
"Customize `ivy-format-functions-alist' for `counsel-faces'.
Each candidate is formatted based on the given FORMAT string."
(let ((formatter
(lambda (name)
(format counsel--faces-format name
(propertize list-faces-sample-text
'face (intern name))))))
(lambda (name)
(funcall formatter (ivy--add-face name 'ivy-current-match)))
formatter names "\n")))
(defun counsel-faces ()
"Complete faces with preview.
Actions are provided by default for describing or customizing the
selected face."
(let* ((names (mapcar #'symbol-name (face-list)))
(format "%%-%ds %%s"
(apply #'max 0 (mapcar #'string-width names)))))
(ivy-read "Face: " names
:require-match t
:history 'face-name-history
:preselect (counsel--face-at-point)
:sort t
:action counsel-describe-face-function
:caller 'counsel-faces)))
(add-to-list 'ivy-format-functions-alist '(counsel-faces . counsel--faces-format-function))
'(("c" counsel-customize-face "customize")
("C" counsel-customize-face-other-window "customize other window")))
;;* Git
;;** `counsel-git'
(defvar counsel-git-cmd "git ls-files --full-name --"
"Command for `counsel-git'.")
'(("j" find-file-other-window "other window")
("x" counsel-find-file-extern "open externally")))
(defun counsel--dominating-file (file &optional dir)
"Look up directory hierarchy for FILE, starting in DIR.
Like `locate-dominating-file', but DIR defaults to
`default-directory' and the return value is expanded."
(and (setq dir (locate-dominating-file (or dir default-directory) file))
(expand-file-name dir)))
(defun counsel-locate-git-root ()
"Return the root of the Git repository containing the current buffer."
(or (counsel--git-root)
(error "Not in a Git repository")))
(defun counsel-git-cands ()
(let ((default-directory (counsel-locate-git-root)))
(shell-command-to-string counsel-git-cmd)
(defun counsel-git (&optional initial-input)
"Find file in the current Git repository.
INITIAL-INPUT can be given as the initial minibuffer input."
(counsel-require-program counsel-git-cmd)
(let ((default-directory (counsel-locate-git-root)))
(ivy-read "Find file: " (counsel-git-cands)
:initial-input initial-input
:action #'counsel-git-action
:caller 'counsel-git)))
(defun counsel-git-action (x)
"Find file X in current Git repository."
(let ((default-directory (ivy-state-directory ivy-last)))
(find-file x))))
(defun counsel-git-occur ()
"Occur function for `counsel-git' using `counsel-cmd-to-dired'."
(cd (ivy-state-directory ivy-last))
(format "%s | %s | xargs ls"
(defvar counsel-dired-listing-switches "-alh"
"Switches passed to `ls' for `counsel-cmd-to-dired'.")
(defun counsel-cmd-to-dired (full-cmd &optional filter)
"Adapted from `find-dired'."
(let ((inhibit-read-only t))
(dired-mode default-directory counsel-dired-listing-switches)
(insert " " default-directory ":\n")
(let ((point (point)))
(insert " " full-cmd "\n")
(dired-insert-set-properties point (point)))
(setq-local dired-sort-inhibit t)
(setq-local revert-buffer-function
(lambda (_1 _2) (counsel-cmd-to-dired full-cmd)))
(setq-local dired-subdir-alist
(list (cons default-directory (point-min-marker))))
(let ((proc (start-process-shell-command
"counsel-cmd" (current-buffer) full-cmd)))
(set-process-filter proc filter)
(lambda (process _msg)
(when (and (eq (process-status process) 'exit)
(zerop (process-exit-status process)))
(goto-char (point-min))
(forward-line 2)
(ivy-set-occur 'counsel-git 'counsel-git-occur)
;;** `counsel-git-grep'
(defvar counsel-git-grep-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-l") 'ivy-call-and-recenter)
(define-key map (kbd "M-q") 'counsel-git-grep-query-replace)
(define-key map (kbd "C-c C-m") 'counsel-git-grep-switch-cmd)
(ivy-set-occur 'counsel-git-grep 'counsel-git-grep-occur)
(ivy-set-display-transformer 'counsel-git-grep 'counsel-git-grep-transformer)
(defvar counsel-git-grep-cmd-default "git --no-pager grep --full-name -n --no-color -i -I -e \"%s\""
"Initial command for `counsel-git-grep'.")
(defvar counsel-git-grep-cmd nil
"Store the command for `counsel-git-grep'.")
(defvar counsel-git-grep-history nil
"History for `counsel-git-grep'.")
(defvar counsel-git-grep-cmd-history
(list counsel-git-grep-cmd-default)
"History for `counsel-git-grep' shell commands.")
(defcustom counsel-grep-post-action-hook nil
"Hook that runs after the point moves to the next candidate.
Typical value: '(recenter)."
:type 'hook)
(defcustom counsel-git-grep-cmd-function #'counsel-git-grep-cmd-function-default
"How a git-grep shell call is built from the input."
:type '(radio
(function-item counsel-git-grep-cmd-function-default)
(function-item counsel-git-grep-cmd-function-ignore-order)
(function :tag "Other")))
(defun counsel-git-grep-cmd-function-default (str)
(format counsel-git-grep-cmd
(setq ivy--old-re (ivy--regex str t))))
(defun counsel-git-grep-cmd-function-ignore-order (str)
(setq ivy--old-re (ivy--regex str t))
(let ((parts (split-string str " " t)))
"git --no-pager grep --full-name -n --no-color -i -e "
(mapconcat #'shell-quote-argument parts " --and -e "))))
(defun counsel-git-grep-function (string)
"Grep in the current Git repository for STRING."
(funcall counsel-git-grep-cmd-function string))
(defun counsel-git-grep-action (x)
"Go to occurrence X in current Git repository."
(when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" x)
(let ((file-name (match-string-no-properties 1 x))
(line-number (match-string-no-properties 2 x)))
(find-file (expand-file-name
(ivy-state-directory ivy-last)))
(goto-char (point-min))
(forward-line (1- (string-to-number line-number)))
(when (re-search-forward (ivy--regex ivy-text t) (line-end-position) t)
(when swiper-goto-start-of-match
(goto-char (match-beginning 0))))
(run-hooks 'counsel-grep-post-action-hook)
(unless (eq ivy-exit 'done)
(swiper--add-overlays (ivy--regex ivy-text))))))
(defun counsel-git-grep-transformer (str)
"Higlight file and line number in STR."
(when (string-match "\\`\\([^:]+\\):\\([^:]+\\):" str)
(ivy-add-face-text-property (match-beginning 1) (match-end 1)
(ivy-add-face-text-property (match-beginning 2) (match-end 2)
(defvar counsel-git-grep-projects-alist nil
"An alist of project directory to \"git-grep\" command.
Allows to automatically use a custom \"git-grep\" command for all
files in a project.")
(defun counsel--git-grep-cmd-and-proj (cmd)
(let ((dd (expand-file-name default-directory))
((stringp cmd))
(if (setq proj
(lambda (x)
(string-match (car x) dd))
(setq cmd (cdr proj))
(setq cmd
(ivy-read "cmd: " counsel-git-grep-cmd-history
:history 'counsel-git-grep-cmd-history
:re-builder #'ivy--regex))
(setq counsel-git-grep-cmd-history
(delete-dups counsel-git-grep-cmd-history))))
(setq cmd counsel-git-grep-cmd-default)))
(cons proj cmd)))
(defun counsel--call (command &optional result-fn)
"Synchronously call COMMAND and return its output as a string.
COMMAND comprises the program name followed by its arguments, as
in `make-process'. Signal `file-error' and emit a warning if
COMMAND fails. Obey file handlers based on `default-directory'.
On success, RESULT-FN is called in output buffer with no arguments."
(let ((stderr (make-temp-file "counsel-call-stderr-"))
(setq status (apply #'process-file (car command) nil
(list t stderr) nil (cdr command)))
(if (eq status 0)
(if result-fn
(funcall result-fn)
;; Return all output except trailing newline.
(buffer-substring (point-min)
(- (point)
(if (eq (bobp) (bolp))
;; Convert process status into error list.
(setq status (list 'file-error
(mapconcat #'identity `(,@command "failed") " ")
;; Print stderr contents, if any, to *Warnings* buffer.
(let ((msg (condition-case err
(unless (zerop (cadr (insert-file-contents
stderr nil nil nil t)))
(error (error-message-string err)))))
(lwarn 'ivy :warning "%s" (apply #'concat
(error-message-string status)
(and msg (list "\n" msg)))))
;; Signal `file-error' with process status.
(signal (car status) (cdr status))))
(delete-file stderr))))
(defun counsel--command (&rest command)
"Forward COMMAND to `counsel--call'."
(counsel--call command))
(defun counsel-git-grep (&optional cmd initial-input)
"Grep for a string in the current Git repository.
When CMD is a string, use it as a \"git grep\" command.
When CMD is non-nil, prompt for a specific \"git grep\" command.
INITIAL-INPUT can be given as the initial minibuffer input."
(interactive "P")
(let ((proj-and-cmd (counsel--git-grep-cmd-and-proj cmd))
(setq proj (car proj-and-cmd))
(setq counsel-git-grep-cmd (cdr proj-and-cmd))
(counsel-require-program counsel-git-grep-cmd)
(let ((collection-function
(if proj
(lambda ()
(default-directory (if proj
(car proj)
(ivy-read "git grep: " collection-function
:initial-input initial-input
:dynamic-collection t
:keymap counsel-git-grep-map
:action #'counsel-git-grep-action
:unwind unwind-function
:history 'counsel-git-grep-history
:caller 'counsel-git-grep))))
(cl-pushnew 'counsel-git-grep ivy-highlight-grep-commands)
(defun counsel-git-grep-proj-function (str)
"Grep for STR in the current Git repository."
(let ((regex (setq ivy--old-re
(ivy--regex str t))))
(counsel--async-command (format counsel-git-grep-cmd regex))
(defun counsel-git-grep-switch-cmd ()
"Set `counsel-git-grep-cmd' to a different value."
(setq counsel-git-grep-cmd
(ivy-read "cmd: " counsel-git-grep-cmd-history
:history 'counsel-git-grep-cmd-history))
(setq counsel-git-grep-cmd-history
(delete-dups counsel-git-grep-cmd-history))
(unless (ivy-state-dynamic-collection ivy-last)
(setq ivy--all-candidates
(all-completions "" 'counsel-git-grep-function))))
(defun counsel--normalize-grep-match (str)
;; Prepend ./ if necessary:
(unless (ivy--starts-with-dotslash str)
(setq str (concat "./" str)))
;; Remove column info if any:
(when (string-match
"[^\n:]+?[^\n/:]:[\t ]*[1-9][0-9]*[\t ]*:\\([1-9][0-9]*:\\)"
(setq str (replace-match "" t t str 1))))
(defun counsel-git-grep-occur ()
"Generate a custom occur buffer for `counsel-git-grep'.
When REVERT is non-nil, regenerate the current *ivy-occur* buffer."
(unless (eq major-mode 'ivy-occur-grep-mode)
(setq default-directory (ivy-state-directory ivy-last)))
(setq ivy-text
(and (string-match "\"\\(.*\\)\"" (buffer-name))
(match-string 1 (buffer-name))))
(let* ((regex (funcall ivy--regex-function ivy-text))
(positive-pattern (replace-regexp-in-string
;; git-grep can't handle .*?
"\\.\\*\\?" ".*"
(ivy-re-to-str regex)))
(if (stringp regex) ""
(mapconcat (lambda (x)
(and (null (cdr x))
(format "| grep -v %s" (car x))))
" ")))
(cmd (concat (format counsel-git-grep-cmd positive-pattern) negative-patterns))
(setq cands (counsel--split-string (shell-command-to-string cmd)))
;; Need precise number of header lines for `wgrep' to work.
(insert (format "-*- mode:grep; default-directory: %S -*-\n\n\n"
(insert (format "%d candidates:\n" (length cands)))
(mapcar #'counsel--normalize-grep-match cands))))
(defun counsel-git-grep-query-replace ()
"Start `query-replace' with string to replace from last search string."
(unless (window-minibuffer-p)
"Should only be called in the minibuffer through `counsel-git-grep-map'"))
(let* ((enable-recursive-minibuffers t)
(from (ivy--regex ivy-text))
(to (query-replace-read-to from "Query replace" t)))
(lambda (_)
(let (done-buffers)
(dolist (cand ivy--old-cands)
(when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" cand)
(let ((file-name (match-string-no-properties 1 cand)))
(setq file-name (expand-file-name
(ivy-state-directory ivy-last)))
(unless (member file-name done-buffers)
(push file-name done-buffers)
(find-file file-name)
(goto-char (point-min)))
(perform-replace from to t t nil))))))))))
;;** `counsel-git-stash'
(defun counsel-git-stash-kill-action (x)
"Add git stash command to kill ring.
The git command applies the stash entry where candidate X was found in."
(when (string-match "\\([^:]+\\):" x)
(kill-new (message (format "git stash apply %s" (match-string 1 x))))))
(defun counsel-git-stash ()
"Search through all available git stashes."
(let* ((default-directory (counsel-locate-git-root))
(cands (split-string (shell-command-to-string
for i in `git stash list --format=\"%gd\"`; do
git stash show -p $i | grep -H --label=\"$i\" \"$1\"
done") "\n" t)))
(ivy-read "git stash: " cands
:action #'counsel-git-stash-kill-action
:caller 'counsel-git-stash)))
;;** `counsel-git-log'
(defvar counsel-git-log-cmd "GIT_PAGER=cat git log --grep '%s'"
"Command used for \"git log\".")
(defvar counsel-git-log-split-string-re "^commit "
"The `split-string' separates when split output of `counsel-git-log-cmd'.")
'counsel-git-log-split-string-re 'counsel-async-split-string-re-alist "<2019-07-16 Tue>")
(defun counsel-git-log-function (str)
"Search for STR in git log."
;; `counsel--yank-pop-format-function' uses this
(setq ivy--old-re (funcall ivy--regex-function str))
;; "git log --grep" likes to have groups quoted e.g. \(foo\).
;; But it doesn't like the non-greedy ".*?".
(format counsel-git-log-cmd
(replace-regexp-in-string "\\.\\*\\?" ".*"
(ivy-re-to-str ivy--old-re))))
(defun counsel-git-log-action (x)
"Add candidate X to kill ring."
(message "%S" (kill-new x)))
(declare-function magit-show-commit "ext:magit-diff")
(defun counsel-git-log-show-commit-action (log-entry)
"Visit the commit corresponding to LOG-ENTRY."
(require 'magit-diff)
(let ((commit (substring-no-properties log-entry 0 (string-match-p "\\W" log-entry))))
(magit-show-commit commit)))
'(("v" counsel-git-log-show-commit-action "visit commit")))
;;** `counsel-git-change-worktree'
(defun counsel-git-change-worktree-action (git-root-dir tree)
"Find the corresponding file in the worktree located at tree.
The current buffer is assumed to be in a subdirectory of GIT-ROOT-DIR.
TREE is the selected candidate."
(let* ((new-root-dir (counsel-git-worktree-parse-root tree))
(tree-filename (file-relative-name buffer-file-name git-root-dir))
(file-name (expand-file-name tree-filename new-root-dir)))
(find-file file-name)))
(defun counsel-git-worktree-list ()
"List worktrees in the Git repository containing the current buffer."
(let ((default-directory (counsel-locate-git-root)))
(split-string (shell-command-to-string "git worktree list") "\n" t)))
(defun counsel-git-worktree-parse-root (tree)
"Return worktree from candidate TREE."
(substring tree 0 (string-match-p " " tree)))
(defun counsel-git-close-worktree-files-action (root-dir)
"Close all buffers from the worktree located at ROOT-DIR."
(setq root-dir (counsel-git-worktree-parse-root root-dir))
(dolist (buf (buffer-list))
(set-buffer buf)
(and buffer-file-name
(string= "." (file-relative-name root-dir (counsel-locate-git-root)))
(kill-buffer buf)))))
'(("k" counsel-git-close-worktree-files-action "kill all")))
(defun counsel-git-change-worktree ()
"Find the file corresponding to the current buffer on a different worktree."
(let ((default-directory (counsel-locate-git-root)))
(ivy-read "Select worktree: "
(or (cl-delete default-directory (counsel-git-worktree-list)
:key #'counsel-git-worktree-parse-root :test #'string=)
(error "No other worktrees!"))
:action (lambda (tree)
(ivy-state-directory ivy-last) tree))
:require-match t
:caller 'counsel-git-change-worktree)))
;;** `counsel-git-checkout'
(defun counsel-git-checkout-action (branch)
"Switch branch by invoking git-checkout(1).
The command is passed a single argument comprising all characters
in BRANCH up to, but not including, the first space
character (#x20), or the string's end if it lacks a space."
(format "git checkout %s"
(substring branch 0 (string-match-p " " branch))))))
(defun counsel-git-branch-list ()
"Return list of branches in the current Git repository.
Value comprises all local and remote branches bar the one
currently checked out."
(cl-mapcan (lambda (line)
(and (string-match "\\`[[:blank:]]+" line)
(list (substring line (match-end 0)))))
(let ((default-directory (counsel-locate-git-root)))
(split-string (shell-command-to-string "git branch -vv --all")
"\n" t))))
(defun counsel-git-checkout ()
"Call the \"git checkout\" command."
(ivy-read "Checkout branch: " (counsel-git-branch-list)
:action #'counsel-git-checkout-action
:caller 'counsel-git-checkout))
(defvar counsel-yank-pop-truncate-radius)
(defun counsel--git-log-format-function (str)
(let ((counsel-yank-pop-truncate-radius 5))
(counsel--yank-pop-format-function str)))
(defun counsel-git-log ()
"Call the \"git log --grep\" shell command."
(ivy-read "Grep log: " #'counsel-git-log-function
:dynamic-collection t
:action #'counsel-git-log-action
:unwind #'counsel-delete-process
:caller 'counsel-git-log))
(add-to-list 'ivy-format-functions-alist '(counsel-git-log . counsel--git-log-format-function))
(add-to-list 'ivy-height-alist '(counsel-git-log . 4))
(add-to-list 'counsel-async-split-string-re-alist '(counsel-git-log . "^commit "))
(add-to-list 'counsel-async-ignore-re-alist '(counsel-git-log . "^[ \n]*$"))
;;* File
;;** `counsel-find-file'
(defvar counsel-find-file-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-DEL") 'counsel-up-directory)
(define-key map (kbd "C-<backspace>") 'counsel-up-directory)
(define-key map (kbd "`") (ivy-make-magic-action 'counsel-find-file "b"))
(define-obsolete-function-alias 'counsel-yank-directory 'ivy-insert-current-full
"<2019-06-13 Thu>")
(when (executable-find "git")
(add-to-list 'ivy-ffap-url-functions 'counsel-github-url-p)
(add-to-list 'ivy-ffap-url-functions 'counsel-emacs-url-p))
(add-to-list 'ivy-ffap-url-functions 'counsel-url-expand)
(defun counsel-find-file-cd-bookmark-action (_)
"Reset `counsel-find-file' from selected directory."
(ivy-read "cd: "
(mapcar (lambda (x) (file-name-directory (cdr x)))
:action (lambda (x)
(let ((default-directory (file-name-directory x)))
(defcustom counsel-root-command "sudo"
"Command to gain root privileges."
:type 'string)
(defun counsel-find-file-as-root (x)
"Find file X with root privileges."
(counsel-require-program counsel-root-command)
(let* ((host (file-remote-p x 'host))
(file-name (format "/%s:%s:%s"
(or host "")
(if host
(file-remote-p x 'localname)
;; If the current buffer visits the same file we are about to open,
;; replace the current buffer with the new one.
(if (eq (current-buffer) (get-file-buffer x))
(find-alternate-file file-name)
(find-file file-name))))
(defun counsel--yes-or-no-p (fmt &rest args)
"Ask user a yes or no question created using FMT and ARGS.
If Emacs 26 user option `read-answer-short' is bound, use it to
choose between `yes-or-no-p' and `y-or-n-p'; otherwise default to
(funcall (if (and (boundp 'read-answer-short)
(cond ((eq read-answer-short t))
((eq read-answer-short 'auto)
(eq (symbol-function 'yes-or-no-p) 'y-or-n-p))))
(apply #'format fmt args)))
(defun counsel-find-file-copy (x)
"Copy file X."
(require 'dired-aux)
(counsel--find-file-1 "Copy file to: "
(lambda (new-name)
(dired-copy-file x new-name 1))
(defun counsel-find-file-delete (x)
"Delete file X."
(when (or delete-by-moving-to-trash
;; `dired-delete-file', which see, already prompts for directories
(eq t (car (file-attributes x)))
(counsel--yes-or-no-p "Delete %s? " x))
(dired-delete-file x dired-recursive-deletes delete-by-moving-to-trash)
(dired-clean-up-after-deletion x)
(let ((win (and (not (eq ivy-exit 'done))
(when win (with-selected-window win (ivy--cd ivy--directory))))))
(defun counsel-find-file-move (x)
"Move or rename file X."
(require 'dired-aux)
(counsel--find-file-1 "Rename file to: "
(lambda (new-name)
(dired-rename-file x new-name 1))
(defun counsel-find-file-mkdir-action (_x)
"Create a directory and any nonexistent parent dirs from `ivy-text'."
(let ((dir (file-name-as-directory
(expand-file-name ivy-text ivy--directory)))
(win (and (not (eq ivy-exit 'done))
(make-directory dir t)
(when win (with-selected-window win (ivy--cd dir)))))
'(("j" find-file-other-window "other window")
("f" find-file-other-frame "other frame")
("b" counsel-find-file-cd-bookmark-action "cd bookmark")
("x" counsel-find-file-extern "open externally")
("r" counsel-find-file-as-root "open as root")
("k" counsel-find-file-delete "delete")
("c" counsel-find-file-copy "copy file")
("m" counsel-find-file-move "move or rename")
("d" counsel-find-file-mkdir-action "mkdir")))
(defcustom counsel-find-file-at-point nil
"When non-nil, add file-at-point to the list of candidates."
:type 'boolean)
(defcustom counsel-preselect-current-file nil
"When non-nil, preselect current file in list of candidates."
:type 'boolean)
(defcustom counsel-find-file-ignore-regexp nil
"A regexp of files to ignore while in `counsel-find-file'.
These files are un-ignored if `ivy-text' matches them. The
common way to show all files is to start `ivy-text' with a dot.
Example value: \"\\(?:\\`[#.]\\)\\|\\(?:[#~]\\'\\)\". This will hide
temporary and lock files.
Choosing the dotfiles option, \"\\`\\.\", might be convenient,
since you can still access the dotfiles if your input starts with
a dot. The generic way to toggle ignored files is \\[ivy-toggle-ignore],
but the leading dot is a lot faster."
:type `(choice
(const :tag "None" nil)
(const :tag "Dotfiles and Lockfiles" "\\(?:\\`\\|[/\\]\\)\\(?:[#.]\\)")
(const :tag "Ignored Extensions"
,(regexp-opt completion-ignored-extensions))
(regexp :tag "Regex")))
(defun counsel--find-file-matcher (regexp candidates)
"Return REGEXP matching CANDIDATES.
Skip some dotfiles unless `ivy-text' requires them."
(let ((res
regexp candidates
(lambda (re-str)
(lambda (x)
(string-match re-str (directory-file-name x)))))))
(if (or (null ivy-use-ignore)
(null counsel-find-file-ignore-regexp)
(string-match-p "\\`\\." ivy-text))
(or (cl-remove-if
(lambda (x)
(string-match-p counsel-find-file-ignore-regexp x)
(not (member x ivy-extra-directories))))
(declare-function ffap-guesser "ffap")
(defvar counsel-find-file-speedup-remote t
"Speed up opening remote files by disabling `find-file-hook' for them.")
(defcustom counsel-find-file-extern-extensions '("mp4" "mkv" "xlsx")
"List of extensions that make `counsel-find-file' use `counsel-find-file-extern'."
:type '(repeat string))
(defun counsel-find-file-action (x)
"Find file X."
(cond ((and counsel-find-file-speedup-remote
(file-remote-p ivy--directory))
(let ((find-file-hook nil))
(find-file (expand-file-name x ivy--directory))))
((member (file-name-extension x) counsel-find-file-extern-extensions)
(counsel-find-file-extern x))
(find-file (expand-file-name x ivy--directory))))))
(defun counsel--preselect-file ()
"Return candidate to preselect during filename completion.
The preselect behavior can be customized via user options
`counsel-find-file-at-point' and
`counsel-preselect-current-file', which see."
(when counsel-find-file-at-point
(require 'ffap)
(let ((f (ffap-guesser)))
(when f (expand-file-name f))))
(and counsel-preselect-current-file
(file-name-nondirectory buffer-file-name))))
(defun counsel--find-file-1 (prompt initial-input action caller)
(ivy-read prompt #'read-file-name-internal
:matcher #'counsel--find-file-matcher
:initial-input initial-input
:action action
:preselect (counsel--preselect-file)
:require-match 'confirm-after-completion
:history 'file-name-history
:keymap counsel-find-file-map
:caller caller))
(defun counsel-find-file (&optional initial-input)
"Forward to `find-file'.
When INITIAL-INPUT is non-nil, use it in the minibuffer during completion."
"Find file: " initial-input
(ivy-set-occur 'counsel-find-file 'counsel-find-file-occur)
(defvar counsel-find-file-occur-cmd "ls -a | %s | xargs -d '\\n' ls -d --group-directories-first"
"Format string for `counsel-find-file-occur'.")
(defvar counsel-find-file-occur-use-find (not (eq system-type 'gnu/linux))
"When non-nil, `counsel-find-file-occur' will use \"find\" as the base cmd.")
(defun counsel--expand-ls (cmd)
"Expand CMD that ends in \"ls\" with switches."
(concat cmd " " counsel-dired-listing-switches " | sed -e \"s/^/ /\""))
(defvar counsel-file-name-filter-alist
'(("ag -i '%s'" . t)
("ack -i '%s'" . t)
("perl -ne '/(%s.*)/i && print \"$1\\n\";'" . t)
("grep -i -E '%s'"))
"Alist of file name filtering commands.
The car is a shell command and the cdr is t when the shell
command supports look-arounds. The executable for the commands
will be checked for existence via `executable-find'. The first
one that exists will be used.")
(defun counsel--file-name-filter (&optional use-ignore)
"Return a command that filters a file list to match ivy candidates.
If USE-IGNORE is non-nil, try to generate a command that respects
(let ((regex ivy--old-re))
(if (= 0 (length regex))
(let ((filter-cmd (cl-find-if
(lambda (x)
(car (split-string (car x)))))
(when (and use-ignore ivy-use-ignore
(cdr filter-cmd)
(not (string-match-p "\\`\\." ivy-text))
(not (string-match-p counsel-find-file-ignore-regexp
(or (car ivy--old-cands) ""))))
(let ((ignore-re (list (counsel--elisp-to-pcre
(setq regex (if (stringp regex)
(list ignore-re (cons regex t))
(cons ignore-re regex)))))
(setq cmd (format (car filter-cmd)
(counsel--elisp-to-pcre regex (cdr filter-cmd))))
(if (string-match-p "csh\\'" shell-file-name)
(replace-regexp-in-string "\\?!" "?\\\\!" cmd)
(defun counsel--occur-cmd-find ()
(let ((cmd (format
"find . -maxdepth 1 | %s | xargs -I {} find {} -maxdepth 0 -ls"
(counsel--file-name-filter t))))
(counsel--cmd-to-dired-by-type "d" cmd)
" && "
(counsel--cmd-to-dired-by-type "f" cmd))))
(defun counsel--cmd-to-dired-by-type (type cmd)
(let ((exclude-dots
(if (string-match "^\\." ivy-text)
" | grep -v '/\\\\.'")))
" | grep"
(concat " -type " type exclude-dots " | grep") cmd)))
(defun counsel-find-file-occur ()
(require 'find-dired)
(cd ivy--directory)
(if counsel-find-file-occur-use-find
(format counsel-find-file-occur-cmd
(if (string-match-p "grep" counsel-find-file-occur-cmd)
;; for backwards compatibility
(counsel--elisp-to-pcre ivy--old-re)
(counsel--file-name-filter t)))))))
(defvar counsel-up-directory-level t
"Control whether `counsel-up-directory' goes up a level or always a directory.
If non-nil, then `counsel-up-directory' will remove the final level of the path.
For example: /a/long/path/file.jpg => /a/long/path/
/a/long/path/ => /a/long/
If nil, then `counsel-up-directory' will go up a directory.
For example: /a/long/path/file.jpg => /a/long/
/a/long/path/ => /a/long/")
(defun counsel-up-directory ()
"Go to the parent directory preselecting the current one.
If the current directory is remote and it's not possible to go up any
further, make the remote prefix editable.
See variable `counsel-up-directory-level'."
(let* ((cur-dir (directory-file-name (expand-file-name ivy--directory)))
(up-dir (file-name-directory cur-dir)))
(if (and (file-remote-p cur-dir) (string-equal cur-dir up-dir))
;; make the remote prefix editable
(setq ivy--old-cands nil)
(setq ivy--old-re nil)
(ivy-set-index 0)
(setq ivy--directory "")
(setq ivy--all-candidates nil)
(setq ivy-text "")
(insert up-dir))
(if (and counsel-up-directory-level (not (string= ivy-text "")))
(delete-region (line-beginning-position) (line-end-position))
(ivy--cd up-dir)
(setf (ivy-state-preselect ivy-last)
(file-name-as-directory (file-name-nondirectory cur-dir)))))))
(defun counsel-down-directory ()
"Descend into the current directory."
(defun counsel-at-git-issue-p ()
"When point is at an issue in a Git-versioned file, return the issue string."
(and (looking-at "#[0-9]+")
(or (eq (vc-backend buffer-file-name) 'Git)
(memq major-mode '(magit-commit-mode vc-git-log-view-mode))
(bound-and-true-p magit-commit-mode))
(match-string-no-properties 0)))
(defun counsel-github-url-p ()
"Return a Github issue URL at point."
(counsel-require-program "git")
(let ((url (counsel-at-git-issue-p)))
(when url
(let ((origin (shell-command-to-string
"git remote get-url origin"))
user repo)
(cond ((string-match "\\`\\([^/]+\\)/\\(.*\\)\\.git$"
(setq user (match-string 1 origin))
(setq repo (match-string 2 origin)))
((string-match "\\`\\([^/]+\\)/\\(.*\\)$"
(setq user (match-string 1 origin))
(setq repo (match-string 2 origin))))
(when user
(setq url (format ""
user repo (substring url 1))))))))
(defun counsel-emacs-url-p ()
"Return a Debbugs issue URL at point."
(counsel-require-program "git")
(let ((url (counsel-at-git-issue-p)))
(when url
(let ((origin (shell-command-to-string
"git remote get-url origin")))
(when (string-match "" origin)
(format ""
(substring url 1)))))))
(defvar counsel-url-expansions-alist nil
"Map of regular expressions to expansions.
This variable should take the form of a list of (REGEXP . FORMAT)
`counsel-url-expand' will expand the word at point according to
FORMAT for the first matching REGEXP. FORMAT can be either a
string or a function. If it is a string, it will be used as the
format string for the `format' function, with the word at point
as the next argument. If it is a function, it will be called
with the word at point as the sole argument.
For example, a pair of the form:
'(\"\\`BSERV-[[:digit:]]+\\'\" . \"\")
will expand to URL `'
when the word at point is BSERV-100.
If the format element is a function, more powerful
transformations are possible. As an example,
'(\"\\`issue\\([[:digit:]]+\\)\\'\" .
(lambda (word)
(concat \"\"
(match-string 1 word))))
trims the \"issue\" prefix from the word at point before creating the URL.")
(defun counsel-url-expand ()
"Expand word at point using `counsel-url-expansions-alist'.
The first pair in the list whose regexp matches the word at point
will be expanded according to its format. This function is
intended to be used in `ivy-ffap-url-functions' to browse the
result as a URL."
(let ((word-at-point (current-word)))
(when word-at-point
(lambda (pair)
(let ((regexp (car pair))
(formatter (cdr pair)))
(when (string-match regexp word-at-point)
(if (functionp formatter)
(funcall formatter word-at-point)
(format formatter word-at-point)))))
;;** `counsel-dired'
(declare-function dired "dired")
(defun counsel-dired (&optional initial-input)
"Forward to `dired'.
When INITIAL-INPUT is non-nil, use it in the minibuffer during completion."
"Dired (directory): " initial-input
(lambda (d) (dired (expand-file-name d)))
;;** `counsel-recentf'
(defvar recentf-list)
(declare-function recentf-mode "recentf")
(defun counsel-recentf ()
"Find a file on `recentf-list'."
(require 'recentf)
(ivy-read "Recentf: " (mapcar #'substring-no-properties recentf-list)
:action (lambda (f)
(find-file f)))
:require-match t
:caller 'counsel-recentf))
'(("j" find-file-other-window "other window")
("f" find-file-other-frame "other frame")
("x" counsel-find-file-extern "open externally")))
(defun counsel-buffer-or-recentf-candidates ()
"Return candidates for `counsel-buffer-or-recentf'."
(require 'recentf)
(let ((buffers
(delq nil
(mapcar (lambda (b)
(when (buffer-file-name b)
(buffer-file-name b)))
(cl-remove-if (lambda (f) (member f buffers))
(mapcar #'substring-no-properties recentf-list)))))
(defun counsel-buffer-or-recentf ()
"Find a buffer visiting a file or file on `recentf-list'."
(ivy-read "Buffer File or Recentf: " (counsel-buffer-or-recentf-candidates)
:action (lambda (s)
(if (bufferp s)
(switch-to-buffer s)
(find-file s))))
:require-match t
:caller 'counsel-buffer-or-recentf))
'(("j" find-file-other-window "other window")
("f" find-file-other-frame "other frame")
("x" counsel-find-file-extern "open externally")))
(defun counsel-buffer-or-recentf-transformer (var)
"Propertize VAR if it's a buffer visiting a file."
(if (member var (mapcar #'buffer-file-name (buffer-list)))
(ivy-append-face var 'ivy-highlight-face)
'counsel-buffer-or-recentf 'counsel-buffer-or-recentf-transformer)
;;** `counsel-bookmark'
(defcustom counsel-bookmark-avoid-dired nil
"If non-nil, open directory bookmarks with `counsel-find-file'.
By default `counsel-bookmark' opens a dired buffer for directories."
:type 'boolean)
(defvar bookmark-alist)
(declare-function bookmark-location "bookmark")
(declare-function bookmark-all-names "bookmark")
(declare-function bookmark-get-filename "bookmark")
(declare-function bookmark-maybe-load-default-file "bookmark")
(defun counsel-bookmark ()
"Forward to `bookmark-jump' or `bookmark-set' if bookmark doesn't exist."
(require 'bookmark)
(ivy-read "Create or jump to bookmark: "
:history 'bookmark-history
:action (lambda (x)
(cond ((and counsel-bookmark-avoid-dired
(member x (bookmark-all-names))
(file-directory-p (bookmark-location x)))
(let ((default-directory (bookmark-location x)))
((member x (bookmark-all-names))
(bookmark-jump x)))
(bookmark-set x))))
:caller 'counsel-bookmark))
(defun counsel--apply-bookmark-fn (fn)
"Return a function applying FN to a bookmark's location."
(lambda (bookmark)
(funcall fn (bookmark-location bookmark))))
`(("d" bookmark-delete "delete")
("e" bookmark-rename "edit")
("x" ,(counsel--apply-bookmark-fn #'counsel-find-file-extern)
"open externally")
("r" ,(counsel--apply-bookmark-fn #'counsel-find-file-as-root)
"open as root")))
;;** `counsel-bookmarked-directory'
(defun counsel-bookmarked-directory--candidates ()
"Get a list of bookmarked directories sorted by file path."
(sort (cl-remove-if-not
(delq nil (mapcar #'bookmark-get-filename bookmark-alist)))
(defun counsel-bookmarked-directory ()
"Ivy interface for bookmarked directories.
With a prefix argument, this command creates a new bookmark which points to the
current value of `default-directory'."
(require 'bookmark)
(ivy-read "Bookmarked directory: "
:caller 'counsel-bookmarked-directory
:action #'dired))
(ivy-set-actions 'counsel-bookmarked-directory
'(("j" dired-other-window "other window")
("x" counsel-find-file-extern "open externally")
("r" counsel-find-file-as-root "open as root")
("f" (lambda (dir)
(let ((default-directory dir))
(call-interactively #'find-file)))
;;** `counsel-file-register'
(defun counsel-file-register ()
"Search file in register.
You cannot use Emacs' normal register commands to create file
registers. Instead you must use the `set-register' function like
so: `(set-register ?i \"/home/eric/.emacs.d/init.el\")'. Now you
can use `C-x r j i' to open that file."
(ivy-read "File Register: "
;; Use the `register-alist' variable to filter out file
;; registers. Each entry for a file register will have the
;; following layout:
;; (NUMBER 'file . "string/path/to/file")
;; So we go through each entry and see if the `cadr' is
;; `eq' to the symbol `file'. If so then add the filename
;; (`cddr') which `ivy-read' will use for its choices.
(mapcar (lambda (register-alist-entry)
(if (eq 'file (cadr register-alist-entry))
(cddr register-alist-entry)))
:sort t
:require-match t
:history 'counsel-file-register
:caller 'counsel-file-register
:action (lambda (register-file)
(with-ivy-window (find-file register-file)))))
'(("j" find-file-other-window "other window")))
;;** `counsel-locate'
(defcustom counsel-locate-cmd (cond ((memq system-type '(darwin berkeley-unix))
((and (eq system-type 'windows-nt)
(executable-find "es.exe"))
"The function for producing a locate command string from the input.
The function takes a string - the current input, and returns a
string - the full shell command to run."
:type '(choice
(const :tag "Default" counsel-locate-cmd-default)
(const :tag "No regex" counsel-locate-cmd-noregex)
(const :tag "mdfind" counsel-locate-cmd-mdfind)
(const :tag "everything" counsel-locate-cmd-es)))
'(("x" counsel-locate-action-extern "xdg-open")
("r" counsel-find-file-as-root "open as root")
("d" counsel-locate-action-dired "dired")))
(counsel-set-async-exit-code 'counsel-locate 1 "Nothing found")
(defvar counsel-locate-history nil
"History for `counsel-locate'.")
(defun counsel-locate-action-extern (x)
"Pass X to `xdg-open' or equivalent command via the shell."
(interactive "FFile: ")
(if (and (eq system-type 'windows-nt)
(fboundp 'w32-shell-execute))
(w32-shell-execute "open" x)
(call-process-shell-command (format "%s %s"
(cl-case system-type
(darwin "open")
(cygwin "cygstart")
(t "xdg-open"))
(shell-quote-argument x))
nil 0)))
(defalias 'counsel-find-file-extern #'counsel-locate-action-extern)
(declare-function dired-jump "dired-x")
(defun counsel-locate-action-dired (x)
"Use `dired-jump' on X."
(dired-jump nil x))
(defun counsel-locate-cmd-default (input)
"Return a shell command based on INPUT."
(counsel-require-program "locate")
(format "locate -i --regex '%s'"
(ivy--regex input))))
(defun counsel-locate-cmd-noregex (input)
"Return a shell command based on INPUT."
(counsel-require-program "locate")
(format "locate -i '%s'" input))
(defun counsel-locate-cmd-mdfind (input)
"Return a shell command based on INPUT."
(counsel-require-program "mdfind")
(format "mdfind -name '%s'" input))
(defun counsel-locate-cmd-es (input)
"Return a shell command based on INPUT."
(counsel-require-program "es.exe")
(format "es.exe -i -r -p %s"
(ivy--regex input t))))
(defun counsel-locate-function (input)
"Call the \"locate\" shell command with INPUT."
(funcall counsel-locate-cmd input))
'("" "working..."))))
(defcustom counsel-locate-db-path "~/.local/mlocate.db"
"Location where to put the locatedb in case your home folder is encrypted."
:type 'file)
(defun counsel--locate-updatedb ()
(when (file-exists-p "~/.Private")
(let ((db-fname (expand-file-name counsel-locate-db-path)))
(setenv "LOCATE_PATH" db-fname)
(when (or (not (file-exists-p db-fname))
(> (time-to-seconds
(nth 5 (file-attributes db-fname))))
(message "Updating %s..." db-fname)
"updatedb" "-l" "0" "-o" db-fname "-U" (expand-file-name "~"))))))
(defun counsel-locate (&optional initial-input)
"Call the \"locate\" shell command.
INITIAL-INPUT can be given as the initial minibuffer input."
(ivy-read "Locate: " #'counsel-locate-function
:initial-input initial-input
:dynamic-collection t
:history 'counsel-locate-history
:action (lambda (file)
(when file
(concat (file-remote-p default-directory) file)))))
:unwind #'counsel-delete-process
:caller 'counsel-locate))
;;** `counsel-fzf'
(defvar counsel-fzf-cmd "fzf -f \"%s\""
"Command for `counsel-fzf'.")
(defvar counsel--fzf-dir nil
"Store the base fzf directory.")
(defvar counsel-fzf-dir-function 'counsel-fzf-dir-function-projectile
"Function that returns a directory for fzf to use.")
(defun counsel-fzf-dir-function-projectile ()
(if (and
(fboundp 'projectile-project-p)
(fboundp 'projectile-project-root)
(defun counsel-fzf-function (str)
(let ((default-directory counsel--fzf-dir))
(setq ivy--old-re (ivy--regex-fuzzy str))
(format counsel-fzf-cmd str)))
(defun counsel-fzf (&optional initial-input initial-directory fzf-prompt)
"Open a file using the fzf shell command.
INITIAL-INPUT can be given as the initial minibuffer input.
INITIAL-DIRECTORY, if non-nil, is used as the root directory for search.
FZF-PROMPT, if non-nil, is passed as `ivy-read' prompt argument."
(let ((fzf-basename (car (split-string counsel-fzf-cmd))))
(list nil
(when current-prefix-arg
(read-directory-name (concat
" in directory: "))))))
(counsel-require-program counsel-fzf-cmd)
(setq counsel--fzf-dir
(or initial-directory
(funcall counsel-fzf-dir-function)))
(ivy-read (or fzf-prompt "fzf: ")
:initial-input initial-input
:re-builder #'ivy--regex-fuzzy
:dynamic-collection t
:action #'counsel-fzf-action
:unwind #'counsel-delete-process
:caller 'counsel-fzf))
(defun counsel-fzf-action (x)
"Find file X in current fzf directory."
(let ((default-directory counsel--fzf-dir))
(find-file x))))
(defun counsel-fzf-occur ()
"Occur function for `counsel-fzf' using `counsel-cmd-to-dired'."
(cd counsel--fzf-dir)
"%s --print0 | xargs -0 ls"
(format counsel-fzf-cmd ivy-text)))))
(ivy-set-occur 'counsel-fzf 'counsel-fzf-occur)
'(("x" counsel-locate-action-extern "xdg-open")
("d" counsel-locate-action-dired "dired")))
(counsel-set-async-exit-code 'counsel-fzf 1 "Nothing found")
;;** `counsel-dpkg'
(defun counsel-dpkg ()
"Call the \"dpkg\" shell command."
(counsel-require-program "dpkg")
(let ((cands (mapcar
(lambda (x)
(let ((y (split-string x " +")))
(cons (format "%-40s %s"
(nth 1 y) 40)
(nth 4 y))
(mapconcat #'identity y " "))))
(shell-command-to-string "dpkg -l | tail -n+6") "\n" t))))
(ivy-read "dpkg: " cands
:action (lambda (x)
(message (cdr x)))
:caller 'counsel-dpkg)))
;;** `counsel-rpm'
(defun counsel-rpm ()
"Call the \"rpm\" shell command."
(counsel-require-program "rpm")
(let ((cands (mapcar
(lambda (x)
(let ((y (split-string x "|")))
(cons (format "%-40s %s"
(nth 0 y) 40)
(nth 1 y))
(mapconcat #'identity y " "))))
(shell-command-to-string "rpm -qa --qf \"%{NAME}|%{SUMMARY}\\n\"") "\n" t))))
(ivy-read "rpm: " cands
:action (lambda (x)
(message (cdr x)))
:caller 'counsel-rpm)))
(defun counsel--find-return-list (args)
(unless (listp args)
(user-error "`counsel-file-jump-args' is a list now, please customize accordingly."))
(cons find-program args)
(lambda ()
(let (files)
(goto-char (point-min))
(while (< (point) (point-max))
(when (looking-at "\\./")
(goto-char (match-end 0)))
(push (buffer-substring (point) (line-end-position)) files)
(beginning-of-line 2))
(nreverse files)))))
(defcustom counsel-file-jump-args (split-string ". -name .git -prune -o -type f -print")
"Arguments for the `find-command' when using `counsel-file-jump'."
:type '(repeat string))
;;** `counsel-file-jump'
(defun counsel-file-jump (&optional initial-input initial-directory)
"Jump to a file below the current directory.
List all files within the current directory or any of its sub-directories.
INITIAL-INPUT can be given as the initial minibuffer input.
INITIAL-DIRECTORY, if non-nil, is used as the root directory for search."
(list nil
(when current-prefix-arg
(read-directory-name "From directory: "))))
(counsel-require-program find-program)
(let ((default-directory (or initial-directory default-directory)))
(ivy-read "Find file: "
(counsel--find-return-list counsel-file-jump-args)
:matcher #'counsel--find-file-matcher
:initial-input initial-input
:action #'find-file
:preselect (counsel--preselect-file)
:require-match 'confirm-after-completion
:history 'file-name-history
:keymap counsel-find-file-map
:caller 'counsel-file-jump)))
`(("d" ,(lambda (x)
(dired (or (file-name-directory x) default-directory)))
"open in dired")))
(defcustom counsel-dired-jump-args (split-string ". -name .git -prune -o -type d -print")
"Arguments for the `find-command' when using `counsel-dired-jump'."
:type '(repeat string))
;;** `counsel-dired-jump'
(defun counsel-dired-jump (&optional initial-input initial-directory)
"Jump to a directory (see `dired-jump') below the current directory.
List all sub-directories within the current directory.
INITIAL-INPUT can be given as the initial minibuffer input.
INITIAL-DIRECTORY, if non-nil, is used as the root directory for search."
(list nil
(when current-prefix-arg
(read-directory-name "From directory: "))))
(counsel-require-program find-program)
(let ((default-directory (or initial-directory default-directory)))
(ivy-read "Find directory: "
(counsel--find-return-list counsel-dired-jump-args))
:matcher #'counsel--find-file-matcher
:initial-input initial-input
:action (lambda (d) (dired-jump nil (expand-file-name d)))
:history 'file-name-history
:keymap counsel-find-file-map
:caller 'counsel-dired-jump)))
;;* Grep
(defun counsel--grep-mode-occur (git-grep-dir-is-file)
"Generate a custom occur buffer for grep like commands.
If GIT-GREP-DIR-IS-FILE is t, then `ivy-state-directory' is treated as a full
path to a file rather than a directory (e.g. for `counsel-grep-occur').
This function expects that the candidates have already been filtered.
It applies no filtering to ivy--all-candidates."
(unless (eq major-mode 'ivy-occur-grep-mode)
(let ((directory
(if git-grep-dir-is-file
(file-name-directory (ivy-state-directory ivy-last))
(ivy-state-directory ivy-last))))
(setq default-directory directory)
;; Need precise number of header lines for `wgrep' to work.
(insert (format "-*- mode:grep; default-directory: %S -*-\n\n\n" default-directory))
(insert (format "%d candidates:\n" (length ivy--all-candidates)))
(mapcar #'counsel--normalize-grep-match ivy--all-candidates))))
;;** `counsel-ag'
(defvar counsel-ag-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-l") 'ivy-call-and-recenter)
(define-key map (kbd "M-q") 'counsel-git-grep-query-replace)
(define-key map (kbd "C-'") 'swiper-avy)
(define-key map (kbd "C-x C-d") 'counsel-cd)
(defcustom counsel-ag-base-command
(if (memq system-type '(ms-dos windows-nt))
"ag --vimgrep %s"
"ag --nocolor --nogroup %s")
"Format string to use in `counsel-ag-function' to construct the command.
The %s will be replaced by optional extra ag arguments followed by the
regex string."
:type 'string)
(defvar counsel-ag-command nil)
(defvar counsel--grep-tool-look-around t)
(defvar counsel--regex-look-around nil)
(counsel-set-async-exit-code 'counsel-ag 1 "No matches found")
(ivy-set-occur 'counsel-ag 'counsel-ag-occur)
(ivy-set-display-transformer 'counsel-ag 'counsel-git-grep-transformer)
(defconst counsel--command-args-separator "-- ")
(defun counsel--split-command-args (arguments)
"Split ARGUMENTS into its switches and search-term parts.
Return pair of corresponding strings (SWITCHES . SEARCH-TERM)."
(let ((switches "")
(search-term arguments))
(when (string-prefix-p "-" arguments)
(let ((index (string-match counsel--command-args-separator arguments)))
(when index
(setq search-term
(substring arguments (+ (length counsel--command-args-separator) index)))
(setq switches (substring arguments 0 index)))))
(cons switches search-term)))
(defun counsel--format-ag-command (extra-args needle)
"Construct a complete `counsel-ag-command' as a string.
EXTRA-ARGS is a string of the additional arguments.
NEEDLE is the search string."
(format counsel-ag-command
(if (string-match " \\(--\\) " extra-args)
(replace-match needle t t extra-args 1)
(concat extra-args " " needle))))
(defun counsel--grep-regex (str)
(setq ivy--old-re
(funcall ivy--regex-function str))
(defun counsel--ag-extra-switches (regex)
"Get additional switches needed for look-arounds."
(and (stringp counsel--regex-look-around)
;; using look-arounds
(string-match-p "\\`\\^(\\?[=!]" regex)
(concat " " counsel--regex-look-around " ")))
(defun counsel-ag-function (string)
"Grep in the current directory for STRING."
(let* ((command-args (counsel--split-command-args string))
(search-term (cdr command-args)))
(let ((ivy-text search-term))
(let* ((default-directory (ivy-state-directory ivy-last))
(regex (counsel--grep-regex search-term))
(switches (concat (car command-args)
(counsel--ag-extra-switches regex)
(and (ivy--case-fold-p string) " -i "))))
(counsel--async-command (counsel--format-ag-command
(shell-quote-argument regex)))
(cl-defun counsel-ag (&optional initial-input initial-directory extra-ag-args ag-prompt
&key caller)
"Grep for a string in the current directory using ag.
INITIAL-INPUT can be given as the initial minibuffer input.
INITIAL-DIRECTORY, if non-nil, is used as the root directory for search.
EXTRA-AG-ARGS string, if non-nil, is appended to `counsel-ag-base-command'.
AG-PROMPT, if non-nil, is passed as `ivy-read' prompt argument.
CALLER is passed to `ivy-read'."
(setq counsel-ag-command counsel-ag-base-command)
(setq counsel--regex-look-around counsel--grep-tool-look-around)
(counsel-require-program counsel-ag-command)
(when current-prefix-arg
(setq initial-directory
(or initial-directory
(read-directory-name (concat
(car (split-string counsel-ag-command))
" in directory: "))))
(setq extra-ag-args
(or extra-ag-args
(read-from-minibuffer (format
"%s args: "
(car (split-string counsel-ag-command)))))))
(setq counsel-ag-command (counsel--format-ag-command (or extra-ag-args "") "%s"))
(let ((default-directory (or initial-directory
(ivy-read (or ag-prompt
(concat (car (split-string counsel-ag-command)) ": "))
:initial-input initial-input
:dynamic-collection t
:keymap counsel-ag-map
:history 'counsel-git-grep-history
:action #'counsel-git-grep-action
:unwind (lambda ()
:caller (or caller 'counsel-ag))))
(defun counsel-cd ()
"Change the directory for the currently running Ivy command."
(let ((input ivy-text)
(new-dir (read-directory-name "cd: ")))
(let ((default-directory new-dir))
(funcall (ivy-state-caller ivy-last) input)))))
(cl-pushnew 'counsel-ag ivy-highlight-grep-commands)
(defun counsel-grep-like-occur (cmd-template)
(unless (eq major-mode 'ivy-occur-grep-mode)
(setq default-directory (ivy-state-directory ivy-last)))
(setq ivy-text
(and (string-match "\"\\(.*\\)\"" (buffer-name))
(match-string 1 (buffer-name))))
(let* ((command-args (counsel--split-command-args ivy-text))
(regex (counsel--grep-regex (cdr command-args)))
(switches (concat (car command-args)
(counsel--ag-extra-switches regex)))
(cmd (format cmd-template
(shell-quote-argument regex))))
(cands (counsel--split-string (shell-command-to-string cmd))))
;; Need precise number of header lines for `wgrep' to work.
(insert (format "-*- mode:grep; default-directory: %S -*-\n\n\n"
(insert (format "%d candidates:\n" (length cands)))
(mapcar #'counsel--normalize-grep-match cands))))
(defun counsel-ag-occur ()
"Generate a custom occur buffer for `counsel-ag'."
;;** `counsel-pt'
(defcustom counsel-pt-base-command "pt --nocolor --nogroup -e %s"
"Alternative to `counsel-ag-base-command' using pt."
:type 'string)
(defun counsel-pt (&optional initial-input)
"Grep for a string in the current directory using pt.
INITIAL-INPUT can be given as the initial minibuffer input.
This uses `counsel-ag' with `counsel-pt-base-command' instead of
(let ((counsel-ag-base-command counsel-pt-base-command)
(counsel--grep-tool-look-around nil))
(counsel-ag initial-input :caller 'counsel-pt)))
(cl-pushnew 'counsel-pt ivy-highlight-grep-commands)
;;** `counsel-ack'
(defcustom counsel-ack-base-command
(or (executable-find "ack-grep") "ack"))
" --nocolor --nogroup %s")
"Alternative to `counsel-ag-base-command' using ack."
:type 'string)
(defun counsel-ack (&optional initial-input)
"Grep for a string in the current directory using ack.
INITIAL-INPUT can be given as the initial minibuffer input.
This uses `counsel-ag' with `counsel-ack-base-command' replacing
(let ((counsel-ag-base-command counsel-ack-base-command)
(counsel--grep-tool-look-around t))
(counsel-ag initial-input :caller 'counsel-ack)))
;;** `counsel-rg'
(defcustom counsel-rg-base-command
(if (memq system-type '(ms-dos windows-nt))
"rg --with-filename --no-heading --line-number --color never %s ."
"rg --with-filename --no-heading --line-number --color never %s")
"Alternative to `counsel-ag-base-command' using ripgrep.
Note: don't use single quotes for the regex."
:type 'string)
(counsel-set-async-exit-code 'counsel-rg 1 "No matches found")
(ivy-set-occur 'counsel-rg 'counsel-ag-occur)
(ivy-set-display-transformer 'counsel-rg 'counsel-git-grep-transformer)
(defun counsel--rg-targets ()
"Return a list of files to operate on, based on `dired-mode' marks."
(if (eq major-mode 'dired-mode)
(let ((files
(dired-get-marked-files 'no-dir nil nil t)))
(if (and (null (cdr files))
(not (when (string-match-p "\\*ivy-occur" (buffer-name))
(setq files (dired-get-marked-files 'no-dir))
" "
(mapconcat #'shell-quote-argument (delq t files) " "))))
(defun counsel-rg (&optional initial-input initial-directory extra-rg-args rg-prompt)
"Grep for a string in the current directory using rg.
INITIAL-INPUT can be given as the initial minibuffer input.
INITIAL-DIRECTORY, if non-nil, is used as the root directory for search.
EXTRA-RG-ARGS string, if non-nil, is appended to `counsel-rg-base-command'.
RG-PROMPT, if non-nil, is passed as `ivy-read' prompt argument.
Example input with inclusion and exclusion file patterns:
-g*.py -g!*test* -- ..."
(let ((counsel-ag-base-command
(concat counsel-rg-base-command (counsel--rg-targets)))
(let ((rg (car (split-string counsel-rg-base-command)))
(switch "--pcre2"))
(and (eq 0 (call-process rg nil nil nil switch "--version"))
(counsel-ag initial-input initial-directory extra-rg-args rg-prompt
:caller 'counsel-rg)))
(cl-pushnew 'counsel-rg ivy-highlight-grep-commands)
;;** `counsel-grep'
(defvar counsel-grep-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-l") 'ivy-call-and-recenter)
(define-key map (kbd "M-q") 'swiper-query-replace)
(define-key map (kbd "C-'") 'swiper-avy)
(defcustom counsel-grep-base-command "grep -E -n -e %s %s"
"Format string used by `counsel-grep' to build a shell command.
It should contain two %-sequences (see function `format') to be
substituted by the search regexp and file, respectively. Neither
%-sequence should be contained in single quotes."
:type 'string)
(defvar counsel-grep-command nil)
(defun counsel-grep-function (string)
"Grep in the current directory for STRING."
(let ((regex (counsel--elisp-to-pcre
(setq ivy--old-re
(ivy--regex string)))))
(format counsel-grep-command (shell-quote-argument regex)))
(defun counsel-grep-action (x)
"Go to candidate X."
(let ((default-directory
(ivy-state-directory ivy-last)))
file-name line-number)
(when (cond ((string-match "\\`\\([0-9]+\\):\\(.*\\)\\'" x)
(setq file-name (buffer-file-name (ivy-state-buffer ivy-last)))
(setq line-number (match-string-no-properties 1 x)))
((string-match "\\`\\([^:]+\\):\\([0-9]+\\):\\(.*\\)\\'" x)
(setq file-name (match-string-no-properties 1 x))
(setq line-number (match-string-no-properties 2 x))))
;; If the file buffer is already open, just get it. Prevent doing
;; `find-file', as that file could have already been opened using
;; `find-file-literally'.
(with-current-buffer (or (get-file-buffer file-name)
(find-file file-name))
(setq line-number (string-to-number line-number))
(if counsel-grep-last-line
(forward-line (- line-number counsel-grep-last-line))
(goto-char (point-min))
(forward-line (1- line-number)))
(setq counsel-grep-last-line line-number)
(when (re-search-forward (ivy--regex ivy-text t) (line-end-position) t)
(when swiper-goto-start-of-match
(goto-char (match-beginning 0))))
(run-hooks 'counsel-grep-post-action-hook)
(if (eq ivy-exit 'done)
(isearch-range-invisible (line-beginning-position)
(swiper--add-overlays (ivy--regex ivy-text))))))))
(defun counsel-grep-occur ()
"Generate a custom occur buffer for `counsel-grep'."
"grep -niE %%s %s /dev/null"
(ivy-state-buffer ivy-last)))))))
(ivy-set-occur 'counsel-grep 'counsel-grep-occur)
(counsel-set-async-exit-code 'counsel-grep 1 "")
(defvar counsel-grep-history nil
"History for `counsel-grep'.")
(defun counsel-grep (&optional initial-input)
"Grep for a string in the file visited by the current buffer.
When non-nil, INITIAL-INPUT is the initial search pattern."
(unless buffer-file-name
(user-error "Current buffer is not visiting a file"))
(counsel-require-program counsel-grep-base-command)
(setq counsel-grep-last-line nil)
(setq counsel-grep-command
(format counsel-grep-base-command
"%s" (shell-quote-argument
(let ((default-directory (file-name-directory buffer-file-name))
(init-point (point))
(setq res (ivy-read "grep: " 'counsel-grep-function
:initial-input initial-input
:dynamic-collection t
(when (< (- (line-end-position) (line-beginning-position)) 300)
(format "%d:%s"
:keymap counsel-grep-map
:history 'counsel-grep-history
:update-fn 'auto
:re-builder #'ivy--regex
:action #'counsel-grep-action
:unwind (lambda ()
:caller 'counsel-grep))
(unless res
(goto-char init-point)))))
(defun counsel-grep-backward (&optional initial-input)
"Grep for a string in the file visited by the current buffer going
backward similar to `swiper-backward'. When non-nil, INITIAL-INPUT is
the initial search pattern."
(let ((ivy-index-functions-alist
'((counsel-grep . ivy-recompute-index-swiper-async-backward))))
(counsel-grep initial-input)))
;;** `counsel-grep-or-swiper'
(defcustom counsel-grep-swiper-limit 300000
"Buffer size threshold for `counsel-grep-or-swiper'.
When the number of characters in a buffer exceeds this threshold,
`counsel-grep' will be used instead of `swiper'."
:type 'integer)
(defcustom counsel-grep-use-swiper-p #'counsel-grep-use-swiper-p-default
"When this function returns non-nil, call `swiper', else `counsel-grep'."
:type '(choice
(const :tag "Rely on `counsel-grep-swiper-limit'."
(const :tag "Always use `counsel-grep'." ignore)
(function :tag "Custom")))
(defun counsel-grep-use-swiper-p-default ()
(<= (buffer-size)
(/ counsel-grep-swiper-limit
(if (eq major-mode 'org-mode) 4 1))))
(defun counsel-grep-or-swiper (&optional initial-input)
"Call `swiper' for small buffers and `counsel-grep' for large ones.
When non-nil, INITIAL-INPUT is the initial search pattern."
(if (or (not buffer-file-name)
(file-remote-p buffer-file-name))
(jka-compr-get-compression-info buffer-file-name)
(funcall counsel-grep-use-swiper-p))
(swiper initial-input)
(when (file-writable-p buffer-file-name)
(counsel-grep initial-input)))
;;** `counsel-grep-or-swiper-backward'
(defun counsel-grep-or-swiper-backward (&optional initial-input)
"Call `swiper-backward' for small buffers and `counsel-grep-backward' for
large ones. When non-nil, INITIAL-INPUT is the initial search pattern."
(let ((ivy-index-functions-alist
'((swiper . ivy-recompute-index-swiper-backward)
(counsel-grep . ivy-recompute-index-swiper-async-backward))))
(counsel-grep-or-swiper initial-input)))
;;** `counsel-recoll'
(defun counsel-recoll-function (str)
"Run recoll for STR."
(format "recoll -t -b %s"
(shell-quote-argument str)))
;; This command uses the recollq command line tool that comes together
;; with the recoll (the document indexing database) source:
;; You need to build it yourself (together with recoll):
;; cd ./query && make && sudo cp recollq /usr/local/bin
;; You can try the GUI version of recoll with:
;; sudo apt-get install recoll
;; Unfortunately, that does not install recollq.
(defun counsel-recoll (&optional initial-input)
"Search for a string in the recoll database.
You'll be given a list of files that match.
Selecting a file will launch `swiper' for that file.
INITIAL-INPUT can be given as the initial minibuffer input."
(counsel-require-program "recoll")
(ivy-read "recoll: " 'counsel-recoll-function
:initial-input initial-input
:dynamic-collection t
:history 'counsel-git-grep-history
:action (lambda (x)
(when (string-match "file://\\(.*\\)\\'" x)
(let ((file-name (match-string 1 x)))
(find-file file-name)
(unless (string-match "pdf$" x)
(swiper ivy-text)))))
:unwind #'counsel-delete-process
:caller 'counsel-recoll))
;;* Org
;;** `counsel-org-tag'
(defvar counsel-org-tags nil
"Store the current list of tags.")
(defvar org-outline-regexp)
(defvar org-indent-mode)
(defvar org-indent-indentation-per-level)
(defvar org-tags-column)
(declare-function org-get-tags-string "org")
(declare-function org-get-tags "org")
(declare-function org-make-tag-string "org")
(declare-function org-move-to-column "org-compat")
(defun counsel--org-make-tag-string ()
(if (fboundp #'org-make-tag-string)
;; >= Org 9.2
(org-make-tag-string (counsel--org-get-tags))
(defun counsel-org-change-tags (tags)
"Change tags of current org headline to TAGS."
(let ((current (counsel--org-make-tag-string))
(col (current-column))
;; Insert new tags at the correct column
(beginning-of-line 1)
(setq level (or (and (looking-at org-outline-regexp)
(- (match-end 0) (point) 1))
((and (equal current "") (equal tags "")))
(concat "\\([ \t]*" (regexp-quote current) "\\)[ \t]*$")
(line-end-position) t)
(if (equal tags "")
(match-beginning 0)
(match-end 0))
(goto-char (match-beginning 0))
(let* ((c0 (current-column))
;; compute offset for the case of org-indent-mode active
(di (if (bound-and-true-p org-indent-mode)
(* (1- org-indent-indentation-per-level) (1- level))
(p0 (if (equal (char-before) ?*) (1+ (point)) (point)))
(tc (+ org-tags-column (if (> org-tags-column 0) (- di) di)))
(c1 (max (1+ c0) (if (> tc 0) tc (- (- tc) (string-width tags)))))
(rpl (concat (make-string (max 0 (- c1 c0)) ?\ ) tags)))
(replace-match rpl t t)
(and c0 indent-tabs-mode (tabify p0 (point)))
(t (error "Tags alignment failed")))
(org-move-to-column col)))
(defun counsel-org--set-tags ()
"Set tags of current org headline to `counsel-org-tags'."
(if counsel-org-tags
(format ":%s:"
(mapconcat #'identity counsel-org-tags ":"))
(defvar org-agenda-bulk-marked-entries)
(declare-function org-get-at-bol "org")
(declare-function org-agenda-error "org-agenda")
(defun counsel-org-tag-action (x)
"Add tag X to `counsel-org-tags'.
If X is already part of the list, remove it instead. Quit the selection if
X is selected by either `ivy-done', `ivy-alt-done' or `ivy-immediate-done',
otherwise continue prompting for tags."
(if (member x counsel-org-tags)
(setq counsel-org-tags (delete x counsel-org-tags)))
(unless (equal x "")
(setq counsel-org-tags (append counsel-org-tags (list x)))
(unless (member x ivy--all-candidates)
(setq ivy--all-candidates (append ivy--all-candidates (list x))))))
(let ((prompt (counsel-org-tag-prompt)))
(setf (ivy-state-prompt ivy-last) prompt)
(setq ivy--prompt (concat "%-4d " prompt)))
(cond ((memq this-command '(ivy-done
(if (eq major-mode 'org-agenda-mode)
(if (null org-agenda-bulk-marked-entries)
(let ((hdmarker (or (org-get-at-bol 'org-hd-marker)
(with-current-buffer (marker-buffer hdmarker)
(goto-char hdmarker)
(let ((add-tags (copy-sequence counsel-org-tags)))
(dolist (m org-agenda-bulk-marked-entries)
(with-current-buffer (marker-buffer m)
(goto-char m)
(setq counsel-org-tags
(append (counsel--org-get-tags) add-tags)))
((eq this-command 'ivy-call)
(with-selected-window (active-minibuffer-window)
(defun counsel-org-tag-prompt ()
"Return prompt for `counsel-org-tag'."
(format "Tags (%s): "
(mapconcat #'identity counsel-org-tags ", ")))
(defvar org-setting-tags)
(defvar org-last-tags-completion-table)
(defvar org-tag-persistent-alist)
(defvar org-tag-alist)
(defvar org-complete-tags-always-offer-all-agenda-tags)
(declare-function org-at-heading-p "org")
(declare-function org-back-to-heading "org")
(declare-function org-get-buffer-tags "org")
(declare-function org-global-tags-completion-table "org")
(declare-function org-agenda-files "org")
(declare-function org-agenda-set-tags "org-agenda")
(declare-function org-tags-completion-function "org")
(defun counsel--org-get-tags ()
(delete "" (condition-case nil
(org-get-tags nil t)
(error (org-get-tags)))))
(defun counsel-org-tag ()
"Add or remove tags in `org-mode'."
(if (eq major-mode 'org-agenda-mode)
(if org-agenda-bulk-marked-entries
(setq counsel-org-tags nil)
(let ((hdmarker (or (org-get-at-bol 'org-hd-marker)
(with-current-buffer (marker-buffer hdmarker)
(goto-char hdmarker)
(setq counsel-org-tags (counsel--org-get-tags)))))
(unless (org-at-heading-p)
(org-back-to-heading t))
(setq counsel-org-tags (counsel--org-get-tags)))
(let ((org-last-tags-completion-table
(append (and (or org-complete-tags-always-offer-all-agenda-tags
(eq major-mode 'org-agenda-mode))
(unless (boundp 'org-current-tag-alist)
(or (if (boundp 'org-current-tag-alist)
(ivy-read (counsel-org-tag-prompt)
(lambda (str _pred _action)
(all-completions str #'org-tags-completion-function)))
:history 'org-tags-history
:action #'counsel-org-tag-action
:caller 'counsel-org-tag))))
(defvar org-version)
(defun counsel-org-tag-agenda ()
"Set tags for the current agenda item."
(cl-letf (((symbol-function (if (version< org-version "9.2")
(define-obsolete-variable-alias 'counsel-org-goto-display-tags
'counsel-org-headline-display-tags "0.10.0")
(defcustom counsel-org-headline-display-tags nil
"If non-nil, display tags in matched `org-mode' headlines."
:type 'boolean)
(define-obsolete-variable-alias 'counsel-org-goto-display-todo
'counsel-org-headline-display-todo "0.10.0")
(defcustom counsel-org-headline-display-todo nil
"If non-nil, display todo keywords in matched `org-mode' headlines."
:type 'boolean)
(defcustom counsel-org-headline-display-priority nil
"If non-nil, display priorities in matched `org-mode' headlines."
:type 'boolean)
(declare-function org-get-heading "org")
(declare-function org-goto-marker-or-bmk "org")
(declare-function outline-next-heading "outline")
(defalias 'counsel-org-goto #'counsel-outline)
(defcustom counsel-org-goto-all-outline-path-prefix nil
"Prefix for outline candidates in `counsel-org-goto-all'."
:type '(choice
(const :tag "None" nil)
(const :tag "File name" file-name)
(const :tag "File name (nondirectory part)" file-name-nondirectory)
(const :tag "Buffer name" buffer-name)))
(defun counsel-org-goto-all--outline-path-prefix ()
(cl-case counsel-org-goto-all-outline-path-prefix
(file-name buffer-file-name)
(file-name-nondirectory (file-name-nondirectory buffer-file-name))
(buffer-name (buffer-name))))
(defvar counsel-outline-settings
:outline-regexp ";;[;*]+[\s\t]+"
:outline-level counsel-outline-level-emacs-lisp)
:outline-title counsel-outline-title-org
:action counsel-org-goto-action
:history counsel-org-goto-history
:caller counsel-org-goto)
;; markdown-mode package
:outline-title counsel-outline-title-markdown)
;; Built-in mode or AUCTeX package
:outline-title counsel-outline-title-latex))
"Alist mapping major modes to their `counsel-outline' settings.
Each entry is a pair (MAJOR-MODE . PLIST). `counsel-outline'
checks whether an entry exists for the current buffer's
MAJOR-MODE and, if so, loads the settings specified by PLIST
instead of the default settings. The following settings are
- `:outline-regexp' is a regexp to match the beginning of an
outline heading. It is only checked at the start of a line and
so need not start with \"^\".
Defaults to the value of the variable `outline-regexp'.
- `:outline-level' is a function of no arguments which computes
the level of an outline heading. It is called with point at
the beginning of `outline-regexp' and with the match data
corresponding to `outline-regexp'.
Defaults to the value of the variable `outline-level'.
- `:outline-title' is a function of no arguments which returns
the title of an outline heading. It is called with point at
the end of `outline-regexp' and with the match data
corresponding to `outline-regexp'.
Defaults to the function `counsel-outline-title'.
- `:action' is a function of one argument, the selected outline
heading to jump to. This setting corresponds directly to its
eponymous `ivy-read' keyword, as used by `counsel-outline', so
the type of the function's argument depends on the value
returned by `counsel-outline-candidates'.
Defaults to the function `counsel-outline-action'.
- `:history' is a history list, usually a symbol representing a
history list variable. It corresponds directly to its
eponymous `ivy-read' keyword, as used by `counsel-outline'.
Defaults to the symbol `counsel-outline-history'.