Friday, October 29, 2010

Keep your VIM updated with VAM

I'm a huge fan of VIM. In fact, that's one of the main reason I've developed VoraX. However, I still feel that I have a lot to learn, but that's the beauty of this beast.

One thing that I always considered annoying is the installation of a fresh VIM. I use Unix and Windows boxes. On both VIM is a MUST-HAVE editor. To install the standard package is not a big deal: you just use your distro repository or, on Windows, you download the installer, next, next, next and you're done! But, wait a minute! Where are my killer plugins, and those neat mappings I customized for myself? And hey, I don't want the default color-scheme either. Okey, it's clear I have a problem because the installation of VIM cannot be considered finished until I have all my plugins and settings in place.

The quick and dirty method would be to copy all plugins along with the .vimrc file from a computer where's everything is in place. Whiles this always work without a problem it has a major drawback: it's difficult to maintain all these files across many computers. For example, at work it happens to discover a new cool vim setting which I put it right away in my .vimrc. Then I go home and when I open VIM from my personal computer I discover it looks different. This means I have to access the .vimrc file from my office and then to manually synchronize those files. That's so ugly!

So, at that point was clear that I needed a central repository to put those files and to access them in a shared way. I goggled to see how other people solved that and I find out that many prefer using a SCM repository. That seemed to be a solution, so I subscribed to a free SVN account on http://www.xp-dev.com/ and I put all my plugins and my .vimrc file there. This approach was fine but I found it cumbersome to use. First of all, installation of a new plugin was not so easy because you had to download it, to put it into the right location under your vim runtime path and then to also add those files to the SVN and then to commit. As far as .vimrc was concerned it also happened to change something and then to simply forget to commit the change. A SCM is the right solution for source code but not so good for my VIM plugins/.vimrc management... so I kept searching...

The good news is that I ended up with a nice solution I want to share with you. First of all, forget SVN and other source repositories and give a shot to Dropbox. It is a file sharing repository with automatic synchronization across computers. You may access your files from Windows, Unix, Mac... basically from everywhere. A change on a file maintained by Dropbox is automatically synchronized so there's no need to commit anything. And the basic functionality is provided for free. So, we have a nice shared repository where we are going to put our .vimrc file.

Usually vim expects to find .vimrc in a certain location which, for sure, is not into the Dropbox folder. Nevertheless, you can always create a symbolic link for your .vimrc to the one from your Dropbox folder. For example, on my Ubuntu box I can use:
cd ~
ln -s Dropbox/.vimrc .vimrc
On Windows it's not so easy because of the leak of the symbolic link functionality for files. The only option you have is to create hard links. For example, on my XP system I've downloaded the coreutils package and then I've created a link like this:
ln "c:\Documents and Settings\alec.FITS\My Documents\My Dropbox\.vimrc" "c:\Documents and Settings\alec.FITS\.vimrc"
It's also possible to use fsutil tool:
cd %HOME%
fsutil hardlink create .vimrc "C:\Documents and Settings\alec.FITS\My Documents\My Dropbox\.vimrc"
There is a problem however with this hard link on Windows: the changes on the underlying dropbox file are not correctly propagated. My assumption is that Dropbox synchronize the changed files by deleting the old file and recreating it afterwords. The side effect is that our hard link is broken and will not point to the dropbox .vimrc file anymore. The only solution I found is to create a dummy .vimrc file which source the .vimrc file from my dropbox folder. For example, you may put this into your $HOME/.vimrc:
exe 'source ' . expand('$HOME') . '\My Documents\My Dropbox\.vimrc'
Great! Now, we have one single copy of the .vimrc, shared by more than one computer. You might say: hey, let's put the plugins too into our nice Dropbox. We can, but I have a better solution. Let's use a plugin management engine for Vim.

There are a few such vim addons which provide plugins management like: Vimana, Vim Script Management System or Vundle. However, the best I like is VAM (Vim Addons Management). It's actively developed and you get quick feedback in case of troubles. Mark Weber has done a great job with this add-on. You can automatically install/update various plugins and because VAM puts every new installed plugin into its own directory is quite easy to uninstall them by simply deleting the corresponding folder. Those who already use Pathogen should be familiar with this concept.

Now, the best thing out of this is the fact that you can basically have just the .vimrc around and the rest will be done by VAM, even when you are running a fresh installation of Vim. If you're using plugins published on vim.org then a plain curl and the proper tools to unpack the downloaded archives are enough, but VAM also provides the possibility to install plugins via git, mercurial or subversion. However, all these are nicely explained by the VAM documentation.

Another cool feature of VAM is that it maintains a so-called well known plugins repository. Using it you may install whatever plugin you want without knowing where it's located, if is provided as a zip archive or as a vba etc. You simply don't care. Whiles this repository is very handy to install and to experiment with various plugins I tend to avoid it on my “production” Vim. The main reason is because some bundles have ugly names. For example, SQLUtilities plugin can be found under the “SQLUtilities_-_SQL_utilities_-_Formatting_generate” name which means it will be installed into that long ugly directory. That's not VAM fault, it's because the list of vim plugins is automatically generated based on the script names provided by their own developers on vim.org site.

