Desktop

Saving session information in Emacs is made possible with GIT:desktop.el. More information is available from Saving Emacs Sessions.

There are many other packages providing similar functionality. See SessionManagement for alternatives.

Loading a Desktop Saved from a Previous Session at Startup

Desktop will load, at startup, the buffers you were editing when you last quit Emacs. It will also attempt to restore frames and frame sizes, unless you’ve set ‘desktop-restore-frames’ to nil.

In Emacs 22+, add the following to your .emacs file:

    (desktop-save-mode 1)

You can add any extra variables you want saved across sessions to the list ‘desktop-globals-to-save’. For example:

    (setq history-length 250)
    (add-to-list 'desktop-globals-to-save 'file-name-history)

In Emacs 20 and 21, use code like the following, instead:

    (desktop-load-default)
    ;; Customization goes between desktop-load-default and desktop-read
    (setq history-length 250)
    (add-to-list 'desktop-globals-to-save 'file-name-history)
    (desktop-read)
    (setq desktop-enable t)

Specifying Files Not to be Opened

You can specify buffers which should not be saved, by name or by mode, e.g.:

   (setq desktop-buffers-not-to-save
        (concat "\\("
                "^nn\\.a[0-9]+\\|\\.log\\|(ftp)\\|^tags\\|^TAGS"
                "\\|\\.emacs.*\\|\\.diary\\|\\.newsrc-dribble\\|\\.bbdb"
	        "\\)$"))
   (add-to-list 'desktop-modes-not-to-save 'dired-mode)
   (add-to-list 'desktop-modes-not-to-save 'Info-mode)
   (add-to-list 'desktop-modes-not-to-save 'info-lookup-mode)
   (add-to-list 'desktop-modes-not-to-save 'fundamental-mode)

Since all lists will be truncated when saved, it is important to have a high default history length, for example. If that is not enough, follow the suggestions in the doc-string of ‘desktop-globals-to-save’:

An element may be variable name (a symbol) or a cons cell of the form (VAR . MAX-SIZE), which means to truncate VAR’s value to at most MAX-SIZE elements (if the value is a list) before saving the value.

Auto-Saving the Desktop

I’m starting to work on a new package called desktop-recover.el with some improvements like this. Alternatively, you can just add something like this to your init file to auto-save your desktop when Emacs is idle: – Doom

  (require 'desktop)
  (desktop-save-mode 1)
  (defun my-desktop-save ()
    (interactive)
    ;; Don't call desktop-save-in-desktop-dir, as it prints a message.
    (if (eq (desktop-owner) (emacs-pid))
        (desktop-save desktop-dirname)))
  (add-hook 'auto-save-hook 'my-desktop-save)

The above doesn’t seem to work when emacs is run in daemon mode (--daemon)

Automatically Overriding Stale Locks

If you are using desktop-mode and your emacs crashes (or more likely, your system crashes), then your desktop file will not be released, and emacs will bother you about using it next time you start it up. To avoid this, I wrote the following code to override the lock on a desktop file if the indicated process is not still running.

;;; desktop-override-stale-locks.el begins here
(defun emacs-process-p (pid)
  "If pid is the process ID of an emacs process, return t, else nil.
Also returns nil if pid is nil."
  (when pid
    (let* ((cmdline-file (concat "/proc/" (int-to-string pid) "/cmdline")))
      (when (file-exists-p cmdline-file)
        (with-temp-buffer
          (insert-file-contents-literally cmdline-file)
          (goto-char (point-min))
          (search-forward "emacs" nil t)
          pid)))))

(defadvice desktop-owner (after pry-from-cold-dead-hands activate)
  "Don't allow dead emacsen to own the desktop file."
  (when (not (emacs-process-p ad-return-value))
    (setq ad-return-value nil)))
;;; desktop-override-stale-locks.el ends here

If anyone has a more robust implementation of `emacs-process-p,’ feel free to provide it.

I implemented ‘emacs-process-p’ by following way, it could work on both Windows and Linux.

(defun emacs-process-p (pid)
  "If pid is the process ID of an emacs process, return t, else nil.
Also returns nil if pid is nil."
  (when pid
    (let ((attributes (process-attributes pid)) (cmd))
      (dolist (attr attributes)
        (if (string= "comm" (car attr))
            (setq cmd (cdr attr))))
      (if (and cmd (or (string= "emacs" cmd) (string= "emacs.exe" cmd))) t))))

I think the original function contains an error. Should it not end something like:

          (when (search-forward "emacs" nil t)
            pid))))))

Since none of the above are really fully portable (I use a combination of MacOS, Linux and Windows), I choose to combine them into the following implementation which seem to work across all systems even if they don’t support ‘process-attributes’:

  (defun sylvain/desktop-owner-advice (original &rest args)
    (let ((owner (apply original args)))
      (if (and owner (/= owner (emacs-pid)))
          (and (car (member owner (list-system-processes)))
               (let (cmd (attrlist (process-attributes owner)))
                 (if (not attrlist) owner
                   (dolist (attr attrlist)
                     (and (string= "comm" (car attr))
                          (setq cmd (car attr))))
                   (and cmd (string-match-p "[Ee]macs" cmd) owner))))
        owner)))
  ;; Ensure that dead system processes don't own it.
  (advice-add #'desktop-owner :around #'sylvain/desktop-owner-advice)

Minimal Setup

This is for people who only want minimal session management functionality, and don’t want their previous sessions automatically restored at start-up. Note that you need desktop-save-mode NOT ENABLED for this to work as intended.

It works for me with one desktop, with more than one may need some tweaking.

;; use only one desktop
(setq desktop-path '("~/.emacs.d/"))
(setq desktop-dirname "~/.emacs.d/")
(setq desktop-base-file-name "emacs-desktop")

;; remove desktop after it's been read
(add-hook 'desktop-after-read-hook
	  '(lambda ()
	     ;; desktop-remove clears desktop-dirname
	     (setq desktop-dirname-tmp desktop-dirname)
	     (desktop-remove)
	     (setq desktop-dirname desktop-dirname-tmp)))

(defun saved-session ()
  (file-exists-p (concat desktop-dirname "/" desktop-base-file-name)))

;; use session-restore to restore the desktop manually
(defun session-restore ()
  "Restore a saved emacs session."
  (interactive)
  (if (saved-session)
      (desktop-read)
    (message "No desktop found.")))

;; use session-save to save the desktop manually
(defun session-save ()
  "Save an emacs session."
  (interactive)
  (if (saved-session)
      (if (y-or-n-p "Overwrite existing desktop? ")
	  (desktop-save-in-desktop-dir)
	(message "Session not saved."))
  (desktop-save-in-desktop-dir)))

;; ask user whether to restore desktop at start-up
(add-hook 'after-init-hook
	  '(lambda ()
	     (if (saved-session)
		 (if (y-or-n-p "Restore desktop? ")
		     (session-restore)))))

Then type ‘M-x session-save’, or ‘M-x session-restore’ whenever you want to save or restore a desktop. Restored desktops are deleted from disk.

Desktops as Bookmarks

The usual way to use a desktop is to (1) define (i.e., save) it as your final Emacs state, when you exit, and (2) restore it at the beginning of a new Emacs session. Library desktop.el provides for the possibility of having multiple saved desktops, but only one per directory. It searches your ‘desktop-path’ to find a desktop file to load.

If you use Bookmark+, then you can have any number of desktops and use them as bookmarks, jumping from one to another at any time. You create a desktop bookmark using ‘C-x r K’ (command ‘bmkp-set-desktop-bookmark’).

Ordinary desktop files are used to record the information. The bookmark itself records only the location of the desktop file. Bookmarked desktops are intended to be used only as bookmarks, not in the ordinary way (e.g. at Emacs startup), so the locations of the desktop files used are unimportant. In particular, you can have any number of (bookmark) desktop files per directory.

Troubleshooting

Restoring a large session is painfully slow

Something to keep in mind is that all enabled modes for a buffer are saved in the desktop file, and re-enabled when restoring, and global modes in particular have the potential to cause performance issues.

global-whitespace-mode is a current example – when you enable it, it processes every buffer. As the mode is active for all buffers, it will be listed against every buffer in the desktop file, and hence the processing done by this mode when restoring n buffers is an o(n^2) operation.

(The desktop-minor-mode-table variable looks like it provides a workaround for this problem, by enabling you to specify a ‘nil’ custom handler for modes which should not be enabled when restoring the desktop.)

Open Desktop Questions

I’m having trouble with desktop loading lazy loading, as my final line in .emacs is

   (plan)

So it loads planner, but then the planner buffer gets lost when desktop starts restoring the other buffers. I tried to set desktop-restore-eager to ‘t, without any success. Any idea?

Yup, I’ve been able to solve this problem just using

  (add-hook 'desktop-after-read-hook 'plan)

When I try to add an array of strings to desktop-globals-to-save it saves it with (setq name-of-array “Unprintable entity”). Has anyone an idea of how to save an array in desktop?

I could set up desktop-files-not-to-save var for any buffer opening remote file not to be saved into .emacs.desktop file while invoking (desktop-save) with its default value ("^/[^/:]*:").

In the same time, I also want save dired-mode buffers except buffers that show remote directories. I found that desktop package only check its (buffer-file-name) and (buffer-name) whether he save a ceratain buffer or not, and unfortunately (buffer-file-name) has no meaning in dired-mode buffer(it just returns nil) so finally… dired-mode buffer displaying remote directory is saved in .emacs.desktop file. How can I exclude those kind of dired-mode buffers? --JoonhwanLee


Great entry! Thanks. I had been using recentf, but desktop is more up my alley – TerrenceBrannon


On Emacs 21.4 (from Debian Etch) the desktop does not work as expected. The only way I could make it work was with

  (load-library "/usr/share/emacs/21.4/lisp/desktop.elc")
  (setq desktop-enable t)

in my .emacs. – Randolf Schultz


A simple example of moving the desktop file save locations ~/.emacs.desktop(.lock) to ~/.emacs.d/desktop/emacs.desktop(.lock) would be very helpful.

Presuming you want to MOVE (ie you’ve already used desktop) then the steps I did for this are

  1. Save to a new desktop and lock file. Use ‘M-x desktop-save’ and emacs asks you for a ‘Directory to save desktop file’, so enter your new location
  2. Set desktop-path so that next time you start emacs, it will find the desktop in the new location. Either put ‘setq desktop-path ’(”~/.emacs.d/desktop”))’ in your .emacs, or set it by ‘M-h v desktop-path’ and then choose the Customise option, set the value and “Save for future session”
  3. Check the desktop file is in the new location 😊
  4. Remove the old desktop file. Desktop will search along the desktop-path var at startup and use the first desktop file it finds, so I exited emacs, renamed the desktop file, then restarted

And that was me all done – TimMeadowcroft


Look, let’s just be honest. It doesn’t work, does it?

/usr/bin/emacs --no-desktop eventually results in

Debugger entered--Lisp error: (error "Desktop file conflict")
  signal(error ("Desktop file conflict"))
  error("Desktop file conflict")
  desktop-save("/home/jg/.desktop/")
  desktop-save-in-desktop-dir()
  (lambda nil (desktop-save-in-desktop-dir))()
  run-hooks(auto-save-hook)

and the request

Current desktop was not loaded from a file.  Overwrite this desktop file? (y or n)

which is is impossible to say ‘n’ to! (Pressing ‘n’ simply repeats the message.)

Let’s just dump this in a sack in the local canal.



Not sure where to mention this but I recommend adding the &optional release argument to the desktop-save-in-desktop-dir. This can then be passed through to save-desktop if desired. I have manually added this into my distribution and causes no issues anywhere else. I think it should be added as below.

    (defun desktop-save-in-desktop-dir (&optional release)
      "Save the desktop in directory `desktop-dirname'."
      (interactive)
      (if desktop-dirname
          (desktop-save desktop-dirname release)
        (call-interactively 'desktop-save))
      (message "Desktop saved in %s" (abbreviate-file-name desktop-dirname)))

Is there any way not to save the list of activated minor modes? I just want to revisit the list of files. – Kiwon

Here’s what I do to solve that problem:

    (loop for mode in minor-mode-list do (add-to-list 'desktop-minor-mode-table (list mode nil)))

Be sure to require all of the minor modes you use, and put this at the bottom!

Here is what I came up with, seems to work

    (defun clear-minor-modes ()
      (setq minor-mode-alist nil))

along with customizing desktop-save-hook to call it.

You could use the emacs facilities to customize the variable, or maybe in your .emacs: (setq desktop-save-hook '(clear-minor-modes))

Your minor-mode-alist will be back next time you use emacs, no worries (I think)


When saving a desktop that contains ~/.emacs and, let’s say, the file main.cpp, desktop restores both, but although its text is displayed correctly, ~/.emacs is loaded in c++-mode as main.cpp<2>. This must be a bug. Does someone know about this? – marcm

I’m not sure I understand your description, but I don’t need to understand it. This is not the place to report desktop.el bugs (or enhancement requests). The way to do that is to use ‘M-x report-emacs-bug’. The Emacs developers will take a look at your report and let you know whether it is a bug, etc. If they need more info from you they will ask for it. – DrewAdams


I added an entry for DesktopMultipleSaveFiles. It’s not the most elegant approach (uses an environment variable to set desktop-path) but it’s simple and works sufficiently for my purposes.


CategoryDotEmacs CategoryPersistence