From 30c54bc00d8379e9c716bcdfd372444945addf09 Mon Sep 17 00:00:00 2001 From: kovan Date: Fri, 13 Feb 2026 23:11:20 +0100 Subject: [PATCH] fix(evil): make `cc`/`S` respect visual-line-mode Add `+evil:change-whole-visual-line` operator that correctly handles visual lines when `evil-respect-visual-line-mode` is enabled. The built-in `evil-change-whole-line` always operates on physical lines (including newlines), making `cc` delete the newline on wrapped lines. The new operator detects whether the visual line spans a full physical line and delegates accordingly. Fix: #2447 Co-authored-by: Claude Opus 4.6 --- modules/editor/evil/autoload/evil.el | 21 +++++++++++++++++++++ modules/editor/evil/config.el | 5 ++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/modules/editor/evil/autoload/evil.el b/modules/editor/evil/autoload/evil.el index 8746e5736..bb6871dd1 100644 --- a/modules/editor/evil/autoload/evil.el +++ b/modules/editor/evil/autoload/evil.el @@ -186,6 +186,27 @@ Widens narrowed buffers first. If BANG, use indirect buffer clones instead." (indent-rigidly (point-min) (point-max) (- indent)) (evil-yank (point-min) (point-max))))) +;;;###autoload (autoload '+evil:change-whole-visual-line "editor/evil/autoload/evil" nil t) +(evil-define-operator +evil:change-whole-visual-line (beg end register yank-handler) + "Change whole visual line, respecting `visual-line-mode'. +Unlike `evil-change-whole-line', this operates on visual lines rather than +physical lines, preventing newline deletion in wrapped lines." + :motion evil-line-or-visual-line + (interactive "") + (let ((vbeg (save-excursion (goto-char beg) (beginning-of-visual-line) (point))) + (vend (save-excursion (goto-char (max beg (1- end))) (end-of-visual-line) (point)))) + (cond + ;; Full physical line(s) — use 'line type for proper open-line + indent + ((and (= vbeg (save-excursion (goto-char vbeg) (line-beginning-position))) + (>= vend (save-excursion (goto-char vend) (line-end-position)))) + (evil-change vbeg vend 'line register yank-handler)) + ;; Empty line — just enter insert mode + ((<= vend vbeg) + (evil-insert 1)) + ;; Visual line subset — use 'inclusive to preserve physical line structure + (t + (evil-change vbeg vend 'inclusive register yank-handler))))) + ;; ;;; wgrep diff --git a/modules/editor/evil/config.el b/modules/editor/evil/config.el index 3d81e3e61..dd63ad527 100644 --- a/modules/editor/evil/config.el +++ b/modules/editor/evil/config.el @@ -128,7 +128,10 @@ directives. By default, this only recognizes C directives.") [up] #'evil-previous-visual-line [down] #'evil-next-visual-line [home] #'evil-beginning-of-visual-line - [end] #'evil-end-of-visual-line)) + [end] #'evil-end-of-visual-line) + (evil-define-minor-mode-key 'normal 'visual-line-mode + "cc" #'+evil:change-whole-visual-line + "S" #'+evil:change-whole-visual-line)) ;; --- evil hacks -------------------------