Особлива подяка @Tobias за написання функції пошуку наступної / попередньої часової позначки в історії скасування / повторення: https://emacs.stackexchange.com/a/32415/2287 ; а також для написання ряду функцій для копіювання дерева скасування: https://emacs.stackexchange.com/a/32230/2287 .
Як деякі читачі, можливо, вже знають, вилки приймаються MELPA лише в екстремальних обставинах. Створення надбудови можливо, можливо, але не здається практичним, враховуючи кількість змін, внесених @lawlist - включаючи, але не обмежуючись цим, додавання елементів до базових векторів структури даних та зміну імен декількох функції / змінні, які не відповідали undo-tree-...
умові іменування префіксів тощо. @lawlist вже звернувся до оригінального автора (Dr. Cubitt), щоб запропонувати цю нову функцію, а також різні виправлення помилок та вдосконалення.
Якщо когось цікавить, не соромтеся дати цій новій функції вир. Коментар містить зразок форми подання звіту про помилки, починаючи з emacs -q
того випадку, якщо хтось має проблеми.
Вихідний код: https://github.com/lawlist/undo_tree
Коментар:
;;; This unofficial modification by @lawlist to the `undo-tree.el` library authored
;;; by Toby Cubitt adds semi-linear undo/redo support and a corresponding visualizer
;;; view accessible with `C-u C-x u` or by using the 3-way toggle with the letter `t`
;;; in the visualization buffer. This entire library is meant to be a replacement
;;; of the stock version of `undo-tree.el`, which would need to be completely removed
;;; from the `load-path'. In the visualization buffer, the letters `u` / `r`
;;; or `z` / `Z` are used for semi-linear undo/redo. In the working buffer,
;;; `super-u` / `super-r` or `super-z`/`super-Z` are used for semi-linear undo/redo.
;;; Semi-linear undo/redo also work in the classic views of the visualization buffer.
;;; All previous keyboard shortcuts remain unchanged. The mouse can be used to
;;; select semi-linear nodes or branch-point timestamps in the visualization buffer.
;;;
;;; The term `semi-linear` was chosen because the time-line is generally structured
;;; as follows: When undoing, the movement is in an upward direction from the
;;; leaf to the branch-point and then the previous branch begins undoing from the
;;; leaf. When redoing, the movement is in a downward direction from the branch-
;;; point to the leaf and then the next branch begins redoing from the branch-point.
;;; It is not a situation where we walk-up and back-down the same branch, or walk-
;;; down and back-up the same branch again. If that missing feature is useful,
;;; then perhaps it could be implemented someday....
;;;
;;; In a nutshell, the classic version of undo-tree undo/redo limits a user to
;;; the active branch (skipping over inactive branches), unless the user calls
;;; `undo-tree-switch-branch' or `undo-tree-visualize-switch-branch-right' or
;;; `undo-tree-visualize-switch-branch-left' to select an alternative branch. This
;;; generally means a user must pop-open the visualizer buffer to see what is going
;;; on to make a proper decision. The new semi-linear feature is essentially
;;; "mindless" where the user can just hold down the forward/reverse button and
;;; go through every node of the tree in chronological order -- i.e., all branches
;;; and nodes are visited in the process (nothing is skipped over).
;;;
;;; The labels in the visualization buffer remain the same: `o`, `x`, `s`, register.
;;; The branches are labeled consecutively as they are drawn with lowercase letters.
;;; The branch-points are labeled consecutively as they are drawn with uppercase
;;; letters. The branches coming off of each branch-point are labeled with the nth
;;; numeric position of the branch -- e.g., far left is always nth 0. The nodes of
;;; each branch are numbered consecutively commencing just after the branch-point.
;;;
;;; The features that are available in `undo-tree.el` version 0.6.6 remain the same;
;;; however, some of the functions have been consolidated and the names have changed.
;;;
;;; `undo-tree-history-save' and `undo-tree-history-restore' support input/output
;;; to/from a string or a file. The history string/file contains three components:
;;; `buffer-file-name' (if it exists; else `nil`); SHA1 string; the `undo-tree-list'.
;;; Histories created with the unmodified stock version of `undo-tree.el` contained 2
;;; components and those previous versions are no longer supported. Saving/exporting
;;; excludes all text-properties, yasnippet entries, and multiple-cursors entries.
;;; `read' chokes when encountering #<marker in no buffer> or #<overlay in no buffer>,
;;; that can make their way into the `undo-tree-list' when killing the visualizer
;;; buffer by brute force or when using the yasnippet library. Those two known
;;; situations have been dealt with programmatically. However, there are surely
;;; other libraries that use markers and/or overlays that could make their way into
;;; the tree and new ways of dealing with those entries will be required. If you
;;; encounter an error message when performing `undo-tree-history-save', please
;;; inspect the `*Messages*` buffer for clues such as the above examples. Inasmuch
;;; as there is now a sanity check at the tail end of `undo-tree-history-save', any
;;; problems should materialize before a user actually tries to restore the history.
;;;
;;; The persistent undo storage has been expanded by adding certain features borrowed
;;; from the built-in `image-dired.el' library:
;;;
;;; `undo-tree-history-autosave': When non-nil, `undo-tree-mode' will save undo
;;; history to a file when a buffer is saved; and,
;;; the save/restore functions attached to the
;;; following hooks will become active:
;;; - `write-file-functions'
;;; - `find-file-hook'
;;; To exclude certain files, users may wish to let-
;;; bind this variable to `nil` if it has a global
;;; non-nil value. See also the next variable below.
;;;
;;; `undo-tree-history-file-exclusions': A list of absolute file names that will be
;;; excluded from the auto save/restore process.
;;;
;;; `undo-tree-history-alist': Used when `undo-tree-history-storage' is 'classic.
;;; See the doc-string for customization tips/tricks.
;;;
;;; `undo-tree-history-directory': Directory where history files are stored when
;;; `undo-tree-history-storage' is 'central.
;;;
;;; `undo-tree-history-storage': How to store undo-tree history files.
;;; 'classic: See `undo-tree-history-alist'.
;;; 'home (md5): A folder in the HOME directory.
;;; 'central (md5): See `undo-tree-history-directory'.
;;; 'local: Create sub-directory in working directory.
;;;
;;; For those users who wish to use Emacs to view the saved/exported history, be
;;; aware that the undo history is one long string, and Emacs has trouble viewing a
;;; buffer with very long lines. `(setq-default bidi-display-reordering nil)` will
;;; help permit Emacs to view buffers with very long lines without bogging down.
;;;
;;; The primary interactive functions for undo/redo in the working buffer are:
;;;
;;; M-x undo-tree-classic-undo
;;; M-x undo-tree-classic-redo
;;; M-x undo-tree-linear-undo
;;; M-x undo-tree-linear-redo
;;;
;;; The primary interactive functions for undo/redo in the visualization buffer are:
;;;
;;; M-x undo-tree-visualize-classic-undo
;;; M-x undo-tree-visualize-classic-redo
;;; M-x undo-tree-visualize-linear-undo
;;; M-x undo-tree-visualize-linear-redo
;;;
;;; If the built-in undo amalgamation business is not to your liking, it can be
;;; disabled to permit undo boundaries after every command:
;;;
;;; ;;; https://stackoverflow.com/a/41560712/2112489
;;; (advice-add 'undo-auto--last-boundary-amalgamating-number :override #'ignore)
;;;
;;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27214
;;; https://emacs.stackexchange.com/q/33248/2287
;;; GARBAGE COLLECTION: @lawlist has encountered a few situations where garbage
;;; collection truncates the `undo-tree-canary' in the `buffer-undo-list', which
;;; causes `undo-tree-transfer-list' to replace the existing `undo-tree-list'
;;; with the new tree fragment obtained from the `buffer-undo-list'. In this
;;; circumstance, the user loses the entire undo-tree saved history! The internal
;;; function responsible is `truncate_undo_list' in `undo.c`. @lawlist has added a
;;; programmatic warning when loss of the existing `undo-tree-list' is about to
;;; occur; however, that does not fix the problem. The relevant section from
;;; `truncate_undo_list' in `undo.c` is as follows:
;;; /* When we get to a boundary, decide whether to truncate
;;; either before or after it. The lower threshold, undo_limit,
;;; tells us to truncate after it. If its size pushes past
;;; the higher threshold undo_strong_limit, we truncate before it. */
;;; if (NILP (elt))
;;; {
;;; if (size_so_far > undo_strong_limit)
;;; break;
;;; last_boundary = prev;
;;; if (size_so_far > undo_limit)
;;; break;
;;; }
;;; @lawlist opines that setting the `undo-limit' to the same value as
;;; `undo-strong-limit' will cause `truncate_undo_list' to preserve the
;;; `undo-tree-canary' in the `buffer-undo-list' by truncating before the boundary.
;;; This workaround is not ideal because a more recent undo would be truncated in
;;; lieu of an older undo. One idea would be to convince the Emacs team to modify
;;; `truncate_undo_list' to preserve certain user-defined elements; e.g., a symbol
;;; of `undo-tree-canary'.
;;;
;;; The built-in function named `primitive-undo' defined in `simple.el` was used
;;; in the original version of `undo-tree.el`. @lawlist created a modified
;;; function named `undo-tree--primitive-undo' that serves the same purpose, but
;;; permits setting a window-point in the working buffer while a user is in a
;;; different window such as the visualization buffer. The revised version also
;;; merely reports a problem with a message instead of throwing an error when it
;;; encounters an `undo-tree-canary' in the wrong location. This bug was noticed
;;; by @lawlist when performing undo/redo in region, and a Google search revealed
;;; that others too have experienced the same problem. The bug is fairly easy to
;;; reproduce, but @lawlist has not yet invested the time to look for the cause
;;; and try to come up with a solution. For anyone who wishes to work on fixing
;;; this and view other mentions of the same problem on the internet, Google:
;;; "Unrecognized entry in undo list undo-tree-canary"
;;;
;;; The semi-linear visualization buffer view looks like this:
;;;
;;; o-00001-a-0
;;; 20.34.55.46
;;; |
;;; o-br/pt-A-0
;;; 20.47.57.25
;;; 20.34.55.47
;;; ____|_______________________________
;;; / \
;;; o-00001-b-0 o-00001-c-1
;;; 20.47.57.26 20.34.55.48
;;; | |
;;; o-00002-b-0 o-00002-c-1
;;; 20.47.57.27 20.34.55.49
;;; | |
;;; o-00003-b-0 o-00003-c-1
;;; 20.47.57.28 20.34.55.50
;;; |
;;; o-00004-c-1
;;; 20.34.55.51
;;; |
;;; o-br/pt-B-1
;;; 21.25.32.05
;;; 20.35.06.89
;;; 20.35.02.23
;;; 20.34.58.43
;;; 20.34.55.57
;;; _____________________________________|________________________
;;; / / | \
;;; o-00001-d-0 o-00001-e-1 o-br/pt-C-2 o-00001-f-3
;;; 21.25.32.06 20.35.06.90 23.03.45.34 20.34.58.44
;;; | 00.27.40.07 |
;;; o-00002-e-1 20.35.02.24 o-00002-f-3
;;; 20.35.06.91 ___________|___________ 20.34.58.45
;;; | / | \ |
;;; o-00003-e-1 o-00001-g-0 o-00001-h-1 o-00001-i-2 o-00003-f-3
;;; 20.35.06.92 23.03.45.35 00.27.40.08 20.35.02.25 20.34.58.46
;;; | | | | |
;;; o-00004-e-1 x-00002-g-0 o-00002-h-1 o-00002-i-2 o-00004-f-3
;;; 20.35.06.93 23:03:45:36 00.27.44.51 20.35.02.26 20.34.58.47
;;; | | |
;;; o-00005-e-1 o-00003-i-2 o-00005-f-3
;;; 20.35.06.94 20.35.02.27 20.34.58.48
;;; | | |
;;; o-00006-e-1 o-00004-i-2 o-00006-f-3
;;; 20.35.06.95 20.35.02.28 20.34.58.49
;;; | | |
;;; o-00007-e-1 o-00005-i-2 o-00007-f-3
;;; 20.35.06.96 20.35.02.29 20.34.58.50
;;; | | |
;;; o-00008-e-1 o-00006-i-2 o-00008-f-3
;;; 20.35.06.97 20.35.02.30 20.34.58.51
;;;
;;; To check for updates, please visit the source-code of the link listed at the
;;; top and also review the "Change Log" at the bottom.
;;;
;;; Bug reports and feature requests may be submitted via email to the address at
;;; the top. Essentially, if it breaks in half, I can guarantee that you will
;;; have 2 pieces that may not necessarily be the same size. :) That being said,
;;; I will certainly make efforts to fix any problem that may arise relating to
;;; the semi-linear undo/redo feature. A step 1-2-3 recipe starting from emacs -q
;;; would be very helpful so that @lawlist can observe the same behavior described
;;; in the bug report. Here is an example to get you started:
;;;
;;; 1. In an internet browser, visit: https://www.lawlist.com/lisp/undo-tree.el
;;;
;;; Select/highlight all and copy everything to the clipboard.
;;;
;;; 2. Launch Emacs without any user settings whatsoever: emacs -q
;;;
;;; If possible, please use the latest stable public release of Emacs.
;;; @lawlist is using the GUI version of Emacs 25.2.1 on OSX.
;;;
;;; 3. Switch to the `*scratch*` buffer.
;;;
;;; 4. Paste the entire contents of the clpipboard into the `*scratch*` buffer.
;;;
;;; 5. M-x eval-buffer RET
;;;
;;; 6. M-x eval-expression RET (setq undo-tree-history-autosave t) RET
;;;
;;; 7. M-x undo-tree-mode RET
;;;
;;; The mode-line indicates `UT`, meaning that `undo-tree-mode' is active.
;;;
;;; 8. M-x save-buffer RET
;;;
;;; @lawlist chose to save the file to his desktop with the name `foo`, and
;;; also chose to overwrite the file if it already existed; i.e., `y`.
;;;
;;; Look at the lower left-hand side of the mode-line and notice that it
;;; indicates an unmodified state; i.e., U:--- foo ....
;;;
;;; 9. M-x undo-tree-classic-undo RET
;;;
;;; Look at the lower left-hand side of the mode-line and notice that it
;;; indicates we have returned to a modified state; i.e., U:**- foo ....
;;;
;;; 10. M-x undo-tree-classic-undo RET
;;;
;;; The `undo-tree' library that we had previously pasted to the `*scratch*`
;;; buffer should now be completely undone; i.e., removed.
;;;
;;; 11. M-x undo-tree-classic-undo RET
;;;
;;; The buffer should be completely empty at this point; i.e., the initial
;;; `*scratch*` message has been removed.
;;;
;;; 12. M-x undo-tree-classic-undo RET
;;;
;;; The following `user-error' appears:
;;;
;;; "user-error: undo-tree--undo-or-redo: No further undo information."
;;;
;;; This is exactly the behavior that @lawlist expected would happen, so
;;; everything up to this point appears to be working correctly.
undo-tree
виглядає так, що це частина концепції, що ви можете вільно перемикатися міжbuffer-undo-tree
таbuffer-undo-list
перемиканнямundo-tree-mode
. Як я розумію ваше запитання, оригінальне поводження зbuffer-undo-list
вами робить те, що ви хочете. Тому він пропонує собі тимчасове вимкненняundo-tree-mode
для ваших цілей. Шкода, що перемикання міжbuffer-undo-tree
іbuffer-undo-list
здається баггі (принаймні для мене). Тож, можливо, дорогою було б просто повідомлення про помилку для технічного обслуговуванняundo-tree
. Але, можливо, моя здогадка про оригінальну концепцію помилкова.