Subject: bug#75355: [PATCH 1/1] Improve comment cycling in log-edit

From: Jonas Bernoulli <[email-masked]>
Date: Sat, 4 Jan 2025 18:11:08 +0100

Save the current message before cycling to older messages, making it
possible to cycle back to that initial message.

* lisp/vc/log-edit.el (log-edit-buffer-comment): New function.
(log-edit-save-comment): New command, using new function.
(log-edit-mode-map, log-edit-menu): Bind new command.
(log-edit-previous-comment): Use new function.

Port log-edit-comment-ring improvements from git-commit.el
---
 lisp/vc/log-edit.el | 62 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 59 insertions(+), 3 deletions(-)

diff --git a/lisp/vc/log-edit.el b/lisp/vc/log-edit.el
index e23e7414a18..79ea89bc728 100644
--- a/lisp/vc/log-edit.el
+++ b/lisp/vc/log-edit.el
@@ -61,6 +61,7 @@ log-edit-mode-map
   "C-c C-d" #'log-edit-show-diff
   "C-c C-f" #'log-edit-show-files
   "C-c C-k" #'log-edit-kill-buffer
+  "C-c C-s" #'log-edit-save-comment
   "M-n"     #'log-edit-next-comment
   "M-p"     #'log-edit-previous-comment
   "M-r"     #'log-edit-comment-search-backward
@@ -86,6 +87,8 @@ log-edit-menu
     ["List files" log-edit-show-files
      :help "Show the list of relevant files."]
     "--"
+    ["Save comment"		log-edit-save-comment
+     :help "Save the current comment to comment history"]
     ["Previous comment"		log-edit-previous-comment
      :help "Cycle backwards through comment history"]
     ["Next comment"		log-edit-next-comment
@@ -280,15 +283,68 @@ log-edit-new-comment-index
 	(t stride))
        len))
 
+(defun log-edit-buffer-comment ()
+  "Return the comment in the current buffer.
+Remove lines after the scissors line (\"------- >8 ------\") and
+commented lines from the returned string.  Also remove leading and
+trailing whitespace.  If the comment consists solely of whitespace,
+return nil."
+  (let ((flush (concat "^" comment-start))
+        (str (buffer-substring-no-properties (point-min) (point-max))))
+    (with-temp-buffer
+      (insert str)
+      (goto-char (point-min))
+      (when (re-search-forward (concat flush " -+ >8 -+$") nil t)
+        (delete-region (line-beginning-position) (point-max)))
+      (goto-char (point-min))
+      (flush-lines flush)
+      (goto-char (point-max))
+      (unless (eq (char-before) ?\n)
+        (insert ?\n))
+      (setq str (buffer-string)))
+    (and (not (string-match "\\`[ \t\n\r]*\\'" str))
+         (progn
+           (when (string-match "\\`\n\\{2,\\}" str)
+             (setq str (replace-match "\n" t t str)))
+           (when (string-match "\n\\{2,\\}\\'" str)
+             (setq str (replace-match "\n" t t str)))
+           str))))
+
+(defun log-edit-save-comment ()
+  "Save current comment to `log-edit-comment-ring'."
+  (interactive)
+  (if-let* ((comment (log-edit-buffer-comment)))
+      (progn
+        (when-let* ((index (ring-member log-edit-comment-ring comment)))
+          (ring-remove log-edit-comment-ring index))
+        (ring-insert log-edit-comment-ring comment)
+        ;; This hook can be used, e.g., to store this in an alternative,
+        ;; repository-local ring.
+        (run-hooks 'log-edit-save-comment-hook)
+        (message "Comment saved"))
+    (message "Only whitespace and/or comments; message not saved")))
+
 (defun log-edit-previous-comment (arg)
   "Cycle backwards through VC commit comment history.
 With a numeric prefix ARG, go back ARG comments."
   (interactive "*p")
   (let ((len (ring-length log-edit-comment-ring)))
     (if (<= len 0)
-	(progn (message "Empty comment ring") (ding))
-      ;; Don't use `erase-buffer' because we don't want to `widen'.
-      (delete-region (point-min) (point-max))
+        (progn (message "Empty comment ring") (ding))
+      (when-let* ((comment (log-edit-buffer-comment))
+                  ((not (ring-member log-edit-comment-ring comment))))
+        (ring-insert log-edit-comment-ring comment)
+        (cl-incf arg)
+        (setq len (ring-length log-edit-comment-ring)))
+      ;; Delete the message but not the instructions at the end.
+      (save-restriction
+        (goto-char (point-min))
+        (narrow-to-region
+         (point)
+         (if (re-search-forward (concat "^" comment-start) nil t)
+             (max 1 (- (point) 2))
+           (point-max)))
+        (delete-region (point-min) (point)))
       (setq log-edit-comment-ring-index (log-edit-new-comment-index arg len))
       (message "Comment %d" (1+ log-edit-comment-ring-index))
       (insert (ring-ref log-edit-comment-ring log-edit-comment-ring-index)))))
-- 
2.47.1