branch: scratch/evil commit 4ad8679623dd3cc09c25689e7471edf8e8f9c3ed Author: Stefan Monnier <monn...@iro.umontreal.ca> Commit: Stefan Monnier <monn...@iro.umontreal.ca>
Revert "Urgent revert of recent commits while evil is broken" This reverts commit b7b4961a14cd1a51e9a10564fd6c741567d39891. --- README.md | 9 +- evil-command-window.el | 3 +- evil-commands.el | 370 ++++++++++++++++++++++--------------------------- evil-common.el | 88 +++++------- evil-core.el | 76 ++++------ evil-ex.el | 52 +++---- evil-jumps.el | 15 +- evil-macros.el | 4 +- evil-maps.el | 10 +- evil-search.el | 59 ++++---- evil-states.el | 21 ++- evil-test-helpers.el | 8 +- evil-tests.el | 122 ++++++++-------- evil-types.el | 2 +- evil-vars.el | 9 -- evil.el | 4 +- 16 files changed, 379 insertions(+), 473 deletions(-) diff --git a/README.md b/README.md index 198b18317c..02215faf2a 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,8 @@ file. * Evil requires any of the following for `C-r`: * `undo-redo` from Emacs 28 - * The [undo-tree](https://gitlab.com/tsc25/undo-tree) package - (available via GNU ELPA) - * The [undo-fu](https://gitlab.com/ideasman42/emacs-undo-fu) package - (available via MELPA) + * The [undo-tree] package (available via GNU ELPA) + * The [undo-fu] package (available via MELPA and NonGNU ELPA) * For the motions `g;` `g,` and for the last-change-register `.`, Evil requires the [goto-chg.el](https://github.com/emacs-evil/goto-chg) @@ -80,3 +78,6 @@ Visit us on `irc.libera.chat #evil-mode`. See [CONTRIBUTING.md](https://github.com/emacs-evil/evil/blob/master/CONTRIBUTING.md) for guidelines for issues and pull requests. + +[undo-tree]: https://gitlab.com/tsc25/undo-tree +[undo-fu]: https://codeberg.org/ideasman42/emacs-undo-fu diff --git a/evil-command-window.el b/evil-command-window.el index 68068a8384..c364e41868 100644 --- a/evil-command-window.el +++ b/evil-command-window.el @@ -44,8 +44,7 @@ (define-derived-mode evil-command-window-mode fundamental-mode "Evil-cmd" "Major mode for the Evil command line window." (auto-fill-mode 0) - (setq-local after-change-functions - (cons #'evil-command-window-draw-prefix after-change-functions))) + (add-hook 'after-change-functions #'evil-command-window-draw-prefix nil t)) (defun evil-command-window (history cmd-key execute-fn) "Open a command line window for HISTORY with CMD-KEY and EXECUTE-FN. diff --git a/evil-commands.el b/evil-commands.el index 45f6b596c0..7dfc1e0ae8 100644 --- a/evil-commands.el +++ b/evil-commands.el @@ -37,7 +37,6 @@ (require 'cl-lib) (require 'reveal) -(declare-function flyspell-overlay-p "flyspell") (declare-function imenu--in-alist "imenu") ;;; Motions @@ -215,10 +214,9 @@ move COUNT - 1 screen lines downward first." "Move the cursor to COUNT % of the width of the current line. If no COUNT is given, default to 50%." :type exclusive - (let ((line-length (- (line-end-position) - (line-beginning-position) + (let ((line-length (- (line-end-position) (line-beginning-position) (if evil-move-beyond-eol -1 0)))) - (move-to-column (truncate (* line-length (/ (or count 50) 100.0)))))) + (move-to-column (truncate (* line-length (or count 50)) 100)))) (evil-define-motion evil-first-non-blank () "Move the cursor to the first non-blank character of the current line." @@ -229,13 +227,9 @@ If no COUNT is given, default to 50%." "Move the cursor to the last non-blank character of the current line. If COUNT is given, move COUNT - 1 lines downward first." :type inclusive - (goto-char - (save-excursion - (evil-move-beginning-of-line count) - (if (re-search-forward "[ \t]*$") - (max (line-beginning-position) - (1- (match-beginning 0))) - (line-beginning-position))))) + (evil-move-end-of-line count) + (skip-chars-backward " \t") + (unless (bolp) (backward-char))) (evil-define-motion evil-first-non-blank-of-visual-line () "Move the cursor to the first non blank character @@ -498,30 +492,28 @@ and jump to the corresponding one." (t (let* ((open (point-max)) (close (point-max)) - (open-pair (condition-case nil - (save-excursion - ;; consider the character right before eol given that - ;; point may be placed there, e.g. in visual state - (when (and (eolp) (not (bolp))) - (backward-char)) - (setq open (1- (scan-lists (point) 1 -1))) - (when (< open (line-end-position)) - (goto-char open) - (forward-list) - (1- (point)))) - (error nil))) - (close-pair (condition-case nil - (save-excursion - ;; consider the character right before eol given that - ;; point may be placed there, e.g. in visual state - (when (and (eolp) (not (bolp))) - (backward-char)) - (setq close (1- (scan-lists (point) 1 1))) - (when (< close (line-end-position)) - (goto-char (1+ close)) - (backward-list) - (point))) - (error nil)))) + (open-pair (ignore-errors + (save-excursion + ;; consider the character right before eol given that + ;; point may be placed there, e.g. in visual state + (when (and (eolp) (not (bolp))) + (backward-char)) + (setq open (1- (scan-lists (point) 1 -1))) + (when (< open (line-end-position)) + (goto-char open) + (forward-list) + (1- (point)))))) + (close-pair (ignore-errors + (save-excursion + ;; consider the character right before eol given that + ;; point may be placed there, e.g. in visual state + (when (and (eolp) (not (bolp))) + (backward-char)) + (setq close (1- (scan-lists (point) 1 1))) + (when (< close (line-end-position)) + (goto-char (1+ close)) + (backward-list) + (point)))))) (cond ((not (or open-pair close-pair)) ;; nothing found, check if we are inside a string @@ -548,6 +540,7 @@ and jump to the corresponding one." ((< open close) (goto-char open-pair)) (t (goto-char close-pair))))))) +(declare-function flyspell-overlay-p "flyspell") (evil-define-motion evil-next-flyspell-error (count) "Go to the COUNT'th spelling mistake after point." :jump t @@ -608,27 +601,16 @@ and jump to the corresponding one." (defun evil--next-mark (forwardp) "Move to next lowercase mark. -Move forward if FORWARDP is truthy or backward if falsey. -Loop back to the top of buffer if the end is reached." - (let ((pos (point)) - (sorted-markers (sort (evil--lowercase-markers) - (lambda (a b) (< (cdr a) (cdr b)))))) - (cond - ((null sorted-markers) - (user-error "No marks in this buffer")) - (forwardp - (let ((next-marker (cl-some (lambda (x) (and (< pos (cdr x)) (cdr x))) - sorted-markers))) - (if next-marker - (goto-char (marker-position next-marker)) - (goto-char (marker-position (cdar sorted-markers)))))) - (t - (let* ((descending-markers (reverse sorted-markers)) - (prev-marker (cl-some (lambda (x) (and (> pos (cdr x)) (cdr x))) - descending-markers))) - (if prev-marker - (goto-char (marker-position prev-marker)) - (goto-char (marker-position (cdar descending-markers))))))))) +Move forward if FORWARDP is non-nil, and backward otherwise. Loop back +to the beginning of buffer if the end is reached." + (let* ((pos (if forwardp (1+ (point)) (point))) + (centered-markers + (cl-sort + (or (evil--lowercase-markers) (user-error "No marks in this buffer")) + (if forwardp #'< #'>) + :key (lambda (x) (+ (if (< (cdr x) pos) 0 most-negative-fixnum) + (cdr x)))))) + (goto-char (cdar centered-markers)))) (evil-define-motion evil-next-mark (count) "Go to COUNT next lowercase mark." @@ -645,12 +627,11 @@ Loop back to the top of buffer if the end is reached." :repeat nil :type exclusive :jump t - (if (evil--lowercase-markers) - (dotimes (_ (or count 1)) - (evil-end-of-line) - (evil--next-mark t) - (evil-first-non-blank)) - (user-error "No marks in this buffer"))) + (unless (evil--lowercase-markers) (user-error "No marks in this buffer")) + (dotimes (_ (or count 1)) + (move-end-of-line nil) + (evil--next-mark t)) + (evil-first-non-blank)) (evil-define-motion evil-previous-mark (count) "Go to COUNT previous lowercase mark." @@ -667,15 +648,15 @@ Loop back to the top of buffer if the end is reached." :repeat nil :type exclusive :jump t - (if (evil--lowercase-markers) - (dotimes (_ (or count 1)) - (evil-beginning-of-line) - (evil--next-mark nil) - (evil-first-non-blank)) - (user-error "No marks in this buffer"))) + (unless (evil--lowercase-markers) (user-error "No marks in this buffer")) + (dotimes (_ (or count 1)) + (move-beginning-of-line nil) + (evil--next-mark nil)) + (evil-first-non-blank)) (evil-define-command evil-set-col-0-mark (beg end mark) - "Set MARK at column 0 of line of END. Default is cursor line." + "Set MARK at column 0 of line of END. +Default is cursor line." (interactive "<r><a>") (if (< 1 (length mark)) (user-error "Trailing characters") @@ -686,7 +667,8 @@ Loop back to the top of buffer if the end is reached." (evil-define-motion evil-find-char (count char) "Move to the next COUNT'th occurrence of CHAR. -Movement is restricted to the current line unless `evil-cross-lines' is non-nil." +Movement is restricted to the current line unless `evil-cross-lines' +is non-nil." :type inclusive (interactive "<c><C>") (setq count (or count 1)) @@ -725,12 +707,11 @@ Movement is restricted to the current line unless `evil-cross-lines' is non-nil. :type inclusive (interactive "<c><C>") (unwind-protect - (progn - (evil-find-char count char) - (if (> (or count 1) 0) - (backward-char) - (forward-char))) - (setcar evil-last-find #'evil-find-char-to))) + (evil-find-char count char) + (setcar evil-last-find #'evil-find-char-to)) + (if (> (or count 1) 0) + (backward-char) + (forward-char))) (evil-define-motion evil-find-char-to-backward (count char) "Move before the previous COUNT'th occurrence of CHAR." @@ -742,26 +723,22 @@ Movement is restricted to the current line unless `evil-cross-lines' is non-nil. "Repeat the last find COUNT times." :type inclusive (setq count (or count 1)) - (if evil-last-find - (let ((cmd (car evil-last-find)) - (char (nth 1 evil-last-find)) - (fwd (nth 2 evil-last-find)) - evil-last-find) - ;; ensure count is non-negative - (when (< count 0) - (setq count (- count) - fwd (not fwd))) - ;; skip next character when repeating t or T - (and (eq cmd #'evil-find-char-to) - evil-repeat-find-to-skip-next - (= count 1) - (or (and fwd (= (char-after (1+ (point))) char)) - (and (not fwd) (= (char-before) char))) - (setq count (1+ count))) - (funcall cmd (if fwd count (- count)) char) - (unless (nth 2 evil-last-find) - (setq evil-this-type 'exclusive))) - (user-error "No previous search"))) + (cl-destructuring-bind (cmd char fwd) + (or evil-last-find (user-error "No previous search")) + ;; ensure count is non-negative + (when (< count 0) + (setq count (- count) + fwd (not fwd))) + ;; skip next character when repeating t or T + (and (eq cmd #'evil-find-char-to) + evil-repeat-find-to-skip-next + (= count 1) + (eql (if fwd (char-after (1+ (point))) (char-before)) char) + (setq count (1+ count))) + (let (evil-last-find) + (funcall cmd (if fwd count (- count)) char) + (unless (nth 2 evil-last-find) + (setq evil-this-type 'exclusive))))) (evil-define-motion evil-repeat-find-char-reverse (count) "Repeat the last find COUNT times in the opposite direction." @@ -2007,9 +1984,7 @@ If a `#' is included before the mark args, the lines are numbered." :move-point nil :type line (save-excursion - (condition-case nil - (fill-region beg end) - (error nil)))) + (ignore-errors (fill-region beg end)))) (evil-define-operator evil-fill-and-move (beg end) "Fill text and move point to the end of the filled region." @@ -2017,12 +1992,10 @@ If a `#' is included before the mark args, the lines are numbered." :type line (let ((marker (make-marker))) (move-marker marker (1- end)) - (condition-case nil - (progn - (fill-region beg end) - (goto-char marker) - (evil-first-non-blank)) - (error nil)))) + (ignore-errors + (fill-region beg end) + (goto-char marker) + (evil-first-non-blank)))) (evil-define-operator evil-indent (beg end) "Indent text." @@ -2407,10 +2380,8 @@ leave the cursor just after the new text." (when evil-kill-on-visual-paste (current-kill -1)) ;; Ensure that gv can restore visually pasted area... - (setq evil-visual-previous-mark evil-visual-mark - evil-visual-mark (evil-get-marker (if (< 0 dir) ?\[ ?\]) t) - evil-visual-previous-point evil-visual-point - evil-visual-point (evil-get-marker (if (< 0 dir) ?\] ?\[) t)) + (set-marker evil-visual-point (evil-get-marker (if (< dir 0) ?\[ ?\]) t)) + (set-marker evil-visual-mark (evil-get-marker (if (< dir 0) ?\] ?\[) t)) ;; mark the last paste as visual-paste (setq evil-last-paste (list (nth 0 evil-last-paste) @@ -2423,26 +2394,23 @@ leave the cursor just after the new text." (defun evil-paste-from-register (register) "Paste from REGISTER." (interactive - (let* ((opoint (point)) - (overlay (make-overlay opoint (+ opoint (if (evil-replace-state-p) 1 0))))) + (let ((ov (make-overlay (point) (+ (point) (if (evil-replace-state-p) 1 0))))) (unwind-protect (progn - (overlay-put overlay 'invisible t) - (overlay-put overlay 'after-string (propertize "\"" - 'face 'minibuffer-prompt - 'cursor 1)) + (overlay-put ov 'invisible t) + (overlay-put ov 'after-string + #("\"" 0 1 (face minibuffer-prompt cursor 1))) (list (or evil-this-register (read-char)))) - (delete-overlay overlay)))) + (delete-overlay ov)))) (let ((opoint (point)) - (evil-move-cursor-back nil) - reg-length chars-to-delete) + evil-move-cursor-back) (evil-paste-before nil register t) (when (evil-replace-state-p) - (setq reg-length (- (point) opoint) - chars-to-delete (min (- (line-end-position) (point)) reg-length)) - ;; TODO: handle multi-line paste backspacing - (evil-update-replace-alist (point) reg-length chars-to-delete chars-to-delete) - (delete-char chars-to-delete)))) + (let* ((reg-length (- (point) opoint)) + (chars-to-delete (min (- (line-end-position) (point)) reg-length))) + ;; TODO: handle multi-line paste backspacing + (evil-update-replace-alist (point) reg-length chars-to-delete chars-to-delete) + (delete-char chars-to-delete))))) (defun evil-paste-last-insertion () "Paste last insertion." @@ -2498,10 +2466,8 @@ will be opened instead." ((eq register ?\C-g) (keyboard-quit)) ((and evil-this-macro defining-kbd-macro) - (setq evil-macro-buffer nil) - (condition-case nil - (setq last-macro (evil-end-and-return-macro)) - (error nil)) + (setq evil-macro-buffer nil + last-macro (ignore-errors (evil-end-and-return-macro))) (when last-macro (evil-set-register evil-this-macro last-macro)) (setq evil-this-macro nil)) @@ -2773,26 +2739,23 @@ switch to insert state." (defun evil-quoted-insert (count) "Like `quoted-insert' but delete COUNT chars forward in replace state. -Adds a `^' overlay as an input prompt." +Adds a \"^\" overlay as an input prompt." (interactive "p") (let* ((opoint (point)) - chars-to-delete insert-prompt) + chars-to-delete + (ov (if (not (evil-replace-state-p)) + (make-overlay opoint opoint) + (setq chars-to-delete (min (- (line-end-position) opoint) count)) + (evil-update-replace-alist opoint count chars-to-delete) + (make-overlay opoint (+ chars-to-delete opoint))))) (unwind-protect (progn - (if (evil-replace-state-p) - (progn - (setq chars-to-delete (min (- (line-end-position) opoint) count) - insert-prompt (make-overlay opoint (+ chars-to-delete opoint))) - (evil-update-replace-alist opoint count chars-to-delete)) - (setq insert-prompt (make-overlay opoint opoint))) - (overlay-put insert-prompt 'invisible t) - (overlay-put insert-prompt 'after-string (propertize "^" - 'face 'escape-glyph - 'cursor 1)) - (let (overwrite-mode) ;; Force `read-quoted-char' + (overlay-put ov 'invisible t) + (overlay-put ov 'after-string #("^" 0 1 (face escape-glyph cursor 1))) + (let (overwrite-mode) ; Force `read-quoted-char' (quoted-insert count)) - (when (evil-replace-state-p) (delete-char chars-to-delete))) - (when insert-prompt (delete-overlay insert-prompt))))) + (when chars-to-delete (delete-char chars-to-delete))) + (delete-overlay ov)))) (evil-define-command evil-open-above (count) "Insert a new line above point and switch to Insert state. @@ -3194,10 +3157,9 @@ The search is unbounded, i.e., the pattern is not wrapped in (let ((identifier (save-excursion (goto-char position) (xref-backend-identifier-at-point (xref-find-backend))))) - (condition-case () - (progn - (xref-find-definitions identifier) - t) + (condition-case nil + (progn (xref-find-definitions identifier) + t) (user-error nil))))) (defun evil-goto-definition-search (string _position) @@ -3402,8 +3364,7 @@ files." (evil-define-command evil-goto-error (count) "Go to error number COUNT. - -If no COUNT supplied, move to the current error. +If no COUNT is supplied, move to the current error. Acts like `first-error' other than when given no counts, goes to the current error instead of the first, like in Vim's :cc @@ -3463,17 +3424,12 @@ for the last window in each frame." ;; if the buffer which was initiated by emacsclient, ;; call `server-edit' from server.el to avoid ;; "Buffer still has clients" message - (if (and (fboundp 'server-edit) - (boundp 'server-buffer-clients) - server-buffer-clients) + (if (and (bound-and-true-p server-buffer-clients) + (fboundp 'server-edit)) (server-edit) (kill-buffer nil)) ;; close all windows that showed this buffer - (mapc #'(lambda (w) - (condition-case nil - (delete-window w) - (error nil))) - wins)))) + (dolist (w wins) (ignore-errors (delete-window w)))))) (evil-define-command evil-quit (&optional force) "Close the current window, current frame, current tab, Emacs. @@ -3484,10 +3440,9 @@ is closed." (condition-case nil (delete-window) (error - (if (and (boundp 'server-buffer-clients) + (if (and (bound-and-true-p server-buffer-clients) (fboundp 'server-edit) - (fboundp 'server-buffer-done) - server-buffer-clients) + (fboundp 'server-buffer-done)) (if force (server-buffer-done (current-buffer)) (server-edit)) @@ -3521,10 +3476,7 @@ The FORCE argument is only there for compatibility and is ignored. This function fails with an error if Emacs is run in server mode." :repeat nil (interactive "<!>") - (if (and (boundp 'server-buffer-clients) - (fboundp 'server-edit) - (fboundp 'server-buffer-done) - server-buffer-clients) + (if (bound-and-true-p server-buffer-clients) (user-error "Cannot exit client process with error code.") (kill-emacs 1))) @@ -4271,7 +4223,7 @@ Use `evil-flush-lines' if INVERT is nil, or `evil-keep-lines' if not." (let ((evil--ex-global-active-p t)) (dolist (marker markers) (goto-char marker) - (eval command-form)))) + (eval command-form t)))) ;; ensure that all markers are deleted afterwards, ;; even in the event of failure (dolist (marker markers) @@ -4453,8 +4405,11 @@ The \"!\" argument means to sort in reverse order." "Toggle side windows, evaluate BODY, restore side windows." (declare (indent defun) (debug (&rest form))) (let ((sides (make-symbol "sidesvar"))) - `(let ((,sides (and (functionp 'window-toggle-side-windows) + `(let ((,sides (and (fboundp 'window-toggle-side-windows) (window-with-parameter 'window-side)))) + ;; The compiler doesn't understand that all uses are protected + ;; by `fboundp' :-( + (declare-function window-toggle-side-windows "window") (when ,sides (window-toggle-side-windows)) (unwind-protect @@ -4524,17 +4479,14 @@ the deleted window's parent window are rebalanced." (let ((p (window-parent))) ;; If tabs are enabled and this is the only visible window, then attempt to ;; close this tab. - (if (and (boundp 'tab-bar-mode) - tab-bar-mode - (not p)) + (if (and (bound-and-true-p tab-bar-mode) + (null p)) (tab-close) - (delete-window)) - (when evil-auto-balance-windows - ;; balance-windows raises an error if the parent does not have - ;; any further children (then rebalancing is not necessary anyway) - (condition-case nil - (balance-windows p) - (error))))) + (delete-window) + (when evil-auto-balance-windows + ;; balance-windows raises an error if the parent does not have + ;; any further children (then rebalancing is not necessary anyway) + (ignore-errors (balance-windows p)))))) (evil-define-command evil-window-split (&optional count file) "Split the current window horizontally, COUNT lines height, @@ -5118,39 +5070,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)) + (state 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 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)))) (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 e423a1c4f0..9504cda27f 100644 --- a/evil-common.el +++ b/evil-common.el @@ -35,7 +35,6 @@ (declare-function evil-visual-restore "evil-states") (declare-function evil-motion-state "evil-states") (declare-function evil-replace-state-p "evil-states") -(declare-function evil-ex-p "evil-ex") (declare-function evil-set-jump "evil-jumps") ;;; Compatibility with different Emacs versions @@ -47,31 +46,38 @@ (defalias 'evil-set-selection (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)) +(defmacro evil-with-delay (condition hook &rest body) + "Execute BODY when CONDITION becomes true, checking with HOOK. +HOOK can be a simple symbol or 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)) + (cl-destructuring-bind (hook-sym &optional append local name) + (mapcar #'macroexp-quote (if (consp hook) hook (list hook))) + (macroexp-let2* nil + ((fun-name `(make-symbol + ,(or name (format "evil-delay-in-%s" hook-sym)))) + (fun `(apply-partially + (lambda (name &rest _) + (when ,(or condition t) + (remove-hook ,hook-sym name ,local) + ,@body + t)) + ,fun-name))) + `(unless ,(and condition `(funcall ,fun)) + (progn (fset ,fun-name ,fun) + ,@(when local `((put ,fun-name 'permanent-local-hook t))) + (add-hook ,hook-sym ,fun-name ,append ,local)))))) (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 "1.15.0") (indent 2)) + (eval `(evil-with-delay ,condition (,hook ,append ,local ,name) ,form) t)) ;;; List functions @@ -757,15 +763,14 @@ cursor type is either `evil-force-cursor' or the current state." (defmacro evil-save-cursor (&rest body) "Save the current cursor; execute BODY; restore the cursor." - (declare (indent defun) - (debug t)) + (declare (indent defun) (debug t) (obsolete nil "1.15.0")) `(let ((cursor cursor-type) (color (frame-parameter (selected-frame) 'cursor-color)) (inhibit-quit t)) (unwind-protect (progn ,@body) - (evil-set-cursor cursor) - (evil-set-cursor color)))) + (setq cursor-type cursor) + (evil-set-cursor-color color)))) (defun evil-echo (string &rest args) "Display an unlogged message in the echo area. @@ -793,15 +798,12 @@ Does not restore if `evil-write-echo-area' is non-nil." (defmacro evil-save-echo-area (&rest body) "Save the echo area; execute BODY; restore the echo area. Intermittent messages are not logged in the *Messages* buffer." - (declare (indent defun) - (debug t)) + (declare (indent defun) (debug t)) `(let ((inhibit-quit t) - evil-echo-area-message - evil-write-echo-area) + evil-echo-area-message evil-write-echo-area) + (evil-echo-area-save) (unwind-protect - (progn - (evil-echo-area-save) - ,@body) + (progn ,@body) (evil-echo-area-restore)))) (defmacro evil-without-display (&rest body) @@ -1888,7 +1890,7 @@ If INPUT starts with a number, +, -, or . use `calc-eval' instead." (result (if calcable-p (let ((calc-multiplication-has-precedence nil)) (calc-eval input)) - (eval (car (read-from-string input)))))) + (eval (car (read-from-string input)) t)))) (cond ((stringp result) result) ((or (numberp result) (symbolp result)) @@ -2160,21 +2162,6 @@ The earlier settings of Transient Mark mode are stored in (funcall var (if val 1 -1)) (setq var val)))))) -(defun evil-save-mark () - "Save the current mark, including whether it is transient. -See also `evil-restore-mark'." - (unless evil-visual-previous-mark - (setq evil-visual-previous-mark (mark t)) - (evil-save-transient-mark-mode))) - -(defun evil-restore-mark () - "Restore the mark, including whether it was transient. -See also `evil-save-mark'." - (when evil-visual-previous-mark - (evil-restore-transient-mark-mode) - (evil-move-mark evil-visual-previous-mark) - (setq evil-visual-previous-mark nil))) - ;; In theory, an active region implies Transient Mark mode, and ;; disabling Transient Mark mode implies deactivating the region. ;; In practice, Emacs never clears `mark-active' except in Transient @@ -2534,9 +2521,6 @@ is negative this is a more recent kill." (unless evil-last-paste (user-error "Previous paste command used a register")) (evil-undo-pop) - (when (eq last-command 'evil-visual-paste) - (evil-swap evil-visual-previous-mark evil-visual-mark) - (evil-swap evil-visual-previous-point evil-visual-point)) (goto-char (nth 2 evil-last-paste)) (setq this-command (nth 0 evil-last-paste)) ;; use temporary kill-ring, so the paste cannot modify it diff --git a/evil-core.el b/evil-core.el index bb170ae5d5..23937e7d30 100644 --- a/evil-core.el +++ b/evil-core.el @@ -127,7 +127,7 @@ (add-hook 'input-method-activate-hook #'evil-activate-input-method t t) (add-hook 'input-method-deactivate-hook #'evil-deactivate-input-method t t) (add-hook 'activate-mark-hook 'evil-visual-activate-hook nil t) - ;; FIXME: Add these hooks buffer-locally + ;; FIXME: Add these hooks buffer-locally and remove when disabling (add-hook 'pre-command-hook 'evil-repeat-pre-hook) (add-hook 'post-command-hook 'evil-repeat-post-hook)) (evil-refresh-mode-line) @@ -225,15 +225,10 @@ Restore the previous state afterwards." (evil-change-state ',state) ,@body))) -(defun evil-initialize-state (&optional state buffer) - "Set up the initial state for BUFFER. -BUFFER defaults to the current buffer. -Uses STATE if specified, or calls `evil-initial-state-for-buffer'. +(defun evil-initialize-state () + "Set up the initial state for the current buffer. See also `evil-set-initial-state'." - (with-current-buffer (or buffer (current-buffer)) - (evil-change-state - (or state (evil-initial-state-for-buffer buffer))))) -(put 'evil-initialize-state 'permanent-local-hook t) + (evil-change-state (evil-initial-state-for-buffer))) (defun evil-initial-state-for-buffer-name (&optional name default) "Return the initial Evil state to use for a buffer with name NAME. @@ -254,17 +249,12 @@ Matches the name against the regular expressions in (defun evil-initial-state-for-buffer (&optional buffer) "Return the initial Evil state to use for BUFFER. -BUFFER defaults to the current buffer. Returns DEFAULT -if no initial state is associated with BUFFER. -See also `evil-initial-state'." +BUFFER defaults to the current buffer. See also `evil-initial-state'." (with-current-buffer (or buffer (current-buffer)) (or (evil-initial-state-for-buffer-name) - (catch 'done - (dolist (mode minor-mode-map-alist) - (setq mode (car mode)) - (and (boundp mode) (symbol-value mode) - (setq mode (evil-initial-state mode)) - (throw 'done mode)))) + (cl-loop for (mode) in minor-mode-map-alist + when (and (boundp mode) (symbol-value mode)) + thereis (evil-initial-state mode)) (evil-initial-state major-mode nil t) evil-default-state))) @@ -340,11 +330,10 @@ then this function does nothing." ;; otherwise, though, so advise this function to initialize Evil. (defadvice set-window-buffer (before evil) "Initialize Evil in the displayed buffer." - (when evil-mode - (when (get-buffer (ad-get-arg 1)) - (with-current-buffer (ad-get-arg 1) - (unless evil-local-mode - (save-match-data (evil-initialize))))))) + (when (and evil-mode (get-buffer (ad-get-arg 1))) + (with-current-buffer (ad-get-arg 1) + (unless evil-local-mode + (save-match-data (evil-initialize)))))) ;; Refresh cursor color. ;; Cursor color can only be set for each frame but not for each buffer. @@ -452,7 +441,7 @@ This allows input methods to be used in normal-state." "Initialize a buffer-local value for local keymaps as necessary. The initial value is that of `make-sparse-keymap'." (dolist (entry evil-local-keymaps-alist) - (let ((map (cdr entry))) + (let ((map (cdr entry))) (unless (and (keymapp (symbol-value map)) (local-variable-p map)) (set map (make-sparse-keymap)))))) @@ -813,7 +802,6 @@ If AUX is nil, create a new auxiliary keymap." (format "%s state" state))))) (define-key map (vector (intern (format "%s-state" state))) aux) aux) -(put 'evil-set-auxiliary-keymap 'lisp-indent-function 'defun) (defun evil-get-auxiliary-keymap (map state &optional create ignore-parent) "Get the auxiliary keymap for MAP in STATE. @@ -971,21 +959,20 @@ The symbol `local' may also be used, which corresponds to using `global' or `local', it is assumed to be the name of a minor mode, in which case `evil-define-minor-mode-key' is used." (declare (indent defun)) - (cond ((member keymap '('global 'local)) - `(evil-define-key* ,state ,keymap ,key ,def ,@bindings)) - ((eq (car-safe 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)))))) + (cond + ((member keymap '('global 'local)) + `(evil-define-key* ,state ,keymap ,key ,def ,@bindings)) + ((eq (car-safe keymap) 'quote) + `(evil-define-minor-mode-key ,state ,keymap ,key ,def ,@bindings)) + (t `(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))) + (with-demoted-errors "Error in evil-define-key: %S" + (evil-define-key* ,state ,keymap ,key ,def ,@bindings)))))) (defalias 'evil-declare-key #'evil-define-key) (defun evil-define-key* (state keymap key def &rest bindings) @@ -1028,7 +1015,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)) @@ -1285,9 +1272,7 @@ If ARG is nil, don't display a message in the echo area.%s" name doc) (deactivate-input-method))) (unless evil-no-display (evil-refresh-cursor ',state) - (evil-refresh-mode-line ',state) - (when (called-interactively-p 'any) - (redisplay))) + (evil-refresh-mode-line ',state)) ,@body (run-hooks ',entry-hook) (when (and evil-echo-state @@ -1296,8 +1281,7 @@ If ARG is nil, don't display a message in the echo area.%s" name doc) (funcall ,message) (evil-echo "%s" ,message)))))))) - (evil-set-command-property ',toggle :keep-visual t) - (evil-set-command-property ',toggle :suppress-operator t) + (evil-add-command-properties ',toggle :keep-visual t :suppress-operator t) (evil-define-keymap ,keymap nil :mode ,mode diff --git a/evil-ex.el b/evil-ex.el index 5ae3aa4225..f91c1a1447 100644 --- a/evil-ex.el +++ b/evil-ex.el @@ -44,7 +44,6 @@ (require 'evil-common) (require 'evil-states) -(require 'evil-types) (declare-function evil-goto-line "evil-commands") @@ -77,10 +76,9 @@ #'(evil-ex-char-marker-range $2 $4))) (line ((\? base) (\? offset) search (\? offset) - #'(let ((tmp (evil-ex-line $1 $2))) - (save-excursion - (goto-line tmp) - (evil-ex-line $3 $4)))) + #'(save-excursion + (goto-line (evil-ex-line $1 $2)) + (evil-ex-line $3 $4))) (base (\? offset) #'evil-ex-line) (nil offset #'evil-ex-line)) (base @@ -306,6 +304,9 @@ The following symbols have reserved meanings within a grammar: (defvar evil-ex-argument-types nil "Association list of argument handlers.") +(defvar evil-ex-commands nil + "Association list of command bindings and functions.") + (defvar evil-ex-reverse-range nil "Whether the current Ex range was entered reversed.") @@ -399,8 +400,9 @@ symbol, which defaults to `expression'." #'delete-backward-char #'abort-recursive-edit))) -(define-obsolete-function-alias - 'evil-ex-elisp-completion-at-point #'elisp-completion-at-point "1.15.0") +(cl-defstruct (evil-ex-argument-handler (:type list) (:constructor nil) + (:copier nil) (:predicate nil)) + (runner nil :read-only t) (completer nil :read-only t)) (defun evil-ex-setup () "Initialize Ex minibuffer. @@ -413,10 +415,9 @@ actions during Ex state." (defun evil-ex-teardown () "Deinitialize Ex minibuffer. Clean up everything set up by `evil-ex-setup'." - (when evil--ex-argument-handler - (let ((runner (evil-ex-argument-handler-runner - evil--ex-argument-handler))) - (when runner (funcall runner 'stop))))) + (let ((runner (evil-ex-argument-handler-runner + evil--ex-argument-handler))) + (when runner (funcall runner 'stop)))) (put 'evil-ex-teardown 'permanent-local-hook t) (defsubst evil--ex-bang-p (command) @@ -489,13 +490,10 @@ in case of incomplete or unknown commands." (defun evil--ex-remove-echo-overlay () "Remove echo overlay from Ex minibuffer." - (when evil--ex-echo-overlay - (delete-overlay evil--ex-echo-overlay) - (setq evil--ex-echo-overlay nil)) + (delete-overlay evil--ex-echo-overlay) + (setq evil--ex-echo-overlay nil) (remove-hook 'pre-command-hook #'evil--ex-remove-echo-overlay t)) -(define-obsolete-function-alias 'evil-ex-completion #'completion-at-point "1.15.0") - (cl-defun evil-ex-completion-at-point () "Function used for `completion-at-point-functions' in Ex state." (cl-flet ((fix-beg (b) (min (save-excursion @@ -526,9 +524,6 @@ in case of incomplete or unknown commands." (`((expression) (sexp . ,_)) (when (fboundp 'elisp-completion-at-point) (elisp-completion-at-point)))))) -(define-obsolete-function-alias - 'evil-ex-command-completion-at-point #'evil-ex-completion-at-point "1.15.0") - (defun evil-ex-completion-table () (let ((ex-cmds (cl-loop @@ -577,10 +572,18 @@ in case of incomplete or unknown commands." (p2 (eq (get-text-property 0 'face str2) 'evil-ex-commands))) (if (eq p1 p2) (string< str1 str2) p1))))) +(define-obsolete-function-alias 'evil-ex-completion #'completion-at-point "1.15.0") + +(define-obsolete-function-alias + 'evil-ex-command-completion-at-point #'evil-ex-completion-at-point "1.15.0") + (defalias 'evil-ex-argument-completion-at-point #'ignore) (make-obsolete 'evil-ex-argument-completion-at-point #'evil-ex-completion-at-point "1.15.0") +(define-obsolete-function-alias + 'evil-ex-elisp-completion-at-point #'elisp-completion-at-point "1.15.0") + (defun evil-ex-define-cmd (cmd function) "Bind the function FUNCTION to the command CMD." (if (string-match "\\[\\(.*\\)\\]" cmd) @@ -591,15 +594,6 @@ in case of incomplete or unknown commands." abbrev full)) (evil--add-to-alist evil-ex-commands cmd function))) -(defsubst evil-ex-make-argument-handler (runner completer) - (list runner completer)) - -(defun evil-ex-argument-handler-runner (arg-handler) - (car arg-handler)) - -(defun evil-ex-argument-handler-completer (arg-handler) - (cadr arg-handler)) - (defmacro evil-ex-define-argument-type (arg-type doc &rest body) "Define a new handler for argument-type ARG-TYPE. DOC is the documentation string. It is followed by a list of keywords @@ -835,7 +829,7 @@ This function interprets special file names like # and %." (defun evil-ex-last-visual-range () "Return a linewise range of the last visual selection." - (evil-line-expand evil-visual-mark evil-visual-point)) + (evil-range evil-visual-mark evil-visual-point 'line)) (defun evil-ex-marker (marker) "Return MARKER's line number in the current buffer. diff --git a/evil-jumps.el b/evil-jumps.el index 93bf4cbcb6..2fadb8b205 100644 --- a/evil-jumps.el +++ b/evil-jumps.el @@ -38,28 +38,23 @@ (defcustom evil-jumps-cross-buffers t "When non-nil, the jump commands can cross borders between buffers. Otherwise the jump commands act only within the current buffer." - :type 'boolean - :group 'evil-jumps) + :type 'boolean) (defcustom evil-jumps-max-length 100 "The maximum number of jumps to keep track of." - :type 'integer - :group 'evil-jumps) + :type 'integer) (defcustom evil-jumps-pre-jump-hook nil "Hooks to run just before jumping to a location in the jump list." - :type 'hook - :group 'evil-jumps) + :type 'hook) (defcustom evil-jumps-post-jump-hook nil "Hooks to run just after jumping to a location in the jump list." - :type 'hook - :group 'evil-jumps) + :type 'hook) (defcustom evil-jumps-ignored-file-patterns '("COMMIT_EDITMSG$" "TAGS$") "List of regexps to exclude file path from inclusion in the jump list." - :type '(repeat string) - :group 'evil-jumps) + :type '(repeat string)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/evil-macros.el b/evil-macros.el index 90ae28bef0..1a60051e6d 100644 --- a/evil-macros.el +++ b/evil-macros.el @@ -746,10 +746,10 @@ via KEY-VALUE pairs. BODY should evaluate to a list of values. `(lambda ,args ,@(when doc `(,doc)) ,@body) - (macroexp-progn body)))) + `',(macroexp-progn body)))) `(eval-and-compile (evil--add-to-alist - evil-interactive-alist ,code (cons ',func ',properties)) + evil-interactive-alist ,code (cons ,func ',properties)) ,code))) ;;; Highlighting diff --git a/evil-maps.el b/evil-maps.el index 678ebfd80d..fbf5af1695 100644 --- a/evil-maps.el +++ b/evil-maps.el @@ -25,14 +25,14 @@ ;; You should have received a copy of the GNU General Public License ;; along with Evil. If not, see <http://www.gnu.org/licenses/>. +;;; Code: + (require 'evil-states) (require 'evil-ex) (require 'evil-commands) (require 'evil-command-window) (require 'evil-common) -;;; Code: - ;;; Normal state (define-key evil-normal-state-map "a" 'evil-append) @@ -668,10 +668,8 @@ included in `evil-insert-state-bindings' by default." (define-key evil-read-key-map "\r" "\n") ;; command line window -(evil-define-key 'normal - evil-command-window-mode-map (kbd "RET") 'evil-command-window-execute) -(evil-define-key 'insert - evil-command-window-mode-map (kbd "RET") 'evil-command-window-execute) +(evil-define-key* '(normal insert) evil-command-window-mode-map + (kbd "RET") 'evil-command-window-execute) (provide 'evil-maps) diff --git a/evil-search.el b/evil-search.el index ec292a1eb0..d4f870bcb6 100644 --- a/evil-search.el +++ b/evil-search.el @@ -389,9 +389,7 @@ letter, otherwise it will be case-insensitive." ((string-match "\\(?:^\\|[^\\\\]\\)\\(?:\\\\\\\\\\)*\\\\\\([cC]\\)" re) (if (eq (aref (match-string 1 re) 0) ?c) 'insensitive 'sensitive)) ((eq default-case 'smart) - (if (isearch-no-upper-case-p re t) - 'insensitive - 'sensitive)) + (if (isearch-no-upper-case-p re t) 'insensitive 'sensitive)) (t default-case))) ;; a pattern @@ -399,17 +397,15 @@ letter, otherwise it will be case-insensitive." "Create a PATTERN for substitution with FLAGS. This function respects the values of `evil-ex-substitute-case' and `evil-ex-substitute-global'." - (evil-ex-make-pattern regexp - (cond - ((memq ?i flags) 'insensitive) - ((memq ?I flags) 'sensitive) - ((not evil-ex-substitute-case) - evil-ex-search-case) - (t evil-ex-substitute-case)) - (or (and evil-ex-substitute-global - (not (memq ?g flags))) - (and (not evil-ex-substitute-global) - (memq ?g flags))))) + (evil-ex-make-pattern + regexp + (cond ((memq ?i flags) 'insensitive) + ((memq ?I flags) 'sensitive) + ((not evil-ex-substitute-case) evil-ex-search-case) + (t evil-ex-substitute-case)) + (if (memq ?g flags) + (not evil-ex-substitute-global) + evil-ex-substitute-global))) (defun evil-ex-make-search-pattern (regexp) "Create a PATTERN for search. @@ -433,15 +429,14 @@ style regular expression and is not transformed." ;; possibly transform regular expression from vim-style to ;; Emacs-style. (if (and evil-ex-search-vim-style-regexp - (not (or (string-match-p "\\`\\\\_<" regexp) - (string-match-p "\\\\_>\\'" regexp)))) + (not (or (string-prefix-p "\\_<" regexp) + (string-suffix-p "\\_>" regexp)))) (setq re (evil-transform-vim-style-regexp re)) ;; Even for Emacs regular expressions we translate certain ;; whitespace sequences - (setq re (evil-transform-regexp re - '((?t . "\t") - (?n . "\n") - (?r . "\r"))))) + (setq re (evil-transform-regexp re '((?t . "\t") + (?n . "\n") + (?r . "\r"))))) (list re ignore-case whole-line))) (defun evil-ex-pattern-regex (pattern) @@ -716,20 +711,20 @@ This function does nothing if `evil-ex-search-interactive' or (evil-ex-hl-change 'evil-ex-search pattern))))) (defun evil-ex-search (&optional count) - "Search forward or backward COUNT times for the current ex search pattern. -The search pattern is determined by `evil-ex-search-pattern' and -the direcion is determined by `evil-ex-search-direction'." + "Search forward or backward COUNT times for the current Ex search pattern. +The search pattern is determined by `evil-ex-search-pattern', and the +direction by `evil-ex-search-direction'." (setq evil-ex-search-start-point (point) evil-ex-last-was-search t count (or count 1)) (let ((orig (point)) wrapped) (dotimes (_ (or count 1)) - (when (eq evil-ex-search-direction 'forward) - (unless (eobp) (forward-char)) + (when (and (eq evil-ex-search-direction 'forward) (not (eobp))) + (forward-char) ;; maybe skip end-of-line - (when (and (not evil-move-beyond-eol) (eolp) (not (eobp))) - (forward-char))) + (and (not evil-move-beyond-eol) (eolp) (not (eobp)) + (forward-char))) (let ((res (evil-ex-find-next nil nil (not evil-search-wrap)))) (cond ((not res) @@ -739,6 +734,7 @@ the direcion is determined by `evil-ex-search-direction'." ((eq res 'wrapped) (setq wrapped t))))) (if wrapped (let (message-log-max) + (when evil-search-wrap-ring-bell (ding)) (message "Search wrapped"))) (goto-char (match-beginning 0)) (setq evil-ex-search-match-beg (match-beginning 0) @@ -1039,10 +1035,11 @@ current search result." evil-ex-search-match-end (match-end 0)) (evil-ex-search-goto-offset offset) (evil-push-search-history search-string (eq direction 'forward)) - (when (and (not evil-ex-search-incremental) evil-ex-search-highlight-all) - (evil-ex-search-activate-highlight pattern)) - (when (and evil-ex-search-incremental (not evil-ex-search-persistent-highlight)) - (evil-ex-delete-hl 'evil-ex-search))) + (if evil-ex-search-incremental + (unless evil-ex-search-persistent-highlight + (evil-ex-delete-hl 'evil-ex-search)) + (when evil-ex-search-highlight-all + (evil-ex-search-activate-highlight pattern)))) (t (goto-char evil-ex-search-start-point) (evil-ex-delete-hl 'evil-ex-search) diff --git a/evil-states.el b/evil-states.el index 450ad2b5a3..c15b963d2e 100644 --- a/evil-states.el +++ b/evil-states.el @@ -381,17 +381,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) diff --git a/evil-test-helpers.el b/evil-test-helpers.el index 7d67c560d9..c7eeb71d59 100644 --- a/evil-test-helpers.el +++ b/evil-test-helpers.el @@ -167,17 +167,15 @@ raised. Remaining forms are evaluated as-is. `(execute-kbd-macro (apply #'vconcat (mapcar #'listify-key-sequence - (mapcar #'eval ',form))))) + (list ,@form))))) ((memq (car-safe form) '(kbd vconcat)) `(execute-kbd-macro ,form)) - (t - form)))) + (t form)))) (if error-symbol `(should-error ,result :type ',error-symbol) result)))) body))) - (and (buffer-name buffer) - (kill-buffer buffer)))))) + (when (buffer-name buffer) (kill-buffer buffer)))))) (defmacro evil-test-selection (string &optional end-string before-predicate after-predicate) diff --git a/evil-tests.el b/evil-tests.el index 5d323b8923..b1b3f797b0 100644 --- a/evil-tests.el +++ b/evil-tests.el @@ -1,4 +1,4 @@ -;; evil-tests.el --- unit tests for Evil -*- coding: utf-8 -*- +;; evil-tests.el --- unit tests for Evil -*- coding: utf-8; lexical-binding: t; -*- ;; Author: Vegard Øye <vegard_oye at hotmail.com> ;; Maintainer: Vegard Øye <vegard_oye at hotmail.com> @@ -61,11 +61,15 @@ ;; ;; This file is NOT part of Evil itself. +;; FIXME: Merely loading an ELisp file should not change Emacs's config! (setq load-prefer-newer t) (require 'cl-lib) (require 'elp) (require 'ert) +;; Load non-compiled `evil-ex'. (It defines `evil-parser' - which is +;; needed by `evil-test-parser' - only inside an `eval-when-compile'.) +(require 'evil-ex "evil-ex.el") (require 'evil) (require 'evil-digraphs) (require 'evil-test-helpers) @@ -81,22 +85,21 @@ (defun evil-tests-initialize (&optional tests profiler interactive) (setq profiler (or profiler evil-tests-profiler)) - (when (listp profiler) + (when (consp profiler) (setq profiler (car profiler))) (when profiler (setq evil-tests-profiler t) (setq profiler - (or (cdr (assq profiler - '((call . elp-sort-by-call-count) - (average . elp-sort-by-average-time) - (total . elp-sort-by-total-time)))))) + (cdr (assq profiler + '((call . elp-sort-by-call-count) + (average . elp-sort-by-average-time) + (total . elp-sort-by-total-time))))) (setq elp-sort-by-function (or profiler 'elp-sort-by-call-count)) (elp-instrument-package "evil")) (if interactive (if (y-or-n-p-with-timeout "Run tests? " 2 t) (evil-tests-run tests interactive) - (message "You can run the tests at any time \ -with `M-x evil-tests-run'")) + (message "You can run the tests at any time with `M-x evil-tests-run'")) (evil-tests-run tests))) (defun evil-tests-run (&optional tests interactive) @@ -201,13 +204,12 @@ with `M-x evil-tests-run'")) (defun evil-test-change-state (state) "Change state to STATE and check keymaps" - (let (mode keymap local-mode local-keymap tag) - (evil-change-state state) - (setq mode (evil-state-property state :mode) - keymap (evil-state-property state :keymap t) - local-mode (evil-state-property state :local) - local-keymap (evil-state-property state :local-keymap t) - tag (evil-state-property state :tag t)) + (evil-change-state state) + (let ((mode (evil-state-property state :mode)) + ;; (keymap (evil-state-property state :keymap t)) + (local-mode (evil-state-property state :local)) + ;; (local-keymap (evil-state-property state :local-keymap t)) + (tag (evil-state-property state :tag t))) (when (functionp tag) (setq tag (funcall tag))) (ert-info ("Update `evil-state'") @@ -443,14 +445,16 @@ when exiting Operator-Pending state") (ert-deftest evil-test-auxiliary-maps () "Test auxiliary keymaps" :tags '(evil state) - (let ((map (make-sparse-keymap)) aux) + ;; `evil-define-key' can't be used on a lexically-scoped keymap var. + (defvar evil--map) + (let ((evil--map (make-sparse-keymap)) aux) (ert-info ("Create a new auxiliary keymap") - (evil-define-key 'normal map "f" 'foo) - (setq aux (evil-get-auxiliary-keymap map 'normal)) + (evil-define-key 'normal evil--map "f" 'foo) + (setq aux (evil-get-auxiliary-keymap evil--map 'normal)) (should (evil-auxiliary-keymap-p aux)) (should (eq (lookup-key aux "f") 'foo))) (ert-info ("Add to auxiliary keymap") - (evil-define-key 'normal map "b" 'bar) + (evil-define-key 'normal evil--map "b" 'bar) (should (eq (lookup-key aux "f") 'foo)) (should (eq (lookup-key aux "b") 'bar))))) @@ -493,10 +497,8 @@ when exiting Operator-Pending state") (let* ((first-line 1) (second-line (progn (forward-line) - (point))) - (third-line (progn - (forward-line) - (point)))) + (point)))) + (forward-line) (ert-info ("Return the beginning and end unchanged \ if they are the same") (should (equal (evil-normalize 1 1 'exclusive) @@ -6033,7 +6035,7 @@ Line 2")) (ert-deftest evil-test-text-object () "Test `evil-define-text-object'" :tags '(evil text-object) - (let ((object (evil-define-text-object nil (count &optional beg end type) + (let ((object (evil-define-text-object nil (count &optional beg end _type) (let ((sel (and beg end (evil-range beg end)))) (when (and sel (> count 0)) (forward-char 1)) (let ((range (if (< count 0) @@ -7437,6 +7439,12 @@ charlie delta <echo foxtrot golf h[o]>tel"))) +(ert-deftest evil-test-visual-separate-from-operator-marks () + "Test that visual selection is kept separate from the '[ and '] marks (#1744)." + (evil-test-buffer "x\ny" + ("ylvpjxgv") + "[x]\n")) + ;;; Replace state (ert-deftest evil-test-replacement () @@ -8533,6 +8541,15 @@ maybe we need one line more with some text\n") ("vj!sort" [return]) "line 5\n[l]ine 3\nline 4\nline 2\nline 1\n"))) +(defmacro evil-with-both-search-modules (&rest body) + `(mapc (lambda (search-module) + (setq evil-search-forward-history nil + evil-search-backward-history nil + evil-ex-search-history nil) + (evil-select-search-module 'evil-search-module search-module) + ,@body) + '(isearch evil-search))) + (ert-deftest evil-test-global () "Test `evil-ex-global'." :tags '(evil ex global) @@ -8901,15 +8918,6 @@ Source (execute-kbd-macro "q:") (should (= (length (window-list)) num-windows)))))) -(defmacro evil-with-both-search-modules (&rest body) - `(mapc (lambda (search-module) - (setq evil-search-forward-history nil - evil-search-backward-history nil - evil-ex-search-history nil) - (evil-select-search-module 'evil-search-module search-module) - ,@body) - '(isearch evil-search))) - (ert-deftest evil-test-command-window-search-history () "Test command window with forward and backward search history" (skip-unless (not noninteractive)) @@ -9121,25 +9129,26 @@ parameter set." (ert-deftest evil-test-properties () "Test `evil-get-property' and `evil-put-property'" :tags '(evil util) - (let (alist) + (defvar evil--alist) + (let (evil--alist) (ert-info ("Set properties") - (evil-put-property 'alist 'wibble :foo t) - (should (equal alist '((wibble . (:foo t))))) - (evil-put-property 'alist 'wibble :bar nil) - (should (equal alist '((wibble . (:foo t :bar nil))))) - (evil-put-property 'alist 'wobble :foo nil :bar nil :baz t) - (should (equal alist '((wobble . (:foo nil :bar nil :baz t)) + (evil-put-property 'evil--alist 'wibble :foo t) + (should (equal evil--alist '((wibble . (:foo t))))) + (evil-put-property 'evil--alist 'wibble :bar nil) + (should (equal evil--alist '((wibble . (:foo t :bar nil))))) + (evil-put-property 'evil--alist 'wobble :foo nil :bar nil :baz t) + (should (equal evil--alist '((wobble . (:foo nil :bar nil :baz t)) (wibble . (:foo t :bar nil)))))) (ert-info ("Get properties") - (should (evil-get-property alist 'wibble :foo)) - (should-not (evil-get-property alist 'wibble :bar)) - (should-not (evil-get-property alist 'wobble :foo)) - (should-not (evil-get-property alist 'wibble :baz)) - (should (equal (evil-get-property alist t :foo) + (should (evil-get-property evil--alist 'wibble :foo)) + (should-not (evil-get-property evil--alist 'wibble :bar)) + (should-not (evil-get-property evil--alist 'wobble :foo)) + (should-not (evil-get-property evil--alist 'wibble :baz)) + (should (equal (evil-get-property evil--alist t :foo) '((wibble . t) (wobble . nil)))) - (should (equal (evil-get-property alist t :bar) + (should (equal (evil-get-property evil--alist t :bar) '((wibble . nil) (wobble . nil)))) - (should (equal (evil-get-property alist t :baz) + (should (equal (evil-get-property evil--alist t :baz) '((wobble . t))))))) (ert-deftest evil-test-filter-list () @@ -9474,26 +9483,26 @@ parameter set." (evil-with-temp-file file-name "" (evil-test-buffer (vconcat "i" file-name [escape]) - (should (not (equal file-name (buffer-file-name (current-buffer))))) + (should (not (equal file-name (buffer-file-name)))) ("gf") - (should (equal file-name (buffer-file-name (current-buffer))))))) + (should (equal file-name (buffer-file-name)))))) (ert-info ("Find file at point (visual state)") (evil-with-temp-file file-name "" (evil-test-buffer (vconcat "iuser@localhost:" file-name "$" [escape]) - (should (not (equal file-name (buffer-file-name (current-buffer))))) + (should (not (equal file-name (buffer-file-name)))) ("0f:lvt$gf") - (should (equal file-name (buffer-file-name (current-buffer))))))) + (should (equal file-name (buffer-file-name)))))) (ert-info ("Find file at point with line number") (let* ((line-number 3) (file-content (make-string (* 2 line-number) ?\n))) (evil-with-temp-file file-name (insert file-content) (evil-test-buffer (vconcat "i" file-name (format ":%d" line-number) [escape]) - (should (and (not (equal file-name (buffer-file-name (current-buffer)))) + (should (and (not (equal file-name (buffer-file-name))) (not (equal line-number (line-number-at-pos))))) ("gF") - (should (and (equal file-name (buffer-file-name (current-buffer))) + (should (and (equal file-name (buffer-file-name)) (equal line-number (line-number-at-pos)))))))) (ert-info ("Find file at point with line and column numbers") (let* ((line-number 3) @@ -9505,11 +9514,11 @@ parameter set." (evil-with-temp-file file-name (insert file-content) (evil-test-buffer (vconcat "i" file-name (format ":%d:%d" line-number column-number) [escape]) - (should (and (not (equal file-name (buffer-file-name (current-buffer)))) + (should (and (not (equal file-name (buffer-file-name))) (not (equal line-number (line-number-at-pos))) (not (equal column-number (current-column))))) ("gF") - (should (and (equal file-name (buffer-file-name (current-buffer))) + (should (and (equal file-name (buffer-file-name)) (equal line-number (line-number-at-pos)) (equal column-number (1+ (current-column)))))))))) @@ -9690,6 +9699,8 @@ main(argc, argv) char **argv; { (ert-deftest evil-test-initial-state () "Test `evil-initial-state'" :tags '(evil core) + ;; FIXME: These have a global effect, so better move them out and give them + ;; a proper namespace prefix. (define-derived-mode test-1-mode prog-mode "Test1") (define-derived-mode test-2-mode test-1-mode "Test2") (evil-set-initial-state 'test-1-mode 'insert) @@ -9719,6 +9730,7 @@ main(argc, argv) char **argv; { ;; is sufficient for `evil-initial-state-for-buffer' to work. (should-error (evil-initial-state-for-buffer))) (put 'test-1-mode 'derived-mode-parent 'prog-mode)))) + ;; FIXME: Same as above. (defalias 'test-1-alias-mode #'test-1-mode) (define-derived-mode test-3-mode test-1-alias-mode "Test3") (evil-set-initial-state 'test-1-mode 'insert) diff --git a/evil-types.el b/evil-types.el index 5969ddc346..0e347d00fe 100644 --- a/evil-types.el +++ b/evil-types.el @@ -373,7 +373,7 @@ If visual state is inactive then those values are nil." (let ((expr (evil-ex-parse (or evil-ex-argument "")))) (if (eq (car expr) 'evil-goto-line) (save-excursion (goto-char evil-ex-point) - (eval (cadr expr))) + (eval (cadr expr) t)) (user-error "Invalid address")))))) (evil-define-interactive-code "<!>" diff --git a/evil-vars.el b/evil-vars.el index 4d4aa38839..f9de9074b6 100644 --- a/evil-vars.el +++ b/evil-vars.el @@ -1733,15 +1733,9 @@ instead of `buffer-undo-list'.") (evil-define-local-var evil-visual-point nil "The position of point in Visual state, a marker.") -(evil-define-local-var evil-visual-previous-point nil - "The position of point before Visual state, a marker.") - (evil-define-local-var evil-visual-mark nil "The position of mark in Visual state, a marker.") -(evil-define-local-var evil-visual-previous-mark nil - "The position of mark before Visual state, a marker.") - (evil-define-local-var evil-visual-selection nil "The kind of Visual selection. This is a selection as defined by `evil-define-visual-selection'.") @@ -1909,9 +1903,6 @@ Key sequences bound in this map are immediately executed.") This content of this variable is appended to the Ex command line when Ex is started interactively.") -(defvar evil-ex-commands nil - "Association list of command bindings and functions.") - (defvar evil-ex-history nil "History of Ex commands.") diff --git a/evil.el b/evil.el index 5bd33e86f9..e5d7e45991 100644 --- a/evil.el +++ b/evil.el @@ -1,4 +1,4 @@ -;;; evil.el --- extensible vi layer +;;; evil.el --- Extensible vi layer -*- lexical-binding: t; -*- ;; The following list of authors was kept up to date until the beginning of ;; 2017, when evil moved under new maintainers. For authors since then, please @@ -114,7 +114,7 @@ ;; Evil requires undo-redo (Emacs 28), undo-fu or undo-tree for redo ;; functionality. Otherwise, Evil uses regular Emacs undo. ;; -;; https://gitlab.com/ideasman42/emacs-undo-fu +;; https://codeberg.org/ideasman42/emacs-undo-fu ;; https://melpa.org/#/undo-fu ;; https://gitlab.com/tsc25/undo-tree ;; https://elpa.gnu.org/packages/undo-tree.html