aboutsummaryrefslogtreecommitdiff
path: root/.config/emacs/lisp/evil-collection/evil-collection.el
diff options
context:
space:
mode:
Diffstat (limited to '.config/emacs/lisp/evil-collection/evil-collection.el')
-rw-r--r--.config/emacs/lisp/evil-collection/evil-collection.el1129
1 files changed, 1129 insertions, 0 deletions
diff --git a/.config/emacs/lisp/evil-collection/evil-collection.el b/.config/emacs/lisp/evil-collection/evil-collection.el
new file mode 100644
index 0000000..a7e149a
--- /dev/null
+++ b/.config/emacs/lisp/evil-collection/evil-collection.el
@@ -0,0 +1,1129 @@
+;;; evil-collection.el --- A set of keybindings for Evil mode -*- lexical-binding: t -*-
+
+;; Copyright (C) 2017, 2023 James Nguyen
+
+;; Author: James Nguyen <james@jojojames.com>
+;; Pierre Neidhardt <mail@ambrevar.xyz>
+;; Maintainer: James Nguyen <james@jojojames.com>
+;; Pierre Neidhardt <mail@ambrevar.xyz>
+;; URL: https://github.com/emacs-evil/evil-collection
+;; Version: 0.0.2
+;; Package-Requires: ((emacs "26.3") (evil "1.2.13"))
+;; Keywords: evil, tools
+
+;; 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 <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;; A set of keybindings for Evil mode.
+;;
+;; If you want to use Evil in the minibuffer, you'll have to enable it by
+;; setting `evil-collection-setup-minibuffer' to t before loading this package.
+;; This is so because many users find it confusing.
+;; Some minibuffer-related packages such as Helm rely on this option.
+
+;;; Code:
+
+;; `evil' requires `seq-into'?
+;; This require on `seq' before loading `evil 'prevents `evil' from erroring
+;; out with the below message on Emacs 29.
+;; Symbol's function definition is void: seq-into
+;; Looks like this error can be traced through evil ->
+;; Look at the commit that moved this line above `evil' to see the error message.
+;; evil -> evil-vars -> read-kbd-macro -> seq-into -> error.
+;; https://github.com/emacs-evil/evil/issues/1627
+(require 'seq)
+(require 'cl-lib)
+(require 'evil)
+;; `annalist' is optional; only used to record and describe bindings via
+;; `evil-collection-describe-bindings'. Install it to enable that feature.
+(require 'annalist nil t)
+
+(declare-function annalist-record "annalist")
+(declare-function annalist-describe "annalist")
+(declare-function annalist-define-view "annalist")
+(declare-function annalist-string-< "annalist")
+
+(defvar evil-collection-base-dir (file-name-directory load-file-name)
+ "Store the directory evil-collection.el was loaded from.")
+
+(defvar evil-want-integration)
+(defvar evil-want-keybinding)
+
+(defvar evil-collection--startup-warning-flag nil)
+
+(defun evil-collection-maybe-display-startup-warning ()
+ "Warn if `evil-want-keybinding' was not set before loading Evil.
+Runs at most once."
+ (unless evil-collection--startup-warning-flag
+ (setq evil-collection--startup-warning-flag t)
+ (when (featurep 'evil-keybindings)
+ (if evil-want-keybinding
+ (display-warning
+ '(evil-collection)
+ "Make sure to set `evil-want-keybinding' to nil before loading evil \
+or evil-collection.\
+\n
+See https://github.com/emacs-evil/evil-collection/issues/60 for more details.")
+ (display-warning
+ '(evil-collection)
+ "`evil-want-keybinding' was set to nil but not before loading evil.\
+\n
+Make sure to set `evil-want-keybinding' to nil before loading evil \
+or evil-collection.\
+\n
+See https://github.com/emacs-evil/evil-collection/issues/60 for more details.")))))
+
+(unless (featurep 'evil-integration)
+ (message "Requiring evil-integration. Set evil-want-integration to t to\
+ remove this message.\
+\n
+See https://github.com/emacs-evil/evil-collection/issues/60 for more details.")
+ (require 'evil-integration))
+
+(defgroup evil-collection nil
+ "A set of keybindings for Evil mode."
+ :group 'evil)
+
+(defcustom evil-collection-setup-minibuffer nil
+ "Whether to setup Evil bindings in the minibuffer."
+ :type 'boolean
+ :group 'evil-collection)
+
+(defcustom evil-collection-calendar-want-org-bindings nil
+ "Whether to bind Org functions in calendar keymap."
+ :type 'boolean
+ :group 'evil-collection)
+
+(defcustom evil-collection-setup-debugger-keys t
+ "Whether to bind debugger keys when debugger is active.
+
+Debugger in this case is dependent on mode.
+
+This is only relevant for debug modes that are part of another mode,
+
+e.g. `indium'. Modes like `edebug' or `realgud' needs to be explicitly disabled
+
+through removing their entry from `evil-collection-mode-list'."
+ :type 'boolean
+ :group 'evil-collection)
+
+(defcustom evil-collection-want-unimpaired-p t
+ "Whether to enable unimpaired style bindings globally."
+ :type 'boolean
+ :group 'evil-collection)
+
+(defcustom evil-collection-want-find-usages-bindings t
+ "Whether to bind `xref-find-references'-like bindings.
+
+This will bind additional find-* type commands, e.g. usages, assignments, etc.."
+ :type 'boolean
+ :group 'evil-collection)
+
+(defcustom evil-collection-want-g-bindings t
+ "Whether to bind g* bindings."
+ :type 'boolean
+ :group 'evil-collection)
+
+(defvar evil-collection--modes-with-delayed-setup
+ `(emms
+ eshell)
+ "List of modes whose keybinds aren't completely set up after the mode is
+loaded. This can be a problem for cases where we're doing key translations
+using `evil-collection-setup-hook' which would result in an empty keymap.
+
+Normally we run `evil-collection-setup-hook' right away after the mode
+is loaded in `with-eval-after-load' (see `evil-collection-init') but for these
+modes, we skip running that hook and let the corresponding `evil-collection'
+package handle running `evil-collection-setup-hook'.
+
+Elements in this list either match a target mode symbol or the car of a list in
+`evil-collection--supported-modes'.
+
+If `evil-collection-always-run-setup-hook-after-load' is t, this list isn't
+read and `evil-collection-setup-hook' will be ran in the
+`with-eval-after-load' block in `evil-collection-init'.")
+
+(defcustom evil-collection-always-run-setup-hook-after-load nil
+ "Whether to always run `evil-collection-setup-hook' after mode is loaded.
+
+See `evil-collection-init' and `evil-collection--modes-with-delayed-setup'."
+ :type 'boolean
+ :group 'evil-collection)
+
+(defcustom evil-collection-defer-delay 3
+ "Default idle delay in seconds used when deferring mode initialization.
+
+Used as a fallback when no explicit delay is specified for a mode in
+`evil-collection-defer'."
+ :type 'number
+ :group 'evil-collection)
+
+(defvar evil-collection--supported-modes
+ `(2048-game
+ ag
+ agent-shell
+ alchemist
+ anaconda-mode
+ apropos
+ arc-mode
+ atomic-chrome
+ auto-package-update
+ beginend
+ bluetooth
+ bm
+ bookmark
+ (buff-menu "buff-menu")
+ bufler
+ calc
+ calendar
+ cider
+ citre
+ cmake-mode
+ color-rg
+ comint
+ company
+ compile
+ consult
+ corfu
+ crdt
+ (csv "csv-mode")
+ (custom cus-edit)
+ cus-theme
+ dape
+ dashboard
+ daemons
+ deadgrep
+ debbugs
+ debug
+ devdocs
+ dictionary
+ diff-hl
+ diff-mode
+ difftastic
+ dired
+ dired-sidebar
+ disk-usage
+ distel
+ doc-view
+ docker
+ eat
+ ebib
+ ebuku
+ eca
+ edbi
+ edebug
+ ediff
+ eglot
+ elpaca
+ ement
+ explain-pause-mode
+ eldoc
+ elfeed
+ elisp-mode
+ elisp-refs
+ elisp-slime-nav
+ embark
+ emms
+ ,@(when (>= emacs-major-version 29) '(emoji))
+ epa
+ ert
+ eshell
+ eval-sexp-fu
+ evil-mc
+ eww
+ fanyi
+ finder
+ flycheck
+ flymake
+ forge
+ free-keys
+ fzfa
+ geiser
+ ggtags
+ git-timemachine
+ gited
+ gnus
+ go-mode
+ gptel
+ grep
+ guix
+ hackernews
+ helm
+ help
+ helpful
+ hg-histedit
+ hideshow
+ hungry-delete
+ hyrolo
+ ibuffer
+ ielm
+ (image image-mode)
+ image-dired
+ image+
+ imenu
+ imenu-list
+ (indent "indent")
+ indium
+ info
+ ivy
+ js2-mode
+ ,@(when (>= emacs-major-version 30) '(kmacro))
+ leetcode
+ lispy
+ lms
+ log-edit
+ log-view
+ lsp-ui-imenu
+ lua-mode
+ kotlin-mode
+ macrostep
+ man
+ (magit magit-submodule) ;; See https://github.com/emacs-evil/evil-collection/issues/637
+ magit-repos
+ magit-section
+ magit-todos
+ markdown-mode
+ message
+ minesweeper
+ ,@(when evil-collection-setup-minibuffer '(minibuffer))
+ monky
+ mpc
+ mpdel
+ mpdired
+ mu4e
+ mu4e-conversation
+ neotree
+ newsticker
+ notmuch
+ nov
+ omnisharp
+ org
+ org-agenda
+ org-present
+ org-roam
+ osx-dictionary
+ outline
+ ovpn-mode
+ p4
+ (package-menu package)
+ pass
+ (pdf pdf-view)
+ popup
+ proced
+ (process-menu simple)
+ prodigy
+ profiler
+ p-search
+ python
+ quickrun
+ racer
+ racket-describe
+ reader
+ realgud
+ reftex
+ replace ;; For `occur'.
+ restclient
+ rg
+ ripgrep
+ rjsx-mode
+ robe
+ rtags
+ ruby-mode
+ scheme
+ scroll-lock
+ selectrum
+ sh-script
+ shell-maker
+ ,@(when (>= emacs-major-version 28) '(shortdoc))
+ simple
+ simple-mpc
+ slime
+ sly
+ smerge-mode
+ snake
+ so-long
+ speedbar
+ ,@(when (>= emacs-major-version 27) '(tab-bar))
+ tablist
+ tabulated-list
+ tar-mode
+ telega
+ (term term ansi-term multi-term)
+ tetris
+ ,@(when (>= emacs-major-version 27) '(thread))
+ tide
+ timer-list
+ transmission
+ trashed
+ tuareg
+ typescript-mode
+ ultra-scroll
+ vc-annotate
+ vc-dir
+ vc-git
+ vdiff
+ vertico
+ view
+ vlf
+ vterm
+ vundo
+ w3m
+ wdired
+ wgrep
+ which-key
+ with-editor
+ woman
+ xref
+ xwidget
+ yaml-mode
+ youtube-dl
+ zmusic
+ (ztree ztree-diff ztree-dir))
+ "List of modes supported by evil-collection. Elements are
+either target mode symbols or lists which `car' is the mode
+symbol and `cdr' the packages to register.")
+
+(dolist (mode evil-collection--supported-modes)
+ (let ((ec-mode-name (if (listp mode) (car mode) mode)))
+ (autoload
+ (intern (format "evil-collection-%s-setup" ec-mode-name))
+ (expand-file-name
+ (format "modes/%s/evil-collection-%s" ec-mode-name ec-mode-name)
+ evil-collection-base-dir))))
+
+(defcustom evil-collection-mode-list evil-collection--supported-modes
+ "The list of modes which will be evilified by `evil-collection-init'.
+Elements are either target mode symbols or lists which `car' is the
+mode symbol and `cdr' the packages to register.
+
+By default, `minibuffer' is not included because many users find
+this confusing. It will be included if
+`evil-collection-setup-minibuffer' is set to t."
+ :type '(repeat (choice symbol sexp))
+ :group 'evil-collection)
+
+(defcustom evil-collection-config
+ '((buff-menu :defer t)
+ (calc :defer t)
+ (comint :defer t)
+ (debug :defer t)
+ (diff-mode :defer t)
+ (dired :hook dired-mode)
+ (edebug :defer t)
+ (eldoc :defer t)
+ (help :defer t)
+ (image :defer t)
+ (indent :defer t)
+ (info :defer t)
+ (replace :defer t)
+ (outline :defer t)
+ (package-menu :defer t)
+ (process-menu :defer t)
+ (simple :defer t)
+ (tab-bar :defer t)
+ (tabulated-list :defer t)
+ (xref :defer t))
+ "The list of modes with special configuration.
+
+These modes should match entries within `evil-collection-mode-list'.
+
+This variable is consumed only by `evil-collection-setup'.
+
+
+NOTE: The API of this variable may change drastically.
+
+Currently supported keys:
+
+:defer t or TIME in seconds to defer loading mode.
+
+:hook HOOK-NAME to initialize the mode when a hook fires. The hook
+name does not need the \"-hook\" suffix; it will be appended
+automatically. When :hook is present, :defer is ignored."
+ :type '(repeat (choice symbol sexp))
+ :group 'evil-collection)
+
+(defcustom evil-collection-key-whitelist '()
+ "List of keys that may be used by Evil Collection.
+This is a list of strings that are suitable for input to
+`kbd'. If there are no keys in the list, the whitelist will be ignored."
+ :type '(repeat string)
+ :group 'evil-collection)
+
+(defcustom evil-collection-key-blacklist '()
+ "List of keys that may not be used by Evil Collection.
+This is a list of strings that are suitable for input to `kbd'."
+ :type '(repeat string)
+ :group 'evil-collection)
+
+(defcustom evil-collection-state-passlist '()
+ "List of evil states that may be used by Evil Collection.
+This is a list of symbols that are suitable for input to
+ `evil-define-key'. Ignore when there are no states in the list."
+ :type '(repeat symbol)
+ :group 'evil-collection)
+
+(defcustom evil-collection-state-denylist
+ (if (bound-and-true-p evil-disable-insert-state-bindings)
+ '(insert)
+ '())
+ "List of evil states that may not be used by Evil Collection.
+This is a list of symbols that are suitable for input to
+ `evil-define-key'."
+ :type '(repeat symbol)
+ :group 'evil-collection)
+
+(defcustom evil-collection-repl-submit-state 'normal
+ "Evil state in which RET submits the prompt in REPL-like buffers.
+
+The other state gets RET bound to `newline'. S-RET always inserts a
+newline regardless of state."
+ :type '(choice (const :tag "Submit in normal state" normal)
+ (const :tag "Submit in insert state" insert))
+ :group 'evil-collection)
+
+(defvar evil-collection-setup-hook nil
+ "Hook run by `evil-collection-init' for each mode that is evilified.
+This hook runs after all setup (including keybindings) for a mode has already
+taken place. The arguments passed to functions for this hook are the name of the
+mode and a list of keymap names (i.e. symbols, not actual keymaps) customized by
+Evil Collection for that mode. More arguments may be added in the future, so
+functions added to this hook should include a \"&rest _rest\" for forward
+compatibility.")
+
+(defun evil-collection-define-operator-key (operator map-sym &rest bindings)
+ "Define a key on a specific OPERATOR e.g. yank or delete.
+
+This function is useful for adding specific binds to operator maps
+\(e.g. `evil-yank' or `evil-delete') without erasing the original bind.
+
+For example, say one wants to bind \"yf\" to something but also wants to keep
+\"yy\".
+
+This function takes care of checking the whitelist/blacklist against the full
+binding.
+
+For example:
+\(evil-collection-define-operator-key \='yank
+ \='pass-mode-map \"f\" \='pass-copy-field)
+
+This will check \"yf\" against a user's white/blacklist and also record the
+binding in `annalist' as so."
+ (declare (indent defun))
+ (let* ((prefix (if (eq operator 'yank) "y" "d"))
+ (operators (if (eq operator 'yank)
+ 'evil-collection-yank-operators
+ 'evil-collection-delete-operators))
+ (remap (if (eq operator 'yank) [remap evil-yank] [remap evil-delete]))
+ (whitelist (mapcar 'kbd evil-collection-key-whitelist))
+ (blacklist (mapcar 'kbd evil-collection-key-blacklist))
+ filtered-bindings)
+ (while bindings
+ (let* ((key (pop bindings))
+ (key-with-prefix (concat prefix key))
+ (def (pop bindings))
+ (def-with-menu-item
+ `(menu-item
+ ""
+ nil
+ :filter
+ (lambda (&optional _)
+ (when (or
+ (eq evil-this-operator (key-binding ,remap))
+ (memq evil-this-operator ,operators))
+ (setq evil-inhibit-operator t)
+ ',def)))))
+ (when (or (and whitelist (member key-with-prefix whitelist))
+ (not (member key-with-prefix blacklist)))
+ (when (featurep 'annalist)
+ (annalist-record 'evil-collection 'keybindings
+ ;; Record the binding as if it was in 'normal mode
+ ;; instead of 'operator mode as the user would be
+ ;; in normal mode when triggering the operator.
+ (list map-sym 'normal key-with-prefix def)
+ :local (or (eq map-sym 'local)
+ (local-variable-p map-sym))))
+ ;; Use the original key declared when actually setting the binding.
+ (push key filtered-bindings)
+ ;; Use the definition attached to the menu-item when setting the
+ ;; binding.
+ (push def-with-menu-item filtered-bindings))))
+ (setq filtered-bindings (nreverse filtered-bindings))
+ (evil-collection--define-key 'operator map-sym filtered-bindings)))
+
+(defun evil-collection--filter-states (state)
+ "Return a list states after filtering STATE (a single symbol or list of symbols).
+The return value adheres to `evil-collection-state-passlist' and
+`evil-collection-state-denylist'. When the STATE is nil, which
+means all states for `evil-define-key', return nil."
+ (let ((states (if (listp state) state (list state))))
+ (seq-difference
+ (if evil-collection-state-passlist
+ (seq-intersection states evil-collection-state-passlist)
+ states)
+ evil-collection-state-denylist)))
+
+(defun evil-collection--keys-conflict (key-a key-b)
+ "Return t if KEY-A and KEY-B conflict.
+Keys conflict if they're equal or if one is a prefix of the other."
+ (setq key-a (vconcat key-a)
+ key-b (vconcat key-b))
+ (let ((len (min (length key-a) (length key-b))))
+ (equal (substring key-a 0 len) (substring key-b 0 len))))
+
+(defsubst evil-collection--can-bind-key (key whitelist blacklist)
+ "Return t if KEY can be bound.
+Return nil if KEY conflicts with a key in BLACKLIST and is not
+a member of WHITELIST.
+
+Both WHITELIST and BLACKLIST must be lists of keys in Emacs'
+internal key representation (i.e., after calling `kbd' on the key
+description."
+ (or (member key whitelist)
+ (not (cl-member key blacklist
+ :test #'evil-collection--keys-conflict))))
+
+(defun evil-collection-define-key (state map-sym &rest bindings)
+ "Wrapper for `evil-define-key*' with additional features.
+Unlike `evil-define-key*' MAP-SYM should be a quoted keymap other than the
+unquoted keymap required for `evil-define-key*'. This function adds the ability
+to filter keys on the basis of `evil-collection-key-whitelist' and
+`evil-collection-key-blacklist'. It also records bindings with annalist.el."
+ (declare (indent defun))
+ (let* ((whitelist (mapcar 'kbd evil-collection-key-whitelist))
+ (blacklist (mapcar 'kbd evil-collection-key-blacklist))
+ (states-to-bind (evil-collection--filter-states state))
+ filtered-bindings)
+ (when (or states-to-bind (null state))
+ (while bindings
+ (let ((key (pop bindings))
+ (def (pop bindings)))
+ (when (evil-collection--can-bind-key key whitelist blacklist)
+ (when (featurep 'annalist)
+ (annalist-record 'evil-collection 'keybindings
+ (list map-sym state key def)
+ :local (or (eq map-sym 'local)
+ (local-variable-p map-sym))))
+ (push key filtered-bindings)
+ (push def filtered-bindings))))
+ (setq filtered-bindings (nreverse filtered-bindings))
+ (evil-collection--define-key states-to-bind map-sym filtered-bindings))))
+
+(defun evil-collection-can-bind-key (key)
+ "Return whether or not we should bind KEY."
+ (let* ((whitelist (mapcar 'kbd evil-collection-key-whitelist))
+ (blacklist (mapcar 'kbd evil-collection-key-blacklist)))
+ (evil-collection--can-bind-key key whitelist blacklist)))
+
+(defun evil-collection--define-key (state map-sym bindings)
+ "Workhorse function for `evil-collection-define-key'.
+
+See `evil-collection-define-key' docstring for more details."
+ (cond ((null bindings))
+ ((and (boundp map-sym) (keymapp (symbol-value map-sym)))
+ (condition-case-unless-debug err
+ (apply #'evil-define-key*
+ state (symbol-value map-sym) bindings)
+ (error
+ (message "evil-collection: error setting key in %s %S"
+ map-sym err))))
+ ((boundp map-sym)
+ (user-error "evil-collection: %s is not a keymap" map-sym))
+ (t
+ (let* ((fname (format "evil-collection-define-key-in-%s" map-sym))
+ (fun (make-symbol fname)))
+ (fset fun `(lambda (&rest args)
+ (when (and (boundp ',map-sym) (keymapp ,map-sym))
+ (remove-hook 'after-load-functions #',fun)
+ (condition-case-unless-debug err
+ (apply #'evil-define-key*
+ ',state ,map-sym ',bindings)
+ (error
+ (message
+ ,(format
+ "evil-collection: error setting key in %s %%S"
+ map-sym)
+ err))))))
+ (add-hook 'after-load-functions fun t)))))
+
+(defun evil-collection-inhibit-insert-state (map-sym)
+ "Unmap insertion keys from normal state.
+This is particularly useful for read-only modes."
+ (evil-collection-define-key 'normal map-sym
+ [remap evil-append] #'ignore
+ [remap evil-append-line] #'ignore
+ [remap evil-insert] #'ignore
+ [remap evil-insert-line] #'ignore
+ [remap evil-change] #'ignore
+ [remap evil-change-line] #'ignore
+ [remap evil-substitute] #'ignore
+ [remap evil-change-whole-line] #'ignore
+ [remap evil-delete] #'ignore
+ [remap evil-delete-line] #'ignore
+ [remap evil-delete-char] #'ignore
+ [remap evil-delete-backward-char] #'ignore
+ [remap evil-replace] #'ignore
+ [remap evil-replace-state] #'ignore
+ [remap evil-open-below] #'ignore
+ [remap evil-open-above] #'ignore
+ [remap evil-paste-after] #'ignore
+ [remap evil-paste-before] #'ignore
+ [remap evil-join] #'ignore
+ [remap evil-indent] #'ignore
+ [remap evil-shift-left] #'ignore
+ [remap evil-shift-right] #'ignore
+ [remap evil-invert-char] #'ignore))
+
+(defun evil-collection-set-readonly-bindings (map-sym)
+ "Unmap insertion keys from normal state. Additionally q can `quit-window'.
+This is particularly useful for read-only modes. Make sure it's
+called before setting up other evil bindings so that it can be
+overriden."
+ (evil-collection-inhibit-insert-state map-sym)
+ (evil-collection-define-key 'normal map-sym
+ "q" #'quit-window
+ "ZZ" #'quit-window
+ "ZQ" #'evil-quit))
+
+(defun evil-collection--binding-lessp (a b)
+ "Comparison function used to sort bindings of the form (state key def)."
+ (let ((a-state (symbol-name (nth 0 a)))
+ (b-state (symbol-name (nth 0 b)))
+ (a-key (nth 1 a))
+ (b-key (nth 1 b)))
+ (if (not (string= a-state b-state))
+ (string-lessp a-state b-state)
+ (string-lessp a-key b-key))))
+
+(with-eval-after-load 'annalist
+ (annalist-define-view 'keybindings 'evil-collection-valid
+ (list (list 'keymap :sort #'annalist-string-<)
+ (list 'state :sort #'annalist-string-<))
+ :inherit 'valid)
+
+ (annalist-define-view 'keybindings 'evil-collection-active
+ (list (list 'keymap :sort #'annalist-string-<)
+ (list 'state :sort #'annalist-string-<))
+ :inherit 'active))
+
+(defun evil-collection-describe-bindings (&optional arg)
+ "Print bindings made by Evil Collection to separate buffer.
+
+With non-nil ARG, restrict to bindings corresponding to active
+modes in the current buffer.
+
+Requires the optional `annalist' package."
+ (interactive "P")
+ (unless (require 'annalist nil t)
+ (user-error
+ "`evil-collection-describe-bindings' requires the `annalist' package"))
+ (annalist-describe 'evil-collection 'keybindings
+ (if arg
+ 'evil-collection-active
+ 'evil-collection-valid)))
+
+(defun evil-collection--mode-file (mode file)
+ "Return path to FILE for MODE. Return nil if it doesn't exist."
+ (let ((path (expand-file-name
+ (format "modes/%s/%s" mode file) evil-collection-base-dir)))
+ (when (file-exists-p path)
+ path)))
+
+(defun evil-collection-open-config-file (mode)
+ "Open configuration file corresponding to MODE."
+ (interactive
+ (list
+ (completing-read
+ "Mode: "
+ (cl-remove-if-not
+ (lambda (mode)
+ (evil-collection--mode-file mode (format "evil-collection-%s.el" mode)))
+ (directory-files
+ (expand-file-name "modes" evil-collection-base-dir)
+ nil "^[^.]")))))
+ (find-file (evil-collection--mode-file mode (format "evil-collection-%s.el" mode))))
+
+(defun evil-collection-open-readme (mode)
+ "Open README.org corresponding to MODE."
+ (interactive
+ (list
+ (completing-read
+ "Mode: "
+ (cl-remove-if-not
+ (lambda (mode)
+ (evil-collection--mode-file mode "README.org"))
+ (directory-files
+ (expand-file-name "modes" evil-collection-base-dir)
+ nil "^[^.]")))))
+ (find-file (evil-collection--mode-file mode "README.org")))
+
+(defun evil-collection--delay (condition form hook &optional append local name)
+ "Execute FORM when CONDITION becomes true, checking with HOOK.
+NAME specifies the name of the entry added to HOOK. If APPEND is
+non-nil, the entry is appended to the hook. If LOCAL is non-nil,
+the buffer-local value of HOOK is modified.
+
+This is a backport of `evil-delay' without the deprecation notice to deal with
+CI until migration can be done.
+Ref: https://github.com/emacs-evil/evil-collection/issues/750"
+ (eval `(evil-with-delay ,condition (,hook ,append ,local ,name) ,form) t))
+
+;;;###autoload
+(cl-defun evil-collection-translate-minor-mode-key (states modes
+ &rest translations
+ &key destructive
+ &allow-other-keys)
+ "Translate keys in the keymap(s) corresponding to STATES and MODES.
+
+Similar to `evil-collection-translate-key' but for minor modes.
+STATES should be the name of an evil state, a list of states, or nil. MODES
+should be a symbol corresponding to minor-mode to make the translations in or a
+list of minor-mode symbols. TRANSLATIONS corresponds to a list of
+key replacement pairs. For example, specifying \"a\" \"b\" will bind \"a\" to
+\"b\"'s definition in the keymap. Specifying nil as a replacement will unbind a
+key. If DESTRUCTIVE is nil, a backup of the keymap will be stored on the initial
+invocation, and future invocations will always look up keys in the backup
+keymap. When no TRANSLATIONS are given, this function will only create the
+backup keymap without making any translations. On the other hand, if DESTRUCTIVE
+is non-nil, the keymap will be destructively altered without creating a backup.
+For example, calling this function multiple times with \"a\" \"b\" \"b\" \"a\"
+would continue to swap and unswap the definitions of these keys. This means that
+when DESTRUCTIVE is non-nil, all related swaps/cycles should be done in the same
+invocation."
+ (declare (indent defun))
+ (unless (listp modes)
+ (setq modes (list modes)))
+ (unless (and (listp states)
+ (not (null states)))
+ (setq states (list states)))
+ (dolist (mode-symbol modes)
+ (let ((keymap-symbol (intern (format "%S-map" mode-symbol))))
+ (dolist (state states)
+ (let ((hook-name
+ (symbol-name
+ (gensym
+ (format "evil-collection-translate-key-in-%s" keymap-symbol)))))
+ (evil-collection--delay `(and (boundp ',keymap-symbol)
+ (keymapp ,keymap-symbol))
+ `(evil-collection--translate-minor-mode-key
+ ',state
+ ',mode-symbol
+ ',translations
+ ,destructive)
+ 'after-load-functions
+ t
+ nil
+ hook-name))))))
+
+
+(defun evil-collection--translate-minor-mode-key (state
+ mode-symbol
+ translations
+ destructive)
+ "Helper function for `evil-collection-translate-minor-mode-key'.
+In the minor mode keymap corresponding to STATE and MODE-SYMBOL, make the key
+TRANSLATIONS. When DESTRUCTIVE is non-nil, make the TRANSLATIONS destructively
+without creating/referencing a backup keymap."
+ (let* ((keymap-symbol (intern (format "%S-map" mode-symbol)))
+ (backup-keymap-symbol (intern (format "evil-collection-%s%s-backup-map"
+ mode-symbol
+ (if state
+ (format "-%s-state" state)
+ ""))))
+ (keymap (symbol-value keymap-symbol))
+ (lookup-keymap (if (and (not destructive)
+ (boundp backup-keymap-symbol))
+ (symbol-value backup-keymap-symbol)
+ (copy-keymap
+ (if state
+ (evil-get-minor-mode-keymap state mode-symbol)
+ keymap))))
+ (maps (cl-loop for (key replacement) on translations by 'cddr
+ ;; :destructive can be in TRANSLATIONS
+ unless (keywordp key)
+ collect key
+ and collect (when replacement
+ (evil-lookup-key lookup-keymap replacement)))))
+ (unless (or destructive
+ (boundp backup-keymap-symbol))
+ (set backup-keymap-symbol lookup-keymap))
+ (apply #'evil-define-minor-mode-key state mode-symbol maps)))
+
+(defun evil-collection--translate-key (state keymap-symbol
+ translations
+ destructive)
+ "Helper function for `evil-collection-translate-key'.
+In the keymap corresponding to STATE and KEYMAP-SYMBOL, make the key
+TRANSLATIONS. When DESTRUCTIVE is non-nil, make the TRANSLATIONS destructively
+without creating/referencing a backup keymap."
+ (let* ((backup-keymap-symbol (intern (format "evil-collection-%s%s-backup-map"
+ keymap-symbol
+ (if state
+ (format "-%s-state" state)
+ ""))))
+ (keymap (symbol-value keymap-symbol))
+ (lookup-keymap (if (and (not destructive)
+ (boundp backup-keymap-symbol))
+ (symbol-value backup-keymap-symbol)
+ (copy-keymap
+ (if state
+ (evil-get-auxiliary-keymap keymap state t t)
+ keymap))))
+ (maps (cl-loop for (key replacement) on translations by 'cddr
+ ;; :destructive can be in TRANSLATIONS
+ unless (keywordp key)
+ collect key
+ and collect (when replacement
+ (evil-lookup-key lookup-keymap replacement)))))
+ (unless (or destructive
+ (boundp backup-keymap-symbol))
+ (set backup-keymap-symbol lookup-keymap))
+ (apply #'evil-define-key* state keymap maps)))
+
+;;;###autoload
+(cl-defun evil-collection-translate-key (states keymaps
+ &rest translations
+ &key destructive
+ &allow-other-keys)
+ "Translate keys in the keymap(s) corresponding to STATES and KEYMAPS.
+STATES should be the name of an evil state, a list of states, or nil. KEYMAPS
+should be a symbol corresponding to the keymap to make the translations in or a
+list of keymap symbols. Like `evil-define-key', when a keymap does not exist,
+the keybindings will be deferred until the keymap is defined, so
+`with-eval-after-load' is not necessary. TRANSLATIONS corresponds to a list of
+key replacement pairs. For example, specifying \"a\" \"b\" will bind \"a\" to
+\"b\"'s definition in the keymap. Specifying nil as a replacement will unbind a
+key. If DESTRUCTIVE is nil, a backup of the keymap will be stored on the initial
+invocation, and future invocations will always look up keys in the backup
+keymap. When no TRANSLATIONS are given, this function will only create the
+backup keymap without making any translations. On the other hand, if DESTRUCTIVE
+is non-nil, the keymap will be destructively altered without creating a backup.
+For example, calling this function multiple times with \"a\" \"b\" \"b\" \"a\"
+would continue to swap and unswap the definitions of these keys. This means that
+when DESTRUCTIVE is non-nil, all related swaps/cycles should be done in the same
+invocation."
+ (declare (indent defun))
+ (unless (listp keymaps)
+ (setq keymaps (list keymaps)))
+ (unless (and (listp states)
+ (not (null states)))
+ (setq states (list states)))
+ (dolist (keymap-symbol keymaps)
+ (dolist (state states)
+ (let ((hook-name
+ (symbol-name
+ (gensym
+ (format "evil-collection-translate-key-in-%s" keymap-symbol)))))
+ (evil-collection--delay `(and (boundp ',keymap-symbol)
+ (keymapp ,keymap-symbol))
+ `(evil-collection--translate-key
+ ',state
+ ',keymap-symbol
+ ',translations
+ ,destructive)
+ 'after-load-functions
+ t
+ nil
+ hook-name)))))
+
+
+;;;###autoload
+(defmacro evil-collection-swap-key (states keymaps &rest args)
+ "Wrapper around `evil-collection-translate-key' for swapping keys.
+STATES, KEYMAPS, and ARGS are passed to `evil-collection-translate-key'. ARGS
+should consist of key swaps (e.g. \"a\" \"b\" is equivalent to \"a\" \"b\" \"b\"
+\"a\" with `evil-collection-translate-key') and optionally keyword arguments for
+`evil-collection-translate-key'."
+ (declare (indent defun))
+ (setq args (cl-loop for (key replacement) on args by 'cddr
+ collect key and collect replacement
+ and unless (keywordp key)
+ collect replacement and collect key))
+ `(evil-collection-translate-key ,states ,keymaps ,@args))
+
+;;;###autoload
+(defmacro evil-collection-swap-minor-mode-key (states modes &rest args)
+ "Wrapper around `evil-collection-translate-minor-mode-key' for swapping keys.
+STATES, MODES, and ARGS are passed to
+`evil-collection-translate-minor-mode-key'. ARGS should consist of key swaps
+\(e.g. \"a\" \"b\" is equivalent to \"a\" \"b\" \"b\" \"a\"
+with `evil-collection-translate-minor-mode-key') and optionally keyword
+arguments for `evil-collection-translate-minor-mode-key'."
+ (declare (indent defun))
+ (setq args (cl-loop for (key replacement) on args by 'cddr
+ collect key and collect replacement
+ and unless (keywordp key)
+ collect replacement and collect key))
+ `(evil-collection-translate-minor-mode-key ,states ,modes ,@args))
+
+;;;###autoload
+(defun evil-collection-require (mode &optional noerror)
+ "Require the evil-collection-MODE file, but do not activate it.
+
+MODE should be a symbol. This requires the evil-collection-MODE
+feature without needing to manipulate `load-path'. NOERROR is
+forwarded to `require'."
+ (let* ((mode-name (symbol-name mode))
+ (feature (intern (format "evil-collection-%s" mode-name)))
+ (file (expand-file-name
+ (format "modes/%s/evil-collection-%s" mode-name mode-name)
+ evil-collection-base-dir)))
+ (require feature file noerror)))
+
+(declare-function evil-collection-unimpaired-setup "evil-collection-unimpaired")
+
+;;;###autoload
+(defun evil-collection-init (&optional modes)
+ "Register the Evil bindings for all modes in `evil-collection-mode-list'.
+
+Alternatively, you may register select bindings manually, for
+instance:
+
+ (with-eval-after-load \='calendar
+ (evil-collection-calendar-setup))
+
+If MODES is specified (as either one mode or a list of modes), use those modes
+instead of the modes in `evil-collection-mode-list'."
+ (interactive)
+ (evil-collection-maybe-display-startup-warning)
+ (if modes
+ (or (listp modes) (setq modes (list modes)))
+ (setq modes evil-collection-mode-list))
+ (dolist (mode modes)
+ (let ((m mode)
+ (reqs (list mode)))
+ (when (listp mode)
+ (setq m (car mode)
+ reqs (cdr mode)))
+ (dolist (req reqs)
+ (with-eval-after-load req
+ ;; (message (format "Loaded %S..." req))
+ (evil-collection-require m)
+ (funcall (intern (concat "evil-collection-" (symbol-name m)
+ "-setup")))
+ (let ((mode-keymaps
+ (ignore-errors
+ (symbol-value
+ (intern (format "evil-collection-%s-maps" m))))))
+ (when (or evil-collection-always-run-setup-hook-after-load
+ (not (memq m evil-collection--modes-with-delayed-setup)))
+ (run-hook-with-args 'evil-collection-setup-hook
+ m mode-keymaps)))))))
+ (when evil-collection-want-unimpaired-p
+ (evil-collection-require 'unimpaired)
+ (evil-collection-unimpaired-setup)))
+
+(defun evil-collection-setup (&optional modes)
+ "Register the Evil bindings for all modes in `evil-collection-mode-list'.
+
+----------------------EXPERIMENTAL------------------------------------------
+
+This is a special wrapper over `evil-collection-init' that respects
+configuration from `evil-collection-config'. This function is experimental,
+so don't use if you don't want breakages or API changes.
+
+If MODES is specified (as either one mode or a list of modes), use those modes
+instead of the modes in `evil-collection-mode-list'.
+
+----------------------EXPERIMENTAL------------------------------------------"
+ (if modes
+ (or (listp modes) (setq modes (list modes)))
+ (setq modes evil-collection-mode-list))
+ (let ((configs evil-collection-config)
+ (deferred) ; alist of (mode . delay)
+ (hooked)) ; alist of (mode . hook)
+ (dolist (config configs)
+ (if-let* ((hook (plist-get (cdr config) :hook)))
+ (push (cons (car config) hook) hooked)
+ (when-let* ((defer (plist-get (cdr config) :defer)))
+ (push (cons (car config) defer) deferred))))
+ (let ((filtered-modes
+ (cl-remove-if
+ (lambda (mode)
+ ;; `evil-collection-config' format is slightly different
+ ;; than `evil-collection-mode-list', so use the mode
+ ;; entry from the mode list instead.
+ (cl-some
+ (lambda (m)
+ (or (when-let* ((entry (assq m deferred)))
+ (setf (car entry) mode) t)
+ (when-let* ((entry (assq m hooked)))
+ (setf (car entry) mode) t)))
+ (if (consp mode) mode (list mode))))
+ modes)))
+ (evil-collection-init filtered-modes))
+ (message (format "Deferring: %S" (mapcar #'car deferred)))
+ (dolist (entry deferred)
+ (let ((mode (car entry))
+ (delay (cdr entry)))
+ (run-with-idle-timer
+ (if (numberp delay) delay evil-collection-defer-delay) nil
+ (apply-partially 'evil-collection-init (list mode)))))
+ (dolist (entry hooked)
+ (let* ((mode (car entry))
+ (hook-sym (cdr entry))
+ (hook-name (let ((name (symbol-name hook-sym)))
+ (if (string-suffix-p "-hook" name)
+ hook-sym
+ (intern (concat name "-hook")))))
+ (mode-sym (if (consp mode) (car mode) mode))
+ (fn-sym (intern (format "evil-collection--init-%s" mode-sym))))
+ (defalias fn-sym
+ (lambda ()
+ (remove-hook hook-name fn-sym)
+ (evil-collection-init (list mode))))
+ (add-hook hook-name fn-sym)))))
+
+(defvar evil-collection-delete-operators '(evil-delete
+ evil-cp-delete
+ evil-sp-delete
+ lispyville-delete)
+ "List of delete operators.")
+
+(defvar evil-collection-yank-operators '(evil-yank
+ evil-cp-yank
+ evil-sp-yank
+ lispyville-yank)
+ "List of yank operators.")
+
+;;* Search
+
+(defun evil-collection-evil-search-enabled ()
+ (eq evil-search-module 'evil-search))
+
+(defvar evil-collection-evil-search-forward
+ '(menu-item "" nil :filter (lambda (&optional _)
+ (if (eq evil-search-module 'evil-search)
+ #'evil-ex-search-forward
+ #'evil-search-forward))))
+
+(defvar evil-collection-evil-search-backward
+ '(menu-item "" nil :filter (lambda (&optional _)
+ (if (eq evil-search-module 'evil-search)
+ #'evil-ex-search-backward
+ #'evil-search-backward))))
+
+(defvar evil-collection-evil-search-next
+ '(menu-item "" nil :filter (lambda (&optional _)
+ (if (eq evil-search-module 'evil-search)
+ #'evil-ex-search-next
+ #'evil-search-next))))
+
+(defvar evil-collection-evil-search-previous
+ '(menu-item "" nil :filter (lambda (&optional _)
+ (if (eq evil-search-module 'evil-search)
+ #'evil-ex-search-previous
+ #'evil-search-previous))))
+
+(provide 'evil-collection)
+;;; evil-collection.el ends here