Cara membuat undo-tree linear - undo-tree-undo / redo

10

Ketika menjalankan perintah secara undo-tree-undo/redoberurutan, semuanya dibatalkan / diperbaiki berdasarkan cabang aktif saat ini. Meskipun pengguna menerima pesan titik cabang di sepanjang jalan, cabang sebelumnya diabaikan kecuali pengguna secara manual memilih cabang yang berbeda.

Saya mengerti bahwa saya dapat membuka visualizer dan memilih cabang yang berbeda, namun, akan sangat berguna untuk hanya menahan tombol undo / redo dan melihat semuanya terjadi dalam urutan sebaliknya. Idealnya, ini harus bekerja terlepas dari apakah visualizerbuffer terbuka - yaitu, secara terprogram menghitung urutan dari awal hingga awal, dan dari awal hingga akhir.

T : Ini pada dasarnya adalah permintaan fitur untuk memperpanjang undo-treeuntuk memungkinkan undo / redo linear berturut-turut, terlepas dari apakah buffer visualizer terbuka. [Fungsi baru dan pintasan keyboard alternatif untuk fitur baru ini tentu dapat diterima.]

daftar hukum
sumber
Kode undo-treepenampilan seperti itu adalah bagian dari konsep yang dapat Anda beralih secara bebas antara buffer-undo-treedan buffer-undo-listdengan beralih undo-tree-mode. Saat saya memahami pertanyaan Anda, penanganan awal buffer-undo-listmelakukan apa yang Anda inginkan. Jadi itu menyarankan dirinya untuk sementara mematikan undo-tree-modeuntuk tujuan Anda. Kasihan beralih antara buffer-undo-treedan buffer-undo-listtampaknya menjadi buggy (setidaknya untuk saya). Jadi mungkin, cara untuk pergi hanyalah laporan bug kepada pengelola undo-tree. Tapi, mungkin dugaan saya tentang konsep aslinya salah.
Tobias
Saya pikir solusi terbaik untuk masalah Anda adalah memperbaiki undo-tree-modesehingga beralih di antara buffer-undo-treedan buffer-undo-listbekerja tanpa cacat. Sudahkah Anda mempertimbangkan untuk menerbitkan laporan bug di `toby-undo-tree @ dr-qubit.org`?
Tobias
@Tobias - Saya ingin melihat dan menggunakan visualizer undo-treesaat menggunakan linearfitur baru ; dan, saya ingin linearfitur baru berfungsi tanpa buffer visualizer yang terlihat. Secara umum, saya menerapkan perbaikan / modifikasi saya sendiri untuk Emacs, baik perpustakaan pihak ketiga bawaan maupun opsional. Ketika saya macet atau saya melihat sesuatu yang sangat rumit seperti undofitur itu, saya meminta bantuan. Permintaan fitur kepada pengelola tidak ada salahnya, tapi saya lebih suka menanganinya di sini.
lawlist
@ Toki - Dalam melihat undo-tree.elkode hari ini, saya melihat bahwa ada fitur cap waktu node. Saya tidak yakin apakah setiap node memiliki cap waktu yang valid dan apakah mereka bertahan untuk sesi Emacs berikutnya (ketika memulihkan sejarah), tapi itu terdengar seperti jalan pintas untuk menyelesaikan permintaan fitur baru ini - yaitu, menyortir dan memilih sebelumnya atau berikutnya dalam waktu. Saya belum melihat bagaimana tata letak tanah di titik cabang, tapi ... itu adalah pikiran awal saya. . . .
hukum
Saya pikir kunci untuk berjalan linear melalui undo-tree adalah undo-list-rebuild-from-tree. Seseorang harus let-mengikat variabel buffer-undo-listdan biarkan undo-list-rebuild-from-treemelakukan tugasnya. Setelah itu salin nilai itu ke variabel lokal lain, katakan my-undo-list, dan biarkan let-form untuk mengikat buffer-undo-list. Variabel my-undo-listmenentukan jalur linear melalui undo-tree. Melihat undo-list-rebuild-from-treeAnda melihat bahwa fungsi ini juga menggunakan perangko waktu. Saya sebelumnya mempertimbangkan menggunakan cap waktu. Tapi, saya mendapat kesan bahwa perubahan itu terlalu sering.
Tobias

Jawaban:

7

Berikut ini adalah implementasi prototipe dari perkiraan apa yang Anda inginkan. Ini mengeksploitasi fakta bahwa cabang-cabang baru di undo-tree ditambahkan di sisi kiri node saat ini.