So, instead of relying to the well know plugins repository I like to stick to a fixed list of plugins, manually specified into my .vimrc file. Below is how my .vimrc starts:

" Maintainer: talek
" Version: 2.0 - 29/10/10 10:45:58
" Description: configuration for vim

" We don't want vi compatibility.
set nocompatible

" Activate Addons Management
let g:vim_script_manager = { 'known_repos_activation_policy' : 'never', 'auto_install' : 1, 'plugin_sources' : {} }
let g:vim_script_manager.plugin_sources['nerd_commenter'] = {'type': 'git', 'url': 'git://github.com/scrooloose/nerdcommenter.git'}
let g:vim_script_manager.plugin_sources['surround'] = {'type': 'git', 'url': 'git://github.com/tpope/vim-surround.git'}
let g:vim_script_manager.plugin_sources['repeat'] = {'type': 'git', 'url': 'git://github.com/tpope/vim-repeat.git'}
let g:vim_script_manager.plugin_sources['supertab'] = {'type': 'git', 'url': 'git://github.com/ervandew/supertab.git'}
let g:vim_script_manager.plugin_sources['align'] = {'version': '35/41', 'url': 'http://www.vim.org/scripts/download_script.php?src_id=10110', 'vim_version': '7.0', 'date': '2009-03-04', 'vim_script_nr': 294, 'type': 'archive', 'script-type': 'utility', 'archive_name': 'Align.vba.gz', 'author': 'Charles Campbell'}
let g:vim_script_manager.plugin_sources['sqlutil'] = {'version': '4.00', 'url': 'http://www.vim.org/scripts/download_script.php?src_id=13576', 'vim_version': '7.0', 'date': '2010-08-15', 'vim_script_nr': 492, 'type': 'archive', 'script-type': 'utility', 'archive_name': 'sqlutil_400.zip', 'author': 'David Fishburn'}
let g:vim_script_manager.plugin_sources['taglist'] = {'version': '4.5', 'url': 'http://www.vim.org/scripts/download_script.php?src_id=7701', 'vim_version': '6.0', 'date': '2007-09-21', 'vim_script_nr': 273, 'type': 'archive', 'script-type': 'utility', 'archive_name': 'taglist_45.zip', 'author': 'Yegappan Lakshmanan'}
let g:vim_script_manager.plugin_sources['fuzzyfinder'] = {'type': 'hg', 'url': 'http://bitbucket.org/ns9tks/vim-fuzzyfinder'}
let g:vim_script_manager.plugin_sources['nerd_tree'] = {'version': '4.1.0', 'url': 'http://www.vim.org/scripts/download_script.php?src_id=11834', 'vim_version': '7.0', 'date': '2009-12-01', 'vim_script_nr': 1658, 'type': 'archive', 'script-type': 'utility', 'archive_name': 'NERD_tree.zip', 'author': 'Marty Grenfell'}
let g:vim_script_manager.plugin_sources['L9'] = {'type': 'hg', 'url': 'http://bitbucket.org/ns9tks/vim-l9'}
let g:vim_script_manager.plugin_sources['xptemplate'] = {'version': '0.4.8-r994', 'url': 'http://www.vim.org/scripts/download_script.php?src_id=13740', 'vim_version': '7.2', 'date': '2010-09-01', 'vim_script_nr': 2611, 'type': 'archive', 'script-type': 'utility', 'archive_name': 'xpt-0.4.8-r994.tgz', 'author': 'drdr xp'}
let g:vim_script_manager.plugin_sources['xptemplate']['strip-components'] = 0
let g:vim_script_manager.plugin_sources['command-T'] = { 'type' : 'git', 'url' : 'git://git.wincent.com/command-t.git' }
let g:vim_script_manager.plugin_sources['tcalc'] = {'version': '0.11', 'url': 'http://www.vim.org/scripts/download_script.php?src_id=8028', 'vim_version': '7.0', 'date': '2007-12-05', 'vim_script_nr': 2040, 'type': 'archive', 'script-type': 'utility', 'archive_name': 'tcalc.zip', 'author': 'Tom Link'}
let g:vim_script_manager.plugin_sources['bufexplorer'] = {'version': '7.2.7', 'url': 'http://www.vim.org/scripts/download_script.php?src_id=12904', 'vim_version': '7.0', 'date': '2010-04-26', 'vim_script_nr': 42, 'type': 'archive', 'script-type': 'utility', 'archive_name': 'bufexplorer.zip', 'author': 'jeff lanzarotta'}
let g:vim_script_manager.plugin_sources['ack'] = {'type': 'git', 'url': 'http://github.com/mileszs/ack.vim.git' }
let g:vim_script_manager.plugin_sources['genutils'] = {'version': '2.5', 'url': 'http://www.vim.org/scripts/download_script.php?src_id=11399', 'vim_version': '7.0', 'date': '2009-09-17', 'vim_script_nr': 197, 'type': 'archive', 'script-type': 'utility', 'archive_name': 'genutils-2.5.zip', 'author': 'Hari Krishna Dara'}
let g:vim_script_manager.plugin_sources['matchit'] = {'version': '1.13.2', 'url': 'http://www.vim.org/scripts/download_script.php?src_id=8196', 'vim_version': '6.0', 'date': '2008-01-29', 'vim_script_nr': 39, 'type': 'archive', 'script-type': 'utility', 'archive_name': 'matchit.zip', 'author': 'Benji Fisher'}
let g:vim_script_manager.plugin_sources['arpeggio'] = {'type': 'git', 'url': 'http://github.com/kana/vim-arpeggio.git' }
let g:vim_script_manager.plugin_sources['minibufexplorer'] = {'version': '6.3.3', 'url': 'http://www.vim.org/scripts/download_script.php?src_id=13838', 'vim_version': '7.0', 'date': '2010-09-15', 'vim_script_nr': 3239, 'type': 'archive', 'script-type': 'plugin', 'archive_name': 'minibufexplpp.vim', 'author': 'Oliver Uvman'}
let g:vim_script_manager.plugin_sources['vorax'] = {'title': 'vorax', 'version': '2.5', 'url': 'http://www.vim.org/scripts/download_script.php?src_id=14147', 'vim_version': '7.0', 'date': '2010-10-02', 'vim_script_nr': 3154, 'type': 'archive', 'script-type': 'utility', 'archive_name': 'vorax-2.5.zip', 'author': 'Alexandru Tica'} 

