branch: externals/counsel
commit 82585a61ee5d610afc86a801a83526b6584dfcd6
Merge: 4b275b4bdf 847ba97f6b
Author: Basil L. Contovounesios <[email protected]>
Commit: Basil L. Contovounesios <[email protected]>
Merge branch 'master' into externals/counsel
---
.dir-locals.el | 13 +-
counsel.el | 863 +++++++++++++++++++++++++++++++++++---------------------
targets/elpa.el | 125 ++++++++
3 files changed, 678 insertions(+), 323 deletions(-)
diff --git a/.dir-locals.el b/.dir-locals.el
index f18455c3a6..9920229883 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -2,17 +2,14 @@
;;; For more information see (info "(emacs) Directory Variables")
((nil
- ;; Emacs 28+ automatically sets up these `bug-reference-mode' variables
- ;; in a more general way, so setting them here is not future-proof. If
- ;; you still need these settings in older Emacs versions, you can add
- ;; them to your personal `.dir-locals-2.el' file in the meantime.
- ;; (bug-reference-bug-regexp . "\\(#\\([[:digit:]]+\\)\\)")
- ;; (bug-reference-url-format . "https://github.com/abo-abo/swiper/issues/%s")
(copyright-names-regexp . "Free Software Foundation, Inc\\.")
(sentence-end-double-space . t))
(emacs-lisp-mode
(indent-tabs-mode . nil)
- (outline-regexp . ";;\\([;*]+ [^\s\t\n]\\|###autoload\\)\\|(")
;; extra config here:
https://github.com/abo-abo/oremacs/blob/github/modes/ora-elisp-style-guide.el
;; (lisp-indent-function . common-lisp-indent-function)
- ))
+ )
+ (markdown-mode
+ (fill-column . 70))
+ (org-mode
+ (fill-column . 70)))
diff --git a/counsel.el b/counsel.el
index 55aee57980..5c5a8d1022 100644
--- a/counsel.el
+++ b/counsel.el
@@ -44,18 +44,26 @@
(require 'ivy)
(require 'swiper)
-(require 'compile)
-(require 'dired)
-
(eval-when-compile
(require 'subr-x))
+(eval-when-compile
+ (unless (fboundp 'static-if)
+ (defmacro static-if (condition then-form &rest else-forms)
+ "Expand to THEN-FORM or ELSE-FORMS based on compile-time CONDITION.
+Polyfill for Emacs 30 `static-if'."
+ (declare (debug (sexp sexp &rest sexp)) (indent 2))
+ (if (eval condition lexical-binding)
+ then-form
+ (macroexp-progn else-forms)))))
+
(defgroup counsel nil
"Completion functions using Ivy."
:group 'matching
:prefix "counsel-")
+
+;;; Utility
-;;* 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,
@@ -139,13 +147,16 @@ When NOERROR is non-nil, return nil instead of raising an
error."
(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."
+ (declare (obsolete "it is no longer used." "0.16.0"))
(require 'esh-util)
(let* ((dir (ivy-state-directory ivy-last))
- (parts (nthcdr 3 (eshell-split-path dir)))
+ (parts (nthcdr 3 (funcall (if (fboundp 'eshell-split-filename)
+ ;; New name since Emacs 30.
+ #'eshell-split-filename
+ '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.
@@ -182,8 +193,9 @@ Return a list or string depending on input."
(defalias 'counsel--null-device
(if (fboundp 'null-device) #'null-device (lambda () null-device))
"Compatibility shim for Emacs 28 function `null-device'.")
+
+;;;; Async utility
-;;* Async Utility
(defvar counsel--async-time nil
"Store the time when a new process was started.
Or the time of the last minibuffer update.")
@@ -357,8 +369,9 @@ Update the minibuffer with the amount of lines collected
every
(let ((process (get-process (or name " *counsel*"))))
(when process
(delete-process process))))
+
+;;; Completion at point
-;;* Completion at point
(define-obsolete-function-alias 'counsel-el
#'complete-symbol "0.13.2 (2020-05-20)")
(define-obsolete-function-alias 'counsel-cl
@@ -368,7 +381,8 @@ Update the minibuffer with the amount of lines collected
every
(define-obsolete-function-alias 'counsel-clj
#'complete-symbol "0.13.2 (2020-05-20)")
-;;** `counsel-company'
+;;;; `counsel-company'
+
(defvar company-candidates)
(declare-function company-abort "ext:company")
(declare-function company-complete "ext:company")
@@ -399,7 +413,8 @@ Update the minibuffer with the amount of lines collected
every
(when annot
(company--clean-string annot)))))
-;;** `counsel-irony'
+;;;; `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")
@@ -432,9 +447,10 @@ Update the minibuffer with the amount of lines collected
every
(ivy-configure #'counsel-irony
:display-fn #'ivy-display-function-overlay)
+
+;;; Elisp symbols
+;;;; `counsel-describe-variable'
-;;* Elisp symbols
-;;** `counsel-describe-variable'
(defvar counsel-describe-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-.") #'counsel-find-symbol)
@@ -443,8 +459,8 @@ Update the minibuffer with the amount of lines collected
every
(ivy-set-actions
'counsel-describe-variable
- '(("I" counsel-info-lookup-symbol "info")
- ("d" counsel--find-symbol "definition")))
+ `(("I" ,#'counsel-info-lookup-symbol "info")
+ ("d" ,#'counsel--find-symbol "definition")))
(defvar counsel-describe-symbol-history ()
"History list for variable and function names.
@@ -455,24 +471,28 @@ Used by commands `counsel-describe-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)
+(function-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--push-xref-marker (&optional m)
+ "Compatibility shim for `xref-push-marker-stack'."
+ (static-if (require 'xref nil t)
+ ;; Added in Emacs 25.1.
+ (progn
+ (unless (fboundp 'xref-push-marker-stack)
+ (require 'xref))
+ (xref-push-marker-stack m))
+ (unless (boundp 'find-tag-marker-ring)
+ (require 'etags))
+ (unless (fboundp 'ring-insert)
+ (require 'ring))
+ (defvar find-tag-marker-ring)
+ (declare-function ring-insert "ring" (ring item))
+ (ring-insert find-tag-marker-ring (or m (point-marker)))))
(defun counsel--find-symbol (x)
"Find symbol definition that corresponds to string X."
@@ -537,11 +557,12 @@ Variables declared using `defcustom' are highlighted
according to
:parent 'counsel-describe-symbol
:display-transformer-fn #'counsel-describe-variable-transformer)
-;;** `counsel-describe-function'
+;;;; `counsel-describe-function'
+
(ivy-set-actions
'counsel-describe-function
- '(("I" counsel-info-lookup-symbol "info")
- ("d" counsel--find-symbol "definition")))
+ `(("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."
@@ -563,6 +584,10 @@ Variables declared using `defcustom' are highlighted
according to
(function-item ivy-thing-at-point)
(function-item ivy-function-called-at-point)))
+(defun counsel--describe-function (candidate)
+ "Pass string CANDIDATE to `counsel-describe-function-function'."
+ (funcall counsel-describe-function-function (intern candidate)))
+
;;;###autoload
(defun counsel-describe-function ()
"Forward to `describe-function'.
@@ -579,16 +604,16 @@ to `ivy-highlight-face'."
: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)))
+ :action #'counsel--describe-function
: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
+;;;; `counsel-describe-symbol'
+
+(defcustom counsel-describe-symbol-function 'describe-symbol
"Function to call to describe a symbol passed as parameter."
:type 'function)
@@ -599,6 +624,7 @@ to `ivy-highlight-face'."
(unless (functionp 'describe-symbol)
(user-error "This command requires Emacs 25.1 or later"))
(require 'help-mode)
+ (defvar describe-symbol-backends)
(let ((enable-recursive-minibuffers t))
(ivy-read "Describe symbol: " obarray
:predicate (lambda (sym)
@@ -622,7 +648,8 @@ to `ivy-highlight-face'."
`(("I" ,#'counsel-info-lookup-symbol "info")
("d" ,#'counsel--find-symbol "definition")))
-;;** `counsel-set-variable'
+;;;; `counsel-set-variable'
+
(defvar counsel-set-variable-history nil
"Store history for `counsel-set-variable'.")
@@ -746,7 +773,8 @@ With a prefix arg, restrict list to variables defined using
(when doc
(lv-delete-window)))))
-;;** `counsel-apropos'
+;;;; `counsel-apropos'
+
;;;###autoload
(defun counsel-apropos ()
"Show all matching symbols.
@@ -778,7 +806,8 @@ a symbol and how to search for them."
(ivy-configure 'counsel-apropos
:sort-fn #'ivy-string<)
-;;** `counsel-info-lookup-symbol'
+;;;; `counsel-info-lookup-symbol'
+
(defvar info-lookup-mode)
(declare-function info-lookup-guess-default "info-look")
(declare-function info-lookup->completions "info-look")
@@ -813,7 +842,8 @@ With prefix arg MODE a query for the symbol help mode is
offered."
(ivy-configure 'counsel-info-lookup-symbol
:sort-fn #'ivy-string<)
-;;** `counsel-M-x'
+;;;; `counsel-M-x'
+
(defface counsel-key-binding
'((t :inherit font-lock-keyword-face))
"Face used by `counsel-M-x' for key bindings."
@@ -968,10 +998,11 @@ when available, in that order of precedence."
(ivy-set-actions
'counsel-M-x
- `(("d" counsel--find-symbol "definition")
- ("h" ,(lambda (x) (funcall counsel-describe-function-function (intern x)))
"help")))
+ `(("d" ,#'counsel--find-symbol "definition")
+ ("h" ,#'counsel--describe-function "help")))
+
+;;;; `counsel-command-history'
-;;** `counsel-command-history'
(defun counsel-command-history-action-eval (cmd)
"Eval the command CMD."
(eval (read cmd) t))
@@ -994,7 +1025,8 @@ when available, in that order of precedence."
:action #'counsel-command-history-action-eval
:caller 'counsel-command-history))
-;;** `counsel-load-library'
+;;;; `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) "\\'"))
@@ -1051,9 +1083,10 @@ The libraries are offered from `load-path'."
(ivy-set-actions
'counsel-load-library
- '(("d" counsel--find-symbol "definition")))
+ `(("d" ,#'counsel--find-symbol "definition")))
+
+;;;; `counsel-find-library'
-;;** `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))))
@@ -1081,7 +1114,8 @@ The libraries are offered from `load-path'."
:keymap counsel-describe-map
:caller 'counsel-find-library)))
-;;** `counsel-load-theme'
+;;;; `counsel-load-theme'
+
(declare-function powerline-reset "ext:powerline")
(defun counsel-load-theme-action (x)
@@ -1106,7 +1140,8 @@ Usable with `ivy-resume', `ivy-next-line-and-call' and
:action #'counsel-load-theme-action
:caller 'counsel-load-theme))
-;;** `counsel-descbinds'
+;;;; `counsel-descbinds'
+
(ivy-set-actions
'counsel-descbinds
'(("d" counsel-descbinds-action-find "definition")
@@ -1191,7 +1226,8 @@ BUFFER defaults to the current one."
:history 'counsel-descbinds-history
:caller 'counsel-descbinds))
-;;** `counsel-describe-face'
+;;;; `counsel-describe-face'
+
(defcustom counsel-describe-face-function #'describe-face
"Function to call to describe a face or face name argument."
:type 'function)
@@ -1239,7 +1275,8 @@ back to the face of the character after point, and
finally the
'(("c" counsel-customize-face "customize")
("C" counsel-customize-face-other-window "customize other window")))
-;;** `counsel-faces'
+;;;; `counsel-faces'
+
(defvar counsel--faces-format "%-40s %s")
(defun counsel--faces-format-function (names)
@@ -1281,8 +1318,79 @@ selected face."
("C" counsel-customize-face-other-window "customize other window")
("h" counsel-highlight-with-face "highlight")))
-;;* Git
-;;** `counsel-git'
+;;;; Modes
+
+(defvar counsel-minor-history nil
+ "History for `counsel-minor'.")
+
+(defun counsel--minor-candidates ()
+ "Return completion alist for `counsel-minor'.
+
+The alist element is cons of minor mode string with its lighter
+and minor mode symbol."
+ (cl-mapcan
+ (let ((suffix (propertize " \"%s\"" 'face 'font-lock-string-face)))
+ (lambda (mode)
+ (when (and (boundp mode) (commandp mode))
+ (let ((lighter (cdr (assq mode minor-mode-alist))))
+ (list (cons (concat
+ (if (symbol-value mode) "-" "+")
+ (symbol-name mode)
+ (and lighter
+ (format suffix
+ (format-mode-line (cons t lighter)))))
+ mode))))))
+ minor-mode-list))
+
+;;;###autoload
+(defun counsel-minor ()
+ "Enable or disable minor mode.
+
+Disabled minor modes are prefixed with \"+\", and
+selecting one of these will enable it.
+Enabled minor modes are prefixed with \"-\", and
+selecting one of these will enable it.
+
+Additional actions:\\<ivy-minibuffer-map>
+
+ \\[ivy-dispatching-done] d: Go to minor mode definition
+ \\[ivy-dispatching-done] h: Describe minor mode"
+
+ (interactive)
+ (ivy-read "Minor modes (enable +mode or disable -mode): "
+ (counsel--minor-candidates)
+ :require-match t
+ :history 'counsel-minor-history
+ :action (lambda (x)
+ (call-interactively (cdr x)))))
+
+(ivy-configure 'counsel-minor
+ :initial-input "^+"
+ :sort-fn #'ivy-string<)
+
+(ivy-set-actions
+ 'counsel-minor
+ `(("d" ,(lambda (x) (find-function (cdr x))) "definition")
+ ("h" ,(lambda (x) (describe-function (cdr x))) "help")))
+
+;;;###autoload
+(defun counsel-major ()
+ (interactive)
+ (ivy-read "Major modes: " obarray
+ :predicate (lambda (f)
+ (and (commandp f)
+ (string-suffix-p "-mode" (symbol-name f))
+ (or (and (autoloadp (symbol-function f))
+ (let ((doc-split (help-split-fundoc
(documentation f) f)))
+ ;; major mode starters have no
arguments
+ (and doc-split (null (cdr (read (car
doc-split)))))))
+ (null (help-function-arglist f)))))
+ :action #'counsel-M-x-action
+ :caller 'counsel-major))
+
+;;; Git
+;;;; `counsel-git'
+
(defvar counsel-git-cmd "git ls-files -z --full-name --"
"Command for `counsel-git'.")
@@ -1353,6 +1461,10 @@ INITIAL-INPUT can be given as the initial minibuffer
input."
(let ((inhibit-read-only t))
(erase-buffer)
(dired-mode default-directory counsel-dired-listing-switches)
+ (defvar dired-sort-inhibit)
+ (defvar dired-subdir-alist)
+ (declare-function dired-insert-set-properties "dired")
+ (declare-function dired-move-to-filename "dired")
(insert " " default-directory ":\n")
(let ((point (point)))
(insert " " full-cmd "\n")
@@ -1374,7 +1486,8 @@ INITIAL-INPUT can be given as the initial minibuffer
input."
(forward-line 2)
(dired-move-to-filename)))))))
-;;** `counsel-git-grep'
+;;;; `counsel-git-grep'
+
(defvar counsel-git-grep-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-l") #'ivy-call-and-recenter)
@@ -1696,7 +1809,8 @@ When CMD is non-nil, prompt for a specific \"git grep\"
command."
(goto-char (point-min)))
(perform-replace from to t t nil))))))))))
-;;** `counsel-git-stash'
+;;;; `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."
@@ -1717,7 +1831,8 @@ done") "\n" t)))
:action #'counsel-git-stash-kill-action
:caller 'counsel-git-stash)))
-;;** `counsel-git-log'
+;;;; `counsel-git-log'
+
(defvar counsel-git-log-cmd "GIT_PAGER=cat git log --no-color --grep '%s'"
"Command used for \"git log\".")
@@ -1751,7 +1866,8 @@ done") "\n" t)))
'counsel-git-log
'(("v" counsel-git-log-show-commit-action "visit commit")))
-;;** `counsel-git-change-worktree'
+;;;; `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.
@@ -1799,7 +1915,8 @@ TREE is the selected candidate."
:require-match t
:caller 'counsel-git-change-worktree)))
-;;** `counsel-git-checkout'
+;;;; `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
@@ -1852,16 +1969,17 @@ currently checked out."
(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'
-;;* 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)
+ (define-key map `[remap ,#'undo] #'counsel-find-file-undo)
map))
(defun counsel-file-jump-from-find ()
@@ -1922,7 +2040,8 @@ choose between `yes-or-no-p' and `y-or-n-p'; otherwise
default to
(defun counsel-find-file-copy (x)
"Copy file X."
- (require 'dired-aux)
+ ;; Autoloaded by `dired'.
+ (declare-function dired-copy-file "dired-aux")
(counsel--find-file-1 "Copy file to: "
ivy--directory
(lambda (new-name)
@@ -1931,6 +2050,9 @@ choose between `yes-or-no-p' and `y-or-n-p'; otherwise
default to
(defun counsel-find-file-delete (x)
"Delete file X."
+ (defvar dired-recursive-deletes)
+ (declare-function dired-clean-up-after-deletion "dired")
+ (declare-function dired-delete-file "dired")
(when (or delete-by-moving-to-trash
;; `dired-delete-file', which see, already prompts for directories
(eq t (car (file-attributes x)))
@@ -1943,7 +2065,8 @@ choose between `yes-or-no-p' and `y-or-n-p'; otherwise
default to
(defun counsel-find-file-move (x)
"Move or rename file X."
- (require 'dired-aux)
+ ;; Autoloaded by `dired'.
+ (declare-function dired-rename-file "dired-aux")
(counsel--find-file-1 "Rename file to: "
ivy--directory
(lambda (new-name)
@@ -2063,8 +2186,9 @@ The preselect behavior can be customized via user options
(file-name-nondirectory buffer-file-name))))
(defun counsel--find-file-1 (prompt initial-input action caller)
+ (declare-function dired-current-directory "dired")
(let ((default-directory
- (if (eq major-mode 'dired-mode)
+ (if (derived-mode-p 'dired-mode)
(dired-current-directory)
default-directory)))
(ivy-read prompt #'read-file-name-internal
@@ -2082,6 +2206,7 @@ The preselect behavior can be customized via user options
"Forward to `find-file'.
When INITIAL-INPUT is non-nil, use it in the minibuffer during completion."
(interactive)
+ (require 'dired)
(defvar tramp-archive-enabled)
(let ((tramp-archive-enabled nil)
(default-directory (or initial-directory default-directory)))
@@ -2311,14 +2436,14 @@ result as a URL."
(format formatter word-at-point)))))
counsel-url-expansions-alist))))
-;;** `counsel-dired'
-(declare-function dired "dired")
+;;;; `counsel-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)
+ (require 'dired)
(let ((counsel--find-file-predicate #'file-directory-p))
(counsel--find-file-1
"Dired (directory): " initial-input
@@ -2328,7 +2453,8 @@ When INITIAL-INPUT is non-nil, use it in the minibuffer
during completion."
(ivy-configure 'counsel-dired
:parent 'read-file-name-internal)
-;;** `counsel-recentf'
+;;;; `counsel-recentf'
+
(defvar recentf-list)
(declare-function recentf-mode "recentf")
@@ -2344,7 +2470,6 @@
https://www.freedesktop.org/wiki/Specifications/desktop-bookmark-spec"))
(defun counsel-recentf ()
"Find a file on `recentf-list'."
(interactive)
- (require 'recentf)
(recentf-mode)
(ivy-read "Recentf: " (counsel-recentf-candidates)
:action (lambda (f)
@@ -2411,30 +2536,29 @@ For convenience, BEG and END default to `point-min' and
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)
+ (unless (eval-and-compile (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))))))
+ (when (fboundp 'dom-attr) ;; Pacify Emacs 24.
+ (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 #'buffer-file-name (buffer-list)))))
(nconc
@@ -2470,7 +2594,8 @@ This function uses the `dom' library from Emacs 25.1 or
later."
(ivy-append-face var 'ivy-highlight-face)
var))
-;;** `counsel-bookmark'
+;;;; `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."
@@ -2520,7 +2645,8 @@ By default `counsel-bookmark' opens a dired buffer for
directories."
("r" ,(counsel--apply-bookmark-fn #'counsel-find-file-as-root)
"open as root")))
-;;** `counsel-bookmarked-directory'
+;;;; `counsel-bookmarked-directory'
+
(defun counsel-bookmarked-directory--candidates ()
"Get a list of bookmarked directories sorted by file path."
(bookmark-maybe-load-default-file)
@@ -2543,15 +2669,16 @@ current value of `default-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")
+ `(("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'
+;;;; `counsel-file-register'
+
;;;###autoload
(defun counsel-file-register ()
"Search file in register.
@@ -2588,7 +2715,8 @@ can use `C-x r j i' to open that file."
'counsel-file-register
'(("j" find-file-other-window "other window")))
-;;** `counsel-locate'
+;;;; `counsel-locate'
+
(defcustom counsel-locate-cmd (cond ((memq system-type '(darwin berkeley-unix))
#'counsel-locate-cmd-noregex)
((and (eq system-type 'windows-nt)
@@ -2633,7 +2761,10 @@ string - the full shell command to run."
(defalias 'counsel-find-file-extern #'counsel-locate-action-extern)
-(declare-function dired-jump "dired-x")
+(eval-and-compile
+ ;; Autoloaded by `dired' since Emacs 28.
+ (unless (fboundp 'dired-jump)
+ (autoload 'dired-jump "dired-x" nil t)))
(defun counsel-locate-action-dired (x)
"Use `dired-jump' on X."
@@ -2735,7 +2866,8 @@ INITIAL-INPUT can be given as the initial minibuffer
input."
:unwind-fn #'counsel-delete-process
:exit-codes '(1 "Nothing found"))
-;;** `counsel-tracker'
+;;;; `counsel-tracker'
+
(defun counsel-tracker-function (input)
"Call the \"tracker\" shell command with INPUT."
(or
@@ -2764,7 +2896,8 @@ INITIAL-INPUT can be given as the initial minibuffer
input."
:display-transformer-fn #'counsel-tracker-transformer
:unwind-fn #'counsel-delete-process)
-;;** `counsel-fzf'
+;;;; `counsel-fzf'
+
(defvar counsel-fzf-cmd "fzf -f \"%s\""
"Command for `counsel-fzf'.")
@@ -2839,7 +2972,8 @@ FZF-PROMPT, if non-nil, is passed as `ivy-read' prompt
argument."
'(("x" counsel-locate-action-extern "xdg-open")
("d" counsel-locate-action-dired "dired")))
-;;** `counsel-dpkg'
+;;;; `counsel-dpkg'
+
;;;###autoload
(defun counsel-dpkg ()
"Call the \"dpkg\" shell command."
@@ -2860,7 +2994,8 @@ FZF-PROMPT, if non-nil, is passed as `ivy-read' prompt
argument."
(message (cdr x)))
:caller 'counsel-dpkg)))
-;;** `counsel-rpm'
+;;;; `counsel-rpm'
+
;;;###autoload
(defun counsel-rpm ()
"Call the \"rpm\" shell command."
@@ -2901,7 +3036,8 @@ FZF-PROMPT, if non-nil, is passed as `ivy-read' prompt
argument."
"Arguments for the `find-command' when using `counsel-file-jump'."
:type '(repeat string))
-;;** `counsel-file-jump'
+;;;; `counsel-file-jump'
+
(defvar counsel-file-jump-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "`") #'counsel-find-file-from-jump)
@@ -2943,11 +3079,12 @@ INITIAL-DIRECTORY, if non-nil, is used as the root
directory for search."
(dired (or (file-name-directory x) default-directory)))
"open in dired")))
+;;;; `counsel-dired-jump'
+
(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'
;;;###autoload
(defun counsel-dired-jump (&optional initial-input initial-directory)
"Jump to a directory (see `dired-jump') below the current directory.
@@ -2969,9 +3106,10 @@ INITIAL-DIRECTORY, if non-nil, is used as the root
directory for search."
:history 'file-name-history
:keymap counsel-find-file-map
:caller 'counsel-dired-jump)))
+
+;;; Grep
+;;;; `counsel-ag'
-;;* Grep
-;;** `counsel-ag'
(defvar counsel-ag-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-l") #'ivy-call-and-recenter)
@@ -3201,7 +3339,8 @@ Works for `counsel-git-grep', `counsel-ag', etc."
(counsel-grep-like-occur
counsel-ag-command))
-;;** `counsel-pt'
+;;;; `counsel-pt'
+
(defcustom counsel-pt-base-command "pt --nocolor --nogroup -e %s"
"Alternative to `counsel-ag-base-command' using pt."
:type 'string)
@@ -3222,7 +3361,8 @@ This uses `counsel-ag' with `counsel-pt-base-command'
instead of
:display-transformer-fn #'counsel-git-grep-transformer
:grep-p t)
-;;** `counsel-ack'
+;;;; `counsel-ack'
+
(defcustom counsel-ack-base-command
(concat
(file-name-nondirectory
@@ -3244,7 +3384,8 @@ This uses `counsel-ag' with `counsel-ack-base-command'
replacing
initial-input nil nil nil
:caller 'counsel-ack)))
-;;** `counsel-rg'
+;;;; `counsel-rg'
+
(defcustom counsel-rg-base-command
`("rg"
"--max-columns" "240"
@@ -3264,7 +3405,9 @@ Note: don't use single quotes for the regexp."
(defun counsel--rg-targets ()
"Return a list of files to operate on, based on `dired-mode' marks."
- (when (eq major-mode 'dired-mode)
+ (when (derived-mode-p 'dired-mode)
+ (declare-function dired-get-marked-files "dired")
+ (declare-function dired-toggle-marks "dired")
(let ((files
(dired-get-marked-files 'no-dir nil nil t)))
(when (or (cdr files)
@@ -3307,7 +3450,8 @@ Example input with inclusion and exclusion file patterns:
:grep-p t
:exit-codes '(1 "No matches found"))
-;;** `counsel-grep'
+;;;; `counsel-grep'
+
(defvar counsel-grep-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-l") #'ivy-call-and-recenter)
@@ -3451,7 +3595,8 @@ the initial search pattern."
'((counsel-grep . ivy-recompute-index-swiper-async-backward))))
(counsel-grep initial-input)))
-;;** `counsel-grep-or-swiper'
+;;;; `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,
@@ -3487,7 +3632,8 @@ When non-nil, INITIAL-INPUT is the initial search
pattern."
(save-buffer))
(counsel-grep initial-input)))
-;;** `counsel-grep-or-swiper-backward'
+;;;; `counsel-grep-or-swiper-backward'
+
;;;###autoload
(defun counsel-grep-or-swiper-backward (&optional initial-input)
"Call `swiper-backward' for small buffers and `counsel-grep-backward' for
@@ -3498,7 +3644,8 @@ large ones. When non-nil, INITIAL-INPUT is the initial
search pattern."
(counsel-grep . ivy-recompute-index-swiper-async-backward))))
(counsel-grep-or-swiper initial-input)))
-;;** `counsel-recoll'
+;;;; `counsel-recoll'
+
(defun counsel-recoll-function (str)
"Run recoll for STR."
(or
@@ -3539,9 +3686,10 @@ INITIAL-INPUT can be given as the initial minibuffer
input."
(ivy-configure 'counsel-recoll
:unwind-fn #'counsel-delete-process)
+
+;;; Org
+;;;; `counsel-org-tag'
-;;* Org
-;;** `counsel-org-tag'
(defvar counsel-org-tags nil
"Store the current list of tags.")
@@ -3549,13 +3697,11 @@ INITIAL-INPUT can be given as the initial minibuffer
input."
(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)
+ (if (fboundp 'org-make-tag-string)
;; >= Org 9.2
(org-make-tag-string (counsel--org-get-tags))
(with-no-warnings
@@ -3606,7 +3752,10 @@ INITIAL-INPUT can be given as the initial minibuffer
input."
(defvar org-agenda-bulk-marked-entries)
-(declare-function org-get-at-bol "org")
+;; Moved from `org' to `org-macs' in Emacs 27.
+(declare-function org-get-at-bol "org-macs")
+(declare-function org-trim "org-macs")
+
(declare-function org-agenda-error "org-agenda")
(defun counsel-org-tag-action (x)
@@ -3869,7 +4018,8 @@ version. Argument values are based on the
(version< org-version "9.1.1"))
2 0)))
-;;** `counsel-org-file'
+;;;; `counsel-org-file'
+
(declare-function org-attach-dir "org-attach")
(declare-function org-attach-file-list "org-attach")
(defvar org-attach-directory)
@@ -3903,40 +4053,48 @@ include attachments of other Org buffers."
:action #'counsel-locate-action-dired
:caller 'counsel-org-file))
-;;** `counsel-org-entity'
-(defvar org-entities)
-(defvar org-entities-user)
+;;;; `counsel-org-entity'
;;;###autoload
(defun counsel-org-entity ()
"Complete Org entities using Ivy."
(interactive)
(require 'org)
- (ivy-read "Entity: " (cl-loop for element in (append org-entities
org-entities-user)
- unless (stringp element)
- collect (cons
- (format "%20s | %20s | %20s | %s"
- (cl-first element) ; name
- (cl-second element) ; latex
- (cl-fourth element) ; html
- (cl-seventh element)) ; utf-8
- element))
+ (defvar org-entities)
+ (defvar org-entities-user)
+ (ivy-read "Entity: "
+ (cl-loop for element in (append org-entities org-entities-user)
+ when (consp element)
+ collect (cons
+ (format "%20s | %20s | %20s | %s"
+ (nth 0 element) ; Name.
+ (nth 1 element) ; LaTeX.
+ (nth 3 element) ; HTML.
+ (nth 6 element)) ; UTF-8.
+ element))
:require-match t
- :action '(1
- ("u" (lambda (candidate)
- (insert (cl-seventh (cdr candidate)))) "utf-8")
- ("o" (lambda (candidate)
- (insert "\\" (cl-first (cdr candidate))))
"org-entity")
- ("l" (lambda (candidate)
- (insert (cl-second (cdr candidate)))) "latex")
- ("h" (lambda (candidate)
- (insert (cl-fourth (cdr candidate)))) "html")
- ("a" (lambda (candidate)
- (insert (cl-fifth (cdr candidate)))) "ascii")
- ("L" (lambda (candidate)
- (insert (cl-sixth (cdr candidate))) "Latin-1")))))
-
-;;** `counsel-org-capture'
+ :action `(1
+ ("u" ,(lambda (candidate)
+ (insert (nth 6 (cdr candidate))))
+ "UTF-8")
+ ("o" ,(lambda (candidate)
+ (insert "\\" (nth 0 (cdr candidate))))
+ "Org entity")
+ ("l" ,(lambda (candidate)
+ (insert (nth 1 (cdr candidate))))
+ "LaTeX")
+ ("h" ,(lambda (candidate)
+ (insert (nth 3 (cdr candidate))))
+ "HTML")
+ ("a" ,(lambda (candidate)
+ (insert (nth 4 (cdr candidate))))
+ "ASCII")
+ ("L" ,(lambda (candidate)
+ (insert (nth 5 (cdr candidate))))
+ "Latin-1"))))
+
+;;;; `counsel-org-capture'
+
(defvar org-capture-templates)
(defvar org-capture-templates-contexts)
(declare-function org-contextualize-keys "org")
@@ -3995,19 +4153,26 @@ include attachments of other Org buffers."
(customize-variable 'org-capture-templates))
"customize org-capture-templates")))
-;;** `counsel-org-agenda-headlines'
+;;;; `counsel-org-agenda-headlines'
+
(defvar org-odd-levels-only)
-(declare-function org-set-startup-visibility "org")
-(declare-function org-show-entry "org")
(declare-function org-map-entries "org")
(declare-function org-heading-components "org")
(defun counsel-org-agenda-headlines-action-goto (headline)
"Go to the `org-mode' agenda HEADLINE."
(find-file (nth 1 headline))
- (org-set-startup-visibility)
+ (if (fboundp 'org-cycle-set-startup-visibility)
+ (org-cycle-set-startup-visibility)
+ ;; Obsolete alias since Org 9.6 / Emacs 29.
+ (with-no-warnings
+ (org-set-startup-visibility)))
(goto-char (nth 2 headline))
- (org-show-entry))
+ (if (fboundp 'org-fold-show-entry)
+ (org-fold-show-entry)
+ ;; Obsolete alias since Org 9.6 / Emacs 29.
+ (with-no-warnings
+ (org-show-entry))))
(ivy-set-actions
'counsel-org-agenda-headlines
@@ -4090,7 +4255,9 @@ This variable has no effect unless
:history 'counsel-org-agenda-headlines-history
:caller 'counsel-org-agenda-headlines)))
-;;** `counsel-org-link'
+;;;; `counsel-org-link'
+
+;; Moved from `org' to `ol' in Emacs 27.
(declare-function org-insert-link "ol")
(declare-function org-id-get-create "org-id")
@@ -4110,9 +4277,10 @@ This variable has no effect unless
:action #'counsel-org-link-action
:history 'counsel-org-link-history
:caller 'counsel-org-link))
+
+;;; Misc. Emacs
+;;;; `counsel-mark-ring'
-;; Misc. Emacs
-;;** `counsel-mark-ring'
(defface counsel--mark-ring-highlight
'((t :inherit highlight))
"Face for current `counsel-mark-ring' line."
@@ -4216,7 +4384,8 @@ Position of selected mark outside accessible part of
buffer")))
:unwind-fn #'counsel--mark-ring-unwind
:sort-fn #'ivy-string<)
-;;** `counsel-evil-marks'
+;;;; `counsel-evil-marks'
+
(defvar counsel-evil-marks-exclude-registers nil
"List of evil registers to not display in `counsel-evil-marks' by default.
Each member of the list should be a character (stored as an integer).")
@@ -4281,7 +4450,8 @@ When ARG is non-nil, display all active evil registers."
(message "No evil marks are active")))
(user-error "Required feature `evil' not installed or loaded")))
-;;** `counsel-package'
+;;;; `counsel-package'
+
(defvar package--initialized)
(defvar package-alist)
(defvar package-archive-contents)
@@ -4289,7 +4459,7 @@ When ARG is non-nil, display all active evil registers."
(defvar package-user-dir)
(declare-function package-installed-p "package")
(declare-function package-delete "package")
-(declare-function package-desc-extras "package")
+(declare-function package-desc-extras "package" t t)
(defvar counsel-package-history nil
"History for `counsel-package'.")
@@ -4373,16 +4543,16 @@ Additional actions:\\<ivy-minibuffer-map>
'(("d" counsel-package-action-describe "describe package")
("h" counsel-package-action-homepage "open package homepage")))
-;;** `counsel-tmm'
+;;;; `counsel-tmm'
+
(declare-function tmm-get-keymap "tmm" (elt &optional in-x-menu))
-(declare-function tmm--completion-table "tmm" (items))
(defalias 'counsel--menu-keymap
;; Added in Emacs 28.1.
(if (fboundp 'menu-bar-keymap)
#'menu-bar-keymap
- (autoload 'tmm-get-keybind "tmm")
- (declare-function tmm-get-keybind "tmm" (keyseq))
+ ;; Removed in Emacs 28.1.
+ (declare-function tmm-get-keybind "tmm" (keyseq) t)
(lambda () (tmm-get-keybind [menu-bar])))
"Compatibility shim for `menu-bar-keymap'.")
@@ -4394,9 +4564,14 @@ Additional actions:\\<ivy-minibuffer-map>
chosen-string)
(setq tmm-km-list nil)
(map-keymap (lambda (k v) (tmm-get-keymap (cons k v))) menu)
- (setq tmm-km-list (nreverse tmm-km-list))
- (setq out (ivy-read "Menu bar: " (tmm--completion-table tmm-km-list)
- :require-match t))
+ (let ((items (setq tmm-km-list (nreverse tmm-km-list))))
+ (setq out (ivy-read "Menu bar: "
+ ;; From `tmm--completion-table', removed in Emacs 31.
+ (lambda (str pred action)
+ (if (eq action 'metadata)
+ '(metadata (display-sort-function . identity))
+ (complete-with-action action items str pred)))
+ :require-match t)))
(setq choice (cdr (assoc out tmm-km-list)))
(setq chosen-string (car choice))
(setq choice (cdr choice))
@@ -4416,7 +4591,8 @@ Additional actions:\\<ivy-minibuffer-map>
(setq tmm-table-undef nil)
(counsel-tmm-prompt (counsel--menu-keymap)))
-;;** `counsel-yank-pop'
+;;;; `counsel-yank-pop'
+
(defcustom counsel-yank-pop-truncate-radius 2
"Number of context lines around `counsel-yank-pop' candidates."
:type 'integer)
@@ -4666,7 +4842,7 @@ Note: Duplicate elements of `kill-ring' are always
deleted."
:action #'counsel-yank-pop-action
:caller 'counsel-yank-pop)))
-(put #'counsel-yank-pop 'delete-selection 'yank)
+(function-put #'counsel-yank-pop 'delete-selection 'yank)
(ivy-configure 'counsel-yank-pop
:height 5
@@ -4677,7 +4853,8 @@ Note: Duplicate elements of `kill-ring' are always
deleted."
'(("d" counsel-yank-pop-action-remove "delete")
("r" counsel-yank-pop-action-rotate "rotate")))
-;;** `counsel-register'
+;;;; `counsel-register'
+
(defvar counsel-register-actions
'(("\\`buffer" . jump-to-register)
("\\`text" . insert-register)
@@ -4731,7 +4908,8 @@ matching the register's value description against a
regexp in
(ivy-configure 'counsel-register
:sort-fn #'ivy-string<)
-;;** `counsel-evil-registers'
+;;;; `counsel-evil-registers'
+
(defface counsel-evil-register-face
'((t :inherit counsel-outline-1))
"Face for highlighting `evil' registers in ivy."
@@ -4765,14 +4943,15 @@ S will be of the form \"[register]: content\"."
(insert
(replace-regexp-in-string "\\`\\[.*?]: " "" s t t))))
-;;** `counsel-imenu'
-(defvar imenu-auto-rescan)
-(defvar imenu-auto-rescan-maxout)
+;;;; `counsel-imenu'
+
(declare-function imenu--subalist-p "imenu")
(declare-function imenu--make-index-alist "imenu")
(defun counsel--imenu-candidates ()
(require 'imenu)
+ (defvar imenu-auto-rescan)
+ (defvar imenu-auto-rescan-maxout)
(let* ((imenu-auto-rescan t)
(imenu-auto-rescan-maxout (if current-prefix-arg
(buffer-size)
@@ -4842,7 +5021,8 @@ PREFIX is used to create the key."
:history 'counsel-imenu-history
:caller 'counsel-imenu))
-;;** `counsel-list-processes'
+;;;; `counsel-list-processes'
+
(defun counsel-list-processes-action-delete (x)
"Delete process X."
(delete-process x)
@@ -4874,7 +5054,8 @@ An extra action allows to switch to the process buffer."
("s" counsel-list-processes-action-switch "switch"))
:caller 'counsel-list-processes))
-;;** `counsel-ace-link'
+;;;; `counsel-ace-link'
+
(defun counsel-ace-link ()
"Use Ivy completion for `ace-link'."
(interactive)
@@ -4904,7 +5085,8 @@ An extra action allows to switch to the process buffer."
:require-match t
:caller 'counsel-ace-link))))
-;;** `counsel-minibuffer-history'
+;;;; `counsel-minibuffer-history'
+
;;;###autoload
(defun counsel-minibuffer-history ()
"Browse minibuffer history."
@@ -4917,7 +5099,8 @@ An extra action allows to switch to the process buffer."
(insert (substring-no-properties (car x))))
:caller 'counsel-minibuffer-history)))
-;;** `counsel-esh-history'
+;;;; `counsel-esh-history'
+
(defvar comint-input-ring-index)
(defvar eshell-history-index)
(defvar slime-repl-input-history-position)
@@ -5015,7 +5198,8 @@ An extra action allows to switch to the process buffer."
;; `counsel-slime-repl-history' within
;; `counsel--browse-history-action'.
-;;** `counsel-hydra-heads'
+;;;; `counsel-hydra-heads'
+
(defvar hydra-curr-body-fn)
(declare-function hydra-keyboard-quit "ext:hydra")
@@ -5039,7 +5223,9 @@ An extra action allows to switch to the process buffer."
(ivy-read "head: " head-names
:action (lambda (x) (call-interactively (cdr x))))
(hydra-keyboard-quit)))
-;;** `counsel-semantic'
+
+;;;; `counsel-semantic'
+
(declare-function semantic-tag-start "semantic/tag")
(declare-function semantic-tag-class "semantic/tag")
(declare-function semantic-tag-name "semantic/tag")
@@ -5117,8 +5303,7 @@ TREEP is used to expand internal nodes."
(counsel-semantic)
(counsel-imenu)))
-;;** `counsel-outline'
-(declare-function org-trim "org-macs")
+;;;; `counsel-outline'
(defcustom counsel-outline-face-style nil
"Determines how to style outline headings during completion.
@@ -5329,7 +5514,8 @@ the face to apply."
:caller (or (plist-get settings :caller)
'counsel-outline))))
-;;** `counsel-ibuffer'
+;;;; `counsel-ibuffer'
+
(defvar counsel-ibuffer--buffer-name nil
"Name of the buffer to use for `counsel-ibuffer'.")
@@ -5397,7 +5583,8 @@ the values are the corresponding buffer objects."
'(("j" counsel-ibuffer-visit-buffer-other-window "other window")
("v" counsel-ibuffer-visit-ibuffer "switch to Ibuffer")))
-;;** `counsel-switch-to-shell-buffer'
+;;;; `counsel-switch-to-shell-buffer'
+
(defun counsel--buffers-with-mode (mode)
"Return names of buffers with MODE as their `major-mode'."
(let (bufs)
@@ -5427,7 +5614,8 @@ If there is no such buffer, start a new `shell' with
NAME."
(reusable-frames . visible)))
(shell name)))
-;;** `counsel-unicode-char'
+;;;; `counsel-unicode-char'
+
(defvar counsel-unicode-char-history nil
"History for `counsel-unicode-char'.")
@@ -5484,7 +5672,8 @@ COUNT defaults to 1."
'counsel-unicode-char
'(("w" counsel-unicode-copy "copy")))
-;;** `counsel-colors'
+;;;; Colors
+
(defun counsel-colors-action-insert-hex (color)
"Insert the hexadecimal RGB value of COLOR."
(insert (get-text-property 0 'hex color)))
@@ -5493,7 +5682,8 @@ COUNT defaults to 1."
"Kill the hexadecimal RGB value of COLOR."
(kill-new (get-text-property 0 'hex color)))
-;;** `counsel-colors-emacs'
+;;;;; `counsel-colors-emacs'
+
(defvar counsel-colors-emacs-history ()
"History for `counsel-colors-emacs'.")
@@ -5586,7 +5776,8 @@ selected color."
'(("h" counsel-colors-action-insert-hex "insert hexadecimal value")
("H" counsel-colors-action-kill-hex "kill hexadecimal value")))
-;;** `counsel-colors-web'
+;;;;; `counsel-colors-web'
+
(defvar shr-color-html-colors-alist)
(defun counsel-colors--web-alist ()
@@ -5635,7 +5826,8 @@ selected color."
'(("h" counsel-colors-action-insert-hex "insert hexadecimal value")
("H" counsel-colors-action-kill-hex "kill hexadecimal value")))
-;;** `counsel-fonts'
+;;;; `counsel-fonts'
+
(defvar counsel-fonts-history ()
"History for `counsel-fonts'.")
@@ -5663,15 +5855,17 @@ You can insert or kill the name of the selected font."
(propertize "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
'face (list :family font-name))))
-;;** `counsel-kmacro'
+;;;; `counsel-kmacro'
+
(defvar counsel-kmacro-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-k") #'counsel-kmacro-kill)
map))
+;; Avoid (declare (modes ...)) warnings in Emacs < 28.
+(function-put #'counsel-kmacro-kill 'command-modes '(minibuffer-mode))
(defun counsel-kmacro-kill ()
"Kill the line, or delete the currently selected keyboard macro."
- (declare (modes minibuffer-mode))
(interactive)
(unless (window-minibuffer-p)
(user-error "No completion session is active"))
@@ -5911,7 +6105,8 @@ The existing CANDIDATE, its counter and format, are left
unchanged."
("v" ,#'counsel-kmacro-action-copy-initial-counter-value
"copy starting counter value")))
-;;** `counsel-geiser-doc-look-up-manual'
+;;;; `counsel-geiser-doc-look-up-manual'
+
(declare-function geiser-doc-manual-for-symbol "ext:geiser-doc")
(defvar geiser-completion-symbol-list-func)
@@ -5928,9 +6123,10 @@ The existing CANDIDATE, its counter and format, are left
unchanged."
:action (lambda (cand)
(geiser-doc-manual-for-symbol (intern cand)))
:caller 'counsel-geiser-doc-look-up-manual))
+
+;;; Misc. OS
+;;;; `counsel-rhythmbox'
-;;* Misc. OS
-;;** `counsel-rhythmbox'
(declare-function dbus-call-method "dbus")
(declare-function dbus-get-property "dbus")
@@ -6049,7 +6245,7 @@ The existing CANDIDATE, its counter and format, are left
unchanged."
("s" counsel-rhythmbox-toggle-shuffle "Shuffle on/off"))
:caller 'counsel-rhythmbox))
-;;** `counsel-linux-app'
+;;;; `counsel-linux-app'
;; Added in Emacs 26.1.
(require 'xdg nil t)
@@ -6215,7 +6411,8 @@ This function always returns its elements in a stable
order."
(when (file-exists-p dir)
(let ((dir (file-name-as-directory dir)))
;; Function `directory-files-recursively' added in Emacs 25.1.
- (dolist (file (directory-files-recursively dir "\\.desktop\\'"))
+ (dolist (file (and (fboundp 'directory-files-recursively)
+ (directory-files-recursively dir
"\\.desktop\\'")))
(let ((id (subst-char-in-string ?/ ?- (file-relative-name file
dir))))
(when (and (not (gethash id hash)) (file-readable-p file))
(push (cons id file) result)
@@ -6340,7 +6537,8 @@ When ARG is non-nil, ignore NoDisplay property in
*.desktop files."
:action #'counsel-linux-app-action-default
:caller 'counsel-linux-app))
-;;** `counsel-wmctrl'
+;;;; `counsel-wmctrl'
+
(defun counsel-wmctrl-action (x)
"Select the desktop window that corresponds to X."
(counsel--run "wmctrl" "-i" "-a" (cdr x)))
@@ -6457,7 +6655,8 @@ in the current window."
'(("x" counsel-open-buffer-file-externally "open externally")
("j" ivy--switch-buffer-other-window-action "other window")))
-;;** `counsel-compile'
+;;;; `counsel-compile'
+
(defvar counsel-compile-history nil
"History for `counsel-compile'.
@@ -6748,6 +6947,8 @@ This is determined by `counsel-compile-local-builds',
which see."
;; things like infer `default-directory' from 'cd's in the string.
(defun counsel-compile--update-history (_proc)
"Update `counsel-compile-history' from the compilation state."
+ (defvar compilation-arguments)
+ (defvar compilation-environment)
(let* ((srcdir (counsel--compile-root))
(blddir default-directory)
(bldenv compilation-environment)
@@ -6776,6 +6977,7 @@ edited the command, thus losing our embedded state.")
If CMD has the `recursive' property set we call `counsel-compile'
again to further refine the compile options in the directory
specified by the `blddir' property."
+ (defvar compilation-environment)
(let ((blddir (get-text-property 0 'blddir cmd))
(bldenv (get-text-property 0 'bldenv cmd)))
(if (get-text-property 0 'recursive cmd)
@@ -6812,7 +7014,8 @@ handling for the `counsel-compile' metadata."
;; operation which doesn't include the metadata we want.
(defvar counsel-compile-map
(let ((map (make-sparse-keymap)))
- (define-key map [remap ivy-insert-current] #'counsel-compile-edit-command)
+ (define-key map `[remap ,#'ivy-insert-current]
+ #'counsel-compile-edit-command)
map)
"Additional ivy keybindings during command selection.")
@@ -6824,6 +7027,8 @@ Additional actions:
\\{counsel-compile-map}"
(interactive)
+ (require 'compile)
+ (require 'dired) ;; For face `dired-directory'.
(setq counsel-compile--current-build-dir (or dir
(counsel--compile-root)
default-directory))
@@ -6882,134 +7087,155 @@ Additional actions:
(ivy-configure 'counsel-compile-env
:format-fn #'counsel-compile-env--format-hint)
-;;** `counsel-minor'
-(defvar counsel-minor-history nil
- "History for `counsel-minor'.")
-
-(defun counsel--minor-candidates ()
- "Return completion alist for `counsel-minor'.
-
-The alist element is cons of minor mode string with its lighter
-and minor mode symbol."
- (cl-mapcan
- (let ((suffix (propertize " \"%s\"" 'face 'font-lock-string-face)))
- (lambda (mode)
- (when (and (boundp mode) (commandp mode))
- (let ((lighter (cdr (assq mode minor-mode-alist))))
- (list (cons (concat
- (if (symbol-value mode) "-" "+")
- (symbol-name mode)
- (and lighter
- (format suffix
- (format-mode-line (cons t lighter)))))
- mode))))))
- minor-mode-list))
-
-;;;###autoload
-(defun counsel-minor ()
- "Enable or disable minor mode.
-
-Disabled minor modes are prefixed with \"+\", and
-selecting one of these will enable it.
-Enabled minor modes are prefixed with \"-\", and
-selecting one of these will enable it.
-
-Additional actions:\\<ivy-minibuffer-map>
-
- \\[ivy-dispatching-done] d: Go to minor mode definition
- \\[ivy-dispatching-done] h: Describe minor mode"
-
- (interactive)
- (ivy-read "Minor modes (enable +mode or disable -mode): "
- (counsel--minor-candidates)
- :require-match t
- :history 'counsel-minor-history
- :action (lambda (x)
- (call-interactively (cdr x)))))
-
-(ivy-configure 'counsel-minor
- :initial-input "^+"
- :sort-fn #'ivy-string<)
-
-(ivy-set-actions
- 'counsel-minor
- `(("d" ,(lambda (x) (find-function (cdr x))) "definition")
- ("h" ,(lambda (x) (describe-function (cdr x))) "help")))
-
-;;;###autoload
-(defun counsel-major ()
- (interactive)
- (ivy-read "Major modes: " obarray
- :predicate (lambda (f)
- (and (commandp f)
- (string-suffix-p "-mode" (symbol-name f))
- (or (and (autoloadp (symbol-function f))
- (let ((doc-split (help-split-fundoc
(documentation f) f)))
- ;; major mode starters have no
arguments
- (and doc-split (null (cdr (read (car
doc-split)))))))
- (null (help-function-arglist f)))))
- :action #'counsel-M-x-action
- :caller 'counsel-major))
-
-;;** `counsel-search'
-(declare-function request "ext:request")
+;;;; `counsel-search'
(defcustom counsel-search-engine 'ddg
"The search engine choice in `counsel-search-engines-alist'."
:type '(choice
- (const ddg)
- (const google)))
+ (const :tag "DuckDuckGo" ddg)
+ (const :tag "Google" google)))
(defcustom counsel-search-engines-alist
- '((google
- "http://suggestqueries.google.com/complete/search"
- "https://www.google.com/search?q="
- counsel--search-request-data-google)
- (ddg
+ '((ddg
"https://duckduckgo.com/ac/"
"https://duckduckgo.com/html/?q="
- counsel--search-request-data-ddg))
- "Search engine parameters for `counsel-search'."
+ counsel--search-request-data-ddg)
+ (google
+ "https://suggestqueries.google.com/complete/search"
+ "https://www.google.com/search?q="
+ counsel--search-request-data-google))
+ "List of search engine parameters for `counsel-search'.
+Each element is of the form (SYMBOL SUGGEST BROWSE EXTRACT), where:
+SYMBOL identifies the search engine, as per `counsel-search-engine'.
+SUGGEST is the URL to query for suggestions.
+BROWSE is the URL prefix for visiting the selected result.
+EXTRACT is a function that takes the object parsed from the SUGGEST
+ endpoint and transforms it into a set of Ivy candidates."
+ :package-version '(counsel . "0.16.0")
:type '(alist :key-type symbol :value-type (list string string function)))
(defun counsel--search-request-data-google (data)
- (mapcar #'identity (aref data 1)))
+ "Extract Google suggestions from parsed JSON DATA.
+Expects input of the form [\"a\" [\"ab\" \"ac\"] ...]."
+ (append (aref data 1) ()))
(defun counsel--search-request-data-ddg (data)
+ "Extract DuckDuckGo suggestions from parsed JSON DATA.
+Expects input of the form [((phrase . \"ab\")) ...]."
(mapcar #'cdar data))
+(defvar counsel--native-json)
+(put 'counsel--native-json 'variable-documentation
+ "Non-nil if Emacs supports JSON natively, or void.")
+
+(defun counsel--search-update (extract str type)
+ "Call EXTRACT on JSON STR of Content-TYPE."
+ (unless (fboundp 'mail-header-parse-content-type)
+ (require 'mail-parse))
+ (declare-function json-parse-string "json.c")
+ (declare-function json-read-from-string "json")
+ (declare-function mail-content-type-get "mail-parse")
+ (declare-function mail-header-parse-content-type "mail-parse")
+ (let* ((ct (and type (mail-header-parse-content-type type)))
+ (coding (coding-system-from-name (mail-content-type-get ct
'charset))))
+ (when coding
+ (setq str (decode-coding-string str coding t))))
+ (let ((obj (if counsel--native-json
+ (json-parse-string str :object-type 'alist)
+ (defvar json-array-type)
+ (defvar json-object-type)
+ (let ((json-array-type 'vector)
+ (json-object-type 'alist))
+ (json-read-from-string str)))))
+ (ivy-update-candidates (funcall extract obj))))
+
+(defun counsel--search-plz (url extract)
+ "Fetch URL with `plz' and EXTRACT its JSON payload."
+ (declare-function plz "ext:plz")
+ (declare-function plz-response-body "ext:plz")
+ (declare-function plz-response-headers "ext:plz")
+ ;; Doesn't handle Content-Type, so defer decoding+parsing until :then.
+ ;; (See URL `https://github.com/alphapapa/plz.el/pull/66'.)
+ ;; Ask for a `plz-response' object because it already contains the parsed
+ ;; headers (though just widening the response buffer could be quicker).
+ (plz 'get url :as 'response :decode nil :noquery t
+ :then (lambda (response)
+ (let* ((heads (plz-response-headers response))
+ (body (plz-response-body response))
+ (ct (cdr (assq 'content-type heads))))
+ (counsel--search-update extract body ct)))))
+
+(defun counsel--search-request (url extract)
+ "Fetch URL with `request' and EXTRACT its JSON payload."
+ (declare-function request "ext:request")
+ (declare-function request-response-header "ext:request")
+ ;; Doesn't handle Content-Type (expects coding system a priori),
+ ;; so defer decoding+parsing until :success.
+ (request url :type "GET"
+ :success (cl-function
+ (lambda (&key data response &allow-other-keys)
+ (let ((ct (request-response-header response "content-type")))
+ (counsel--search-update extract data ct))))))
+
+(defvar counsel--search-backend)
+(put 'counsel--search-backend 'variable-documentation
+ "Feature symbol indicating available HTTP library, or void.
+Valid values are the keys of `counsel--search-backends'.")
+
+(defvar counsel--search-backends
+ `((plz ,#'counsel--search-plz)
+ (request ,#'counsel--search-request))
+ "List of (BACKEND GETTER) for `counsel-search'.
+BACKEND is a feature symbol like `counsel--search-backend'.
+GETTER is a function taking a URL and an EXTRACT function as in
+ `counsel-search-engines-alist'.")
+
(defun counsel-search-function (input)
"Create a request to a search engine with INPUT.
Return 0 tells `ivy--exhibit' not to update the minibuffer.
We update it in the callback with `ivy-update-candidates'."
(or
(ivy-more-chars)
- (let ((engine (cdr (assoc counsel-search-engine
counsel-search-engines-alist))))
- (request
- (nth 0 engine)
- :type "GET"
- :params (list
- (cons "client" "firefox")
- (cons "q" input))
- :parser 'json-read
- :success (cl-function
- (lambda (&key data &allow-other-keys)
- (ivy-update-candidates
- (funcall (nth 2 engine) data)))))
+ (let* ((backend (assq counsel--search-backend counsel--search-backends))
+ (engine (assq counsel-search-engine counsel-search-engines-alist))
+ (suggest (nth 1 engine))
+ (extract (nth 3 engine))
+ (url (concat suggest (if (ivy--string-search "?" suggest) "&" "?")
+ ;; FIXME: `client' needed only for `google'?
+ (url-build-query-string `(("client" "firefox")
+ ("q" ,input))))))
+ ;; Do we need to cancel requests already in flight?
+ (funcall (nth 1 backend) url extract)
0)))
-(defun counsel-search-action (x)
- "Search for X."
- (browse-url
- (concat
- (nth 2 (assoc counsel-search-engine counsel-search-engines-alist))
- (url-hexify-string x))))
+(defun counsel-search-action (candidate)
+ "Browse the search results for `counsel-search' CANDIDATE."
+ (let ((engine (assq counsel-search-engine counsel-search-engines-alist)))
+ (browse-url (concat (nth 2 engine) (url-hexify-string candidate)))))
(defun counsel-search ()
- "Ivy interface for dynamically querying a search engine."
+ "Ivy interface for querying a search engine.
+Dynamically displays search suggestions for the current input.
+The user options `counsel-search-engine' and
+`counsel-search-engines-alist' determine the engine."
(interactive)
- (require 'request)
- (require 'json)
+ (unless (boundp 'counsel--search-backend)
+ (setq counsel--search-backend
+ ;; `plz' is on GNU ELPA; `request' on NonGNU ELPA.
+ (or (require 'plz nil t)
+ (require 'request nil t)
+ (user-error
+ "Required package `plz' (or `request') not installed"))))
+ ;; - Emacs 27: optional native JSON support.
+ ;; - Emacs 28: `json-available-p'.
+ ;; - Emacs 30: unconditional native JSON support.
+ ;; That means the following sets `counsel--native-json' to nil even for
+ ;; Emacs 27 with native JSON support, in the interest of simplicity.
+ (or (boundp 'counsel--native-json)
+ (setq counsel--native-json
+ (and (fboundp 'json-available-p)
+ (json-available-p)))
+ (require 'json))
(ivy-read "search: " #'counsel-search-function
:action #'counsel-search-action
:dynamic-collection t
@@ -7018,7 +7244,13 @@ We update it in the callback with
`ivy-update-candidates'."
(define-obsolete-function-alias 'counsel-google
#'counsel-search "0.13.2 (2019-10-17)")
-;;** `counsel-compilation-errors'
+;;;; `counsel-compilation-errors'
+
+(declare-function compilation--message->loc "compile" t t)
+(declare-function compilation-buffer-p "compile")
+(declare-function compilation-next-single-property-change "compile")
+(declare-function compile-goto-error "compile")
+
(defun counsel--compilation-errors-buffer (buf)
(with-current-buffer buf
(let ((res nil)
@@ -7052,12 +7284,14 @@ We update it in the callback with
`ivy-update-candidates'."
(defun counsel-compilation-errors ()
"Compilation errors."
(interactive)
+ (require 'compile)
(ivy-read "compilation errors: " (counsel-compilation-errors-cands)
:require-match t
:action #'counsel-compilation-errors-action
:history 'counsel-compilation-errors-history))
-;;** `counsel-flycheck'
+;;;; `counsel-flycheck'
+
(defvar flycheck-current-errors)
(declare-function flycheck-error-filename "ext:flycheck")
(declare-function flycheck-error-line "ext:flycheck")
@@ -7107,34 +7341,33 @@ We update it in the callback with
`ivy-update-candidates'."
:require-match t
:action #'counsel-flycheck-errors-action
:history 'counsel-flycheck-errors-history))
+
+;;; `counsel-mode'
-
-;;* `counsel-mode'
(defvar counsel-mode-map
(let ((map (make-sparse-keymap)))
- (dolist (binding
- '((execute-extended-command . counsel-M-x)
- (describe-bindings . counsel-descbinds)
- (describe-function . counsel-describe-function)
- (describe-variable . counsel-describe-variable)
- (describe-symbol . counsel-describe-symbol)
- (apropos-command . counsel-apropos)
- (describe-face . counsel-describe-face)
- (list-faces-display . counsel-faces)
- (find-file . counsel-find-file)
- (find-library . counsel-find-library)
- (imenu . counsel-imenu)
- (load-library . counsel-load-library)
- (load-theme . counsel-load-theme)
- (yank-pop . counsel-yank-pop)
- (info-lookup-symbol . counsel-info-lookup-symbol)
- (pop-to-mark-command . counsel-mark-ring)
- (geiser-doc-look-up-manual . counsel-geiser-doc-look-up-manual)
- (bookmark-jump . counsel-bookmark)))
- (define-key map (vector 'remap (car binding)) (cdr binding)))
+ (define-key map `[remap ,#'execute-extended-command] #'counsel-M-x)
+ (define-key map `[remap ,#'describe-bindings] #'counsel-descbinds)
+ (define-key map `[remap ,#'describe-function] #'counsel-describe-function)
+ (define-key map `[remap ,#'describe-variable] #'counsel-describe-variable)
+ (define-key map [remap describe-symbol] #'counsel-describe-symbol)
+ (define-key map `[remap ,#'apropos-command] #'counsel-apropos)
+ (define-key map `[remap ,#'describe-face] #'counsel-describe-face)
+ (define-key map `[remap ,#'list-faces-display] #'counsel-faces)
+ (define-key map `[remap ,#'find-file] #'counsel-find-file)
+ (define-key map `[remap ,#'find-library] #'counsel-find-library)
+ (define-key map `[remap ,#'imenu] #'counsel-imenu)
+ (define-key map `[remap ,#'load-library] #'counsel-load-library)
+ (define-key map `[remap ,#'load-theme] #'counsel-load-theme)
+ (define-key map `[remap ,#'yank-pop] #'counsel-yank-pop)
+ (define-key map `[remap ,#'info-lookup-symbol]
#'counsel-info-lookup-symbol)
+ (define-key map `[remap ,#'pop-to-mark-command] #'counsel-mark-ring)
+ (define-key map [remap geiser-doc-look-up-manual]
+ #'counsel-geiser-doc-look-up-manual)
+ (define-key map `[remap ,#'bookmark-jump] #'counsel-bookmark)
map)
- "Map for `counsel-mode'.
-Remaps built-in functions to counsel replacements.")
+ "Keymap for `counsel-mode'.
+Remaps built-in and external functions to Counsel replacements.")
(defcustom counsel-mode-override-describe-bindings nil
"Whether to override `describe-bindings' when `counsel-mode' is active."
diff --git a/targets/elpa.el b/targets/elpa.el
new file mode 100644
index 0000000000..e80dc6a981
--- /dev/null
+++ b/targets/elpa.el
@@ -0,0 +1,125 @@
+;;; targets/elpa.el --- Optional Ivy dependencies -*- lexical-binding: t -*-
+
+;; Copyright (C) 2019-2025 Free Software Foundation, Inc.
+
+;; This program 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 of the License, 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.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'package)
+
+(defvar ivy--elpa-stable
+ (or (getenv "ELPA_STABLE")
+ (getenv "MELPA_STABLE"))
+ "Non-nil if GNU ELPA should be used instead of GNU-devel ELPA.")
+
+(defvar ivy--elpa-dir "~/.elpa"
+ "Parent directory for installing optional dependencies.")
+
+(defvar ivy--elpa-user-dir
+ (expand-file-name
+ (format "%s%s/elpa" emacs-version (if ivy--elpa-stable "-stable" ""))
+ ivy--elpa-dir)
+ "Instance-specific value for `package-user-dir'.")
+
+;; FIXME: Switch to `gnu' once https://bugs.gnu.org/76264 is resolved.
+(defvar ivy--elpa-archive 'melpa
+ "Preferred ELPA archive; keys `ivy--elpa-archives'.")
+
+(defvar ivy--elpa-archives
+ ;; Check default value rather than `gnutls-available-p': even when
+ ;; the latter is non-nil my Emacs 24.5 fails with https://.
+ (let ((s (if (string-prefix-p "https" (cdar package-archives)) "s" "")))
+ `((gnu
+ ("gnu" . ,(format "http%s://elpa.gnu.org/%s/"
+ s (if ivy--elpa-stable "packages" "devel")))
+ ;; For `wgrep'.
+ ("nongnu" . ,(format "http%s://elpa.nongnu.org/nongnu%s/"
+ s (if ivy--elpa-stable "" "-devel"))))
+ (melpa
+ ("melpa" . ,(format "https://%smelpa.org/packages/"
+ (if ivy--elpa-stable "stable." ""))))))
+ "Map ELPA archive symbols to their `package-archives'.")
+
+(defvar ivy--elpa-pkgs
+ '(avy
+ hydra
+ wgrep)
+ "List of optional (or development) package dependencies.")
+
+(defvar ivy--elpa-activated nil
+ "Non-nil if `ivy--elpa-activate' succeeded.")
+
+(defvar ivy--elpa-refreshed nil
+ "Non-nil if `ivy--elpa-refresh' succeeded.")
+
+(defun ivy--elpa-activate ()
+ "Ensure packages under `ivy--elpa-dir' are activated."
+ (unless ivy--elpa-activated
+ (setq package-user-dir ivy--elpa-user-dir)
+ (let ((msg (format "Activating packages in %s" package-user-dir)))
+ (message "%s..." msg)
+ (package-initialize)
+ (message "%s...done" msg))
+ (setq ivy--elpa-activated t)))
+
+(defun ivy--elpa-refresh ()
+ "Ensure archive contents are refreshed."
+ (defvar gnutls-algorithm-priority)
+ (unless ivy--elpa-refreshed
+ (let ((archive ivy--elpa-archive))
+ (setq package-archives (cdr (assq archive ivy--elpa-archives)))
+ (and (eq archive 'melpa)
+ (version< emacs-version "26.3")
+ ;; See https://melpa.org/#/getting-started.
+ (setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")))
+ (package-refresh-contents)
+ (setq ivy--elpa-refreshed (and package-archive-contents t))))
+
+(defun ivy--elpa-install-pkg (pkg)
+ "Compatibility shim for Emacs 25 `package-install'."
+ (condition-case nil
+ (package-install pkg t)
+ (wrong-number-of-arguments
+ (package-install pkg))))
+
+(defun ivy--elpa-install ()
+ "Install any missing `ivy--elpa-pkgs' with demoted errors."
+ (ivy--elpa-activate)
+ (ivy--elpa-refresh)
+ (let ((msg-all (format "Installing in %s" package-user-dir))
+ any-ok any-err)
+ (message "%s..." msg-all)
+ (dolist (pkg ivy--elpa-pkgs)
+ (unless (package-installed-p pkg)
+ (let ((msg (format "Installing %s" pkg))
+ err)
+ (message "%s..." msg)
+ (condition-case-unless-debug e
+ (ivy--elpa-install-pkg pkg)
+ (error (message "Error: %s" (error-message-string e))
+ (message "%s...INCOMPLETE" msg)
+ (setq any-err t)
+ (setq err e)))
+ (unless err
+ (message "%s...done" msg)
+ (setq any-ok t)))))
+ (message "%s...%s" msg-all
+ (cond (any-err "INCOMPLETE")
+ (any-ok "done")
+ (t "already present")))))
+
+;; TODO: upgrade-deps target?
+
+(provide 'targets/elpa)