Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
;;; counsel.el --- Various completion functions using Ivy -*- lexical-binding: t -*-
;; Copyright (C) 2015-2021 Free Software Foundation, Inc.
;; Author: Oleh Krehel <ohwoeowho@gmail.com>
;; URL: https://github.com/abo-abo/swiper
;; Version: 0.13.4
;; Package-Requires: ((emacs "24.5") (ivy "0.13.4") (swiper "0.13.4"))
;; 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
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; For a full copy of the GNU General Public License
;; see <https://www.gnu.org/licenses/>.
;;; 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 'ivy)
(require 'swiper)
(require 'compile)
(require 'dired)
(eval-when-compile
(require 'subr-x))
(defgroup counsel nil
"Completion functions using Ivy."
:group 'matching
:prefix "counsel-")
;;* Utility
(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))))
(concat
"^"
(mapconcat
(lambda (pair)
(let ((subexp (counsel--elisp-to-pcre (car pair))))
(format "(?%c.*%s)"
(if (cdr pair) ?= ?!)
subexp)))
regex
""))
(mapconcat
(lambda (pair)
(let ((subexp (counsel--elisp-to-pcre (car pair))))
(if (string-match-p "|" subexp)
(format "(?:%s)" subexp)
subexp)))
(cl-remove-if-not #'cdr regex)
".*"))
(replace-regexp-in-string
"\\\\[(){}|`']\\|[()]"
(lambda (s)
(or (cdr (assoc s '(("\\(" . "(")
("\\)" . ")")
("(" . "\\(")
(")" . "\\)")
("\\{" . "{")
("\\}" . "}")
("\\|" . "|")
("\\`" . "^")
("\\'" . "$"))))
(error
"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."
(file-name-as-directory
(file-name-nondirectory
(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)))
(put-text-property
0 1 'display
(concat prefix (substring str 0 1))
str)
str))
(defalias 'counsel--executable-find
;; Gained optional argument in 27.1.
(if (>= emacs-major-version 27)
#'executable-find
(lambda (command &optional _remote)
(executable-find command)))
"Compatibility shim for `executable-find'.")
(defun counsel-require-program (cmd &optional noerror)
"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.
When NOERROR is non-nil, return nil instead of raising an error."
(unless (and (stringp cmd) (string-prefix-p " " cmd))
(let ((program (if (listp cmd)
(car cmd)
(car (split-string cmd)))))
(or (and (stringp program)
(not (string= program ""))
(counsel--executable-find program t))
(unless noerror
(user-error "Required program \"%s\" not found in your path" program))))))
(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))))
(ivy-add-prompt-count
(replace-regexp-in-string ; Insert dir before any trailing colon.
"\\(?:: ?\\)?\\'" dir (ivy-state-prompt ivy-last) t t))))
(defalias 'counsel--flatten
;; Added in Emacs 27.1
(if (fboundp 'flatten-tree)
#'flatten-tree
(lambda (tree)
(let (elems)
(while (consp tree)
(let ((elem (pop tree)))
(while (consp elem)
(push (cdr elem) tree)
(setq elem (car elem)))
(if elem (push elem elems))))
(if tree (push tree elems))
(nreverse elems))))
"Compatibility shim for `flatten-tree'.")
(defun counsel--format (formatter &rest args)
"Like `format' but FORMATTER can be a list.
When FORMATTER is a list, only `%s' is replaced with ARGS.
Return a list or string depending on input."
(cond
((listp formatter)
(counsel--flatten (mapcar
(lambda (it) (if (equal it "%s") (pop args) it))
formatter)))
(t (apply #'format formatter args))))
;;* 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.")
(defvar counsel--async-timer nil
"Timer used to dispose `counsel--async-command.")
(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
descriptions.")
(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
cmd
(plist-put plist number str)))))
(defvar counsel-async-split-string-re-alist '((t . "[\r\n]"))
"Store the regexp for splitting shell command output.")
(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-1'.")
(defun counsel--async-command-1 (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',
respectively."
(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))
proc))
(defcustom counsel-async-command-delay 0
"Number of seconds to wait before spawning another async command."
:type 'number)
(defun counsel--async-command (&rest args)
"Like `counsel--async-command-1', with same ARGS, but debounced.
Calls to `counsel--async-command-1' are separated by at least
`counsel-async-command-delay' seconds, so as to avoid issues
caused by spawning too many subprocesses too quickly."
(if (zerop counsel-async-command-delay)
(apply #'counsel--async-command-1 args)
(when counsel--async-timer
(cancel-timer counsel--async-timer))
(setq counsel--async-timer
(apply #'run-with-timer
counsel-async-command-delay
nil
#'counsel--async-command-1
args))))
(defun counsel--split-string (&optional str)
(split-string
(or str (buffer-string))
(ivy-alist-setting counsel-async-split-string-re-alist)
t))
(defun counsel--sync-sentinel-on-exit (process)
(if (zerop (process-exit-status process))
(let ((cur (ivy-state-current ivy-last)))
(ivy--set-candidates
(ivy--sort-maybe
(with-current-buffer (process-buffer process)
(counsel--split-string))))
(when counsel--async-start
(setq counsel--async-duration
(time-to-seconds (time-since counsel--async-start))))
(let ((re (ivy-re-to-str ivy-regex)))
(if ivy--old-cands
(if (eq (ivy-alist-setting ivy-index-functions-alist) 'ivy-recompute-index-zero)
(ivy-set-index 0)
(ivy--recompute-index re ivy--all-candidates))
;; index was changed before a long-running query exited
(unless (string= cur (nth ivy--index ivy--all-candidates))
(let ((func (ivy-alist-setting ivy-index-functions-alist)))
(if func
(funcall func re ivy--all-candidates)
(ivy--preselect-index
(if (> (length re) 0)
cur
(ivy-state-preselect ivy-last))
ivy--all-candidates))))))
(setq ivy--old-cands ivy--all-candidates)
(if ivy--all-candidates
(ivy--exhibit)
(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)
(ivy--exhibit)))
(defun counsel--async-sentinel (process _msg)
"Sentinel function for an asynchronous counsel PROCESS."
(when (eq (process-status process) 'exit)
(counsel--sync-sentinel-on-exit process)))
(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)))
(ivy--set-candidates
(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))
lines)
lines))))
(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
(define-obsolete-function-alias 'counsel-el 'complete-symbol "<2020-05-20 Wed>")
(define-obsolete-function-alias 'counsel-cl 'complete-symbol "<2020-05-20 Wed>")
(define-obsolete-function-alias 'counsel-jedi 'complete-symbol "<2020-05-20 Wed>")
(define-obsolete-function-alias 'counsel-clj 'complete-symbol "<2020-05-20 Wed>")
;;** `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")
(declare-function company-call-backend "ext:company")
(declare-function company--clean-string "ext:company")
;;;###autoload
(defun counsel-company ()
"Complete using `company-candidates'."
(interactive)
(company-mode 1)
(unless company-candidates
(company-complete))
(let ((len (cond ((let (l)
(and company-common
(string= company-common
(buffer-substring
(- (point) (setq l (length company-common)))
(point)))
l)))
(company-prefix
(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
:caller 'counsel-company))))
(ivy-configure 'counsel-company
:display-transformer-fn #'counsel--company-display-transformer
:unwind-fn #'company-abort)
(defun counsel--company-display-transformer (s)
(concat s (let ((annot (company-call-backend 'annotation s)))
(when annot
(company--clean-string annot)))))
;;** `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")
;;;###autoload
(defun counsel-irony ()
"Inline C/C++ completion using Irony."
(interactive)
(irony-completion-candidates-async 'counsel-irony-callback))
(defun counsel-irony-callback (candidates)
"Callback function for Irony to search among CANDIDATES."
(interactive)
(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)))
(ivy-configure #'counsel-irony
:display-fn #'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)
map))
(ivy-set-actions
'counsel-describe-variable
'(("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-symbol',
`counsel-describe-variable', and `counsel-describe-function'.")
(defun counsel-find-symbol ()
"Jump to the definition of the current symbol."
(interactive)
(ivy-exit-with-action #'counsel--find-symbol))
(put 'counsel-find-symbol 'no-counsel-M-x t)
(defun counsel--info-lookup-symbol ()
"Lookup the current symbol in the info docs."
(interactive)
(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
;; Added in Emacs 25.1.
(if (require 'xref nil t)
#'xref-push-marker-stack
(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."
(with-ivy-window
(counsel--push-xref-marker)
(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)
'counsel-describe-variable)
(boundp sym))
(find-variable sym))
((fboundp sym)
(find-function sym))
((boundp sym)
(find-variable sym))
((or (featurep sym)
(locate-library
(prin1-to-string sym)))
(find-library
(prin1-to-string sym)))
(t
(error "Couldn't find definition of %s"
sym))))))))
(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)
var))
;;;###autoload
(defun counsel-describe-variable ()
"Forward to `describe-variable'.
Variables declared using `defcustom' are highlighted according to
`ivy-highlight-face'."
(interactive)
(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)
:action (lambda (x)
(funcall counsel-describe-variable-function (intern x)))
:caller 'counsel-describe-variable)))
(ivy-configure 'counsel-describe-variable
:parent 'counsel-describe-symbol
:display-transformer-fn #'counsel-describe-variable-transformer)
;;** `counsel-describe-function'
(ivy-set-actions
'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)
function-name))
(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)))
;;;###autoload
(defun counsel-describe-function ()
"Forward to `describe-function'.
Interactive functions (i.e., commands) are highlighted according
to `ivy-highlight-face'."
(interactive)
(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)
:action (lambda (x)
(funcall counsel-describe-function-function (intern x)))
:caller 'counsel-describe-function)))
(ivy-configure 'counsel-describe-function
:parent 'counsel-describe-symbol
:display-transformer-fn #'counsel-describe-function-transformer)
;;** `counsel-describe-symbol'
(defcustom counsel-describe-symbol-function #'describe-symbol
"Function to call to describe a symbol passed as parameter."
:type 'function)
;;;###autoload
(defun counsel-describe-symbol ()
"Forward to `describe-symbol'."
(interactive)
(unless (functionp 'describe-symbol)
(user-error "This command requires Emacs 25.1 or later"))
(require 'help-mode)
(let ((enable-recursive-minibuffers t))
(ivy-read "Describe symbol: " obarray
:predicate (lambda (sym)
(cl-some (lambda (backend)
(funcall (cadr backend) sym))
describe-symbol-backends))
:require-match t
:history 'counsel-describe-symbol-history
:keymap counsel-describe-map
:preselect (ivy-thing-at-point)
:action (lambda (x)
(funcall counsel-describe-symbol-function (intern x)))
:caller 'counsel-describe-symbol)))
(ivy-configure 'counsel-describe-symbol
:initial-input "^"
:sort-fn #'ivy-string<)
(ivy-set-actions
'counsel-describe-symbol
`(("I" ,#'counsel-info-lookup-symbol "info")
("d" ,#'counsel--find-symbol "definition")))
;;** `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* ((sym-value (symbol-value sym))
(init (format "(setq %s%S)"
(if (or (consp sym-value)
(and sym-value (symbolp sym-value)))
"'"
"")
sym-value)))
;; Most of this duplicates `read--expression'.
(minibuffer-with-setup-hook
(lambda ()
(set-syntax-table emacs-lisp-mode-syntax-table)
;; Added in Emacs 25.1.
(when (fboundp 'elisp-completion-at-point)
(add-hook 'completion-at-point-functions
#'elisp-completion-at-point nil t))
;; Emacs 27+ already sets up ElDoc in this hook. Emacs 25 added
;; `elisp-eldoc-documentation-function' and Emacs 28 obsoletes it.
(when (< emacs-major-version 27)
(when (fboundp 'elisp-eldoc-documentation-function)
(add-function :before-until (local 'eldoc-documentation-function)
#'elisp-eldoc-documentation-function))
(eldoc-mode))
(run-hooks 'eval-expression-minibuffer-setup-hook)
;; The following diverges from `read--expression'.
(goto-char (minibuffer-prompt-end))
(forward-char 6)
(insert (format "%S " sym)))
(read-from-minibuffer "Eval: " init read-expression-map t
'read-expression-history))))
(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)))
(cons
(concat
(when tag
(concat tag ": "))
(if (stringp v) v (prin1-to-string v)))
(if (symbolp v)
(list 'quote v)
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)
;;;###autoload
(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
`defcustom'."
(interactive (list (intern
(ivy-read "Set variable: " obarray
:predicate (if current-prefix-arg
#'custom-variable-p
#'counsel--variable-p)
: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)))
sym-type
cands)
(unwind-protect
(progn
(when doc
(lv-message (ivy--quote-format-string doc)))
(if (and (boundp sym)
(setq sym-type (get sym 'custom-type))
(cond
((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)
cands
:preselect (prin1-to-string sym-val))))
(when res
(setq res
(if (assoc res cands)
(cdr (assoc res cands))
(read res)))
(kill-new (format "(setq %S %S)" sym res))
(set sym (if (and (listp res) (eq (car res) 'quote))
(cadr res)
res))))
(unless (boundp sym)
(set sym nil))
(let ((expr (counsel-read-setq-expression sym)))
(kill-new (format "%S" expr))
(eval-expression expr))))
(when doc
(lv-delete-window)))))
;;** `counsel-apropos'
;;;###autoload
(defun counsel-apropos ()
"Show all matching symbols.
See `apropos' for further information on what is considered
a symbol and how to search for them."
(interactive)
(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)
: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))
(ivy-configure 'counsel-apropos
:sort-fn #'ivy-string<)
;;** `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")
;;;###autoload
(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."
(interactive
(progn
(require 'info-look)
;; Courtesy of `info-lookup-interactive-arguments'
(let* ((topic 'symbol)
(mode (cond (current-prefix-arg
(info-lookup-change-mode topic))
((info-lookup->mode-value
topic (info-lookup-select-mode))
info-lookup-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)
:caller 'counsel-info-lookup-symbol)
mode))))
(info-lookup-symbol symbol mode))
(ivy-configure 'counsel-info-lookup-symbol
:sort-fn #'ivy-string<)
;;** `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)
(defface counsel-active-mode
'((t :inherit font-lock-builtin-face))
"Face used by `counsel-M-x' for activated modes."
: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* ((sym (intern cmd))
(alias (symbol-function sym))
(key (where-is-internal sym nil t)))
(when (or (eq sym major-mode)
(and
(memq sym minor-mode-list)
(boundp sym)
(buffer-local-value sym (ivy-state-buffer ivy-last))))
(setq cmd (propertize cmd 'face 'counsel-active-mode)))
(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
(amx-initialize))
(when (amx-detect-new-commands)
(amx-update))
(mapcar (lambda (entry)
(symbol-name (car entry)))
amx-cache))
((require 'smex nil t)
(unless smex-initialized-p
(smex-initialize))
(when (smex-detect-new-commands)
(smex-update))
smex-ido-cache)))
(defun counsel--M-x-externs-predicate (cand)
"Return non-nil if `counsel-M-x' should complete CAND.
CAND is a string returned by `counsel--M-x-externs'."
(not (get (intern cand) 'no-counsel-M-x)))
(defun counsel--M-x-make-predicate ()
"Return a predicate for `counsel-M-x' in the current buffer."
(defvar read-extended-command-predicate)
(let ((buf (current-buffer)))
(lambda (sym)
(and (commandp sym)
(not (get sym 'byte-obsolete-info))
(not (get sym 'no-counsel-M-x))
(cond ((not (bound-and-true-p read-extended-command-predicate)))
((functionp read-extended-command-predicate)
(condition-case-unless-debug err
(funcall read-extended-command-predicate sym buf)
(error (message "read-extended-command-predicate: %s: %s"
sym (error-message-string err))))))))))
(defun counsel--M-x-prompt ()
"String for `M-x' plus the string representation of `current-prefix-arg'."
(concat (cond ((null current-prefix-arg)
nil)
((eq current-prefix-arg '-)
"- ")
((integerp current-prefix-arg)
(format "%d " current-prefix-arg))
((= (car current-prefix-arg) 4)
"C-u ")
(t
(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
(subst-char-in-string ?\s ?- (string-remove-prefix "^" 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))
;;;###autoload
(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."
(interactive)
;; 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 (if externs
#'counsel--M-x-externs-predicate
(counsel--M-x-make-predicate))
:require-match t
:history 'counsel-M-x-history
:action #'counsel-M-x-action
:keymap counsel-describe-map
:initial-input initial-input
:caller 'counsel-M-x)))
(ivy-configure 'counsel-M-x
:initial-input "^"
:display-transformer-fn #'counsel-M-x-transformer)
(ivy-set-actions
'counsel-M-x
`(("d" counsel--find-symbol "definition")
("h" ,(lambda (x) (funcall counsel-describe-function-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)))
(ivy-set-actions
'counsel-command-history
'(("r" counsel-command-history-action-eval "eval command")
("e" counsel-command-history-action-edit-and-eval "edit and eval command")))
;;;###autoload
(defun counsel-command-history ()
"Show the history of commands."
(interactive)
(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))
short-name
old-val
dir-parent
res)
(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))
(progn
;; assume going up directory once will resolve name clash
(setq dir-parent (counsel-directory-name (cdr old-val)))
(puthash short-name
(cons
(counsel-string-compose dir-parent (car old-val))
(cdr old-val))
cands)
(setq dir-parent (counsel-directory-name dir))
(puthash (concat dir-parent short-name)
(cons
(propertize
(counsel-string-compose
dir-parent short-name)
'full-name (expand-file-name file dir))
dir)
cands))
(puthash short-name
(cons (propertize
short-name
'full-name (expand-file-name file dir))
dir)
cands)))))))
(maphash (lambda (_k v) (push (car v) res)) cands)
(nreverse res)))
;;;###autoload
(defun counsel-load-library ()
"Load a selected the Emacs Lisp library.
The libraries are offered from `load-path'."
(interactive)
(let ((cands (counsel-library-candidates)))
(ivy-read "Load library: " cands
:action (lambda (x)
(load-library
(get-text-property 0 'full-name x)))
:keymap counsel-describe-map)))
(ivy-set-actions
'counsel-load-library
'(("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)))))
(ivy-set-actions
'counsel-find-library
'(("j" counsel-find-library-other-window "other window")
("f" counsel-find-library-other-frame "other frame")))
;;;###autoload
(defun counsel-find-library ()
"Visit a selected the Emacs Lisp library.
The libraries are offered from `load-path'."
(interactive)
(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
(progn
(mapc #'disable-theme custom-enabled-themes)
(load-theme (intern x) t)
(when (fboundp 'powerline-reset)
(powerline-reset)))
(error "Problem loading theme %s" x)))
;;;###autoload
(defun counsel-load-theme ()
"Forward to `load-theme'.
Usable with `ivy-resume', `ivy-next-line-and-call' and
`ivy-previous-line-and-call'."
(interactive)
(ivy-read "Load custom theme: "
(mapcar 'symbol-name
(custom-available-themes))
:action #'counsel-load-theme-action
:caller 'counsel-load-theme))
;;** `counsel-descbinds'
(ivy-set-actions
'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>")))
res)
(with-temp-buffer
(let ((indent-tabs-mode t))
(describe-buffer-bindings buffer prefix))
(goto-char (point-min))
;; Skip the "Key translations" section
(skip-chars-forward "^\C-l")
(forward-char 2)
(while (not (eobp))
(when (looking-at "^\\([^\t\n]+\\)[\t ]*\\(.*\\)$")
(let ((key (match-string 1))
(fun (match-string 2))
cmd)
(unless (or (member fun '("??" "self-insert-command"))
(string-match-p re-exclude key)
(not (or (commandp (setq cmd (intern-soft fun)))
(equal fun "Prefix Command"))))
(push
(cons (format
"%-15s %s"
(propertize key 'face 'counsel-key-binding)
fun)
(cons key cmd))
res))))
(forward-line)))
(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))))
;;;###autoload
(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."
(interactive)
(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)))
;;;###autoload
(defun counsel-describe-face ()
"Completion for `describe-face'."
(interactive)
(ivy-read "Face: " (face-list)
:require-match t
:history 'face-name-history
:preselect (counsel--face-at-point)
:action counsel-describe-face-function
:caller 'counsel-describe-face))
(ivy-configure 'counsel-describe-face
:sort-fn #'ivy-string<)
(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)))
(declare-function hi-lock-set-pattern "hi-lock")
(defun counsel-highlight-with-face (face)
"Highlight thing-at-point with FACE."
(hi-lock-mode 1)
(let ((thing (ivy-thing-at-point)))
(when (use-region-p)
(deactivate-mark))
(hi-lock-set-pattern (regexp-quote thing) (intern face))))
(ivy-set-actions
'counsel-describe-face
'(("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)
"Format NAMES according to `counsel--faces-format'."
(let ((formatter
(lambda (name)
(format counsel--faces-format name
(propertize list-faces-sample-text
'face (intern name))))))
(ivy--format-function-generic
(lambda (name)
(funcall formatter (ivy--add-face name 'ivy-current-match)))
formatter names "\n")))
;;;###autoload
(defun counsel-faces ()
"Complete faces with preview.
Actions are provided by default for describing or customizing the
selected face."
(interactive)
(let* ((names (mapcar #'symbol-name (face-list)))
(counsel--faces-format
(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)
:action counsel-describe-face-function
:caller 'counsel-faces)))
(ivy-configure 'counsel-faces
:parent 'counsel-describe-face
:format-fn #'counsel--faces-format-function)
(ivy-set-actions
'counsel-faces
'(("c" counsel-customize-face "customize")
("C" counsel-customize-face-other-window "customize other window")
("h" counsel-highlight-with-face "highlight")))
;;* Git
;;** `counsel-git'
(defvar counsel-git-cmd "git ls-files -z --full-name --"
"Command for `counsel-git'.")
(ivy-set-actions
'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 (dir)
(let ((default-directory dir))
(split-string
(shell-command-to-string counsel-git-cmd)
"\0"
t)))
;;;###autoload
(defun counsel-git (&optional initial-input)
"Find file in the current Git repository.
INITIAL-INPUT can be given as the initial minibuffer input."
(interactive)
(counsel-require-program counsel-git-cmd)
(let ((default-directory (counsel-locate-git-root)))
(ivy-read "Find file: " (counsel-git-cands default-directory)
:initial-input initial-input
:action #'counsel-git-action
:caller 'counsel-git)))
(ivy-configure 'counsel-git
:occur #'counsel-git-occur)
(defun counsel-git-action (x)
"Find file X in current Git repository."
(with-ivy-window
(let ((default-directory (ivy-state-directory ivy-last)))
(find-file x))))
(defun counsel-git-occur (&optional _cands)
"Occur function for `counsel-git' using `counsel-cmd-to-dired'."
(cd (ivy-state-directory ivy-last))
(counsel-cmd-to-dired
(counsel--expand-ls
(format "%s | %s | xargs ls"
(replace-regexp-in-string "\\(-0\\)\\|\\(-z\\)" "" counsel-git-cmd)
(counsel--file-name-filter)))))
(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))
(erase-buffer)
(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)
(set-process-sentinel
proc
(lambda (process _msg)
(when (and (eq (process-status process) 'exit)
(zerop (process-exit-status process)))
(goto-char (point-min))
(forward-line 2)
(dired-move-to-filename)))))))
;;** `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)
(define-key map (kbd "C-x C-d") 'counsel-cd)
map))
(defvar counsel-git-grep-cmd-default "git --no-pager grep -n --no-color -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.
This function should set `ivy--old-re'."
: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
(if (eq ivy--regex-function #'ivy--regex-fuzzy)
(replace-regexp-in-string
"\n" "" (ivy--regex-fuzzy str))
(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)))
(concat
"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."
(or
(ivy-more-chars)
(progn
(counsel--async-command
(concat
(funcall counsel-git-grep-cmd-function string)
(if (ivy--case-fold-p string) " -i" "")))
nil)))
(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
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))))
(swiper--ensure-visible)
(run-hooks 'counsel-grep-post-action-hook)
(unless (eq ivy-exit 'done)
(swiper--cleanup)
(swiper--add-overlays (ivy--regex ivy-text))))))
(defun counsel-git-grep-transformer (str)
"Highlight file and line number in STR."
(when (string-match "\\`\\([^:]+\\):\\([^:]+\\):" str)
(add-face-text-property (match-beginning 1) (match-end 1)
'ivy-grep-info nil str)
(add-face-text-property (match-beginning 2) (match-end 2)
'ivy-grep-line-number nil str))
str)
(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))
proj)
(cond
((stringp cmd))
(current-prefix-arg
(if (setq proj
(cl-find-if
(lambda (x)
(string-match (car x) dd))
counsel-git-grep-projects-alist))
(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))))
(t
(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-"))
status)
(unwind-protect
(with-temp-buffer
(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))
0
1))))
;; Convert process status into error list.
(setq status (list 'file-error
(mapconcat #'identity `(,@command "failed") " ")
status))
;; Print stderr contents, if any, to *Warnings* buffer.
(let ((msg (condition-case err
(unless (zerop (cadr (insert-file-contents
stderr nil nil nil t)))
(buffer-string))
(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--grep-unwind ()
(counsel-delete-process)
(swiper--cleanup))
;;;###autoload
(defun counsel-git-grep (&optional initial-input initial-directory cmd)
"Grep for a string in the current Git repository.
INITIAL-INPUT can be given as the initial minibuffer input.
INITIAL-DIRECTORY, if non-nil, is used as the root directory for search.
When CMD is a string, use it as a \"git grep\" command.
When CMD is non-nil, prompt for a specific \"git grep\" command."
(interactive)
(let ((proj-and-cmd (counsel--git-grep-cmd-and-proj cmd))
proj)
(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
#'counsel-git-grep-proj-function
#'counsel-git-grep-function))
(default-directory (or initial-directory
(if proj
(car proj)
(counsel-locate-git-root)))))
(ivy-read "git grep: " collection-function
:initial-input initial-input
:dynamic-collection t
:keymap counsel-git-grep-map
:action #'counsel-git-grep-action
:history 'counsel-git-grep-history
:require-match t
:caller 'counsel-git-grep))))
(defun counsel--git-grep-index (_re-str cands)
(let (name ln)
(cond
(ivy--old-cands
(ivy-recompute-index-swiper-async nil cands))
((unless (with-ivy-window
(when buffer-file-name
(setq ln (line-number-at-pos))
(setq name (file-name-nondirectory buffer-file-name))))
0))
;; Closest to current line going forwards.
((let ((beg (1+ (length name))))
(cl-position-if (lambda (x)
(and (string-prefix-p name x)
(>= (string-to-number (substring x beg)) ln)))
cands)))
;; Closest to current line going backwards.
((cl-position-if (lambda (x)
(string-prefix-p name x))
cands
:from-end t))
(t 0))))
(ivy-configure 'counsel-git-grep
:occur #'counsel-git-grep-occur
:unwind-fn #'counsel--grep-unwind
:index-fn #'counsel--git-grep-index
:display-transformer-fn #'counsel-git-grep-transformer
:grep-p t
:exit-codes '(1 "No matches found"))
(defun counsel-git-grep-proj-function (str)
"Grep for STR in the current Git repository."
(or
(ivy-more-chars)
(let ((regex (setq ivy--old-re
(ivy--regex str t))))
(counsel--async-command
(concat
(format counsel-git-grep-cmd regex)
(if (ivy--case-fold-p str) " -i" "")))
nil)))
(defun counsel-git-grep-switch-cmd ()
"Set `counsel-git-grep-cmd' to a different value."
(interactive)
(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:
(save-match-data
(when (string-match
"[^\n:]+?[^\n/:]:[\t ]*[1-9][0-9]*[\t ]*:\\([1-9][0-9]*:\\)"
str)
(setq str (replace-match "" t t str 1))))
str)
(defun counsel--git-grep-occur-cmd (input)
(let* ((regex ivy--old-re)
(positive-pattern (replace-regexp-in-string
;; git-grep can't handle .*?
"\\.\\*\\?" ".*"
(ivy-re-to-str regex)))
(negative-patterns
(if (stringp regex) ""
(mapconcat (lambda (x)
(and (null (cdr x))
(format "| grep -v %s" (car x))))
regex
" "))))
(concat
(format counsel-git-grep-cmd positive-pattern)
negative-patterns
(if (ivy--case-fold-p input) " -i" ""))))
(defun counsel-git-grep-occur (&optional _cands)
"Generate a custom occur buffer for `counsel-git-grep'."
(counsel-grep-like-occur #'counsel--git-grep-occur-cmd))
(defun counsel-git-grep-query-replace ()
"Start `query-replace' with string to replace from last search string."
(interactive)
(unless (window-minibuffer-p)
(user-error
"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)))
(ivy-exit-with-action
(lambda (_)
(let (done-buffers)
(dolist (cand ivy--old-cands)
(when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" cand)
(with-ivy-window
(let ((file-name (match-string-no-properties 1 cand)))
(setq file-name (expand-file-name
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))))))
;;;###autoload
(defun counsel-git-stash ()
"Search through all available git stashes."
(interactive)
(let* ((default-directory (counsel-locate-git-root))
(cands (split-string (shell-command-to-string
"IFS=$'\n'
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 --no-color --grep '%s'"
"Command used for \"git log\".")
(defun counsel-git-log-function (_)
"Search for `ivy-regex' in git log."
(or
(ivy-more-chars)
(progn
;; `counsel--yank-pop-format-function' uses this
(setq ivy--old-re ivy-regex)
(counsel--async-command
;; "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))))
nil)))
(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)))
(ivy-set-actions
'counsel-git-log
'(("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))
(save-excursion
(dolist (buf (buffer-list))
(set-buffer buf)
(and buffer-file-name
(string= "." (file-relative-name root-dir (counsel-locate-git-root)))
(kill-buffer buf)))))
(ivy-set-actions
'counsel-git-change-worktree
'(("k" counsel-git-close-worktree-files-action "kill all")))
;;;###autoload
(defun counsel-git-change-worktree ()
"Find the file corresponding to the current buffer on a different worktree."
(interactive)
(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)
(counsel-git-change-worktree-action
(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."
(shell-command
(format "git checkout %s"
(shell-quote-argument
(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))))
;;;###autoload
(defun counsel-git-checkout ()
"Call the \"git checkout\" command."
(interactive)
(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)))
;;;###autoload
(defun counsel-git-log ()
"Call the \"git log --grep\" shell command."
(interactive)
(ivy-read "Grep log: " #'counsel-git-log-function
:dynamic-collection t
:action #'counsel-git-log-action
:caller 'counsel-git-log))
(ivy-configure 'counsel-git-log
:height 4
:unwind-fn #'counsel-delete-process
:format-fn #'counsel--git-log-format-function)
(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 "`") #'counsel-file-jump-from-find)
(define-key map (kbd "C-`") (ivy-make-magic-action 'counsel-find-file "b"))
(define-key map [remap undo] 'counsel-find-file-undo)
map))
(defun counsel-file-jump-from-find ()
"Switch to `counsel-file-jump' from `counsel-find-file'."
(interactive)
(ivy-quit-and-run
(counsel-file-jump ivy-text (ivy-state-directory ivy-last))))
(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: "
(progn
(ivy--virtual-buffers)
(delete-dups
(mapcar (lambda (x) (file-name-directory (cdr x)))
ivy--virtual-buffers)))
:action (lambda (x)
(let ((default-directory (file-name-directory x)))
(counsel-find-file)))))
(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"
counsel-root-command
(or host "")
(expand-file-name
(if host
(file-remote-p x 'localname)
x)))))
;; 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
`yes-or-no-p'."
(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))))
#'y-or-n-p
#'yes-or-no-p)
(apply #'format fmt args)))
(defun counsel-find-file-copy (x)
"Copy file X."
(require 'dired-aux)
(counsel--find-file-1 "Copy file to: "
ivy--directory
(lambda (new-name)
(dired-copy-file x new-name 1))
'counsel-find-file-copy))
(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))
(active-minibuffer-window))))
(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: "
ivy--directory
(lambda (new-name)
(dired-rename-file x new-name 1))
'counsel-find-file-move))
(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))
(active-minibuffer-window))))
(make-directory dir t)
(when win (with-selected-window win (ivy--cd dir)))))
(ivy-set-actions
'counsel-find-file
'(("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")
("R" find-file-read-only "read only")
("l" find-file-literally "open literally")
("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.
\\<ivy-minibuffer-map>
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"
,(concat (regexp-opt completion-ignored-extensions) "\\'"))
(regexp :tag "Regex")))
(defvar counsel--find-file-predicate nil
"When non-nil, `counsel--find-file-matcher' will use this predicate.")
(defun counsel--find-file-matcher (regexp candidates)
"Return REGEXP matching CANDIDATES.
Skip some dotfiles unless `ivy-text' requires them."
(let ((res
(ivy--re-filter
regexp candidates
(lambda (re-str)
(lambda (x)
(string-match re-str (directory-file-name x)))))))
(when counsel--find-file-predicate
(let ((default-directory ivy--directory))
(setq res (cl-remove-if-not counsel--find-file-predicate res))))
(if (or (null ivy-use-ignore)
(null counsel-find-file-ignore-regexp)
(string-match-p counsel-find-file-ignore-regexp ivy-text))
res
(or (cl-remove-if
(lambda (x)
(and
(string-match-p counsel-find-file-ignore-regexp x)
(not (member x ivy-extra-directories))))
res)
res))))
(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))
(t
(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."
(or
(when counsel-find-file-at-point
(require 'ffap)
(let ((f (ffap-guesser)))
(when (and f (not (ivy-ffap-url-p f)))
(expand-file-name f))))
(and counsel-preselect-current-file
buffer-file-name
(file-name-nondirectory buffer-file-name))))
(defun counsel--find-file-1 (prompt initial-input action caller)
(let ((default-directory
(if (eq major-mode 'dired-mode)
(dired-current-directory)
default-directory)))
(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)))
;;;###autoload
(defun counsel-find-file (&optional initial-input initial-directory)
"Forward to `find-file'.
When INITIAL-INPUT is non-nil, use it in the minibuffer during completion."
(interactive)
(let ((default-directory (or initial-directory default-directory)))
(counsel--find-file-1 "Find file: " initial-input
#'counsel-find-file-action
'counsel-find-file)))
(ivy-configure 'counsel-find-file
:parent 'read-file-name-internal
:occur #'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
`counsel-find-file-ignore-regexp'."
(let ((regex ivy--old-re))
(if (= 0 (length regex))
"cat"
(let ((filter-cmd (cl-find-if
(lambda (x)
(executable-find
(car (split-string (car x)))))
counsel-file-name-filter-alist))
cmd)
(when (and use-ignore ivy-use-ignore
counsel-find-file-ignore-regexp
(cdr filter-cmd)
(not (string-match-p counsel-find-file-ignore-regexp ivy-text))
(not (string-match-p counsel-find-file-ignore-regexp
(or (car ivy--old-cands) ""))))
(let ((ignore-re (list (counsel--elisp-to-pcre
counsel-find-file-ignore-regexp))))
(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)
cmd)))))
(defun counsel--occur-cmd-find ()
(let ((cmd (format
"find . -maxdepth 1 | %s | xargs -I {} find {} -maxdepth 0 -ls"
(counsel--file-name-filter t))))
(concat
(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 '/\\\\.'")))
(replace-regexp-in-string
" | grep"
(concat " -type " type exclude-dots " | grep") cmd)))
(defun counsel-find-file-occur (&optional _cands)
(require 'find-dired)
(cd ivy--directory)
(if counsel-find-file-occur-use-find
(counsel-cmd-to-dired
(counsel--occur-cmd-find)
'find-dired-filter)
(counsel-cmd-to-dired
(counsel--expand-ls
(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'."
(interactive)
(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))
(progn
;; 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)
(ivy-set-text "")
(delete-minibuffer-contents)
(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."
(interactive)
(ivy--directory-enter))
(defun counsel-find-file-undo ()
(interactive)
(if (string= ivy-text "")
(let ((dir (progn
(pop ivy--directory-hist)
(pop ivy--directory-hist))))
(when dir
(ivy--cd dir)))
(undo)))
(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."
(when (counsel-require-program "git" t)
(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@github.com:\\([^/]+\\)/\\(.*\\)\\.git$"
origin)
(setq user (match-string 1 origin))
(setq repo (match-string 2 origin)))
((string-match "\\`https://github.com/\\([^/]+\\)/\\(.*\\)$"
origin)
(setq user (match-string 1 origin))
(setq repo (match-string 2 origin))))
(when user
(setq url (format "https://github.com/%s/%s/issues/%s"
user repo (substring url 1)))))))))
(defun counsel-emacs-url-p ()
"Return a Debbugs issue URL at point."
(when (counsel-require-program "git" t)
(let ((url (counsel-at-git-issue-p)))
(when url
(let ((origin (shell-command-to-string
"git remote get-url origin")))
(when (string-match "git.sv.gnu.org:/srv/git/emacs.git" origin)
(format "https://debbugs.gnu.org/cgi/bugreport.cgi?bug=%s"
(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)
pairs.
`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:]]+\\'\" . \"https://jira.atlassian.com/browse/%s\")
will expand to URL `https://jira.atlassian.com/browse/BSERV-100'
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 \"https://debbugs.gnu.org/cgi/bugreport.cgi?bug=\"
(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
(cl-some
(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-url-expansions-alist))))
;;** `counsel-dired'
(declare-function dired "dired")
;;;###autoload
(defun counsel-dired (&optional initial-input)
"Forward to `dired'.
When INITIAL-INPUT is non-nil, use it in the minibuffer during completion."
(interactive)
(let ((counsel--find-file-predicate #'file-directory-p))
(counsel--find-file-1
"Dired (directory): " initial-input
(lambda (d) (dired (expand-file-name d)))
'counsel-dired)))
(ivy-configure 'counsel-dired
:parent 'read-file-name-internal)
;;** `counsel-recentf'
(defvar recentf-list)
(declare-function recentf-mode "recentf")
(defcustom counsel-recentf-include-xdg-list nil
"Include recently used files listed by XDG-compliant environments.
Examples of such environments are GNOME and KDE. See the URL
`https://www.freedesktop.org/wiki/Specifications/desktop-bookmark-spec'."
:type 'boolean
:link '(url-link "\
https://www.freedesktop.org/wiki/Specifications/desktop-bookmark-spec"))
;;;###autoload
(defun counsel-recentf ()
"Find a file on `recentf-list'."
(interactive)
(require 'recentf)
(recentf-mode)
(ivy-read "Recentf: " (counsel-recentf-candidates)
:action (lambda (f)
(with-ivy-window
(find-file f)))
:require-match t
:caller 'counsel-recentf))
(ivy-set-actions
'counsel-recentf
`(("j" find-file-other-window "other window")
("f" find-file-other-frame "other frame")
("x" counsel-find-file-extern "open externally")
("d" ,(lambda (file) (setq recentf-list (delete file recentf-list)))
"delete from recentf")))
(defun counsel-recentf-candidates ()
"Return candidates for `counsel-recentf'.
When `counsel-recentf-include-xdg-list' is non-nil, also include
the files in said list, sorting the combined list by file access
time."
(if (and counsel-recentf-include-xdg-list
(>= emacs-major-version 26))
(delete-dups
(sort (nconc (mapcar #'substring-no-properties recentf-list)
(counsel--recentf-get-xdg-recent-files))
(lambda (file1 file2)
(cond ((file-remote-p file1)
nil)
((file-remote-p file2))
(t
;; Added in Emacs 26.1.
(declare-function file-attribute-access-time "files"
(attributes))
(time-less-p (file-attribute-access-time
(file-attributes file2))
(file-attribute-access-time
(file-attributes file1))))))))
(mapcar #'substring-no-properties recentf-list)))
(defalias 'counsel--xml-parse-region
(if (cond ((fboundp 'libxml-available-p)
;; Added in Emacs 27.1.
(libxml-available-p))
((fboundp 'libxml-parse-xml-region)
;; Checking for `fboundp' is not enough on Windows, where it
;; will return non-nil even if the library is not installed.
(with-temp-buffer
(insert "<xml/>")
(libxml-parse-xml-region (point-min) (point-max)))))
(lambda (&optional beg end)
(libxml-parse-xml-region (or beg (point-min)) (or end (point-max))))
#'xml-parse-region)
"Compatibility shim for `libxml-parse-xml-region'.
For convenience, BEG and END default to `point-min' and
`point-max', respectively.
\(fn &optional BEG END)")
(defun counsel--recentf-get-xdg-recent-files ()
"Return list of XDG recent files.
This information is parsed from the file \"recently-used.xbel\",
which lists both files and directories, under `xdg-data-home'.
This function uses the `dom' library from Emacs 25.1 or later."
(unless (require 'dom nil t)
(user-error "This function requires Emacs 25.1 or later"))
(declare-function dom-attr "dom" (node attr))
(declare-function dom-by-tag "dom" (dom tag))
(let ((file-of-recent-files
(expand-file-name "recently-used.xbel" (counsel--xdg-data-home))))
(unless (file-readable-p file-of-recent-files)
(user-error "List of XDG recent files not found: %s"
file-of-recent-files))
(cl-mapcan (lambda (bookmark-node)
(let* ((file (dom-attr bookmark-node 'href))
(file (string-remove-prefix "file://" file))
(file (url-unhex-string file t))
(file (decode-coding-string file 'utf-8 t)))
(and (file-exists-p file)
(list file))))
(let ((dom (with-temp-buffer
(insert-file-contents file-of-recent-files)
(counsel--xml-parse-region))))
(nreverse (dom-by-tag dom 'bookmark))))))
(defun counsel-buffer-or-recentf-candidates ()
"Return candidates for `counsel-buffer-or-recentf'."
(require 'recentf)
(recentf-mode)
(let ((buffers
(delq nil
(mapcar (lambda (b)
(when (buffer-file-name b)
(buffer-file-name b)))
(buffer-list)))))
(append
buffers
(cl-remove-if (lambda (f) (member f buffers))
(counsel-recentf-candidates)))))
;;;###autoload
(defun counsel-buffer-or-recentf ()
"Find a buffer visiting a file or file on `recentf-list'."
(interactive)
(ivy-read "Buffer File or Recentf: " (counsel-buffer-or-recentf-candidates)
:action (lambda (s)
(with-ivy-window
(if (bufferp s)
(switch-to-buffer s)
(find-file s))))
:require-match t
:caller 'counsel-buffer-or-recentf))
(ivy-configure 'counsel-buffer-or-recentf
:display-transformer-fn #'counsel-buffer-or-recentf-transformer)
(ivy-set-actions
'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)
var))
;;** `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")
;;;###autoload
(defun counsel-bookmark ()
"Forward to `bookmark-jump' or `bookmark-set' if bookmark doesn't exist."
(interactive)
(require 'bookmark)
(ivy-read "Create or jump to bookmark: "
(bookmark-all-names)
:history 'bookmark-history
:action (lambda (x)
(cond ((and counsel-bookmark-avoid-dired
(member x (bookmark-all-names))
(file-directory-p (bookmark-location x)))
(with-ivy-window
(let ((default-directory (bookmark-location x)))
(counsel-find-file))))
((member x (bookmark-all-names))
(with-ivy-window
(bookmark-jump x)))
(t
(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))))
(ivy-set-actions
'counsel-bookmark
`(("j" bookmark-jump-other-window "other window")
("d" bookmark-delete "delete")
("e" bookmark-rename "edit")
("s" bookmark-set "overwrite")
("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."
(bookmark-maybe-load-default-file)
(sort (cl-remove-if-not
#'ivy--dirname-p
(delq nil (mapcar #'bookmark-get-filename bookmark-alist)))
#'string<))
;;;###autoload
(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'."
(interactive)
(require 'bookmark)
(ivy-read "Bookmarked directory: "
(counsel-bookmarked-directory--candidates)
: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)))
"find-file")))
;;** `counsel-file-register'
;;;###autoload
(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."
(interactive)
(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)))
register-alist)
:require-match t
:history 'counsel-file-register
:caller 'counsel-file-register
:action (lambda (register-file)
(with-ivy-window (find-file register-file)))))
(ivy-configure 'counsel-file-register
:sort-fn #'ivy-string<)
(ivy-set-actions
'counsel-file-register
'(("j" find-file-other-window "other window")))
;;** `counsel-locate'
(defcustom counsel-locate-cmd (cond ((memq system-type '(darwin berkeley-unix))
#'counsel-locate-cmd-noregex)
((and (eq system-type 'windows-nt)
(executable-find "es.exe"))
#'counsel-locate-cmd-es)
(t
#'counsel-locate-cmd-default))
"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)
(function :tag "Custom")))
(ivy-set-actions
'counsel-locate
'(("x" counsel-locate-action-extern "xdg-open")
("r" counsel-find-file-as-root "open as root")
("d" counsel-locate-action-dired "dired")))
(defvar counsel-locate-history nil
"History for `counsel-locate'.")
;;;###autoload
(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))
(defvar locate-command)
(defun counsel-locate-cmd-default (input)
"Return a `locate' shell command based on regexp INPUT.
This uses the user option `locate-command' from the `locate'
library, which see."
(counsel-require-program locate-command)
(format "%s -i --regex %s"
locate-command
(shell-quote-argument
(counsel--elisp-to-pcre
(ivy--regex input)))))
(defun counsel-locate-cmd-noregex (input)
"Return a `locate' shell command based on INPUT.
This uses the user option `locate-command' from the `locate'
library, which see."
(counsel-require-program locate-command)
(format "%s -i %s"
locate-command
(shell-quote-argument input)))
(defun counsel-locate-cmd-mdfind (input)
"Return a `mdfind' shell command based on INPUT."
(counsel-require-program "mdfind")
(format "mdfind -name %s" (shell-quote-argument input)))
(defun counsel-locate-cmd-es (input)
"Return a `es' shell command based on INPUT."
(defvar w32-ansi-code-page)
(counsel-require-program "es.exe")
(let ((raw-string (format "es.exe -i -p -r %s"
(counsel--elisp-to-pcre
(ivy--regex input t)))))
;; W32 doesn't use Unicode by default, so we encode search command
;; to local codepage to support searching file names containing
;; non-ASCII characters.
(if (and (eq system-type 'windows-nt)
(boundp 'w32-ansi-code-page))
(encode-coding-string raw-string
(intern (format "cp%d" w32-ansi-code-page)))
raw-string)))
(defun counsel-locate-function (input)
"Call a \"locate\" style shell command with INPUT."
(or
(ivy-more-chars)
(progn
(counsel--async-command
(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-file-stale-p (fname seconds)
"Return non-nil if FNAME was modified more than SECONDS ago."
(> (float-time (time-subtract nil (nth 5 (file-attributes fname))))
seconds))
(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))
(counsel-file-stale-p db-fname 60))
(message "Updating %s..." db-fname)
(counsel--command
"updatedb" "-l" "0" "-o" db-fname "-U" (expand-file-name "~"))))))
;;;###autoload
(defun counsel-locate (&optional initial-input)
"Call a \"locate\" style shell command.
INITIAL-INPUT can be given as the initial minibuffer input."
(interactive)
;; For `locate-command', which is honored in some options of `counsel-locate-cmd'.
(require 'locate)
(counsel--locate-updatedb)
(ivy-read "Locate: " #'counsel-locate-function
:initial-input initial-input
:dynamic-collection t
:history 'counsel-locate-history
:action (lambda (file)
(when file
(with-ivy-window
(find-file
(concat (file-remote-p default-directory) file)))))
:caller 'counsel-locate))
(ivy-configure 'counsel-locate
:unwind-fn #'counsel-delete-process
:exit-codes '(1 "Nothing found"))
;;** `counsel-tracker'
(defun counsel-tracker-function (input)
"Call the \"tracker\" shell command with INPUT."
(or
(ivy-more-chars)
(progn
(counsel--async-command
(format
"tracker sparql -q \"SELECT ?url WHERE { ?s a nfo:FileDataObject ; nie:url ?url . FILTER (STRSTARTS (?url, 'file://$HOME/')) . FILTER regex(?url, '%s') }\" | tail -n +2 | head -n -1"
(counsel--elisp-to-pcre (funcall ivy--regex-function input))))
'("" "working..."))))
(defun counsel-tracker-transformer (str)
(if (string-match "file:///" str)
(decode-coding-string (url-unhex-string (substring str 9)) 'utf-8)
str))
;;;###autoload
(defun counsel-tracker ()
(interactive)
(ivy-read "Tracker: " 'counsel-tracker-function
:dynamic-collection t
:action (lambda (s) (find-file (counsel-tracker-transformer s)))
:caller 'counsel-tracker))
(ivy-configure 'counsel-tracker
:display-transformer-fn #'counsel-tracker-transformer
:unwind-fn #'counsel-delete-process)
;;** `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)
(projectile-project-p))
(projectile-project-root)
default-directory))
(defun counsel-fzf-function (str)
(let ((default-directory counsel--fzf-dir))
(setq ivy--old-re (ivy--regex-fuzzy str))
(counsel--async-command
(format counsel-fzf-cmd str)))
nil)
;;;###autoload
(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."
(interactive
(let ((fzf-basename (car (split-string counsel-fzf-cmd))))
(list nil
(when current-prefix-arg
(counsel-read-directory-name (concat
fzf-basename
" 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: ")
#'counsel-fzf-function
:initial-input initial-input
:re-builder #'ivy--regex-fuzzy
:dynamic-collection t
:action #'counsel-fzf-action
:caller 'counsel-fzf))
(ivy-configure 'counsel-fzf
:occur #'counsel-fzf-occur
:unwind-fn #'counsel-delete-process
:exit-codes '(1 "Nothing found"))
(defun counsel-fzf-action (x)
"Find file X in current fzf directory."
(with-ivy-window
(let ((default-directory counsel--fzf-dir))
(find-file x))))
(defun counsel-fzf-occur (&optional _cands)
"Occur function for `counsel-fzf' using `counsel-cmd-to-dired'."
(cd counsel--fzf-dir)
(counsel-cmd-to-dired
(counsel--expand-ls
(format
"%s --print0 | xargs -0 ls"
(format counsel-fzf-cmd ivy-text)))))
(ivy-set-actions
'counsel-fzf
'(("x" counsel-locate-action-extern "xdg-open")
("d" counsel-locate-action-dired "dired")))
;;** `counsel-dpkg'
;;;###autoload
(defun counsel-dpkg ()
"Call the \"dpkg\" shell command."
(interactive)
(counsel-require-program "dpkg")
(let ((cands (mapcar
(lambda (x)
(let ((y (split-string x " +")))
(cons (format "%-40s %s"
(ivy--truncate-string
(nth 1 y) 40)
(nth 4 y))
(mapconcat #'identity y " "))))
(split-string
(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'
;;;###autoload
(defun counsel-rpm ()
"Call the \"rpm\" shell command."
(interactive)
(counsel-require-program "rpm")
(let ((cands (mapcar