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

Reply via email to