Subject: Re: bug#28852: make revert-buffer ('g') in VC diff buffers keep point

From: [email-masked] (Charles A. Roelli)
Date: Tue, 17 Oct 2017 19:58:00 +0200

> Date: Sun, 15 Oct 2017 21:07:30 +0200
> From: [email-masked] (Charles A. Roelli)
> 
> At the moment, when you hit 'g' in a *vc-diff* buffer, the cursor is
> moved back to (point-min), which can be annoying.  Attached is an
> attempt at fixing that (using 'replace-buffer-contents' to keep
> markers/point in the same place).

Hm, I'll try again:

>From 48e3febfee28276b3eb7d9af58342c70d2d798f9 Mon Sep 17 00:00:00 2001
From: "Charles A. Roelli" <[email-masked]>
Date: Sun, 15 Oct 2017 20:58:01 +0200
Subject: [PATCH] Make revert-buffer ('g') keep point in VC diff buffers
 (Bug#28852)

* lisp/vc/vc.el (vc-diff-restore-buffer): New function.
(vc-diff-finish): Update its calling convention to include an
optional 'oldbuf' parameter, and handle it.
(vc-diff-internal): Pass a clone of the incumbent vc-diff
buffer to 'vc-diff-finish'.
---
 lisp/vc/vc.el | 33 +++++++++++++++++++++++++++++----
 1 file changed, 29 insertions(+), 4 deletions(-)

diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index b80f0e6..57d5a50 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -1654,7 +1654,20 @@ vc-diff-switches-list
   (declare (obsolete vc-switches "22.1"))
   `(vc-switches ',backend 'diff))
 
-(defun vc-diff-finish (buffer messages)
+(defun vc-diff-restore-buffer (original new)
+  "Restore point in buffer NEW to where it was in ORIGINAL.
+
+This function works by updating buffer ORIGINAL with the contents
+of NEW (without destroying existing markers), swapping their text
+objects, and finally killing buffer ORIGINAL."
+  (with-current-buffer original
+    (let ((inhibit-read-only t))
+      (replace-buffer-contents new)))
+  (with-current-buffer new
+    (buffer-swap-text original))
+  (kill-buffer original))
+
+(defun vc-diff-finish (buffer messages &optional oldbuf)
   ;; The empty sync output case has already been handled, so the only
   ;; possibility of an empty output is for an async process.
   (when (buffer-live-p buffer)
@@ -1666,7 +1679,13 @@ vc-diff-finish
 	       (insert (cdr messages) ".\n")
 	       (message "%s" (cdr messages))))
 	(diff-setup-whitespace)
-	(goto-char (point-min))
+        ;; `oldbuf' is the buffer that used to show this diff.  Make
+        ;; sure that we restore point in it if it's given.
+	(if oldbuf
+            (progn
+              (vc-diff-restore-buffer oldbuf buffer)
+              (diff-mode))
+          (goto-char (point-min)))
 	(when window
 	  (shrink-window-if-larger-than-buffer window)))
       (when (and messages (not emptyp))
@@ -1692,7 +1711,12 @@ vc-diff-internal
 	 ;; but the only way to set it for each file included would
 	 ;; be to call the back end separately for each file.
 	 (coding-system-for-read
-	  (if files (vc-coding-system-for-diff (car files)) 'undecided)))
+	  (if files (vc-coding-system-for-diff (car files)) 'undecided))
+         (orig-diff-buffer-clone
+          (if (and (get-buffer buffer) revert-buffer-in-progress-p)
+              (with-current-buffer buffer
+                (clone-buffer
+                 (generate-new-buffer-name " *vc-diff-clone*") nil)))))
     ;; On MS-Windows and MS-DOS, Diff is likely to produce DOS-style
     ;; EOLs, which will look ugly if (car files) happens to have Unix
     ;; EOLs.
@@ -1752,7 +1776,8 @@ vc-diff-internal
       ;; after `pop-to-buffer'; the former assumes the diff buffer is
       ;; shown in some window.
       (let ((buf (current-buffer)))
-        (vc-run-delayed (vc-diff-finish buf (when verbose messages))))
+        (vc-run-delayed (vc-diff-finish buf (when verbose messages)
+                                        orig-diff-buffer-clone)))
       ;; In the async case, we return t even if there are no differences
       ;; because we don't know that yet.
       t)))
-- 
2.9.4