[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/vundo 824be35152: diff support with marked ranges and c
From: |
ELPA Syncer |
Subject: |
[elpa] externals/vundo 824be35152: diff support with marked ranges and custom diff buffer |
Date: |
Mon, 11 Dec 2023 01:07:34 -0500 (EST) |
branch: externals/vundo
commit 824be351527f7a606b25637f6ba2a00cceade338
Author: JD Smith <93749+jdtsmith@users.noreply.github.com>
Commit: JD Smith <93749+jdtsmith@users.noreply.github.com>
diff support with marked ranges and custom diff buffer
Add support for computing the diff between a marked and the current
node, displaying in a custom vundo-diff buffer with color-matched
timestamped modifications.
* vundo-diff.el: New file.
* vundo.el (vundo, vundo--node-timestamp, vundo--draw-tree): Return
timestamp from undo-list or unmodified current buffer, bind new keys,
change overlay priority, and separate timestamp display from
calculation.
---
NEWS.txt | 12 ++++
README.txt | 19 ++++++-
vundo-diff.el | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
vundo.el | 80 +++++++++++++++++---------
4 files changed, 259 insertions(+), 29 deletions(-)
diff --git a/NEWS.txt b/NEWS.txt
index e03787989b..dc0ddc5ad5 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -1,3 +1,15 @@
+<2023-12-08 Fri>: Version 2.5.0
+
+vundo-diff introduced, providing on-demand diff functionality. Diff's
+are evaluated between the current node and either its parent node, or,
+if any, a marked node. New key commands:
+
+ (m)ark - mark a node for diff
+ (u)nmark - unmark any marked node
+ (d)iff - diff between current and marked or parent node
+
+The (d)ebug command has been moved to (D)ebug.
+
<2022-04-23 Sat>: Version 2.0.0
Breaking change:
diff --git a/README.txt b/README.txt
index 26291aaea6..9858e3b748 100644
--- a/README.txt
+++ b/README.txt
@@ -68,13 +68,23 @@ to use that font:
(set-face-attribute 'vundo-default nil :family "Symbola")
+Diff:
+
+Vundo uses Emacs' facilities to provide diffs among arbitrary undo
+states: just (m)ark and (d)iff.
+
+Terminal users may encounter unwanted control characters in the diff
+output. Emacs colors diff buffers itself, so this can be remedied by
+instructing diff not to print color codes:
+
+ (setq diff-switches "-u --color=never")
+
Comparing to undo-tree:
Vundo doesn’t need to be turned on all the time nor replace the undo
commands like undo-tree does. Vundo displays the tree horizontally,
-whereas undo-tree displays a tree vertically. Vundo doesn’t have many
-advanced features that undo-tree does (like showing diff), and most
-probably will not add those features in the future.
+whereas undo-tree displays a tree vertically. Diff is provided
+on-demand between any nodes, not just the node immediately prior.
Tests:
@@ -89,6 +99,9 @@ to run those tests interactively, or use the following batch
command:
Changelog (full changelog in NEWS.txt):
+<2023-12-08 Fri>: Version 2.5.0: vundo-diff introduced, supporting
+on-demand diff to parent or any marked node.
+
<2022-04-04 Mon>: Version 1.0.0
<2022-03-29 Tue>: vundo--mode and vundo--mode-map are now vundo-mode
diff --git a/vundo-diff.el b/vundo-diff.el
new file mode 100644
index 0000000000..355ae3b58a
--- /dev/null
+++ b/vundo-diff.el
@@ -0,0 +1,177 @@
+;;; vundo-diff.el --- buffer diff for vundo -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+;;
+;; Author: JD Smith <jdtsmith@gmail.com>
+;; Maintainer: Yuan Fu <casouri@gmail.com>
+;; URL: https://github.com/casouri/vundo
+;; Version: 0.1
+;; Package-Requires: ((emacs "28.1"))
+;;
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; vundo-diff provides simple on-demand diff between arbitray undo
+;; states in the vundo tree.
+
+;;; Code:
+(require 'vundo)
+(require 'diff)
+(require 'diff-mode)
+(eval-when-compile (require 'cl-lib))
+
+(defface vundo-diff-highlight
+ '((((background light)) .
+ (:inherit vundo-highlight :foreground "DodgerBlue4"))
+ (((background dark)) .
+ (:inherit vundo-highlight :foreground "DodgerBlue1")))
+ "Face for nodes marked for diff in the undo tree.")
+
+(defvar-local vundo-diff--marked-node nil)
+(defvar-local vundo-diff--highlight-overlay nil
+ "Overlay used to highlight the selected node.")
+
+(defun vundo-diff--cleanup-diff-buffer (orig-name buf current from to)
+ "Update diff headers in BUF.
+Headers are updated to indicate the diff in the contents of
+buffer named ORIG-NAME, between nodes FROM and TO, and given the
+CURRENT node."
+ (let ((inhibit-read-only t)
+ (info (cl-loop for x in (list from to)
+ for idx = (vundo-m-idx x)
+ for ts = (vundo--node-timestamp vundo--prev-mod-list x)
+ for stat = (if (eq x current) "Current"
+ (if vundo-diff--marked-node "Marked"
"Parent"))
+ collect
+ (list (format "[%d]" idx)
+ (format "<%s> [mod %d] (%s)" orig-name idx stat)
+ (when (consp ts) (format-time-string "%F %r"
ts))))))
+ (with-current-buffer buf
+ (vundo-diff-mode)
+ (goto-char (point-min))
+ (insert (concat (propertize "vundo-diff: " 'font-lock-face 'diff-header)
+ (propertize orig-name 'font-lock-face
+ '(diff-file-header diff-header))
+ "\n"))
+ (let* ((change-files
+ (cl-loop for (name fullname ts) in info
+ for pat in '("---" "+++")
+ if (re-search-forward
+ (rx-to-string `(and bol ,pat (+ space)
+ (group (group (+ (not ?\t)))
+ (* any))
+ eol))
+ nil t)
+ collect (cons (match-string-no-properties 2) name)
+ and do (replace-match
+ (if ts (concat fullname "\t" ts) fullname)
+ t t nil 1)))
+ (lim (point)))
+ (when (eq (length change-files) 2)
+ (goto-char (point-min))
+ (dolist (c change-files) ; change the file names in the diff
+ (when (search-forward (car c) lim t)
+ (replace-match (cdr c)))))))))
+
+;;;###autoload
+(defun vundo-diff-mark (&optional node)
+ "Mark NODE for vundo diff.
+NODE defaults to the current node."
+ (interactive)
+ (let* ((mod-list vundo--prev-mod-list)
+ (node (or node (vundo--current-node mod-list))))
+ (setq vundo-diff--marked-node node)
+ (unless vundo-diff--highlight-overlay
+ (setq vundo-diff--highlight-overlay
+ (make-overlay (1- (vundo-m-point node)) (vundo-m-point node)))
+ (overlay-put vundo-diff--highlight-overlay
+ 'display (vundo--translate "●"))
+ (overlay-put vundo-diff--highlight-overlay
+ 'face 'vundo-diff-highlight)
+ (overlay-put vundo-diff--highlight-overlay 'priority 1))
+ (move-overlay vundo-diff--highlight-overlay
+ (1- (vundo-m-point node))
+ (vundo-m-point node))))
+
+;;;###autoload
+(defun vundo-diff-unmark ()
+ "Unmark the node marked for vundo diff."
+ (interactive)
+ (when vundo-diff--marked-node
+ (setq vundo-diff--marked-node nil)
+ (when vundo-diff--highlight-overlay
+ (delete-overlay vundo-diff--highlight-overlay)
+ (setq vundo-diff--highlight-overlay nil))))
+
+;;;###autoload
+(defun vundo-diff ()
+ "Perform diff between marked and current buffer state.
+Displays in a separate diff buffer with name based on
+the original buffer name."
+ (interactive)
+ (let* ((orig vundo--orig-buffer)
+ (oname (buffer-name orig))
+ (current (vundo--current-node vundo--prev-mod-list))
+ (marked (or vundo-diff--marked-node (vundo-m-parent current)))
+ (swapped (> (vundo-m-idx marked) (vundo-m-idx current)))
+ mrkbuf)
+ (if (or (not current) (not marked) (eq current marked))
+ (message "vundo diff not available.")
+ (setq mrkbuf (get-buffer-create
+ (make-temp-name (concat oname "-vundo-diff-marked"))))
+ (unwind-protect
+ (progn
+ (vundo--check-for-command
+ (vundo--move-to-node current marked orig vundo--prev-mod-list)
+ (with-current-buffer mrkbuf
+ (insert-buffer-substring-no-properties orig))
+ (vundo--refresh-buffer orig (current-buffer) 'incremental)
+ (vundo--move-to-node marked current orig vundo--prev-mod-list)
+ (vundo--trim-undo-list orig current vundo--prev-mod-list)
+ (vundo--refresh-buffer orig (current-buffer) 'incremental))
+ (let* ((a (if swapped current marked))
+ (b (if swapped marked current))
+ (abuf (if swapped orig mrkbuf))
+ (bbuf (if swapped mrkbuf orig))
+ (dbuf (diff-no-select
+ abuf bbuf nil t
+ (get-buffer-create
+ (concat "*vundo-diff-" oname "*")))))
+ (vundo-diff--cleanup-diff-buffer oname dbuf current a b)
+ (display-buffer dbuf)))
+ (kill-buffer mrkbuf)))))
+
+(defconst vundo-diff-font-lock-keywords
+ `((,(rx bol (or "---" "+++") (* nonl) "[mod " (group (+ num)) ?\]
+ (+ ?\s) ?\((group (or "Parent" "Current")) ?\))
+ (1 'diff-index t)
+ (2 'vundo-highlight t))
+ (,(rx bol (or "---" "+++") (* nonl) "[mod " (group (+ num)) ?\]
+ (+ ?\s) ?\((group "Marked") ?\))
+ (1 'diff-index t)
+ (2 'vundo-diff-highlight t)))
+ "Additional font-lock keyword to fontify Parent/Current/Marked.")
+
+(define-derived-mode vundo-diff-mode diff-mode "Vundo Diff"
+ :syntax-table nil
+ :abbrev-table nil
+ (setcar font-lock-defaults
+ (append diff-font-lock-keywords vundo-diff-font-lock-keywords)))
+
+(provide 'vundo-diff)
+
+;;; vundo-diff.el ends here
diff --git a/vundo.el b/vundo.el
index b99b585455..26ab4bb345 100644
--- a/vundo.el
+++ b/vundo.el
@@ -39,6 +39,11 @@
;;
;; a to go back to the last branching point
;; e to go forward to the end/tip of the branch
+;; l to go to the last saved node
+;;
+;; m to mark the current node for diff
+;; u to unmark the marked node
+;; d to show a diff between the marked (or parent) and current nodes
;;
;; q to quit, you can also type C-g
;;
@@ -88,9 +93,7 @@
;;
;; Vundo doesn’t need to be turned on all the time nor replace the undo
;; commands like undo-tree does. Vundo displays the tree horizontally,
-;; whereas undo-tree displays a tree vertically. Vundo doesn’t have many
-;; advanced features that undo-tree does (like showing diff), and most
-;; probably will not add those features in the future.
+;; whereas undo-tree displays a tree vertically.
;;; Developer:
;;
@@ -513,6 +516,36 @@ If FROM non-nil, build from FORM-th modification in
MOD-LIST."
(vundo--sort-mod (cons mod children)
'reverse))))))))))
+;;; Timestamps
+;; buffer-undo-list contains "timestamp entries" like (t . TIMESTAMP)
+;; which capture the file modification time of the saved file which
+;; an undo changed. During tree draw, we collect the last of these, and
+;; indicated nodes which had been saved specially.
+
+(defvar vundo--last-saved-idx)
+(defvar vundo--orig-buffer)
+
+(defun vundo--mod-timestamp (mod-list idx)
+ "Return a timestamp if the mod in MOD-LIST at IDX has a timestamp."
+ ;; If the next mod’s timestamp is non-nil, this mod/node
+ ;; represents a saved state.
+ (let* ((next-mod-idx (1+ idx))
+ (next-mod (when (< next-mod-idx (length mod-list))
+ (aref mod-list next-mod-idx))))
+ (and next-mod (vundo-m-timestamp next-mod))))
+
+(defun vundo--node-timestamp (mod-list node)
+ "Return a timestamp from MOD-LIST for NODE, if any.
+In addition to undo-based timestamps, this includes the modtime of the
+current buffer (if unmodified)."
+ (let* ((idx (vundo-m-idx node))
+ (current (vundo--current-node mod-list)))
+ (or (vundo--mod-timestamp mod-list idx)
+ (and (eq node current) (eq idx vundo--last-saved-idx)
+ (with-current-buffer vundo--orig-buffer
+ (and (buffer-file-name)
+ (not (buffer-modified-p))
+ (visited-file-modtime)))))))
;;; Draw tree
(defun vundo--put-node-at-point (node)
@@ -553,15 +586,6 @@ Translate according to `vundo-glyph-alist'."
vundo-glyph-alist)))
text 'string))
-(defun vundo--mod-timestamp (mod-list idx)
- "Return a timestamp if the mod in MOD-LIST at IDX has a timestramp."
- ;; If the next mod’s timestamp is non-nil, this mod/node
- ;; represents a saved state.
- (let* ((next-mod-idx (1+ idx))
- (next-mod (when (< next-mod-idx (length mod-list))
- (aref mod-list next-mod-idx))))
- (and next-mod (vundo-m-timestamp next-mod))))
-
(defvar vundo--last-saved-idx)
(defun vundo--draw-tree (mod-list orig-buffer-modified)
@@ -584,11 +608,11 @@ corresponding to the index of the last saved node."
(only-child-p (and parent (eq (length siblings) 1)))
(node-last-child-p (and parent (eq node (car (last siblings)))))
(node-idx (vundo-m-idx node))
- (saved-p (and vundo-highlight-saved-nodes
- (vundo--mod-timestamp mod-list node-idx)))
+ (mod-ts (vundo--mod-timestamp mod-list node-idx))
+ (saved-p (and vundo-highlight-saved-nodes mod-ts))
(node-face (if saved-p 'vundo-saved 'vundo-node))
(stem-face (if only-child-p 'vundo-stem 'vundo-branch-stem)))
- (when (and saved-p (> node-idx last-saved-idx))
+ (when (and mod-ts (> node-idx last-saved-idx))
(setq last-saved-idx node-idx))
;; Go to parent.
(if parent (goto-char (vundo-m-point parent)))
@@ -647,8 +671,8 @@ corresponding to the index of the last saved node."
(setq node-queue (append children node-queue))))
;; If the associated buffer is unmodified, the last node must be
- ;; the last saved nodel even though it doesn’t have a next node
- ;; with a timestamp to indicate that.
+ ;; the last saved node even though it doesn’t (yet) have a next
+ ;; node with a timestamp to indicate that.
(setq vundo--last-saved-idx
(if orig-buffer-modified
(if (> last-saved-idx 0) last-saved-idx nil)
@@ -673,6 +697,9 @@ WINDOW is the window that was/is displaying the vundo
buffer."
(with-selected-window window
(kill-buffer-and-window))))
+(declare-function vundo-diff "vundo-diff")
+(declare-function vundo-diff-mark "vundo-diff")
+(declare-function vundo-diff-unmark "vundo-diff")
(defvar vundo-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "f") #'vundo-forward)
@@ -689,8 +716,11 @@ WINDOW is the window that was/is displaying the vundo
buffer."
(define-key map (kbd "q") #'vundo-quit)
(define-key map (kbd "C-g") #'vundo-quit)
(define-key map (kbd "RET") #'vundo-confirm)
+ (define-key map (kbd "m") #'vundo-diff-mark)
+ (define-key map (kbd "u") #'vundo-diff-unmark)
+ (define-key map (kbd "d") #'vundo-diff)
(define-key map (kbd "i") #'vundo--inspect)
- (define-key map (kbd "d") #'vundo--debug)
+ (define-key map (kbd "D") #'vundo--debug)
(define-key map [remap save-buffer] #'vundo-save)
map)
@@ -834,7 +864,7 @@ This function modifies `vundo--prev-mod-list',
'face 'vundo-highlight)
;; Make current node’s highlight override last saved node’s
;; highlight, should they collide.
- (overlay-put vundo--highlight-overlay 'priority 1))
+ (overlay-put vundo--highlight-overlay 'priority 2))
(move-overlay vundo--highlight-overlay
(1- (vundo-m-point node))
(vundo-m-point node)))
@@ -984,7 +1014,7 @@ stop. Eg, (6 5 4 3). Return nil if there’s no valid route."
(defun vundo--list-subtract (l1 l2)
"Return L1 - L2.
-\(vundo--list-subtract '(4 3 2 1) '(2 1))
+\(vundo--list-subtract \='(4 3 2 1) \='(2 1))
=> (4 3)"
(let ((len1 (length l1))
(len2 (length l2)))
@@ -1269,9 +1299,9 @@ Accepts the same interactive arfument ARG as
‘save-buffer’."
(vundo--check-for-command
(with-current-buffer vundo--orig-buffer
(save-buffer arg)))
- (when vundo-highlight-saved-nodes
- (let* ((cur-node (vundo--current-node vundo--prev-mod-list)))
- (setq vundo--last-saved-idx (vundo-m-idx cur-node))
+ (let* ((cur-node (vundo--current-node vundo--prev-mod-list)))
+ (setq vundo--last-saved-idx (vundo-m-idx cur-node))
+ (when vundo-highlight-saved-nodes
(vundo--highlight-last-saved-node cur-node))))
;;; Debug
@@ -1295,9 +1325,7 @@ TYPE is the type of buffer you want."
(mapcar #'vundo-m-idx (vundo--eqv-list-of node))
(and (vundo-m-children node)
(mapcar #'vundo-m-idx (vundo-m-children node)))
- (if-let* ((vundo-highlight-saved-nodes)
- (ts (vundo--mod-timestamp vundo--prev-mod-list
- (vundo-m-idx node)))
+ (if-let* ((ts (vundo--node-timestamp vundo--prev-mod-list node))
((consp ts)))
(format " Saved: %s" (format-time-string "%F %r" ts))
""))))
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [elpa] externals/vundo 824be35152: diff support with marked ranges and custom diff buffer,
ELPA Syncer <=