From e31bff8cb64d773bbfd9a8e326db8645eaee43fd Mon Sep 17 00:00:00 2001 From: Tom Dalziel Date: Mon, 1 Apr 2024 23:21:07 +0200 Subject: [PATCH] Visual eol anchoring, so g$ is sticky Also fixes #1876 --- evil-commands.el | 26 ++++++++++++++++++++++++-- evil-common.el | 5 ++++- evil-tests.el | 23 +++++++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/evil-commands.el b/evil-commands.el index e3af076f..a44cc25d 100644 --- a/evil-commands.el +++ b/evil-commands.el @@ -108,29 +108,49 @@ of the line or the buffer; just return nil." (unless (or (evil-visual-state-p) (evil-operator-state-p)) (evil-adjust-cursor)))))) +(defvar evil--visual-eol-anchored nil + "Non nil if the cursor should be anchored at the end of the visual line. +Only reliably usable via `evil-visual-eol-anchored-p'.") + +(defun evil-visual-eol-anchored-p () + "Return non nil if the cursor should be anchored at the end of the visual line." + (if (memq last-command '(next-line previous-line evil-end-of-visual-line)) + evil--visual-eol-anchored + (setq evil--visual-eol-anchored nil))) + (evil-define-motion evil-next-line (count) "Move the cursor COUNT lines down." :type line (let (line-move-visual) + (unless (memq last-command '(next-line previous-line evil-end-of-visual-line)) + (setq evil--visual-eol-anchored nil)) (evil-line-move (or count 1)))) (evil-define-motion evil-previous-line (count) "Move the cursor COUNT lines up." :type line (let (line-move-visual) + (unless (memq last-command '(next-line previous-line evil-end-of-visual-line)) + (setq evil--visual-eol-anchored nil)) (evil-line-move (- (or count 1))))) (evil-define-motion evil-next-visual-line (count) "Move the cursor COUNT screen lines down." :type exclusive (let ((line-move-visual t)) - (evil-line-move (or count 1)))) + (when (eq most-positive-fixnum temporary-goal-column) + (setq temporary-goal-column (current-column))) ; Fix #1876 + (evil-line-move (or count 1)) + (when (evil-visual-eol-anchored-p) (evil-end-of-visual-line)))) (evil-define-motion evil-previous-visual-line (count) "Move the cursor COUNT screen lines up." :type exclusive (let ((line-move-visual t)) - (evil-line-move (- (or count 1))))) + (when (eq most-positive-fixnum temporary-goal-column) + (setq temporary-goal-column (current-column))) ; Fix #1876 + (evil-line-move (- (or count 1))) + (when (evil-visual-eol-anchored-p) (evil-end-of-visual-line)))) ;; used for repeated commands like "dd" (evil-define-motion evil-line (count) @@ -168,6 +188,7 @@ If COUNT is given, move COUNT - 1 lines downward first." (move-end-of-line count) (when evil-track-eol (setq temporary-goal-column most-positive-fixnum + evil--visual-eol-anchored t this-command 'next-line)) (if (evil-visual-state-p) (when evil-v$-excludes-newline @@ -187,6 +208,7 @@ If COUNT is given, move COUNT - 1 lines downward first." "Move the cursor to the last character of the current screen line. If COUNT is given, move COUNT - 1 screen lines downward first." :type inclusive + (setq evil--visual-eol-anchored t) (end-of-visual-line count)) (evil-define-motion evil-end-of-line-or-visual-line (count) diff --git a/evil-common.el b/evil-common.el index c305c256..2b228c23 100644 --- a/evil-common.el +++ b/evil-common.el @@ -1254,7 +1254,10 @@ Signals an error at buffer boundaries unless NOERROR is non-nil." (car-safe temporary-goal-column) temporary-goal-column))) (line-move-finish col opoint (< count 0))) - (or noerror (/= (point) opoint) (signal (car err) (cdr err))))))) + (or noerror (/= (point) opoint) (signal (car err) (cdr err)))) + (args-out-of-range + (unless (eq most-positive-fixnum temporary-goal-column) + (signal (car err) (cdr err))))))) (defun evil-forward-syntax (syntax &optional count) "Move point to the end or beginning of a sequence of characters in SYNTAX. diff --git a/evil-tests.el b/evil-tests.el index f22a6694..88a8aa5a 100644 --- a/evil-tests.el +++ b/evil-tests.el @@ -3529,6 +3529,29 @@ Below some empty line" ("Gkgk") (should (not (bolp))))) +(ert-deftest evil-test-end-of-visual-line () + "Test `evil-end-of-visual-line'." + :tags '(evil motion) + (skip-unless (and (not noninteractive) (> (window-width) 2))) + (evil-test-buffer + "alpha bravo charlie\nd[e]lta echo\nfoxtrot golf hotel india" + ("g$" "gj") + "alpha bravo charlie\ndelta echo\nfoxtrot golf hotel indi[a]" + ("gkgk") + "alpha bravo charli[e]\ndelta echo\nfoxtrot golf hotel india" + ("ro" "2gj") + "alpha bravo charlio\ndelta echo\nfoxtrot golf hotel[ ]india")) + +(ert-deftest evil-test-eol-anchoring-with-visual-line-movement () + "Test gj and gk once the cursor is anchored at eol with $." + :tags '(evil motion) + (skip-unless (and (not noninteractive) (> (window-width) 2))) + (evil-test-buffer + "Short [l]ine\nA longer line\nThird line" + (visual-line-mode 1) + ("$gj") + "Short line\nA longer lin[e]\nThird line")) + (ert-deftest evil-test-other-commands-preserve-column () "Test other comamnds preserve the column, when appropriate." :tags '(evil motion)