Simple Vim session management: Part 1

By: Jay Sitter
A typical Vim workspace

I’ve known about sessions in Vim for years, but until recently had never had enough need for them to overcome the relative difficulty of getting a proper workflow set up. These days I am rapidly switching among many feature branches, where I need a different set of files open in the same project depending on what feature or bugfix I’m working on.

Now that I’ve dived in to sessions a little bit recently, I’d like to share the details of my setup over the course of a couple posts. It isn’t perfectly streamlined, but it does a lot of what I need with some (optional) small plugins and a minimum of damage to my ~/.vimrc. Hopefully if you’ve been keeping sessions at arm’s length up till now, there’ll be something in these posts that you’ll find useful.

First, though, an introduction. Vim sessions work by saving info about your open buffers, windows, and other stuff to a Vim script file on your disk in a location you specify. Later, you can load these things from that same file to restore your session just as you left it. From :help session:

A Session keeps the Views for all windows, plus the global settings. You can save a Session and when you restore it later the window layout looks the same. You can use a Session to quickly switch between different projects, automatically loading the files you were last working on in that project.

It looks something like this: Say you are working on a project and you have all your buffers and windows and tabs and folds and marks set up just how you want them, but you need to quit Vim for some reason. You can save this setup with the following command:

:mksession ~/sessionname

~/sessionname can be any path and filename you like. Vim will save your environment into this file to be asked for later. You restore the session by sourcing it with this command:

:source ~/sessionname

I’ve always found this to be too burdensome to use for a couple reasons:

  1. Saving sessions. I’m terrible at deciding where to store files and what to name them, so even the first step of creating the session file has enough friction to send me running from sessions. I could store the file somewhere in my home directory, which feels too removed from my project and could litter that directory, or I could save somewhere inside my current working directory — but I rarely know exactly what that is at any given moment, and I don’t want to inadvertently save my session files deep into a project’s hierarchy.
  2. Loading sessions: As if it isn’t hard enough to know where to save session files, if they’re to be useful at all, I’ve got to remember later where I saved them. Knowing myself, this feels like it would result in sessions saved all over my disk, never to be opened again.
  3. Session updating: Sessions don’t “auto-update” when you make changes to your environment, so if I open a new tab, for instance, and I want that to be part of the session I saved earlier, I have to remember to do another :mksession, remembering the location and name of the file in the process.

In the coming blog posts, I’m going to tackle each of these obstacles, hopefully leaving you with a path to session management that makes sense for you, from a light touch with a few remaps (for you Vim purists) to a more complete solution with plugins.

Saving session files

To begin, we’re going to do the easy thing and commit to saving all our session files in a single subdirectory of our home directory. Let’s say ~/vim-sessions. Simple. We’ll think about some more advanced solutions later.

Within that directory, we can plan ahead to help us sort through the files that will eventually populate it by prefixing each filename with some kind of broad project/area/responsibility “scope”; for instance, all my sessions for DockYard projects might start with dy-: dy-project-name.vim.

Note that I’m also giving the file the extension .vim, since technically these are Vim script files, and this could make them easier to identify in the future. Don’t worry about making the filenames too long, because we’re going to prevent you from having to type the whole thing, and don’t worry too much about having to remember the filename, because we’re going to be showing a list of existing files when we need to.

Given all this, saving a session will look something like this:

:mksession ~/vim-sessions/dy-project-name.vim

Lengthy, I know, but don’t worry — very soon we’re going to set things up so we don’t have to type stuff like that manually. (:mksession can be abbreviated to :mks, too.)

Restoring session files

Again, restoring a session consists of sourcing the script file we saved. To restore the session above, we’d use this command:

:source ~/vim-sessions/dy-project-name.vim

(:source can be abbreviated to :so.)

With tab completion and Vim’s “wildmenu,” we can fill in this filename pretty quickly; let’s say we have several DockYard session files in our ~/vim-sessions directory. All we have to do is type:

:so ~/vim-sessions/dy-<TAB>

(where <TAB> is the literal tab key), and we’re presented with something like this:

Nice! Now we can just continue hitting <TAB> till we’re at the file we want and hit <ENTER> to load it.

Note: In order for this to work, wildmenu will need to be set up with options similar to the following:

set wildmenu
set wildmode=full

Keep in mind that loading a session doesn’t delete any of the buffers you may already have open; it only adds to your buffer list. Before loading a session, if you want to start “fresh,” you might want to do something like :bufdo bd to delete all your open buffers, or just restart Vim.

Technically, that’s all we need to get started with a rudimentary workflow for Vim sessions, but it’s still a lot of typing. Let’s make things easier for ourselves with a few remaps in our ~/.vimrc file.

Remaps for faster session management

First, let’s set a global variable specifying our preferred sessions directory (this format assumes you’re on Mac/*nix):

let g:sessions_dir = '~/vim-sessions'

It may come in handy later to modify this when needed. We’ll need to leave off the final forward slash here, as we’ll be adding it ourselves later. (It will become clear why.)

Next, let’s set up a remap to start saving a session file. I’m going to map this to <Leader>ss, “ss” for “session save”:

exec 'nnoremap <Leader>ss :mks! ' . g:session_dir . '/'

Now, when we hit <Leader>ss (by default, \ss), we’ll be presented with the :mks command with the string for our preferred directory already typed in for us. The exclamation point after mks means that we want to overwrite files that already exist, so we can update a previously saved session after we’ve made some changes.

Similarly, we can set up a remap for loading a session file, in this case <Leader>sr for “session restore”:

exec 'nnoremap <Leader>sr :so ' . g:session_dir . '/'

These are going to save us a lot of time. Now whenever we want to save a session (including overwriting an existing session file), all we have to do is type <Leader>ss, and when we want to restore a session, <Leader>sr.

Helping remember our existing saved sessions

We have tab completion and wildmenu to help us find the session file we’re looking for, but we can make it even easier by listing the existing files right out of the gate with <C-D> (“Ctrl+d”) (:h cmdline-completion). For instance, if we type this manually:

:so ~/vim-sessions/*.vim<C-D>

Vim will display a list of all the files matching *.vim. inside of our ~/vim-sessions/ directory:

Let’s add that to our remap!:

exec 'nnoremap <Leader>ss :mksession! ' . g:session_dir . '/*.vim<C-D><BS><BS><BS><BS><BS>'

We put all those backspaces in there (<BS>) to delete the *.vim part of the string after typing it, since we only needed it to narrow down the files that get displayed, and we want to start typing in the name of one after this remap executes.

Similarly. we can do this with our session restore remap:

exec 'nnoremap <Leader>sr :so ' . g:session_dir. '/*.vim<C-D><BS><BS><BS><BS><BS>'

Voila!

All together

With zero plugins and only three new lines in our ~/.vimrc file, we’ve built a pretty sweet way of dealing with sessions in Vim:

let g:sessions_dir = '~/vim-sessions'
exec 'nnoremap <Leader>ss :mks! ' . g:session_dir . '/*.vim<C-D><BS><BS><BS><BS><BS>'
exec 'nnoremap <Leader>sr :so ' . g:session_dir. '/*.vim<C-D><BS><BS><BS><BS><BS>'

In the next post, I’ll look at how to make this even more powerful (while still remaining simple) with a couple plugins.

Happy Vimming!