]> git.eshelyaron.com Git - emacs.git/commitdiff
kill-whole-line: Honor visibility; fix kill-ring when read-only (bug#65734)
authorSebastian Miele <iota@whxvd.name>
Wed, 19 Jun 2024 13:48:59 +0000 (15:48 +0200)
committerEshel Yaron <me@eshelyaron.com>
Fri, 28 Jun 2024 05:49:39 +0000 (07:49 +0200)
* lisp/simple.el (kill-whole-line): Use visibility state before
performing any edits as reference instead of expecting that visibility
cannot change.  First of the two calls to `kill-region' may trigger
`after-change-functions' that might alter the visibility state.
Make sure that we populate the `kill-ring' with the contents of the
whole line when buffer is in `read-only-mode'.

(cherry picked from commit 058bb4ca25adb8f4bf78cc0f09a65d730dbd797d)

lisp/simple.el

index cb1bd819536a8fd6cc7e8a4a96deee5f0da02d64..45113a7b0f237d85a7a141fa7930df68715d1eb5 100644 (file)
@@ -6735,28 +6735,53 @@ If ARG is zero, kill current line but exclude the trailing newline."
   (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.  Otherwise `after-change-functions' may
+  ;;   change visibility (bug#65734).
+  (let (;; The beginning of both regions to kill
+        (regions-begin (point-marker))
+        ;; The end of the first region to kill.  Moreover, after
+        ;; evaluation of the value form, (point) will be the end of
+        ;; the second region to kill.
+        (region1-end (cond ((zerop arg)
+                            (prog1 (save-excursion
+                                     (forward-visible-line 0)
+                                     (point-marker))
+                              (end-of-visible-line)))
+                          ((< arg 0)
+                           (prog1 (save-excursion
+                                     (end-of-visible-line)
+                                     (point-marker))
+                              (forward-visible-line (1+ arg))
+                             (unless (bobp) (backward-char))))
+                          (t
+                           (prog1 (save-excursion
+                                     (forward-visible-line 0)
+                                     (point-marker))
+                             (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.