Function: evil-define-state
evil-define-state is a macro defined in evil-core.el.
Signature
(evil-define-state STATE DOC [[KEY VAL]...] BODY...)
Documentation
Define an Evil state STATE.
DOC is a general description and shows up in all docstrings; the first line of the string should be the full name of the state.
BODY is executed each time the state is enabled or disabled.
Optional keyword arguments:
- :tag - the mode line indicator, e.g. "<T>".
- :message - string shown in the echo area when the state is
activated.
- :cursor - default cursor specification.
- :enable - list of other state keymaps to enable when in this
state.
- :entry-hook - list of functions to run when entering this state.
- :exit-hook - list of functions to run when exiting this state.
- :suppress-keymap - if non-nil, effectively disables bindings to
self-insert-command by making evil-suppress-map the parent of
the global state keymap.
The global keymap of this state will be evil-test-state-map,
the local keymap will be evil-test-state-local-map, and so on.
Source Code
;; Defined in ~/.emacs.d/elpa/evil-20251108.138/evil-core.el
(defmacro evil-define-state (state doc &rest body)
"Define an Evil state STATE.
DOC is a general description and shows up in all docstrings;
the first line of the string should be the full name of the state.
BODY is executed each time the state is enabled or disabled.
Optional keyword arguments:
- `:tag' - the mode line indicator, e.g. \"<T>\".
- `:message' - string shown in the echo area when the state is
activated.
- `:cursor' - default cursor specification.
- `:enable' - list of other state keymaps to enable when in this
state.
- `:entry-hook' - list of functions to run when entering this state.
- `:exit-hook' - list of functions to run when exiting this state.
- `:suppress-keymap' - if non-nil, effectively disables bindings to
`self-insert-command' by making `evil-suppress-map' the parent of
the global state keymap.
The global keymap of this state will be `evil-test-state-map',
the local keymap will be `evil-test-state-local-map', and so on.
\(fn STATE DOC [[KEY VAL]...] BODY...)"
(declare (indent defun)
(doc-string 2)
(debug (&define name
[&optional stringp]
[&rest [keywordp sexp]]
def-body)))
(let* ((name (and (string-match "^\\(.+\\)\\(\\(?:.\\|\n\\)*\\)" doc)
(match-string 1 doc)))
(doc (match-string 2 doc))
(name (and (string-match "^\\(.+?\\)\\.?$" name)
(match-string 1 name)))
(doc (if (or (null doc) (string= doc "")) ""
(format "\n%s" doc)))
(toggle (intern (format "evil-%s-state" state)))
(mode (intern (format "%s-minor-mode" toggle)))
(keymap (intern (format "%s-map" toggle)))
(local (intern (format "%s-local-minor-mode" toggle)))
(local-keymap (intern (format "%s-local-map" toggle)))
(tag (intern (format "%s-tag" toggle)))
(message (intern (format "%s-message" toggle)))
(cursor (intern (format "%s-cursor" toggle)))
(entry-hook (intern (format "%s-entry-hook" toggle)))
(exit-hook (intern (format "%s-exit-hook" toggle)))
(modes (intern (format "%s-modes" toggle)))
(predicate (intern (format "%s-p" toggle)))
arg cursor-value enable entry-hook-value exit-hook-value
input-method key message-value suppress-keymap tag-value)
;; collect keywords
(while (keywordp (car-safe body))
(setq key (pop body)
arg (pop body))
(cond
((eq key :tag)
(setq tag-value arg))
((eq key :message)
(setq message-value arg))
((eq key :cursor)
(setq cursor-value arg))
((eq key :entry-hook)
(setq entry-hook-value arg)
(unless (listp entry-hook-value)
(setq entry-hook-value (list entry-hook-value))))
((eq key :exit-hook)
(setq exit-hook-value arg)
(unless (listp exit-hook-value)
(setq exit-hook-value (list exit-hook-value))))
((eq key :enable)
(setq enable arg))
((eq key :input-method)
(setq input-method arg))
((eq key :suppress-keymap)
(setq suppress-keymap arg))))
;; macro expansion
`(progn
;; Save the state's properties in `evil-state-properties' for
;; runtime lookup. Among other things, this information is used
;; to determine what keymaps should be activated by the state
;; (and, when processing :enable, what keymaps are activated by
;; other states). We cannot know this at compile time because
;; it depends on the current buffer and its active keymaps
;; (to which we may have assigned state bindings), as well as
;; states whose definitions may not have been processed yet.
(let ((plist (list
:name ',name
:toggle ',toggle
:mode (defvar ,mode nil
,(format "Non-nil if %s is enabled.
Use the command `%s' to change this variable." name toggle))
:keymap (defvar ,keymap (make-sparse-keymap)
,(format "Keymap for %s." name))
:local (defvar ,local nil
,(format "Non-nil if %s is enabled.
Use the command `%s' to change this variable." name toggle))
:local-keymap (defvar ,local-keymap nil
,(format "Buffer-local keymap for %s." name))
:tag (defvar ,tag ,tag-value
,(format "Mode line tag for %s." name))
:message (defvar ,message ,message-value
,(format "Echo area message for %s." name))
:cursor (defvar ,cursor ',cursor-value
,(format "Cursor for %s.
May be a cursor type as per `cursor-type', a color string as passed
to `set-cursor-color', a zero-argument function for changing the
cursor, or a list of the above." name))
:entry-hook (defvar ,entry-hook nil
,(format "Hooks to run when entering %s." name))
:exit-hook (defvar ,exit-hook nil
,(format "Hooks to run when exiting %s." name))
:modes (defvar ,modes nil
,(format "Modes that should come up in %s." name))
:input-method ',input-method
:predicate ',predicate
:enable ',enable)))
(evil--add-to-alist evil-state-properties ',state plist))
,@(when suppress-keymap
`((set-keymap-parent ,keymap evil-suppress-map)))
(dolist (func ',entry-hook-value) (add-hook ',entry-hook func))
(dolist (func ',exit-hook-value) (add-hook ',exit-hook func))
(defun ,predicate (&optional state)
,(format "Whether the current state is %s.
\(That is, whether `evil-state' is `%s'.)" name state)
(and evil-local-mode
(eq (or state evil-state) ',state)))
;; define state function
(defun ,toggle (&optional arg)
,(format "Enable %s. Disable with negative ARG.
If ARG is nil, don't display a message in the echo area.%s" name doc)
(interactive)
(cond
((and (numberp arg) (< arg 1))
(setq evil-previous-state evil-state
evil-state nil)
(let ((evil-state ',state))
(run-hooks ',exit-hook)
(setq evil-state nil)
(evil-normalize-keymaps)
,@body))
(t
(unless evil-local-mode (evil-local-mode))
(let ((evil-next-state ',state)
input-method-activate-hook input-method-deactivate-hook)
(evil-change-state nil)
(setq evil-state ',state)
(evil--add-to-alist evil-previous-state-alist
',state evil-previous-state)
(let ((evil-state ',state))
(evil-normalize-keymaps)
(if ',input-method
(activate-input-method evil-input-method)
;; BUG #475: Deactivate the current input method only
;; if there is a function to deactivate it, otherwise
;; an error would be raised. This strange situation
;; should not arise in general and there should
;; probably be a better way to handle this situation.
(when deactivate-current-input-method-function
(deactivate-input-method)))
(unless evil-no-display
(when (eq (window-buffer) (current-buffer))
(evil-refresh-cursor ',state))
(evil-refresh-mode-line ',state))
,@body
(run-hooks ',entry-hook)
(when (and evil-echo-state
arg (not evil-no-display) ,message)
(if (functionp ,message)
(funcall ,message)
(evil-echo "%s" ,message))))))))
(evil-add-command-properties ',toggle :keep-visual t :suppress-operator t)
(evil-define-keymap ,keymap nil
:mode ,mode
:func nil)
(evil-define-keymap ,local-keymap nil
:mode ,local
:local t
:func nil)
',state)))