branch: elpa/evil commit 4ea224bbb6ac6b0965494c7f6e8ba28c806a501c Author: Tom Dalziel <33435574+tomd...@users.noreply.github.com> Commit: GitHub <nore...@github.com>
Improve evil-ensure-column and use with evil-goto-line (#1529) * Add `evil--stick-to-eol-p` and use for `evil-ensure-column` * Evil-shift commands more concise using `evil--stick-to-eol-p` * WIP - track column for evil-goto-line * Move copied line-move-1 code to evil-ensure-column, add tests --- evil-commands.el | 18 ++++++++---------- evil-common.el | 47 ++++++++++++++++++++++++++++++++++++++--------- evil-tests.el | 39 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 20 deletions(-) diff --git a/evil-commands.el b/evil-commands.el index 5e84b6c..dc0174d 100644 --- a/evil-commands.el +++ b/evil-commands.el @@ -1033,8 +1033,9 @@ If the scroll count is zero the command scrolls half the screen." :keep-visual t (interactive "<c>") (evil-ensure-column - (setq count (or count (max 0 evil-scroll-count))) - (setq evil-scroll-count count) + (setq count (or count (max 0 evil-scroll-count)) + evil-scroll-count count + this-command 'next-line) (when (= (point-min) (line-beginning-position)) (signal 'beginning-of-buffer nil)) (when (zerop count) @@ -1058,8 +1059,9 @@ If the scroll count is zero the command scrolls half the screen." :keep-visual t (interactive "<c>") (evil-ensure-column - (setq count (or count (max 0 evil-scroll-count))) - (setq evil-scroll-count count) + (setq count (or count (max 0 evil-scroll-count)) + evil-scroll-count count + this-command 'next-line) (when (eobp) (signal 'end-of-buffer nil)) (when (zerop count) (setq count (/ (window-body-height) 2))) @@ -1942,9 +1944,6 @@ See also `evil-shift-left'." (let ((beg (set-marker (make-marker) beg)) (end (set-marker (make-marker) end)) (col-for-insert (current-column)) - (goal-col (if (consp temporary-goal-column) - (car temporary-goal-column) - temporary-goal-column)) first-shift) ; shift of first line (save-excursion (goto-char beg) @@ -1981,9 +1980,8 @@ See also `evil-shift-left'." (cond ((evil-insert-state-p) (move-to-column (max 0 (+ col-for-insert first-shift)))) (evil-start-of-line (evil-first-non-blank)) - (t (move-to-column (if (and evil-track-eol (= goal-col most-positive-fixnum)) - goal-col - evil-operator-start-col)))) + ((evil--stick-to-eol-p) (move-end-of-line 1)) + (t (move-to-column (or goal-column evil-operator-start-col)))) (setq temporary-goal-column 0))) (evil-define-command evil-shift-right-line (count) diff --git a/evil-common.el b/evil-common.el index f61d247..3977532 100644 --- a/evil-common.el +++ b/evil-common.el @@ -986,17 +986,46 @@ See also `evil-save-goal-column'." ,@body (move-to-column col)))) +(defun evil--stick-to-eol-p () + "Called by vertical movement commands to help determine cursor position." + (let ((goal-col (or goal-column + (if (consp temporary-goal-column) + (car temporary-goal-column) + temporary-goal-column)))) + (and evil-track-eol + (= most-positive-fixnum goal-col) + (eq last-command 'next-line)))) + +(defun evil-eolp () + "Like `eolp' but accounts for `evil-move-beyond-eol' being nil." + (ignore-errors + (save-excursion + (unless (or evil-move-beyond-eol (memq evil-state '(insert replace))) + (forward-char)) + (eolp)))) + (defmacro evil-ensure-column (&rest body) - "Ensures appropriate column after exeution of BODY. -Appropriate column is determined by `evil-start-of-line'." + "Execute BODY as if it is a `next-line' command, insofar as it tracks column. +This mostly copies the approach of Emacs' `line-move-1', but is modified +so it is more compatible with evil's notions of eol & tracking." (declare (indent defun) (debug t)) - `(let ((col (current-column))) - (evil-save-goal-column - ,@body - (if evil-start-of-line - (evil-first-non-blank) - (move-to-column col))))) + `(progn + (setq this-command 'next-line) + (if (consp temporary-goal-column) + (setq temporary-goal-column (+ (car temporary-goal-column) + (cdr temporary-goal-column)))) + (if (not (memq last-command '(next-line previous-line))) + (setq temporary-goal-column + (if (and evil-track-eol + (evil-eolp) + (memq real-last-command '(move-end-of-line evil-end-of-line))) + most-positive-fixnum + (current-column)))) + ,@body + (if evil-start-of-line + (evil-first-non-blank) + (line-move-to-column (truncate (or goal-column temporary-goal-column)))))) (defun evil-narrow (beg end) "Restrict the buffer to BEG and END. @@ -1371,7 +1400,7 @@ If STATE is given it used a parsing state at point." (point))))))) (put 'evil-comment 'bounds-of-thing-at-point #'bounds-of-evil-comment-at-point) -;; The purpose of this function is the provide line motions which +;; The purpose of this function is to provide line motions which ;; preserve the column. This is how `previous-line' and `next-line' ;; work, but unfortunately the behaviour is hard-coded: if and only if ;; the last command was `previous-line' or `next-line', the column is diff --git a/evil-tests.el b/evil-tests.el index 53f968f..9a874a9 100644 --- a/evil-tests.el +++ b/evil-tests.el @@ -3178,7 +3178,7 @@ Below some empty line" (should-error (execute-kbd-macro "j")) (should-error (execute-kbd-macro "42j"))))) -(ert-deftest evil-test-preserve-column () +(ert-deftest evil-test-next+previous-preserve-column () "Test `evil-previous-line' and `evil-next-line' preserve the column." :tags '(evil motion) (ert-info ("Simple") @@ -3203,6 +3203,43 @@ Below some empty line" ("jjjkk") "abc\nab[c]def\n\nabcd\n"))) +(ert-deftest evil-test-other-commands-preserve-column () + "Test other comamnds preserve the column, when appropriate." + :tags '(evil motion) + (ert-info ("evil-goto-line can preserve column") + (let ((evil-start-of-line nil)) + (evil-test-buffer + "Shor[t] line +Average line +The longest line" + ("2G") + "Short line +Aver[a]ge line +The longest line" + ("$G") + "Short line +Average line +The longest lin[e]" + ("hgg") + "Short lin[e] +Average line +The longest line"))) + + (ert-info ("N% (`evil-jump-item' with count) can preserve column") + (let ((evil-start-of-line nil)) + (evil-test-buffer + "Short line +Average line +The lo[n]gest line" + ("5%") + "Short [l]ine +Average line +The longest line" + ("$90%") + "Short line +Average line +The longest lin[e]")))) + (ert-deftest evil-test-beginning-of-line () "Test `evil-beginning-of-line' motion" :tags '(evil motion)