diff options
| author | Stefan Monnier | 2025-12-28 22:32:23 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2025-12-28 22:32:23 -0500 |
| commit | e119514ae8b391f41577d22d4e41cc3fea7ab9eb (patch) | |
| tree | d1d68551bcf4ae83481731de0ffb93f876dc7b07 | |
| parent | 8f2557844d2f0e7a87b4c94d7a28bc6e4207d9da (diff) | |
| download | emacs-e119514ae8b391f41577d22d4e41cc3fea7ab9eb.tar.gz emacs-e119514ae8b391f41577d22d4e41cc3fea7ab9eb.zip | |
track-changes.el (track-changes-undo-only): New var
* lisp/emacs-lisp/track-changes.el (track-changes-undo-only): New var.
(track-changes-fetch): Bind it.
(track-changes--state): New slot `undo`.
(track-changes--after): Set it.
* lisp/vc/diff-mode.el (diff--track-changes-function): Use the
new var.
* doc/lispref/text.texi (Tracking changes): Mention
`track-changes-undo-only`.
| -rw-r--r-- | doc/lispref/text.texi | 13 | ||||
| -rw-r--r-- | etc/NEWS | 4 | ||||
| -rw-r--r-- | lisp/emacs-lisp/track-changes.el | 36 | ||||
| -rw-r--r-- | lisp/vc/diff-mode.el | 3 |
4 files changed, 52 insertions, 4 deletions
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index c6e3b1bdbec..c1457c49562 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi | |||
| @@ -6582,6 +6582,19 @@ risk that the @var{signal} function gets triggered in the middle of it, | |||
| 6582 | because the @var{signal} is re-enabled only after @var{func} finishes. | 6582 | because the @var{signal} is re-enabled only after @var{func} finishes. |
| 6583 | @end defun | 6583 | @end defun |
| 6584 | 6584 | ||
| 6585 | @defvar track-changes-undo-only | ||
| 6586 | If your code uses @code{track-changes} to perform further modifications | ||
| 6587 | to the buffer (for example, to mark the parts of the buffer that have | ||
| 6588 | been edited), then you may want to refrain from making those | ||
| 6589 | modifications when the changes are the result of an undo (which | ||
| 6590 | presumably also undoes the modifications you had applied back when the | ||
| 6591 | corresponding edit was made). | ||
| 6592 | To that end @code{track-changes-fetch} binds | ||
| 6593 | @code{track-changes-undo-only} to non-@code{nil} during calls to | ||
| 6594 | @var{func} if the changes were the result of undo. | ||
| 6595 | @end defvar | ||
| 6596 | |||
| 6597 | |||
| 6585 | @defun track-changes-unregister id | 6598 | @defun track-changes-unregister id |
| 6586 | This function tells the library that the tracker @var{id} does not need | 6599 | This function tells the library that the tracker @var{id} does not need |
| 6587 | to know about buffer changes any more. Most clients will never want to | 6600 | to know about buffer changes any more. Most clients will never want to |
| @@ -1119,6 +1119,10 @@ convention. Also, the ':match?' predicate can now take the regexp as | |||
| 1119 | either the first or second argument, so it works with both tree-sitter | 1119 | either the first or second argument, so it works with both tree-sitter |
| 1120 | convention (regexp arg second) and Emacs convention (regexp arg first). | 1120 | convention (regexp arg second) and Emacs convention (regexp arg first). |
| 1121 | 1121 | ||
| 1122 | +++ | ||
| 1123 | ** Track-changes | ||
| 1124 | *** New variable 'track-changes-undo-only' to distinguish undo changes. | ||
| 1125 | |||
| 1122 | ** Hideshow | 1126 | ** Hideshow |
| 1123 | 1127 | ||
| 1124 | +++ | 1128 | +++ |
diff --git a/lisp/emacs-lisp/track-changes.el b/lisp/emacs-lisp/track-changes.el index bf899eebbe9..569a04bdcc1 100644 --- a/lisp/emacs-lisp/track-changes.el +++ b/lisp/emacs-lisp/track-changes.el | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | ;; Copyright (C) 2024-2025 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 2024-2025 Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | ;; Author: Stefan Monnier <monnier@iro.umontreal.ca> | 5 | ;; Author: Stefan Monnier <monnier@iro.umontreal.ca> |
| 6 | ;; Version: 1.4 | 6 | ;; Version: 1.5 |
| 7 | ;; Package-Requires: ((emacs "24")) | 7 | ;; Package-Requires: ((emacs "24")) |
| 8 | 8 | ||
| 9 | ;; This file is part of GNU Emacs. | 9 | ;; This file is part of GNU Emacs. |
| @@ -76,6 +76,11 @@ | |||
| 76 | 76 | ||
| 77 | ;;; News: | 77 | ;;; News: |
| 78 | 78 | ||
| 79 | ;; v1.5: | ||
| 80 | ;; | ||
| 81 | ;; - New variable `track-changes-undo-only' to distinguish undo changes | ||
| 82 | ;; from others. | ||
| 83 | ;; | ||
| 79 | ;; v1.3: | 84 | ;; v1.3: |
| 80 | ;; | 85 | ;; |
| 81 | ;; - Fix bug#73041. | 86 | ;; - Fix bug#73041. |
| @@ -100,6 +105,19 @@ | |||
| 100 | ;; move t-c--before-beg/end so it scales better when there are | 105 | ;; move t-c--before-beg/end so it scales better when there are |
| 101 | ;; many small changes. | 106 | ;; many small changes. |
| 102 | 107 | ||
| 108 | ;;;; Distinguish undo-vs-nonundo | ||
| 109 | |||
| 110 | ;; In practice, it seems that this distinction matters only for those | ||
| 111 | ;; clients which make further buffer-changes in response to buffer changes, | ||
| 112 | ;; (e.g. updating diff chunk headers on the fly, line-centering on the fly, | ||
| 113 | ;; inserting criticmarkup to keep track of buffer edits, ...). | ||
| 114 | ;; If all or none of the changes occurred during undo, then it's easy. | ||
| 115 | ;; If some did and some didn't and we need to merge them into a single change, | ||
| 116 | ;; there are two options: | ||
| 117 | ;; - Do the disjoint thing. | ||
| 118 | ;; - Merge them into a single change that's considered as "nonundo". | ||
| 119 | ;; We currently don't implement "the disjoint way". | ||
| 120 | |||
| 103 | (require 'cl-lib) | 121 | (require 'cl-lib) |
| 104 | 122 | ||
| 105 | ;;;; Internal types and variables. | 123 | ;;;; Internal types and variables. |
| @@ -130,6 +148,7 @@ state is created." | |||
| 130 | (beg (point-max)) | 148 | (beg (point-max)) |
| 131 | (end (point-min)) | 149 | (end (point-min)) |
| 132 | (before nil) | 150 | (before nil) |
| 151 | (undo t :type boolean) ;Non-nil until proven otherwise. | ||
| 133 | (next nil)) | 152 | (next nil)) |
| 134 | 153 | ||
| 135 | (defvar-local track-changes--trackers () | 154 | (defvar-local track-changes--trackers () |
| @@ -190,6 +209,9 @@ Each call is recorded as a (BUFFER-NAME . BACKTRACE).") | |||
| 190 | The errors are kept in `track-changes--error-log'. | 209 | The errors are kept in `track-changes--error-log'. |
| 191 | If set to `trace', then we additionally keep a trace of recent calls to the API.") | 210 | If set to `trace', then we additionally keep a trace of recent calls to the API.") |
| 192 | 211 | ||
| 212 | (defvar track-changes-undo-only nil | ||
| 213 | "Bound to non-nil by `track-changes-fetch' if the change was an undo.") | ||
| 214 | |||
| 193 | (cl-defun track-changes-register ( signal &key nobefore disjoint immediate) | 215 | (cl-defun track-changes-register ( signal &key nobefore disjoint immediate) |
| 194 | "Register a new tracker whose change-tracking function is SIGNAL. | 216 | "Register a new tracker whose change-tracking function is SIGNAL. |
| 195 | Return the ID of the new tracker. | 217 | Return the ID of the new tracker. |
| @@ -281,7 +303,10 @@ This reflects a bug somewhere, so please report it when it happens. | |||
| 281 | 303 | ||
| 282 | If no changes occurred since the last time, it doesn't call FUNC and | 304 | If no changes occurred since the last time, it doesn't call FUNC and |
| 283 | returns nil, otherwise it returns the value returned by FUNC | 305 | returns nil, otherwise it returns the value returned by FUNC |
| 284 | and re-enable the TRACKER corresponding to ID." | 306 | and re-enable the TRACKER corresponding to ID. |
| 307 | |||
| 308 | During the call to FUNC, `track-changes-undo-only' indicates if the changes | ||
| 309 | were the result of `undo'." | ||
| 285 | (track-changes--trace) | 310 | (track-changes--trace) |
| 286 | (cl-assert (memq id track-changes--trackers)) | 311 | (cl-assert (memq id track-changes--trackers)) |
| 287 | (unless (equal track-changes--buffer-size (buffer-size)) | 312 | (unless (equal track-changes--buffer-size (buffer-size)) |
| @@ -289,6 +314,7 @@ and re-enable the TRACKER corresponding to ID." | |||
| 289 | `(buffer-size ,track-changes--buffer-size ,(buffer-size)))) | 314 | `(buffer-size ,track-changes--buffer-size ,(buffer-size)))) |
| 290 | (let ((beg nil) | 315 | (let ((beg nil) |
| 291 | (end nil) | 316 | (end nil) |
| 317 | (is-undo t) | ||
| 292 | (before t) | 318 | (before t) |
| 293 | (lenbefore 0) | 319 | (lenbefore 0) |
| 294 | (states ())) | 320 | (states ())) |
| @@ -316,6 +342,7 @@ and re-enable the TRACKER corresponding to ID." | |||
| 316 | (setq states nil))) | 342 | (setq states nil))) |
| 317 | 343 | ||
| 318 | (dolist (state states) | 344 | (dolist (state states) |
| 345 | (when is-undo (setq is-undo (track-changes--state-undo state))) | ||
| 319 | (let ((prevbeg (track-changes--state-beg state)) | 346 | (let ((prevbeg (track-changes--state-beg state)) |
| 320 | (prevend (track-changes--state-end state)) | 347 | (prevend (track-changes--state-end state)) |
| 321 | (prevbefore (track-changes--state-before state))) | 348 | (prevbefore (track-changes--state-before state))) |
| @@ -384,7 +411,8 @@ and re-enable the TRACKER corresponding to ID." | |||
| 384 | ;; Update the tracker's state *before* running `func' so we don't risk | 411 | ;; Update the tracker's state *before* running `func' so we don't risk |
| 385 | ;; mistakenly replaying the changes in case `func' exits non-locally. | 412 | ;; mistakenly replaying the changes in case `func' exits non-locally. |
| 386 | (setf (track-changes--tracker-state id) track-changes--state) | 413 | (setf (track-changes--tracker-state id) track-changes--state) |
| 387 | (funcall func beg end (or before lenbefore))) | 414 | (let ((track-changes-undo-only is-undo)) |
| 415 | (funcall func beg end (or before lenbefore)))) | ||
| 388 | ;; Re-enable the tracker's signal only after running `func', so | 416 | ;; Re-enable the tracker's signal only after running `func', so |
| 389 | ;; as to avoid nested invocations. | 417 | ;; as to avoid nested invocations. |
| 390 | (cl-pushnew id track-changes--clean-trackers)))) | 418 | (cl-pushnew id track-changes--clean-trackers)))) |
| @@ -629,6 +657,8 @@ Details logged to `track-changes--error-log'") | |||
| 629 | beg end | 657 | beg end |
| 630 | (track-changes--state-end track-changes--state) | 658 | (track-changes--state-end track-changes--state) |
| 631 | track-changes--before-end))))) | 659 | track-changes--before-end))))) |
| 660 | (unless undo-in-progress | ||
| 661 | (setf (track-changes--state-undo track-changes--state) nil)) | ||
| 632 | (while track-changes--clean-trackers | 662 | (while track-changes--clean-trackers |
| 633 | (let ((tracker (pop track-changes--clean-trackers))) | 663 | (let ((tracker (pop track-changes--clean-trackers))) |
| 634 | (if (track-changes--tracker-immediate tracker) | 664 | (if (track-changes--tracker-immediate tracker) |
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index 5e634e3bfdb..afd152c9ba1 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el | |||
| @@ -1615,7 +1615,8 @@ else cover the whole buffer." | |||
| 1615 | ;; it's safer not to do it on big changes, e.g. when yanking a big | 1615 | ;; it's safer not to do it on big changes, e.g. when yanking a big |
| 1616 | ;; diff, or when the user edits the header, since we might then | 1616 | ;; diff, or when the user edits the header, since we might then |
| 1617 | ;; screw up perfectly correct values. --Stef | 1617 | ;; screw up perfectly correct values. --Stef |
| 1618 | (when (ignore-errors (diff-beginning-of-hunk t)) | 1618 | (when (and (not track-changes-undo-only) |
| 1619 | (ignore-errors (diff-beginning-of-hunk t))) | ||
| 1619 | (let* ((style (if (looking-at "\\*\\*\\*") 'context)) | 1620 | (let* ((style (if (looking-at "\\*\\*\\*") 'context)) |
| 1620 | (start (line-beginning-position (if (eq style 'context) 3 2))) | 1621 | (start (line-beginning-position (if (eq style 'context) 3 2))) |
| 1621 | (mid (if (eq style 'context) | 1622 | (mid (if (eq style 'context) |