> From: Eli Zaretskii <e...@gnu.org> > Date: Tue, 2023-09-05 14:54 +0300 > > […] > > So we could decide that this command needs to become smarter when the > visual line includes invisible text. That is, improve the command > without making any Org-specific changes anywhere. Patches to that > effect are welcome.
The following would do it. I think I tested it rather thoroughly. During testing I found another bug that is addressed by the let-binding of kill-read-only-ok during the first kill-region below. diff --git a/lisp/simple.el b/lisp/simple.el index abd587245fe..d983cb85ab3 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -6631,28 +6631,50 @@ kill-whole-line (unless (eq last-command 'kill-region) (kill-new "") (setq last-command 'kill-region)) - (cond ((zerop arg) - ;; We need to kill in two steps, because the previous command - ;; could have been a kill command, in which case the text - ;; before point needs to be prepended to the current kill - ;; ring entry and the text after point appended. Also, we - ;; need to use save-excursion to avoid copying the same text - ;; twice to the kill ring in read-only buffers. - (save-excursion - (kill-region (point) (progn (forward-visible-line 0) (point)))) - (kill-region (point) (progn (end-of-visible-line) (point)))) - ((< arg 0) - (save-excursion - (kill-region (point) (progn (end-of-visible-line) (point)))) - (kill-region (point) - (progn (forward-visible-line (1+ arg)) - (unless (bobp) (backward-char)) - (point)))) - (t - (save-excursion - (kill-region (point) (progn (forward-visible-line 0) (point)))) - (kill-region (point) - (progn (forward-visible-line arg) (point)))))) + ;; - We need to kill in two steps, because the previous command + ;; could have been a kill command, in which case the text before + ;; point needs to be prepended to the current kill ring entry and + ;; the text after point appended. + ;; - We need to be careful to avoid copying text twice to the kill + ;; ring in read-only buffers. + ;; - We need to determine the boundaries of visible lines before we + ;; do the first kill, because that may change visibility + ;; (bug#65734). + (let ((regions-begin (point-marker)) + region1-end) + (cond ((zerop arg) + (setq region1-end (save-excursion + (forward-visible-line 0) + (point-marker))) + (end-of-visible-line)) + ((< arg 0) + (setq region1-end (save-excursion + (end-of-visible-line) + (point-marker))) + (forward-visible-line (1+ arg)) + (unless (bobp) (backward-char))) + (t + (setq region1-end (save-excursion + (forward-visible-line 0) + (point-marker))) + (progn (forward-visible-line arg)))) + ;; - Pass the marker positions and not the markers themselves. + ;; kill-region determines whether to prepend or append to a + ;; previous kill by checking the direction of the region. But + ;; it deletes the content and hence moves the markers before + ;; that. That effectively makes every region delimited by + ;; markers an (empty) forward region. + ;; - Make the first kill-region emit a non-local exit only if the + ;; second kill-region below would not operate on a non-empty + ;; region. + (let ((kill-read-only-ok (or kill-read-only-ok + (/= regions-begin (point))))) + (kill-region (marker-position regions-begin) + (marker-position region1-end))) + (kill-region (marker-position regions-begin) + (point)) + (set-marker regions-begin nil) + (set-marker region1-end nil))) (defun forward-visible-line (arg) "Move forward by ARG lines, ignoring currently invisible newlines only.