Vim Sessions

Vim, like Emacs and most other decent editors (as if there were any other decent editors out there) is an application that ought to only run in a single session on your machine. When using the console version of vim, this is usually not a problem since it actually kinda forces this kind of workflow. But graphical vim (Gvim and MacVim respectively) actually have a lot to offer. After all, most of us run graphical window managers anyway in order to browse the web, so might as well use them to our benefit and run the slicker and prettier version of the editor.

From there It is quite easy to fall into a trap of running multiple instances of gvim side by side. While being able to look at two files at once is great, the best way to accomplish this with Vim are splits not multiple windows. Why? Well, when you open a new Gvim instance (ether via GUI or by running gvim from the command line it opens up as a separate process. It doesn’t share the environment of your existing vim instance. What does that mean? It means it can’t access the buffers you have open in the other window, which might actually be what you want. It also isn’t affected by any run-time customizations. It’s argument list, search path and work directory are initialized separately and do not interfere with each other. There are times when this is precisely what you want.

Chances are however that you are working on a few files that are part of the same project and thus you would probably want your Gvim instances to be able to interact. For example you might define a macro in one file, and wish to run it on the other – but you can’t. Hell, you can’t even directly yank and paste between the two windows because they have separate sets of registers. The only way to copy text from one window to the other is by way of the system clipboard, which only works if your version was compiled with support for it.

But what to do if you are a shell fiend, and basically just want to open your files from Bash? Well, you just need to learn one command line switch then:

gvim --remote-silent filename

This will find a running instance of gvim and attach a new buffer to it. From there you can put them side by side using :vsplit (to create a vertical split) and Ctrl^ to toggle current split pane to the previous buffer and hide the split with :only when you no longer need it. The only thing you have to remember is that you don’t want to use the :q command because it will close the entire application. When you are done with a buffer you save it and then do :bd which in my mind stands for “Be Done!”. There is also the “Be Well!” command :bw which saves the buffer before closing.

Splits and buffers are really the way to go. Throw in tabs for a good measure and you can set up multiple sets of different split panes and flip between them without ever having to take your hands off the home row. But alas, even with cute mnemonics for the buffer closing commands accidents happen. I can’t tell you how many times I had a dozen files open in a single vim session, and in the middle of coding, out of pure habit I typed :wq. Suddenly, all the buffers are gone. All the splits forgotten. All the tabs purged. No data was lost in the process of course, since the files themselves were saved. The undo history was preserved as well because I use Vim 7.3 and enable the persistent undo feature. But the contents of the registers and the buffer list was blown away.

This used to happen to me at least once per day, until I set up automatic session saving for myself. Vim, as any good editor as a session feature. When you issue :mksession command it will save the list of open buffer and tabs as well as your register contents into a file called Session.vim in the current working directory. You can then run gvim -S and it will look for a session file in the current directory and restore from it. Alternatively you can :source that file.

Unfortunately this does not help you with accidental closing of vim, as you need to remember to save the session yourself. Of course you could always add this to your .vimrc:

autocmd VimLeave * :mksession!

This will automatically save a session file each time you close your editor, which is much better. Unfortunately this will quickly litter your filesystem with session files. It is about as annoying as those damn .swp files vim loves to produce by default. I have disabled those long time ago because I have never actually had to use one and decided they were not worth the hassle. Session files in every directory bring back these bad memories. Fortunately you can redirect your session file to somewhere else, like:

autocmd VimLeave * :mksession! $HOME."/.vimsessions/session.vim"

Now however you have only one session file, which gets overwritten every time the editor closes. This works when you accidentally blow away your buffer history like in the example above. But let’s say you are done for the night, and you close vim confident your session is saved. Then you run git commit to commit your changes into the repository. Git of course helpfully launches Vim to edit your commit log, and then happily overwrites your session. Next morning you want to get back to work, and your editor opens with a stale commit log message rather than a screen-full of buffers. Less than optimal, don’t you think?

I’ve been trying to figure out a good way to manage my sessions and eventually got an idea of naming my sessions. You can name your vim instance using the –servername command line switch like this:

gvim --servername dev

You can grab that name inside Vim using the v:servername variable, and use it as a unique name given to your session. Whenever you work on a specific project you launch it with a relevant servername and that session will be preserved. If you later run Gvim without that switch, it defaults to GVIM or an empty string if you ran the console version. This means that editing a commit log won’t accidentally blow away your session.

Here is how I’ve done it:

" where do you want to save sessions?
let g:session_dir = $HOME."/.vimsessions"

" Save sessions whenever vim closes
autocmd VimLeave * call SaveSession()

" Saves the session to session dir. Creates session dir if it doesn't
" yet exist. Sessions are named after servername paameter
function! SaveSession()

    " get the server (session) name
    let s = v:servername
    
    " create session dir if needed
    if !isdirectory(g:session_dir)
    	call mkdir(g:session_dir, "p")
    endif

    " save session using the server name
    execute "mksession! ".g:session_dir."/".s.".session.vim"
endfunc

That’s only part of the equation. Once you have sessions that save automatically, you also want an easy way to restore them. So I made another function that runs when you open vim:

" Load session when vim is opened
autocmd VimEnter * nested call OpenSession()

" Open a saved session if there were no file-names passed as arguments
" The session opened is based on servername (session name). If there
" is no session for this server, none will be opened
function! OpenSession()

    " check if file names were passed as arguments
    if argc() == 0

    	let sn = v:servername
    	let file = g:session_dir."/".sn.".session.vim"

        " if session file exists, load it
    	if filereadable(file)
            execute "source ".file
        endif
    endif
endfunc

One thing I decided here was that if I was opening a file in Vim, chances are I wasn’t interested in restoring the session. Most of the time when I run Gvim from the command line with a file argument I just want to quickly edit said file and be done with it. So I opted to never bother restoring sessions if a file argument was present.

If vim is opened without specifying a file, then I try to find a session file based on the servername variable. To make this even more seamless I actually added a few aliases to my .bashrc:

alias gv="gvim --servername DEV"
alias g="gvim --servername DEV --remote-silent"
alias gv="gvim --remote-silent"

So when I sit down to work I run gd on its own and it creates a named DEV session for me. Then I use g followed by filename to attach new buffers to it as needed needed. If I ever have to open an unrelated file, I use plain old gvim command which opens up a completely independent instance which doesn’t touch my session data. If I want to attach buffers to that session I can do that with gv. It works quite well, and it also reduces the amount of typing that I need to do at the console – so it’s a win-win in my book.

This entry was posted in Uncategorized. Bookmark the permalink.



2 Responses to Vim Sessions

  1. Mitlik UNITED STATES Mozilla Firefox Windows says:

    I’ve been wanting something like this in Vim. I haven’t gotten around to reading the documentation as fully as I’d like, so thanks for the great info. Is this by any chance committed to your git repo for vim files?

    Also, do you have a handy trick like this for tmux? Having sessions saved across the rare reboot would be great.

    Reply  |  Quote
  2. Luke Maciak UNITED STATES Google Chrome Linux Terminalist says:

    @ Mitlik:

    Well, it is in my .vimrc which is on github. I haven’t really gotten around to making it into a plugin or anything.

    The console vim can usually also take the –servername parameter so as long as you launch it with a name you can later re-store from session. I also did a little trick when the file name of my session is based either on the servername you pass on the command line or on a user defined variable g:sessionname. I also created a command Ses which sets that variable. So at any time I can just go :wall, :Ses FOO and :q.

    Then later I can reopen it by doing gvim –servername FOO or alternatively vim -S ~/.vimsessions/FOO.session.vim.

    Reply  |  Quote

Leave a Reply

Your email address will not be published. Required fields are marked *