如何使撤消树线性化-撤消树撤消/重做


10

undo-tree-undo/redo连续运行命令时,将根据当前活动分支撤消/重做操作。尽管用户一直沿途收到分支点消息,但是除非用户手动选择其他分支,否则将忽略先前的分支。

我知道我可以打开可视化器并选择一个不同的分支,但是,只需按住undo / redo键并观察完全相反的顺序,这将非常有用。理想情况下,无论visualizer缓冲区是否打开,它都应该起作用-即,以编程方式计算从头到尾以及从头到尾的顺序。

:这本质上是一项功能请求,可以扩展undo-tree以允许线性连续撤消/重做,而不考虑可视化器缓冲区是否打开。[肯定可以接受此新功能的新功能和替代键盘快捷键。]


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。但是,也许我对原始概念的猜测是错误的。
Tobias

我认为,解决您的问题的最佳解决方案是进行修理undo-tree-mode,以确保在buffer-undo-tree和之间buffer-undo-list都能正常运行。您是否考虑过在toby-undo-tree@dr-qubit.org上发布错误报告?
Tobias

@Tobias-我想undo-tree在使用新linear功能时看到并使用的可视化工具;并且,我希望新linear功能可以在可视化程序缓冲区不一定可见的情况下运行。总的来说,我对Emacs进行了自己的改进/修改,包括其内置库和可选的第三方库。当我被卡住或看到undo功能非常复杂的东西时,我会寻求帮助。向维护者提出的功能请求不会受到影响,但我希望在这里进行处理。
法律列表

@Tobias-在undo-tree.el今天看代码时,我看到有一个节点时间戳功能。我不确定每个节点是否都具有有效的时间戳,以及这些时间戳是否可以保留到下一个Emacs会话(还原历史记录时),但这听起来可能是解决此新功能请求的捷径-即排序和选择上一个或下一个时间。我尚未看到分支点的土地布局,但是...这是我的初步想法。。。。
法律列表

我认为线性浏览撤消树的关键是undo-list-rebuild-from-tree。一个人应该- let绑定变量buffer-undo-list,让它undo-list-rebuild-from-tree工作。然后,将该值复制到另一个局部变量,例如my-undo-list,并保留let-form进行绑定buffer-undo-list。变量my-undo-list决定通过的线性路径undo-tree。看一看undo-list-rebuild-from-tree,该函数也使用时间戳。我之前曾考虑过使用时间戳。但是,我给人的印象是,这些变化太频繁了。
Tobias

Answers:


7

以下是您想要的近似的原型实现。它利用了以下事实:在撤消树中的新分支被添加到当前节点的左侧。

键序列C-M-_绑定到undo-tree-walk从当前节点开始的撤销树的右上部分。

如果当前节点右侧某个子树的活动分支不是该子树中最左侧的分支,则该行为与您想要的行为不同。

您可以通过非常规的撤消/重做序列进入这种状态。

自己尝试一下,看看是否足够适合您的应用程序。

(defvar-local undo-tree-walk
  "Possible values:
nil: not in undo/redo-chain
undo: traversing undo tree upwards
redo: traversing undo tree downwards
")

(setq undo-tree-walk nil)

(defun undo-tree-walk ()
  "Walk the right-upper part of the undo-tree starting at the current node."
  (interactive)
  (when (eq buffer-undo-list t)
    (user-error "No undo information."))
  (unless undo-tree-mode
    (user-error "`undo-tree-walk' should only be used with `undo-tree-mode'."))
  (undo-list-transfer-to-tree)
  (set-transient-map undo-tree-walk-map t #'undo-tree-walk-off)
  (let ((num-branches (undo-tree-num-branches)))
    (cond
     ((= num-branches 0) ; arrived at leaf
      (setq undo-tree-walk 'undo))
     ((> num-branches 1) ; 
      (let ((branch (undo-tree-node-branch (undo-tree-current buffer-undo-tree))))
    (setf (undo-tree-node-branch (undo-tree-current buffer-undo-tree))
          (if (>= (1+ branch) (undo-tree-num-branches))
          (progn
            (setq undo-tree-walk 'undo)
            (1- (undo-tree-num-branches)))
        (setq undo-tree-walk 'redo)
        (1+ branch))))
      ;; no state change for (= num-branches 1)
      )
     ))
  (case undo-tree-walk
   (undo
    (undo-tree-undo-1))
   (redo
    (undo-tree-redo-1))
   (t
    (setq undo-tree-walk 'undo)
    (undo-tree-undo-1)))
  (let ((curbuf (current-buffer)))
    (undo-tree-visualize)
    (switch-to-buffer-other-window curbuf)))

(defun undo-tree-walk-off ()
  "Switch `undo-tree-walk' off."
  (setq undo-tree-walk nil))

(defvar undo-tree-walk-map
  (make-sparse-keymap)
  "Keymap active while `undo-tree-walk'.")

(define-key undo-tree-walk-map (kbd "C-M-_") #'undo-tree-walk)

(global-set-key (kbd "C-M-_") #'undo-tree-walk)

请注意,我undo-tree-visualize在的末尾添加了undo-tree-walk,显示了使用遍历undo树的后果undo-tree-walk。可以根据自己的喜好修改代码。

还要注意,由于时间限制,我不得不选择最简单的解决方案来解决您的问题。


我一开始就添加了以下内容,undo-tree-walk以克服一些初始错误,可能是因为我没有全局激活该模式。 (when (eq buffer-undo-list t) (user-error "No undo information.")) (undo-list-transfer-to-tree) 在尝试此答案时,这使我在新缓冲区中出现的最初错误消失了。我的下一个观察结果是undo-tree-walk到达分支点,然后切换到右侧的分支,但是在将分支返回到主干之前,仅在分支上下降了一个凹口/小结。我的首选设置为每个按键都创建了一个凹口/结节。
法律列表

绝对不急于解决方案。请随意花费您所有的时间-超出赏金期限是完全可以接受的。实施后,我确信在可预见的将来,我每天都会使用此功能,从到目前为止的投票结果来看,其他人也希望使用此功能。
法律列表

@lawlist可惜,我的时间受到限制。如果可能的话,我会写这个答案而不是评论而不是答案。希望其他人在赏金期结束之前提出更好的建议。
Tobias'4

undo-list-transfer-to-tree根据您的建议添加了@lawlist 。在此之前,我测试是否undo-tree-mode活跃,否则undo-list-transfer-to-tree可能致命。
Tobias'4

由于相关主题的答案,最近获得了赏金奖励: emacs.stackexchange.com/a/32415/2287emacs.stackexchange.com/a/32416/2287,因为它们是实现新功能的关键要素。简而言之,每个节点都有一个时间戳列表-每次成功撤消/重做后以及首次导入时,每个节点变为当前时间戳。我想出了一种可视化的想法,即在每个节点下垂直显示时间戳,并相应地延长分支的长度,这需要花费一些时间来进行编程计算。
法律列表

3

特别感谢@Tobias编写了在撤消/重做历史中查找下一个/上一个时间戳的函数:https ://emacs.stackexchange.com/a/32415/2287 ; 并且还编写了一系列复制撤消树的函数:https : //emacs.stackexchange.com/a/32230/2287

某些读者可能已经知道,只有在极端情况下,MELPA才会接受叉子。创建附加组件可能是可行的,但考虑到@lawlist所做的大量更改,这似乎不切实际-包括但不限于在基础数据结构向量中添加元素,以及更改多个名称不符合undo-tree-...前缀命名约定的函数/变量,等等。@lawlist已联系原始作者(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.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.