func ActivateMyAddons()
let addons_base = substitute(expand('$HOME') . '/vim-addons', '\\\\\|\\', '/', 'g')
" check if addons_base folder is there
if finddir(addons_base, '') == ''
call mkdir(addons_base, '')
endif
let addons_manager = addons_base . '/vim-addon-manager'
exe 'set runtimepath+=' . escape(addons_manager, ' ')
if finddir(addons_manager) == ''
" The addons manager is not installed. Install it now!
exe 'cd ' . addons_base
exe '!git clone git://github.com/MarcWeber/vim-addon-manager.git'
endif
call scriptmanager#Activate(['nerd_commenter',
\  'surround',
\  'repeat',
\  'supertab',
\  'align',
\  'sqlutil',
\  'vorax',
\  'taglist',
\  'L9',
\  'fuzzyfinder',
\  'nerd_tree',
\  'genutils',
\  'bufexplorer',
\  'matchit',
\  'command-T',
\  'minibufexplorer',
\  'vorax',
\  'tcalc',
\  'arpeggio',
\  'ack',
\  'xptemplate'])
endfunc
call ActivateMyAddons()
As you can see I have a list of plugins I always want to have them installed. I just extracted their definition from the well know plugins repository and I've tailored the names to look nicer. For the complete list you may check this list.

If you rather want to go with the know-addons-repository and get up-to-date plugins then the following code should do the job:
" Maintainer: talek
" Version: 2.0 - 29/10/10 10:45:58 
" Description: configuration for vim

" We don't want vi compatibility.
set nocompatible 

" Activate Addons Management
let g:vim_script_manager = { 'known_repos_activation_policy' : 'autoload', 'auto_install' : 1 }

func ActivateMyAddons()
  let addons_base = substitute(expand('$HOME') . '/vim-addons', '\\\\\|\\', '/', 'g')
  " check if addons_base folder is there
  if finddir(addons_base, '') == ''
    call mkdir(addons_base, '') 
  endif
  let addons_manager = addons_base . '/vim-addon-manager'
  exe 'set runtimepath+=' . escape(addons_manager, ' ')
  if finddir(addons_manager) == ''
    " The addons manager is not installed. Install it now!
    exe 'cd ' . addons_base
    exe '!git clone git://github.com/MarcWeber/vim-addon-manager.git'
  endif
  call scriptmanager#Activate(['The_NERD_Commenter', 
                              \  'surround', 
                              \  'repeat', 
                              \  'supertab',
                              \  'Align294', 
                              \  'SQLUtilities_-_SQL_utilities_-_Formatting_generate',
                              \  'vorax',
                              \  'taglist',
                              \  'L9',
                              \  'fuzzyfinder',
                              \  'The_NERD_tree',
                              \  'genutils',
                              \  'bufexplorer.zip',
                              \  'matchit.zip',
                              \  'Command-T',
                              \  'minibufexpl.vim_-_Elegant_buffer_explorer',
                              \  'vorax',
                              \  'tcalc',
                              \  'arpeggio',
                              \  'ack',
                              \  'xptemplate'])
endfunc
call ActivateMyAddons()
Both methods above check if VAM is already installed and if not than it is fetched using git which, by the way, is the recommended way. This means git has to be available on your platform. So before going on, let's have a look at the external tools needed by VAM:
  1. on UNIX:
    • curl (e.g. apt-get install curl)
    • git-core (e.g. apt-get install git-core)
    • mercurial (e.g. apt-get install mercurial)
    • subversion (e.g. apt-get install subversion)
  1. on Windows:
That's it! If all of the above dependencies are installed and they are working fine then VAM should automatically install the specified plugins. This is huge because, from now on, you'll simply need to have just your .vimrc and VAM will do all the rest.

No comments: