branch: elpa/evil commit 643e01d1a07fc472b5a9ff6fa3ec001ddc8681ba Author: Tom Dalziel <tom...@hotmail.com> Commit: Tom Dalziel <33435574+tomd...@users.noreply.github.com>
Add handler for expanding line for line-based operators Most of the work was done by Maxi Wolff (@smile13241324) over 6yrs ago. I've made a few changes, so have assumed authorship. This fixes #968 --- evil-commands.el | 68 +++++++++++++++++++++++++++++++------------------------- evil-tests.el | 37 +++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 31 deletions(-) diff --git a/evil-commands.el b/evil-commands.el index 62a6597228..2ae01d5c29 100644 --- a/evil-commands.el +++ b/evil-commands.el @@ -1436,23 +1436,37 @@ the left edge." (evil-yank-characters beg end register yank-handler) (goto-char beg))))) -(evil-define-operator evil-yank-line (beg end type register) - "Save whole lines into the kill-ring." - :motion evil-line-or-visual-line - :move-point nil - (interactive "<R><x>") +(defun evil-expand-line-for-line-based-operators (beg end type) + "Expand to line when in visual mode possibly changing BEG, END and TYPE. +Avoids double expansion for line based commands like 'V' or 'D'." (when (evil-visual-state-p) (unless (memq type '(line block screen-line)) - (let ((range (evil-expand beg end - (if (and evil-respect-visual-line-mode - visual-line-mode) - 'screen-line - 'line)))) + ;; Subtract 1 from end to avoid expanding to the next line + ;; when \n is part of the visually selected region. + ;; If removed (evil-expand beg end 'line) + ;; will expand to the end of the next line instead of the current + ;; line which will cause line expanding commands like 'Y' to + ;; misbehave when used in visual state. + (when (eq ?\n (char-before end)) + (cl-decf end)) + (let ((range (evil-expand beg end (if (and evil-respect-visual-line-mode + visual-line-mode) + 'screen-line + 'line)))) (setq beg (evil-range-beginning range) end (evil-range-end range) type (evil-type range)))) (evil-exit-visual-state)) - (evil-yank beg end type register)) + (list beg end type)) + +(evil-define-operator evil-yank-line (beg end type register) + "Save whole lines into the kill-ring." + :motion evil-line-or-visual-line + :move-point nil + (interactive "<R><x>") + (cl-destructuring-bind + (beg end type) (evil-expand-line-for-line-based-operators beg end type) + (evil-yank beg end type register))) (evil-define-operator evil-delete (beg end type register yank-handler) "Delete text from BEG to END with TYPE. @@ -1502,24 +1516,17 @@ Save in REGISTER or in the kill-ring with YANK-HANDLER." "Delete to end of line." :motion evil-end-of-line-or-visual-line (interactive "<R><x>") - ;; Act linewise in Visual state - (when (and (evil-visual-state-p) (eq type 'inclusive)) - (let ((range (evil-expand - beg end - (if (and evil-respect-visual-line-mode visual-line-mode) - 'screen-line 'line)))) - (setq beg (car range) - end (cadr range) - type (evil-type range)))) - (if (eq type 'block) - ;; Equivalent to $d, i.e., we use the block-to-eol selection and - ;; call `evil-delete'. In this case we fake the call to - ;; `evil-end-of-line' by setting `temporary-goal-column' and - ;; `last-command' appropriately as `evil-end-of-line' would do. - (let ((temporary-goal-column most-positive-fixnum) - (last-command 'next-line)) - (evil-delete beg end 'block register yank-handler)) - (evil-delete beg end type register yank-handler))) + (cl-destructuring-bind + (beg end type) (evil-expand-line-for-line-based-operators beg end type) + (if (eq type 'block) + ;; Equivalent to $d, i.e., we use the block-to-eol selection and + ;; call `evil-delete'. In this case we fake the call to + ;; `evil-end-of-line' by setting `temporary-goal-column' and + ;; `last-command' appropriately as `evil-end-of-line' would do. + (let ((temporary-goal-column most-positive-fixnum) + (last-command 'next-line)) + (evil-delete beg end 'block register yank-handler)) + (evil-delete beg end type register yank-handler)))) (evil-define-operator evil-delete-whole-line (beg end type register yank-handler) @@ -1672,7 +1679,8 @@ of the block." :motion evil-end-of-line-or-visual-line (interactive "<R><x><y>") (if (and (evil-visual-state-p) (eq type 'inclusive)) - (cl-destructuring-bind (beg end &rest) (evil-line-expand beg end) + (cl-destructuring-bind + (beg end _type) (evil-expand-line-for-line-based-operators beg end type) (evil-change-whole-line beg end register yank-handler)) (evil-change beg end type register yank-handler #'evil-delete-line))) diff --git a/evil-tests.el b/evil-tests.el index 8004633286..b474770568 100644 --- a/evil-tests.el +++ b/evil-tests.el @@ -2013,6 +2013,20 @@ New Tex[t] ("my" "G" ":'y,.y") "1\n2\n3\n4\n5\n6\n7\n8\n9\n[1]0"))) +(ert-deftest evil-test-yank-line () + "Test `evil-yank-line'" + :tags '(evil operator) + (ert-info ("Yank line with eof being part of visual selection") + (evil-test-buffer + ";; This is line one +;; This is lin[e] two +;; This is line three" + ("v$Yjp") + ";; This is line one +;; This is line two +;; This is line three +[;]; This is line two"))) + (ert-deftest evil-test-delete () "Test `evil-delete'" :tags '(evil operator delete) @@ -2144,7 +2158,15 @@ ine3 line3 line3 l\n")) (evil-test-buffer "a[a]a\nbbb\nc\n" ("2D") - "a\nc\n"))) + "a\nc\n")) + (ert-info ("Delete line with eof being part of visual selection") + (evil-test-buffer + ";; This is line one +;; This is lin[e] two +;; This is line three" + ("v$D") + ";; This is line one +;; This is line three"))) (ert-deftest evil-test-delete-folded () "Test `evil-delete' on folded lines." @@ -2342,6 +2364,19 @@ ABCthen enter the text in that file's own buffer."))) [] five"))) +(ert-deftest evil-test-change-line () + "Test `evil-change-line'" + :tags '(evil operator) + (ert-info ("Change line with eof being part of visual selection") + (evil-test-buffer + "This is line one +This is lin[e] two +This is line three" + ("v$C") + "This is line one +[] +This is line three"))) + (ert-deftest evil-test-change-word () "Test changing words" :tags '(evil operator)