From 822e925638c6ad187b09dd584c820253f22ded17 Mon Sep 17 00:00:00 2001 From: JD Smith <93749+jdtsmith@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:13:36 -0400 Subject: [PATCH 1/7] Add truncate-at-node for timestamp-based undo-list truncation --- README.txt | 1 + vundo.el | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/README.txt b/README.txt index 36b46fc..449c959 100644 --- a/README.txt +++ b/README.txt @@ -20,6 +20,7 @@ should pop up. To move around, type: 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 + K to truncate the undo list prior to a selected time-stamped node q to quit, you can also type C-g diff --git a/vundo.el b/vundo.el index e467516..ffbdc48 100644 --- a/vundo.el +++ b/vundo.el @@ -45,6 +45,7 @@ ;; 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 +;; K to truncate the undo list prior to a selected time-stamped node ;; ;; q to quit, you can also type C-g ;; @@ -161,6 +162,7 @@ (require 'cl-lib) (require 'seq) (require 'subr-x) +(require 'time-date) ;;; Customization @@ -768,6 +770,7 @@ WINDOW is the window that was/is displaying the vundo buffer." (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 "K") #'vundo-truncate-undo-at-node) (define-key map [remap save-buffer] #'vundo-save) map) @@ -941,6 +944,45 @@ Consults the alist of TIMESTAMPS. This moves the overlay (move-overlay vundo--highlight-last-saved-overlay (1- node-pt) node-pt)))) +(defun vundo-truncate-undo-at-node (node &optional timestamp) + "Truncate the buffer's undo list, removing entries before NODE. +If TIMESTAMP is provided, use it to identify a saved node to +trim to. Interactively, NODE is selected based on its saved +timestamp, if any saved nodes exist." + (interactive + (let* ((ts-list + (mapcar (lambda (entry) + (cons (concat (format-time-string "%FT%T%z [" (cdr entry)) + (seconds-to-string + (float-time (time-since (cdr entry)))) + "]") + entry)) + (reverse vundo--timestamps))) + (table (lambda (string pred action) + (if (eq action 'metadata) ; timestamps is pre-sorted + `(metadata (display-sort-function . ,#'identity)) + (complete-with-action action ts-list string pred))))) + (or (and ts-list + (let* ((key (completing-read "Trim undo records prior to timestamp: " + table nil t)) + (entry (alist-get key ts-list nil nil #'equal))) + (list (car entry) (cdr entry)))) + (progn (message "No timestamps found.") + (list nil nil))))) + (if (and (null node) timestamp) + (setq node (car (cl-find-if (lambda (x) (equal (cdr x) timestamp)) vundo--timestamps)))) + (when (and node + (or (not timestamp) + (yes-or-no-p + (format "Permanently remove all undo information prior to %s? " + (seconds-to-string + (float-time (time-since timestamp))))))) + (setcdr (vundo-m-undo-list node) nil) + (vundo--refresh-buffer vundo--orig-buffer (current-buffer)) + (when vundo--roll-back-to-this + (setq vundo--roll-back-to-this + (vundo--current-node vundo--prev-mod-list))))) + ;;;###autoload (defun vundo () "Display visual undo for the current buffer." From bbdd5211fd40832c5041939c0aa7d90f4eff6daa Mon Sep 17 00:00:00 2001 From: JD Smith <93749+jdtsmith@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:00:38 -0400 Subject: [PATCH 2/7] Filter out timestamped nodes without prior undo list --- vundo.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vundo.el b/vundo.el index ffbdc48..e6ad18b 100644 --- a/vundo.el +++ b/vundo.el @@ -957,7 +957,10 @@ timestamp, if any saved nodes exist." (float-time (time-since (cdr entry)))) "]") entry)) - (reverse vundo--timestamps))) + (reverse (seq-filter + (lambda (e) ; anything to remove? + (and (car e) (cdr (vundo-m-undo-list (car e))))) + vundo--timestamps)))) (table (lambda (string pred action) (if (eq action 'metadata) ; timestamps is pre-sorted `(metadata (display-sort-function . ,#'identity)) From ea3675bdbb42022c558eaa81e2c314d045f5b342 Mon Sep 17 00:00:00 2001 From: JD Smith <93749+jdtsmith@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:01:29 -0400 Subject: [PATCH 3/7] Prompt even without TIMESTAMP --- vundo.el | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/vundo.el b/vundo.el index e6ad18b..ec0c094 100644 --- a/vundo.el +++ b/vundo.el @@ -962,7 +962,7 @@ timestamp, if any saved nodes exist." (and (car e) (cdr (vundo-m-undo-list (car e))))) vundo--timestamps)))) (table (lambda (string pred action) - (if (eq action 'metadata) ; timestamps is pre-sorted + (if (eq action 'metadata) ; timestamps pre-sorted `(metadata (display-sort-function . ,#'identity)) (complete-with-action action ts-list string pred))))) (or (and ts-list @@ -973,13 +973,13 @@ timestamp, if any saved nodes exist." (progn (message "No timestamps found.") (list nil nil))))) (if (and (null node) timestamp) - (setq node (car (cl-find-if (lambda (x) (equal (cdr x) timestamp)) vundo--timestamps)))) + (setq node (car (cl-find-if (lambda (x) (equal (cdr x) timestamp)) + vundo--timestamps)))) (when (and node - (or (not timestamp) - (yes-or-no-p - (format "Permanently remove all undo information prior to %s? " - (seconds-to-string - (float-time (time-since timestamp))))))) + (yes-or-no-p + (format "Permanently remove all undo information prior to %s? " + (if timestamp (format-time-string "%FT%T%z [" timestamp) + "this node")))) (setcdr (vundo-m-undo-list node) nil) (vundo--refresh-buffer vundo--orig-buffer (current-buffer)) (when vundo--roll-back-to-this From f66a0bfaed1515a423db888dc474afb8bac7ee9f Mon Sep 17 00:00:00 2001 From: JD Smith <93749+jdtsmith@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:06:05 -0400 Subject: [PATCH 4/7] Fix truncate prompt format --- vundo.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vundo.el b/vundo.el index ec0c094..3e91284 100644 --- a/vundo.el +++ b/vundo.el @@ -978,7 +978,7 @@ timestamp, if any saved nodes exist." (when (and node (yes-or-no-p (format "Permanently remove all undo information prior to %s? " - (if timestamp (format-time-string "%FT%T%z [" timestamp) + (if timestamp (format-time-string "%FT%T%z" timestamp) "this node")))) (setcdr (vundo-m-undo-list node) nil) (vundo--refresh-buffer vundo--orig-buffer (current-buffer)) From 92d8310aba167bd78c4af11606f8a71ef53d560c Mon Sep 17 00:00:00 2001 From: JD Smith <93749+jdtsmith@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:06:32 -0400 Subject: [PATCH 5/7] Message with number of truncated records --- vundo.el | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vundo.el b/vundo.el index 3e91284..6353f66 100644 --- a/vundo.el +++ b/vundo.el @@ -980,11 +980,14 @@ timestamp, if any saved nodes exist." (format "Permanently remove all undo information prior to %s? " (if timestamp (format-time-string "%FT%T%z" timestamp) "this node")))) - (setcdr (vundo-m-undo-list node) nil) + (let* ((undo-list (vundo-m-undo-list node)) + (len (length (cdr undo-list)))) + (setcdr undo-list nil) + (message "Trimmed %d undo-list records" len)) (vundo--refresh-buffer vundo--orig-buffer (current-buffer)) (when vundo--roll-back-to-this - (setq vundo--roll-back-to-this - (vundo--current-node vundo--prev-mod-list))))) + (setq vundo--roll-back-to-this + (vundo--current-node vundo--prev-mod-list))))) ;;;###autoload (defun vundo () From 35ef8c33b3acf5b538264694aa75174106025cfb Mon Sep 17 00:00:00 2001 From: JD Smith <93749+jdtsmith@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:24:19 -0400 Subject: [PATCH 6/7] truncate: provide record statistics during node selection --- vundo.el | 66 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/vundo.el b/vundo.el index 6353f66..35c6e8d 100644 --- a/vundo.el +++ b/vundo.el @@ -946,28 +946,33 @@ Consults the alist of TIMESTAMPS. This moves the overlay (defun vundo-truncate-undo-at-node (node &optional timestamp) "Truncate the buffer's undo list, removing entries before NODE. -If TIMESTAMP is provided, use it to identify a saved node to -trim to. Interactively, NODE is selected based on its saved -timestamp, if any saved nodes exist." +If TIMESTAMP is provided and NODE is nil, use the timestamp to +identify a saved node to trim to. Interactively, NODE is +selected based on its saved timestamp, if any saved nodes exist." (interactive - (let* ((ts-list - (mapcar (lambda (entry) - (cons (concat (format-time-string "%FT%T%z [" (cdr entry)) - (seconds-to-string - (float-time (time-since (cdr entry)))) - "]") - entry)) - (reverse (seq-filter - (lambda (e) ; anything to remove? - (and (car e) (cdr (vundo-m-undo-list (car e))))) - vundo--timestamps)))) + (let* ((undo-cnt (length (buffer-local-value + 'buffer-undo-list vundo--orig-buffer))) + (ts-list + (mapcar + (lambda (entry) + (cons (concat (format-time-string "%FT%T%z [" (cdr entry)) + (seconds-to-string + (float-time (time-since (cdr entry)))) + (let* ((trim-cnt (length (cdr (vundo-m-undo-list (car entry))))) + (percentage (* 100 (/ (float trim-cnt) undo-cnt)))) + (format "] (%d records, %0.1f%%)" trim-cnt percentage))) + entry)) + (reverse (seq-filter + (lambda (e) ; anything to remove? + (and (car e) (cdr (vundo-m-undo-list (car e))))) + vundo--timestamps)))) (table (lambda (string pred action) (if (eq action 'metadata) ; timestamps pre-sorted `(metadata (display-sort-function . ,#'identity)) (complete-with-action action ts-list string pred))))) (or (and ts-list (let* ((key (completing-read "Trim undo records prior to timestamp: " - table nil t)) + table nil t)) (entry (alist-get key ts-list nil nil #'equal))) (list (car entry) (cdr entry)))) (progn (message "No timestamps found.") @@ -975,19 +980,24 @@ timestamp, if any saved nodes exist." (if (and (null node) timestamp) (setq node (car (cl-find-if (lambda (x) (equal (cdr x) timestamp)) vundo--timestamps)))) - (when (and node - (yes-or-no-p - (format "Permanently remove all undo information prior to %s? " - (if timestamp (format-time-string "%FT%T%z" timestamp) - "this node")))) - (let* ((undo-list (vundo-m-undo-list node)) - (len (length (cdr undo-list)))) - (setcdr undo-list nil) - (message "Trimmed %d undo-list records" len)) - (vundo--refresh-buffer vundo--orig-buffer (current-buffer)) - (when vundo--roll-back-to-this - (setq vundo--roll-back-to-this - (vundo--current-node vundo--prev-mod-list))))) + (if (and node + (or (not (called-interactively-p 'interactive)) + (yes-or-no-p + (format "Permanently remove all undo information prior to %s? " + (if timestamp (format-time-string "%FT%T%z" timestamp) + "this node"))))) + (progn + (let* ((undo-list (vundo-m-undo-list node)) + (len (length (cdr undo-list)))) + (setcdr undo-list nil) + (when (called-interactively-p 'interactive) + (message "Trimmed %d undo-list records" len))) + (vundo--refresh-buffer vundo--orig-buffer (current-buffer)) + (when vundo--roll-back-to-this + (setq vundo--roll-back-to-this + (vundo--current-node vundo--prev-mod-list)))) + (when (called-interactively-p 'interactive) + (message "No undo-list records removed")))) ;;;###autoload (defun vundo () From bff0694e892cb07e947663a0bfe874702f888bf3 Mon Sep 17 00:00:00 2001 From: JD Smith <93749+jdtsmith@users.noreply.github.com> Date: Sat, 6 Jul 2024 15:07:33 -0400 Subject: [PATCH 7/7] truncate-undo-at-node: use annotation-function Use a simple ISO-formatted timestamp as key, and an annotation function for additional information (records, percentage). --- vundo.el | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/vundo.el b/vundo.el index 35c6e8d..8c967c0 100644 --- a/vundo.el +++ b/vundo.el @@ -955,20 +955,27 @@ selected based on its saved timestamp, if any saved nodes exist." (ts-list (mapcar (lambda (entry) - (cons (concat (format-time-string "%FT%T%z [" (cdr entry)) - (seconds-to-string - (float-time (time-since (cdr entry)))) - (let* ((trim-cnt (length (cdr (vundo-m-undo-list (car entry))))) - (percentage (* 100 (/ (float trim-cnt) undo-cnt)))) - (format "] (%d records, %0.1f%%)" trim-cnt percentage))) - entry)) + (cons (format-time-string "%FT%T%z" (cdr entry)) entry)) (reverse (seq-filter (lambda (e) ; anything to remove? (and (car e) (cdr (vundo-m-undo-list (car e))))) vundo--timestamps)))) + (annotation-function + (lambda (time-string) + (let* ((entry (alist-get time-string ts-list nil nil #'equal)) + (age (seconds-to-string (float-time (time-since (cdr entry))))) + (trim-cnt (length (cdr (vundo-m-undo-list (car entry))))) + (percentage (* 100 (/ (float trim-cnt) undo-cnt))) + (str (format "(%d records, %0.1f%%)" trim-cnt percentage)) + (l (length str))) + (concat " [" age "]" + (propertize " " 'display + `(space :align-to (- right ,l))) + str)))) (table (lambda (string pred action) (if (eq action 'metadata) ; timestamps pre-sorted - `(metadata (display-sort-function . ,#'identity)) + `(metadata (display-sort-function . ,#'identity) + (annotation-function . ,annotation-function)) (complete-with-action action ts-list string pred))))) (or (and ts-list (let* ((key (completing-read "Trim undo records prior to timestamp: " @@ -997,7 +1004,7 @@ selected based on its saved timestamp, if any saved nodes exist." (setq vundo--roll-back-to-this (vundo--current-node vundo--prev-mod-list)))) (when (called-interactively-p 'interactive) - (message "No undo-list records removed")))) + (message "No undo-list records removed")))) ;;;###autoload (defun vundo ()