math0ne
math0ne

Reputation: 762

Is there any way to have Emacs save your undo history between sessions?

Is there any way to have EMACS save your undo history between sessions?

I'm aware of the savehist lib, the saveplace lib, the desktop lib, and the windows lib, these all provide some session control but none seem to save the undo history.

Upvotes: 37

Views: 7473

Answers (7)

meain
meain

Reputation: 861

Add the following to your .emacs file :

(global-undo-tree-mode)
(setq undo-tree-auto-save-history t)
(setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))

Explanation

  • (global-undo-tree-mode) enables undo tree.

  • (setq undo-tree-auto-save-history t) enables auto save of undo history.

  • (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))) so that your project does not get littered with undo-history savefiles.


undo-fu-session is another great alternative as mentioned in another answer. This will also ensure that we avoid changing how undos work by introducing undo-tree.

Upvotes: 21

Arch Stanton
Arch Stanton

Reputation: 394

You can use Undo Fu Session

This package writes undo/redo information upon file save which is restored where possible when the file is loaded again.

After installing the package just add (undo-fu-session-global-mode) to you init file to enable it.

See https://codeberg.org/ideasman42/emacs-undo-fu-session for more information.

Upvotes: 1

Toby Cubitt
Toby Cubitt

Reputation: 453

From version 0.4 onwards, undo-tree supports persistent storage of undo-tree data between sessions "out of the box". (Note that there are significant bug-fixes related to this feature in more recent versions; the latest version at the time of writing is 0.6.3.)

Simply enable the undo-tree-auto-save-history customization option to automatically save and load undo history in undo-tree buffers. Or use the undo-tree-save/load-history commands to save and load undo history manually.

You need at least Emacs version 24.3 for this to work reliably, but with a recent enough Emacs it works very well.

Upvotes: 23

terje
terje

Reputation: 4424

I have managed to get the undo history working by using the information provided here: http://emacs.stackexchange.com/q/3725/2287

Instead of patching the original file desktop.el.gz I created an advice that temporarily overrides (buffer-local-variables) then I use it together with the function that gathers information about the buffer.

(defun +append-buffer-undo-list-to-buffer-local-variables-advice (orig-fn &rest args)
  "Override `buffer-local-variables' and call ORIG-FN with ARGS.
There is a bug in Emacs where the `buffer-undo-list' data is
missing from the output of `buffer-local-variables'. This
advice temporarily overrides the function and appends the
missing data."
  (let ((orig-buffer-local-variables-fn (symbol-function 'buffer-local-variables)))
    (cl-letf (((symbol-function 'buffer-local-variables)
               #'(lambda () (append (funcall orig-buffer-local-variables-fn)
                               `(,(cons 'buffer-undo-list buffer-undo-list))))))
      (apply orig-fn args))))

(advice-add #'desktop-buffer-info :around #'+append-buffer-undo-list-to-buffer-local-variables-advice)
(push 'buffer-undo-list desktop-locals-to-save)
(desktop-save-mode 1)

I hope this helps someone else.

Upvotes: 1

Alex Schröder
Alex Schröder

Reputation: 290

Emacs Session appears to support this:

(add-to-list 'session-locals-include 'buffer-undo-list)

Upvotes: 1

thisirs
thisirs

Reputation: 788

desktop-save-mode does not save buffer-undo-list by default. You just have to tell him!

(add-to-list 'desktop-locals-to-save 'buffer-undo-list)

Upvotes: 7

Trey Jackson
Trey Jackson

Reputation: 74480

Here's some code I wrote which seems to do the trick. It isn't bullet-proof, as in, it doesn't handle all the file handling intricacies that Emacs does (e.g. overriding where auto-save files are put, symlink handling, etc.). But, it seemed to do the trick for some simple text files I manipulated.

(defun save-undo-filename (orig-name)
  "given a filename return the file name in which to save the undo list"
  (concat (file-name-directory orig-name)
          "."
          (file-name-nondirectory orig-name)
          ".undo"))

(defun save-undo-list ()
  "Save the undo list to a file"
  (save-excursion
    (ignore-errors
      (let ((undo-to-save `(setq buffer-undo-list ',buffer-undo-list))
            (undo-file-name (save-undo-filename (buffer-file-name))))
        (find-file undo-file-name)
        (erase-buffer)
        (let (print-level
              print-length)
          (print undo-to-save (current-buffer)))
        (let ((write-file-hooks (remove 'save-undo-list write-file-hooks)))
          (save-buffer))
        (kill-buffer))))
  nil)

(defvar handling-undo-saving nil)

(defun load-undo-list ()
  "load the undo list if appropriate"
  (ignore-errors
    (when (and
           (not handling-undo-saving)
           (null buffer-undo-list)
           (file-exists-p (save-undo-filename (buffer-file-name))))
      (let* ((handling-undo-saving t)
             (undo-buffer-to-eval (find-file-noselect (save-undo-filename (buffer-file-name)))))
        (eval (read undo-buffer-to-eval))))))

(add-hook 'write-file-hooks 'save-undo-list)
(add-hook 'find-file-hook 'load-undo-list)

Upvotes: 12

Related Questions