branch: scratch/evil commit b9298c600fc0d0e08fac8db3fa7ca2e0f2eb72d6 Author: Stefan Monnier <monn...@iro.umontreal.ca> Commit: Stefan Monnier <monn...@iro.umontreal.ca>
(evil-with-delay): New macro, extracted from `evil-delay` Save some kitten by using a macro instead of using `eval`. This also exposes more code to the compiler, so should result in more efficient code and potentially better compiler warnings. * evil-common.el (evil-unquote): Delete unused function. (evil--with-delay): New function, extracted from `evil-delay`. (evil-with-delay): New macro, extracted from `evil-delay`. (evil-delay): Redefine using `evil-with-delay` and declare obsolete. * evil-states.el (evil-visual-activate-hook): * evil-core.el (evil-define-key): * evil-commands.el (evil-execute-in-normal-state): Use `evil-with-delay`. --- evil-commands.el | 68 +++++++++++++++++++++++++++++--------------------------- evil-common.el | 50 ++++++++++++++++++++++++----------------- evil-core.el | 24 +++++++++++--------- evil-states.el | 21 +++++++++-------- 4 files changed, 88 insertions(+), 75 deletions(-) diff --git a/evil-commands.el b/evil-commands.el index 0b28ceac34..347ba5c939 100644 --- a/evil-commands.el +++ b/evil-commands.el @@ -5188,39 +5188,41 @@ Restore the disabled repeat hooks on insert-state exit." (defun evil-execute-in-normal-state () "Execute the next command in Normal state." (interactive) - (evil-delay '(not (memq this-command - '(nil - evil-execute-in-normal-state - evil-replace-state - evil-use-register - digit-argument - negative-argument - universal-argument - universal-argument-minus - universal-argument-more - universal-argument-other-key))) - `(with-current-buffer ,(current-buffer) - ;; If cursor was after EOL before CTRL-O and is now at EOL, - ;; put it after EOL. - (and (or (when evil--execute-normal-eol-pos - (= (1+ (point)) (save-excursion - (goto-char evil--execute-normal-eol-pos) - (set-marker evil--execute-normal-eol-pos nil) - (line-end-position)))) - (and (eq (or goal-column temporary-goal-column) most-positive-fixnum) - (memq this-command '(next-line previous-line)))) - (not (eolp)) - (not (memq this-command - '(evil-insert evil-beginning-of-line evil-first-non-blank))) - (forward-char)) - (unless (memq evil-state '(replace insert)) - (evil-change-state ',evil-state)) - (when (eq 'insert evil-state) - (remove-hook 'pre-command-hook #'evil-repeat-pre-hook) - (remove-hook 'post-command-hook #'evil-repeat-post-hook) - (add-hook 'evil-insert-state-exit-hook #'evil--restore-repeat-hooks)) - (setq evil-execute-normal-keys nil)) - 'post-command-hook) + (let ((buf (current-buffer)) + (estate evil-state)) + (evil-with-delay (not (memq this-command + '(nil + evil-execute-in-normal-state + evil-replace-state + evil-use-register + digit-argument + negative-argument + universal-argument + universal-argument-minus + universal-argument-more + universal-argument-other-key))) + post-command-hook + (with-current-buffer buf + ;; If cursor was after EOL before CTRL-O and is now at EOL, + ;; put it after EOL. + (and (or (when evil--execute-normal-eol-pos + (= (1+ (point)) (save-excursion + (goto-char evil--execute-normal-eol-pos) + (set-marker evil--execute-normal-eol-pos nil) + (line-end-position)))) + (and (eq (or goal-column temporary-goal-column) most-positive-fixnum) + (memq this-command '(next-line previous-line)))) + (not (eolp)) + (not (memq this-command + '(evil-insert evil-beginning-of-line evil-first-non-blank))) + (forward-char)) + (unless (memq evil-state '(replace insert)) + (evil-change-state estate)) + (when (eq 'insert evil-state) + (remove-hook 'pre-command-hook #'evil-repeat-pre-hook) + (remove-hook 'post-command-hook #'evil-repeat-post-hook) + (add-hook 'evil-insert-state-exit-hook #'evil--restore-repeat-hooks)) + (setq evil-execute-normal-keys nil)))) (setq evil-insert-count nil evil--execute-normal-return-state evil-state evil--execute-normal-eol-pos (when (eolp) (point-marker)) diff --git a/evil-common.el b/evil-common.el index 1311ae366d..1613196ad2 100644 --- a/evil-common.el +++ b/evil-common.el @@ -49,30 +49,40 @@ (if (fboundp 'gui-set-selection) 'gui-set-selection 'x-set-selection)) ;; macro helper -(eval-and-compile - (defun evil-unquote (exp) - "Return EXP unquoted." - (while (eq (car-safe exp) 'quote) - (setq exp (cadr exp))) - exp)) +(defun evil--with-delay (cond-fun body-fun hook &optional append local name) + (if (and cond-fun (funcall cond-fun)) + (funcall body-fun) + (let* ((name (or name (format "evil-delay-in-%s" hook))) + (fun (make-symbol name))) + (fset fun (lambda (&rest _) + (when (or (null cond-fun) (funcall cond-fun)) + (remove-hook hook fun local) + (funcall body-fun)))) + (put fun 'permanent-local-hook t) + (add-hook hook fun append local)))) + +(defmacro evil-with-delay (condition hook &rest body) + "Execute BODY when CONDITION becomes true, checking with HOOK. +HOOK can be a simple symbol or it can be of the form +\(HOOK APPEND LOCAL NAME) where: +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." + (declare (debug (form sexp body)) (indent 2)) + (unless (consp hook) (setq hook (list hook))) + ;; FIXME: Now the compiler doesn't know that `body' is only run if `condition' + ;; is true, so we get spurious warnings! :-( + `(evil--with-delay ,(if condition `(lambda () ,condition)) + (lambda () ,@body) + ,@(mapcar #'macroexp-quote hook))) (defun evil-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, +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." - (if (and (not (booleanp condition)) (eval condition)) - (eval form) - (let* ((name (or name (format "evil-delay-form-in-%s" hook))) - (fun (make-symbol name)) - (condition (or condition t))) - (fset fun `(lambda (&rest args) - (when ,condition - (remove-hook ',hook #',fun ',local) - ,form))) - (put fun 'permanent-local-hook t) - (add-hook hook fun append local)))) -(put 'evil-delay 'lisp-indent-function 2) + (declare (obsolete evil-with-delay "2022") (indent 2)) + (eval `(evil-with-delay ,condition (,hook ,append ,local ,name) ,form) t)) ;;; List functions diff --git a/evil-core.el b/evil-core.el index 8f7eba9002..5db6326952 100644 --- a/evil-core.el +++ b/evil-core.el @@ -988,16 +988,18 @@ mode, in which case `evil-define-minor-mode-key' is used." ((and (consp keymap) (eq (car keymap) 'quote)) `(evil-define-minor-mode-key ,state ,keymap ,key ,def ,@bindings)) (t - `(evil-delay ',(if (symbolp keymap) - `(and (boundp ',keymap) (keymapp ,keymap)) - `(keymapp ,keymap)) - '(condition-case-unless-debug err - (evil-define-key* ,state ,keymap ,key ,def ,@bindings) - (error (message "error in evil-define-key: %s" - (error-message-string err)))) - 'after-load-functions t nil - (format "evil-define-key-in-%s" - ',(if (symbolp keymap) keymap 'keymap)))))) + `(evil-with-delay ,(if (symbolp keymap) + ;; BEWARE: Can't work for lexically scoped vars. + `(and (boundp ',keymap) (keymapp ,keymap)) + `(keymapp ,keymap)) + (after-load-functions t nil + ,(format "evil-define-key-in-%s" + (if (symbolp keymap) keymap + 'keymap))) + (condition-case-unless-debug err + (evil-define-key* ,state ,keymap ,key ,def ,@bindings) + (error (message "error in evil-define-key: %s" + (error-message-string err)))))))) (defalias 'evil-declare-key #'evil-define-key) (defun evil-define-key* (state keymap key def &rest bindings) @@ -1040,7 +1042,7 @@ The use is nearly identical to `evil-define-key' with the exception that this is a function and not a macro (and so will not be expanded when compiled which can have unintended consequences). `evil-define-key*' also does not defer any -bindings like `evil-define-key' does using `evil-delay'. This +bindings like `evil-define-key' does using `evil-with-delay'. This allows errors in the bindings to be caught immediately, and makes its behavior more predictable." (declare (indent defun)) diff --git a/evil-states.el b/evil-states.el index abade4c6df..0f20c62869 100644 --- a/evil-states.el +++ b/evil-states.el @@ -390,17 +390,16 @@ otherwise exit Visual state." (defun evil-visual-activate-hook (&optional _command) "Enable Visual state if the region is activated." (unless (evil-visual-state-p) - (evil-delay nil - ;; the activation may only be momentary, so re-check - ;; in `post-command-hook' before entering Visual state - '(unless (or (evil-visual-state-p) - (evil-insert-state-p) - (evil-emacs-state-p)) - (when (and (region-active-p) - (not deactivate-mark)) - (evil-visual-state))) - 'post-command-hook nil t - "evil-activate-visual-state"))) + (evil-with-delay nil + (post-command-hook nil t "evil-activate-visual-state") + ;; the activation may only be momentary, so re-check + ;; in `post-command-hook' before entering Visual state + (unless (or (evil-visual-state-p) + (evil-insert-state-p) + (evil-emacs-state-p)) + (when (and (region-active-p) + (not deactivate-mark)) + (evil-visual-state)))))) (put 'evil-visual-activate-hook 'permanent-local-hook t) (defun evil-visual-deactivate-hook (&optional command)