branch: externals/ivy commit 33b82a09db63abba6fa248ae027e343dda11b368 Author: Basil L. Contovounesios <ba...@contovou.net> Commit: Basil L. Contovounesios <ba...@contovou.net>
Try to load libraries more lazily On my system this reduces the time for loading Ivy from ~50ms to ~13ms, and Counsel from ~180ms to ~80ms (#3019). * colir.el: Don't load color library; autoload color-rgb-to-hex instead. * counsel.el: Don't load 'compile' and 'dired' at top level. Autoload dired-jump in older Emacs versions. (static-if): Also add polyfill here. (counsel--push-xref-marker, counsel-find-file, counsel-dired) (counsel-compile, counsel-compilation-errors): Load library dependencies only when called. (counsel-cmd-to-dired, counsel-find-file-delete) (counsel-compile--update-history, counsel-compile--action): Declare library definitions used. (counsel-find-file-copy, counsel-find-file-move): Depend on Dired's autoloads instead of loading dired-aux. (counsel--find-file-1, counsel--rg-targets): Use derived-mode-p. (counsel-recentf, counsel-buffer-or-recentf-candidates): Remove redundant require. (counsel--imenu-candidates): Limit dynvar scope. * ivy-overlay.el: Reword Commentary. Remove redundant defvar declarations. (ivy-cursor): Move face from here... * ivy-faces.el: ...to here, with all the other Ivy faces. * ivy.el: Autoload ivy-overlay entrypoints instead of loading the library. Don't load ring or delsel libraries; similarly depend on autoloading. (ivy--regex-function): #'-quote function symbol. (ivy--flx-available-p): New variable and function. (ivy--sort, ivy--recompute-index, ivy--highlight-fuzzy): Use it to defer loading 'flx' until it's needed. (ivy-reverse-i-search-kill, ivy-history-contents): Declare non-autoloaded ring.el functions. --- colir.el | 6 ++++- counsel.el | 85 +++++++++++++++++++++++++++++++++++++++++----------------- ivy-faces.el | 7 +++++ ivy-overlay.el | 16 +++-------- ivy.el | 38 +++++++++++++++++--------- 5 files changed, 101 insertions(+), 51 deletions(-) diff --git a/colir.el b/colir.el index a30046f0cc..a06e49584d 100644 --- a/colir.el +++ b/colir.el @@ -32,7 +32,11 @@ ;;; Code: (require 'cl-lib) -(require 'color) + +(eval-and-compile + ;; Autoloaded since Emacs 31. + (unless (fboundp 'color-rgb-to-hex) + (autoload 'color-rgb-to-hex "color"))) (defcustom colir-compose-method #'colir-compose-alpha "The method `colir-blend' uses to compose two color channels." diff --git a/counsel.el b/counsel.el index d01007f1c8..33dcbda9e3 100644 --- a/counsel.el +++ b/counsel.el @@ -44,12 +44,19 @@ (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 @@ -462,17 +469,21 @@ Used by commands `counsel-describe-symbol', (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." @@ -1357,6 +1368,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") @@ -1926,7 +1941,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) @@ -1935,6 +1951,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))) @@ -1947,7 +1966,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) @@ -2067,8 +2087,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 @@ -2086,6 +2107,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))) @@ -2316,13 +2338,13 @@ result as a URL." 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) + (require 'dired) (let ((counsel--find-file-predicate #'file-directory-p)) (counsel--find-file-1 "Dired (directory): " initial-input @@ -2348,7 +2370,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) @@ -2438,7 +2459,6 @@ This function uses the `dom' library from Emacs 25.1 or later." (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 @@ -2637,7 +2657,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." @@ -3268,7 +3291,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) @@ -4777,13 +4802,13 @@ S will be of the form \"[register]: content\"." (replace-regexp-in-string "\\`\\[.*?]: " "" s t t)))) ;;** `counsel-imenu' -(defvar imenu-auto-rescan) -(defvar imenu-auto-rescan-maxout) (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) @@ -6761,6 +6786,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) @@ -6789,6 +6816,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) @@ -6838,6 +6866,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)) @@ -7123,6 +7153,12 @@ The user options `counsel-search-engine' and #'counsel-search "0.13.2 (2019-10-17)") ;;** `counsel-compilation-errors' + +(declare-function compilation--message->loc "compile") +(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) @@ -7156,6 +7192,7 @@ The user options `counsel-search-engine' and (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 diff --git a/ivy-faces.el b/ivy-faces.el index 1a25cfc0a9..5f76ba952e 100644 --- a/ivy-faces.el +++ b/ivy-faces.el @@ -27,6 +27,13 @@ :group 'ivy :group 'faces) +(defface ivy-cursor + '((((class color) (background light)) + :background "black" :foreground "white") + (((class color) (background dark)) + :background "white" :foreground "black")) + "Cursor face for inline completion.") + (defface ivy-current-match '((((class color) (background light)) :background "#1a4b77" :foreground "white" :extend t) diff --git a/ivy-overlay.el b/ivy-overlay.el index f52b5ecbee..d25dc6d9c2 100644 --- a/ivy-overlay.el +++ b/ivy-overlay.el @@ -20,9 +20,9 @@ ;;; Commentary: -;; This package allows to setup Ivy's completion at point to actually -;; show the candidates and the input at point, instead of in the -;; minibuffer. +;; Normally, Ivy displays completion candidates and entered text in +;; the minibuffer. This file enables in-buffer completion to be +;; displayed at point instead. ;;; Code: @@ -30,14 +30,6 @@ (require 'cl-lib) (require 'subr-x)) -(defface ivy-cursor - '((((class color) (background light)) - :background "black" :foreground "white") - (((class color) (background dark)) - :background "white" :foreground "black")) - "Cursor face for inline completion." - :group 'ivy-faces) - (defvar ivy--old-cursor-type t) (defvar ivy-overlay-at nil @@ -90,12 +82,10 @@ Then attach the overlay to the character before point." (declare-function org-current-level "org") (declare-function org-at-heading-p "org") (defvar org-indent-indentation-per-level) -(defvar ivy-height) (defvar ivy-last) (defvar ivy-text) (defvar ivy-completion-beg) (declare-function ivy--get-window "ivy") -(declare-function ivy-state-current "ivy") (declare-function ivy-state-window "ivy") (defun ivy-overlay--current-column () diff --git a/ivy.el b/ivy.el index 3be4b96867..e54bc02899 100644 --- a/ivy.el +++ b/ivy.el @@ -40,11 +40,11 @@ ;;; Code: (require 'colir) -(require 'ivy-overlay) (require 'ivy-faces) +(autoload 'ivy-overlay-cleanup "ivy-overlay") +(autoload 'ivy-display-function-overlay "ivy-overlay") (require 'cl-lib) -(require 'ring) (eval-when-compile (require 'subr-x) @@ -311,7 +311,11 @@ This is a global variable that is set by ivy functions for use in action functions.") ;;* Keymap -(require 'delsel) + +(autoload 'minibuffer-keyboard-quit "delsel" nil t) +(autoload 'hydra-ivy/body "ivy-hydra" nil t) +(autoload 'ivy-hydra-read-action "ivy-hydra" nil t) + (defun ivy-define-key (keymap key def) "Forward to (`define-key' KEYMAP KEY DEF). Remove DEF from `counsel-M-x' list." @@ -376,8 +380,6 @@ Remove DEF from `counsel-M-x' list." (ivy-define-key map "$" #'ivy-magic-read-file-env) map) "Keymap used in the minibuffer.") -(autoload 'hydra-ivy/body "ivy-hydra" "" t) -(autoload 'ivy-hydra-read-action "ivy-hydra" "" t) (defvar ivy-mode-map (let ((map (make-sparse-keymap))) @@ -488,7 +490,7 @@ This allows RET to reverse consecutive DEL.") (defvar ivy-regex "" "Store the regex value that corresponds to `ivy-text'.") -(defvar ivy--regex-function 'ivy--regex +(defvar ivy--regex-function #'ivy--regex "Current function for building a regex.") (defun ivy-set-text (str) @@ -3773,7 +3775,12 @@ The alist VAL is a sorting function with the signature of (let ((default-directory ivy--directory)) (sort (copy-sequence candidates) #'file-newer-than-file-p))) -(defvar ivy--flx-featurep (require 'flx nil 'noerror)) +(defvar ivy--flx-available-p) +(defun ivy--flx-available-p () + "Try to load package `flx' once; return non-nil on success." + (if (boundp 'ivy--flx-available-p) + ivy--flx-available-p + (setq ivy--flx-available-p (require 'flx nil t)))) (defun ivy--sort (name candidates) "Re-sort candidates by NAME. @@ -3781,8 +3788,8 @@ All CANDIDATES are assumed to match NAME." (let (fun) (cond ((setq fun (ivy-alist-setting ivy-sort-matches-functions-alist)) (funcall fun name candidates)) - ((and ivy--flx-featurep - (eq ivy--regex-function 'ivy--regex-fuzzy)) + ((and (eq ivy--regex-function #'ivy--regex-fuzzy) + (ivy--flx-available-p)) (ivy--flx-sort name candidates)) (t candidates)))) @@ -3881,8 +3888,8 @@ CANDS are the current candidates." 0)) ((and (not empty) (not (eq caller 'swiper)) - (not (and ivy--flx-featurep - (eq ivy--regex-function 'ivy--regex-fuzzy) + (not (and (eq ivy--regex-function #'ivy--regex-fuzzy) + (ivy--flx-available-p) ;; Limit to configured number of candidates (null (nthcdr ivy-flx-limit cands)))) ;; If there was a preselected candidate, don't try to @@ -4156,8 +4163,8 @@ with the extended highlighting of `ivy-format-function-line'." (defun ivy--highlight-fuzzy (str) "Highlight STR, using the fuzzy method." - (if (and ivy--flx-featurep - (eq (ivy-alist-setting ivy-re-builders-alist) 'ivy--regex-fuzzy)) + (if (and (eq (ivy-alist-setting ivy-re-builders-alist) #'ivy--regex-fuzzy) + (ivy--flx-available-p)) (let ((flx-name (string-remove-prefix "^" ivy-text))) (ivy--flx-propertize (cons (flx-score str flx-name ivy--flx-cache) str))) @@ -4979,6 +4986,9 @@ This list can be rotated with `ivy-rotate-preferred-builders'." ((symbolp history) (set history (delete current (symbol-value history)))) ((ring-p history) + ;; `ring-p' is autoloaded. + (declare-function ring-member "ring") + (declare-function ring-remove "ring") (ring-remove history (ring-member history current))))) (ivy--kill-current-candidate))) @@ -4996,6 +5006,8 @@ Also set `ivy--reverse-i-search-history' to HISTORY." ((symbolp history) (copy-sequence (symbol-value history))) ((ring-p history) + ;; `ring-p' is autoloaded. + (declare-function ring-elements "ring") (ring-elements history)) ((sequencep history) (copy-sequence history))