Urutan kunci C-M-_terikat ke undo-tree-walkmana berjalan di bagian kanan atas dari pohon undo dimulai pada simpul saat ini.

Perilaku berbeda dari yang Anda inginkan jika cabang aktif dari beberapa sub-pohon di sisi kanan node saat ini bukan cabang paling kiri di sub-pohon itu.

Anda bisa mendapatkan dalam kondisi seperti itu dengan undo / redo-sequence non-trival.

Coba saja sendiri untuk melihat apakah itu cukup untuk aplikasi Anda.

(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)

Perhatikan, bahwa saya menambahkan undo-tree-visualizepada akhir undo-tree-walkuntuk menampilkan konsekuensi dari berjalan undo-tree with undo-tree-walk. Jangan ragu untuk memodifikasi kode sesuai keinginan Anda.

Perhatikan juga, bahwa saya harus memilih perkiraan solusi paling sederhana untuk masalah Anda karena batasan waktu.

Tobias
sumber
Saya menambahkan yang berikut sebagai permulaan undo-tree-walkuntuk melewati beberapa kesalahan awal, mungkin karena saya tidak memiliki mode ini aktif secara global. (when (eq buffer-undo-list t) (user-error "No undo information.")) (undo-list-transfer-to-tree) Itu membungkam kesalahan awal saya di buffer baru saat mencoba jawaban ini. Pengamatan saya berikutnya adalah yang undo-tree-walkmencapai titik cabang dan kemudian beralih ke cabang di sebelah kanan, tetapi hanya turun cabang satu takik / bintil sebelum naik kembali cabang ke batang. Setup pilihan saya menciptakan takik / nodul untuk setiap stroke key.
hukum
Sama sekali tidak terburu-buru pada solusi. Silakan mengambil semua waktu yang Anda butuhkan - di luar periode hadiah sangat dapat diterima. Ketika diimplementasikan, saya yakin bahwa saya akan menggunakan fitur ini setiap hari untuk masa mendatang dan menilai dari upvotes sejauh ini, orang lain juga ingin menggunakan fitur ini.
lawlist
@lawlist Kasihan, waktu saya terbatas. Jika mungkin saya akan menulis jawaban ini bukan sebagai komentar daripada jawaban. Semoga orang lain menghasilkan sesuatu yang lebih baik sebelum periode hadiah berakhir.
Tobias
@lawlist saya tambahkan undo-list-transfer-to-treesaat Anda melamar. Sebelum itu saya menguji apakah undo-tree-modeaktif atau tidak undo-list-transfer-to-treebisa berakibat fatal.
Tobias
Dianugerahi hadiah baru untuk jawaban di utas terkait: emacs.stackexchange.com/a/32415/2287 dan emacs.stackexchange.com/a/32416/2287 karena mereka adalah bahan utama untuk mengimplementasikan fitur baru. Singkatnya, setiap node akan memiliki daftar waktu-perangko - satu untuk setiap kali sebuah node menjadi lancar setelah undo / redo yang berhasil dan juga ketika pada awalnya mengimpor. Saya datang dengan ide untuk memvisualisasikan dengan menampilkan perangko waktu secara vertikal di bawah setiap node, dan memperpanjang cabang sesuai - perlu waktu untuk mencari tahu secara pemrograman.
hukum
3

Terima kasih khusus kepada @Tobias karena telah menulis fungsi untuk menemukan cap waktu berikutnya / sebelumnya dalam sejarah undo / redo: https://emacs.stackexchange.com/a/32415/2287 ; dan, untuk juga menulis serangkaian fungsi untuk menyalin undo-tree: https://emacs.stackexchange.com/a/32230/2287 .

Karena beberapa pembaca mungkin sudah sadar, garpu diterima oleh MELPA hanya dalam keadaan ekstrem. Membuat add-on mungkin bisa dilakukan, tetapi tampaknya tidak praktis mengingat jumlah perubahan yang telah dibuat oleh @lawlist - termasuk, tetapi tidak terbatas pada, menambahkan elemen ke vektor struktur data yang mendasarinya, dan mengubah nama beberapa fungsi / variabel yang tidak sesuai dengan undo-tree-...konvensi penamaan awalan, dll. @lawlist telah menjangkau penulis asli (Dr. Cubitt) untuk menawarkan fitur baru ini, serta berbagai perbaikan bug dan penyempurnaan.

Jika ada yang tertarik, silakan berikan fitur baru ini. Komentari berisi contoh formulir pengiriman laporan bug mulai dari emacs -qjika ada yang mengalami masalah.

Kode Sumber:   https://github.com/lawlist/undo_tree

Komentar:

;;; 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.
daftar hukum
sumber