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

From: Philip Kaludercic <[email-masked]>
Date: Sat, 19 Aug 2023 09:53:58 +0000

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'

>From 9d755a12614fb9c1afe4dd88cecfbe16c3c009c4 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 | 22 +++++++++++++++++++++-
 2 files changed, 28 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..fce61837069 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,25 @@ diff-goto-source
       (goto-char (+ (car pos) (cdr src)))
       (when buffer (next-error-found buffer (current-buffer))))))
 
+(defun diff-kill-ring-save ()
+  " "
+  (interactive)
+  (let ((at-bol (save-excursion
+                  (goto-char (region-beginning))
+                  (bolp)))
+        lines)
+    (save-restriction
+      (narrow-to-region (region-beginning) (region-end))
+      (goto-char (point-min))
+      (while (not (eobp))
+        (let ((line (thing-at-point 'line t)))
+          (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)))))
+      (copy-region-as-kill nil nil t))))
 
 (defun diff-current-defun ()
   "Find the name of function at point.
-- 
2.39.2