Monday, December 21, 2009

Emacs: Named Desktop Sessions

Emacs:  Named Desktop Sessions

You can use the desktop package to save the state of Emacs sessions, i.e. which files are open, where point is in them and so on.  This is cool, but it saves the information in the current directory, which can be inconvenient.  What would be better is if you could save the session by name, like "task-a" or "project-b".

I originally wrote some elisp to do this in answer to a StackOverflow question, but found it so useful myself that I expanded it to do more.  The code is below, but here are the commands:
  • my-desktop-save -- Save the current session by name
  • my-desktop-save-and-clear -- Same as above, but clear out all the buffers so you start with a "clean" session
  • my-desktop-read -- Load a session by name
  • my-desktop-change -- Save the current session and load a different one
  • my-desktop-name -- Echo the current session name
One more thing this code does: when you exit Emacs it automatically saves the current state as "last-session".  Sometimes I fat finger "C-x C-c" and exit accidentally, or I need to restart Emacs or my window manager and this lets me get back to what I was doing quickly.

The Code

(require 'desktop)

(defvar my-desktop-session-dir
  (concat (getenv "HOME") "/.emacs.d/desktop-sessions/")
  "*Directory to save desktop sessions in")

(defvar my-desktop-session-name-hist nil
  "Desktop session name history")

(defun my-desktop-save (&optional name)
  "Save desktop by name."
  (interactive)
  (unless name
    (setq name (my-desktop-get-session-name "Save session" t)))
  (when name
    (make-directory (concat my-desktop-session-dir name) t)
    (desktop-save (concat my-desktop-session-dir name) t)))

(defun my-desktop-save-and-clear ()
  "Save and clear desktop."
  (interactive)
  (call-interactively 'my-desktop-save)
  (desktop-clear)
  (setq desktop-dirname nil))

(defun my-desktop-read (&optional name)
  "Read desktop by name."
  (interactive)
  (unless name
    (setq name (my-desktop-get-session-name "Load session")))
  (when name
    (desktop-clear)
    (desktop-read (concat my-desktop-session-dir name))))

(defun my-desktop-change (&optional name)
  "Change desktops by name."
  (interactive)
  (let ((name (my-desktop-get-current-name)))
    (when name
      (my-desktop-save name))
    (call-interactively 'my-desktop-read)))

(defun my-desktop-name ()
  "Return the current desktop name."
  (interactive)
  (let ((name (my-desktop-get-current-name)))
    (if name
        (message (concat "Desktop name: " name))
      (message "No named desktop loaded"))))

(defun my-desktop-get-current-name ()
  "Get the current desktop name."
  (when desktop-dirname
    (let ((dirname (substring desktop-dirname 0 -1)))
      (when (string= (file-name-directory dirname) my-desktop-session-dir)
        (file-name-nondirectory dirname)))))

(defun my-desktop-get-session-name (prompt &optional use-default)
  "Get a session name."
  (let* ((default (and use-default (my-desktop-get-current-name)))
         (full-prompt (concat prompt (if default
                                         (concat " (default " default "): ")
                                       ": "))))
    (completing-read full-prompt (and (file-exists-p my-desktop-session-dir)
                                      (directory-files my-desktop-session-dir))
                     nil nil nil my-desktop-session-name-hist default)))

(defun my-desktop-kill-emacs-hook ()
  "Save desktop before killing emacs."
  (when (file-exists-p (concat my-desktop-session-dir "last-session"))
    (setq desktop-file-modtime
          (nth 5 (file-attributes (desktop-full-file-name (concat my-desktop-session-dir "last-session"))))))
  (my-desktop-save "last-session"))

(add-hook 'kill-emacs-hook 'my-desktop-kill-emacs-hook)

17 comments:

Anonymous said...

I tried to use this but get errors like the one below whenever a session is saved:

progn: Wrong number of arguments: #[(dirname) ....

Running GNU Emacs 22.0.92.1 on Windows.

Scott Frazer said...

I just tried it on my Windows box and it works okay. I'm running GNU Emacs 23.1.1 though. Could you try "M-x toggle-debug-on-error", do a save and send me the traceback?

Anonymous said...

Pasting the traceback here is not well-behaved. How can I send it to you?

Scott Frazer said...

You can send it to "frazer dot scott at gmail dot com". Although I have a feeling the answer is going to be "upgrade" -- 22.0.92.1 is a beta of the previous release of Emacs. It was buggy.

Anonymous said...

I didn't realize I was so far out of date! I upgraded to 22.3 and the problem went away. Thanks!

vinhdizzo said...

is there anyway to combine a desktop/session package like this with a screen-like package (elscreen)?

I use elscreen everyday. I use desktop at a minimal because they don't play well too nicely together.

any suggestions?

John Doe said...

This looks like a very usefull thing to have! Did you contact the desktop.el maintainer? Would be nice to see this integrated and consequently available to a broader public.

vinhdizzo said...

@John Doe No I've never contacted the maintainer. Maybe you can?

sn said...

Do you know about desktop-menu.el?
http://www.emacswiki.org/emacs/DesktopMenu

Scott Frazer said...

@sn, no I hadn't see that. Looks nice, and has some features my solution doesn't. I'll have to try it out.

In my "defense", that page didn't show up until a couple months ago, and I answered the StackOverflow question well before that. I try not to implement things that have already been done :)

sn said...

Yeah, the wiki page is new, but the package has been there for a long time, just in a somewhat bit-rotten state.

I tried to revamp it a little bit and added the updated version to the wiki and the ELL (after talking to Olaf Sylvester, the original author), as I think it's quite useful -- any suggestions/improvements very welcome! ;-)

Michael Pedersen said...

I figured I would let you know that this update is actually a part of a whole project switcher I've made. I've made my own blog post about it over at <a href="http://codersbuffet.blogspot.com/2010/09/announcing-gogo-bash-project-switcher.html>my blog</a>. The desktops.el was absolutely invaluable to me.

Thank you!

Vicky said...

Works like a charm. I find it much better than the built-in desktop.el functionality. Thank you!

Carlos said...

Hi,

Thanks for this great mode. I only have two suggestions:

1. Ad

(provide 'my-desktop)

at the end (being no emacs expert, it took me half an hour to figure it out)

2. In my-desktop-kill-emacs-hook add as a first line:

(my-desktop-save (my-desktop-get-current-name))

so that your current desktop gets automatically saved on exit.

It would also be fantastic if you could upload my-desktop to emacswiki so that one could install it with auto-install

Cheers,
Carlos

Alex Biddle said...

Hi, thanks for your code. It is interesting.

There's a similar way using named desktop sessions, but using bookmarks+.

Ctrl x p K saves the active desktop.

Ctrl x p e opens up a menu with bookmarks linked to available desktops.

Anonymous said...

how do you delete the current session?

DevAnand Reddy said...

Thanks! This works like a charm. I am running Mavericks, Emacs buid from HEAD, currently at 24.4.50.1.