The Ultimate Javascript, Golang and C Family Vim experience (Part 1)
Intro
How many times have you met someone boasting about Vim (or Emacs or Nano), and the subsequent productivity boosts that they claim to have achieved? If you belong to the programming fraternity, I’m sure it’s quite a lot. I’m not here to wage some kind of editor War, yet my allegiance lay with Vim, simply because that’s what I’ve been using from my fresher year.
I don’t know if I suffer from OCD (Obsessive Compulsive Disorder), but deep down I have this knack for perfection and I simply can’t take my mind off some thing until I have mastered it to perfection.
My philosophy of configuring Vim is really simple –
- Avoid movement of fingers to Arrow Keys, and completely eliminate their usage. The farthest your little finger is allowed to travel is when pressing a backspace. Period.
- Avoid un-necessary keystrokes but don’t introduce hard to put into muscle memory mappings. In short, stick to as much default as possible when it comes to mappings.
I’ll be separating this Article over 4 parts, for I can’t do justice to a language’s configuration in just one. I promise, by the end of this, you’ll have immensely improved your Vim experience, and it will pay itself off in the long run.
Now, let’s shift our gears towards Vim and talk about configuring it.
The Vanilla Vim configuration - Setting the set
commands
Vim, by default provides a host of options ranging from setting the folds
and foldmethods
, to changing the tabstop
width to allowing for expandtab
s. Don’t feel hazy if you don’t understand what these options mean - I’ll do no man’s job and explain them out.
Indentation, Line Numbering and Highlighted Searches
The line numbering can be enabled with set number
(or its shorter version set nu
), and this generally is the first option I toggle whenever I have to use vanilla Vim. For relative numbering, we have the set relativenumber
option, but I find it pretty annoying since the numbers change constantly when scrolling using h, j, k or l.
Most of the projects use either a 2 or 4 level indented code, and it’s best to stick with these two only, otherwise new contributors may have to scratch their heads fixing indentation for any code changes they make. I don’t use Tabs or Spaces uniformly across my own Projects since it’s just a matter of simple set expandtab
to change from Tabs to Spaces, or set noexpandtab
for vice-versa. (GNU guys, please use this link)
The tabstop
option sets the width of Tab when you press the physical Tab key and shiftwidth
option does so when you use >>
in Normal mode to indent your code. The autoindent
and smarttab
are quite handy when you’re using different blocks of code and automatically ident the next line with 1 extra Tab when you press enter at the start of an if
or a while
or a for
block.
For large code bases, its imperative that you learn how to navigate quickly through code and how to quickly spot what you need. The highlighted searching option (set hlsearch
) is extremely essential and I absolutely love it. Moreover, you should also turn on incremental searching which shows matching words while you type.
Folds, History, UndoLevels, Wildmenu and Advanced indentation using cinoptions
When you deal with files having a dozen functions, along with structs and what not,navigation and visibility becomes a mess. You can’t focus on a single function, since the surrounding code becomes obtrusive due to peripheral vision. Fold to our rescue – they basically hide unnecesssary code and can be toggled using zc
to close and zr
to open.
Further, you can also change how folding occurs (i.e. manual vs indented vs syntax based folding). The set foldmethod=indent
sets the mode to indent. But, this will indent all the code blocks including small if
statements or loops. So, I have set foldnestmax=2
which doesn’t do more than 2 levels of folding. You can change it per-buffer (eg. for C++ files having classes and methods defined) but globally, I like to keep it this way.
When you wish to open a new file, either in a split or a buffer or a tab, Tabbed file completions are a necessity and unless you use Ctrl-P Vim (an awesome plugin, more on that later), you’re limited by Vim’s default tabbed completions. set wildmenu
shows a new line above the status bar of vim showing all the tab completions when trying to open a file. Moreover, binary and object files are useless while being served for Tab completions, so I use a wild the following wildignore
options to ignore such files -
set wildignore+=*/tmp/*,*.so,*.swp,*.zip,*.o,*.a " MacOSX/Linux
set wildignore+=*\\tmp\\*,*.swp,*.zip,*.exe " Windows
I find the default Vim command history (50 lines) to be very less, so I cranked it up to 1000 lines using set history=1000
. Also, when re-factoring a piece of code, I find that default undo levels are quite less and I also have it set to 1000 using set undolevels=1000
.
What Plugin Manager should I go with?
There’s a bunch of Plugin Managers available to be used with Vim - vim-plug, Vundle, Pathogen, dein, but I prefer to use Vundle since it’s really easy to setup and works quite well. It can also fetch Plugin sources directly from GitHub, generate HelpTags automatically and clean out unused plugins.
The Vundle GitHub page (here).
Please note than the first thing I wish to be doing is to load our Plugin Manager, so I generally keep my call vundle#begin()
and call vundle#end()
at the beginning of my .vimrc. Also, take care that all the Plugin paths should go between these two calls, like this
call vundle#begin()
Plugin 'VundleVim/Vundle.vim'
...
*Other Plugins*
...
call vundle#end()
The Plugins - Why and How?
At this point, if I had to change several lines of configuration, I’d be happy given my editor. But our goal here is to have a full fledged IDE-like experience with Vim, and I can’t do justice to the readers without introducing them to the various plugins. Each plugin I use serves some purpose for a certain language and you’re free to omit them as you wish.
Auto-Pairs (jiangmiao/auto-pairs)
From the GitHub page - Insert or delete brackets, parens, quotes in pair.
Supertab (‘ervandew/supertab’)
When you’re in INSERT
mode and you try to use Omni-Completions, you’re offered a list of suggestions to choose from. This plugin enables us to use Tab to cycle through those suggestions. Without this, you’ll be inserting a Tab character and will have to cycle through suggestions using Arrow Keys, which violates my philosophy. So, no Arrow Keys.
The tpope Family - VimFugitive and VimSurround
Firstly, a big shoutout to tpope for his awesome Plugins! vim-fugitive enables git support directly from within vim, so you don’t have to press Ctrl-Z, then do some git stuff and be back with fg
.
YouCompleteMe - The superb and blazingly fast auto-completion engine
Vim by default provides Omni-Completions which provide automatic completions, but on the press of some key. It works okay-ish, but what good is an autocompletion tool if you have to press another set of keys just to have a suggestion window?
*Drumroll* Enters YCM. This is THE PLUGIN which makes the coding experience in Vim worth every penny. Once you understand how well it works with C Family (and a whole lot of other languages), you’ll be amazed at how you were even spending your life without this gem of a plugin. I’ll be writing specifically about this in one of the following parts.
My particular configuration with YCM is as follows. I’m using some language specific options here as well lest it shall become all too scattered.
let g:ycm_gocode_binary_path = "$GOPATH/bin/gocode"
let g:ycm_godef_binary_path = "$GOPATH/bin/godef"
let g:ycm_seed_identifiers_with_syntax = 1
let g:ycm_min_num_identifier_candidate_chars = 5
let g:ycm_global_ycm_extra_conf = "~/.vim/.ycm_extra_conf.py"
let g:ycm_python_binary_path = '/usr/bin/python3'
let g:ycm_confirm_extra_conf = 0
let g:ycm_always_populate_location_list = 1
let g:ycm_log_level = "debug"
let g:ycm_semantic_triggers = {
\ 'c' : ['->', '.','re![_a-zA-Z0-9]'],
\ 'objc' : ['->', '.', 're!\[[_a-zA-Z]+\w*\s', 're!^\s*[^\W\d]\w*\s',
\ 're!\[.*\]\s'],
\ 'ocaml' : ['.', '#'],
\ 'cpp,objcpp' : ['->', '.', '::', 're![_a-zA-Z0-9]'],
\ 'perl' : ['->'],
\ 'php' : ['->', '::'],
\ 'cs,java,javascript,typescript,d,python,perl6,scala,vb,elixir,go' : ['.'],
\ 'ruby' : ['.', '::'],
\ 'lua' : ['.', ':'],
\ 'erlang' : [':'],
\ }
let g:ycm_filetype_blacklist = { 'markdown' : 0 }
let g:typescript_compiler_binary = 'tsc'
The semantic triggers tell the YCM client to check which key press should it use to start predicting text. Like, for C you may want to have suggestions only when you reference some property of a struct with .
or ->
. I like to keep it on for any key press specifically for C, and the regex next to ‘c’ in ycm_semantic_triggers
option does exactly that.
The .ycm_extra_conf.py
file tell the YCM engine which compilation flags to use at runtime. More on that later!
Vim multiple cursors
Sublime like multiple cursors. My configuration:
"Vim-Multiple Cursors Configuration
let g:multi_cursor_next_key='<C-n>'
let g:multi_cursor_prev_key='<C-p>'
let g:multi_cursor_skip_key='<C-x>'
let g:multi_cursor_quit_key='<Esc>'
Vim Airline - A beautiful lightweight status line
Statusline on steroids. Great buffer management owning to the solid angular brackets in the top which makes hopping through buffers extremely easy. You may have to install PowerLine fonts in your distro to view the below characters correctly. They’re all UTF-8 encoded chars and may not show in a vanilla distro.
Unfortunately, this blog is written using markdown and hence doesnt’t have support for UTF-8. You can find the configuration in my .vimrc by searching for vim-airline
.
Ending Notes
I may seem like a madman to some since they may ask why I have spent so much time perfecting Vim. The answer to my critics is that it in the long run, it actually saves me a lot of time. I am a touch typist and I type fairly fast and not having to lift my fingers off the keyboard is a boon when you’re in the Programming Jedi mode. Add to that, I can clone my .vimrc
on any server and within a couple of minutes, I have my dev environment up and running. Vim <3!
Lastly, comments and suggestions are welcome! See you in next part. :-)