Subject: bug#65380: [PATCH] Add command to copy contents in a diff-mode buffer

From: Philip Kaludercic <[email-masked]>
Date: Sat, 19 Aug 2023 10:00:49 +0000

Philip Kaludercic <[email-masked]> writes:

> Tags: patch
>
>
> This command solves a long-standing annoyance I have had when working
> with diffs.  To my knowledge there is no existing way to achieve this
> (rectangular selection might be possible, but that behaves differently
> when the text is yanked).  The binding "w" seems intuitive when
> contrasted with "M-w", but might be confused with "W" (widen).  Not sure
> if that is an issue or not.
>
> In GNU Emacs 30.0.50 (build 8, x86_64-pc-linux-gnu, GTK+ Version
>  3.24.37, cairo version 1.16.0) of 2023-08-18 built on quetzal
> Repository revision: 2f74a459d2a82578fd9a92e9e2facdca90dad974
> Repository branch: feature/a-package-for-elpa
> System Description: Debian GNU/Linux 12 (bookworm)
>
> Configured using:
>  'configure --with-pgtk --with-native-compilation --with-imagemagick
>  --with-tree-sitter'

It seems I prepared to patch too hastily, and forgot to add a docstring.
Here is the updated version, with a docstring:

>From fc39bd54ac93dbebec3b4c16aa1fe995cd421c92 Mon Sep 17 00:00:00 2001
From: Philip Kaludercic <[email-masked]>
Date: Sat, 19 Aug 2023 11:47:54 +0200
Subject: [PATCH] Add command to copy contents in a diff-mode buffer

* lisp/vc/diff-mode.el (diff-mode-shared-map): Bind 'diff-kill-ring-save'.
(diff-mode-map): Ensure the "w" binding does not get prefixed.
(diff-kill-ring-save): Add new command
* etc/NEWS: Mention 'diff-kill-ring-save'.
---
 etc/NEWS             |  7 +++++++
 lisp/vc/diff-mode.el | 24 +++++++++++++++++++++++-
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/etc/NEWS b/etc/NEWS
index 6588299c532..9ce510e0f81 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -276,6 +276,13 @@ This allows changing which type of whitespace changes are ignored when
 regenerating hunks with 'diff-ignore-whitespace-hunk'.  Defaults to
 the previously hard-coded "-b".
 
+---
+*** New command 'diff-kill-ring-save'.
+This command behaves like 'kill-ring-save', but removes change
+indicators ("+", "-") from the beginning of a line.  This is useful
+when you wish to copy part of the contents of a diff, but not the diff
+itself.
+
 ** Ediff
 
 ---
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index d776375d681..342d2d8ce90 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -195,6 +195,7 @@ diff-mode-shared-map
   "RET" #'diff-goto-source
   "<mouse-2>" #'diff-goto-source
   "W" #'widen
+  "w" #'diff-kill-ring-save
   "o" #'diff-goto-source                ; other-window
   "A" #'diff-ediff-patch
   "r" #'diff-restrict-view
@@ -207,7 +208,7 @@ diff-mode-map
           ;; We want to inherit most bindings from
           ;; `diff-mode-shared-map', but not all since they may hide
           ;; useful `M-<foo>' global bindings when editing.
-          (dolist (key '("A" "r" "R" "g" "q" "W" "z"))
+          (dolist (key '("A" "r" "R" "g" "q" "W" "w" "z"))
             (keymap-set map key nil))
           map)
   ;; From compilation-minor-mode.
@@ -2079,6 +2080,27 @@ diff-goto-source
       (goto-char (+ (car pos) (cdr src)))
       (when buffer (next-error-found buffer (current-buffer))))))
 
+(defun diff-kill-ring-save (beg end)
+  "Save contents of the region between BEG and END akin to `kill-ring-save'.
+The contents of a region will not include diff indicators at the
+beginning of each line."
+  (interactive (list (region-beginning) (region-end)))
+  (let ((at-bol (save-excursion (goto-char beg) (bolp)))
+        lines)
+    (save-restriction
+      (narrow-to-region beg end)
+      (goto-char (point-min))
+      (while (not (eobp))
+        (let ((line (thing-at-point 'line t)))
+          ;; In case the user has selected a region that begins
+          ;; mid-line, we should not chomp off the first character.
+          (if (and (null lines) (not at-bol))
+              (push line lines)
+            (push (substring line 1) lines)))
+        (forward-line)))
+    (let ((region-extract-function
+           (lambda (_) (apply #'concat (nreverse lines)))))
+      (kill-ring-save beg end t))))
 
 (defun diff-current-defun ()
   "Find the name of function at point.
-- 
2.39.2