Vim Is The Perfect IDE
I have have tried Atom, SublimeText, TextMate, Eclipse, Visual Studio, and most of the Jetbrains products, I'm constantly tweaking and looking for a better setup, however Vim always feels like home to me; and I'm to the point now where I rarely use IDEs – exception being messy and complex projects where IDEs can do a lot of heavily lifting
Over the years I’ve jumped back and forth between many code editors, IDEs and tools; but it seems that somehow I always end up coming right back to VIM, and not only for programming – guess which markdown editor I’m using to write this post.
I’ve have tried Atom, SublimeText, TextMate, Eclipse, Visual Studio, and most of the Jetbrains products, I’m constantly tweaking and looking for a better setup, however Vim always feels like home to me; and I’m to the point now where I rarely use IDEs – exception being messy and complex projects where IDEs can do a lot of heavily lifting (yes, Magento I’m talking about you.)
But other than that Vim is my default Ruby, Elixir, Python, PHP IDE and as well the main tool that I use for writing drafts and books.
What it look like
The Setup
So how does this magical tool work? Is all out of the box right? right? Well no, as with all the worthwhile things in life there is a bit of effort involved on getting the Vim setup just like I wanted it. Fortunately, is far from custom and is mostly the right combination of plugins.
You can find my current Vim configuration and dot files in its corresponding Github repository feel free to fork it and give a shot.
We are in particular interested in the vimrc file, let’s break it down:
""""""""""""""""""""""""""""""""""""" | |
" Allan MacGregor Vimrc configuration | |
""""""""""""""""""""""""""""""""""""" | |
set nocompatible | |
syntax on | |
set nowrap | |
set encoding=utf8 | |
"""" START Vundle Configuration | |
" Disable file type for vundle | |
filetype off " required | |
" set the runtime path to include Vundle and initialize | |
set rtp+=~/.vim/bundle/Vundle.vim | |
call vundle#begin() | |
" let Vundle manage Vundle, required | |
Plugin 'gmarik/Vundle.vim' | |
" Utility | |
Plugin 'scrooloose/nerdtree' | |
Plugin 'majutsushi/tagbar' | |
Plugin 'ervandew/supertab' | |
Plugin 'BufOnly.vim' | |
Plugin 'wesQ3/vim-windowswap' | |
Plugin 'SirVer/ultisnips' | |
Plugin 'junegunn/fzf.vim' | |
Plugin 'junegunn/fzf' | |
Plugin 'godlygeek/tabular' | |
Plugin 'ctrlpvim/ctrlp.vim' | |
Plugin 'benmills/vimux' | |
Plugin 'jeetsukumaran/vim-buffergator' | |
Plugin 'gilsondev/searchtasks.vim' | |
Plugin 'Shougo/neocomplete.vim' | |
Plugin 'tpope/vim-dispatch' | |
" Generic Programming Support | |
Plugin 'jakedouglas/exuberant-ctags' | |
Plugin 'honza/vim-snippets' | |
Plugin 'Townk/vim-autoclose' | |
Plugin 'tomtom/tcomment_vim' | |
Plugin 'tobyS/vmustache' | |
Plugin 'janko-m/vim-test' | |
Plugin 'maksimr/vim-jsbeautify' | |
Plugin 'vim-syntastic/syntastic' | |
Plugin 'neomake/neomake' | |
" Markdown / Writting | |
Plugin 'reedes/vim-pencil' | |
Plugin 'tpope/vim-markdown' | |
Plugin 'jtratner/vim-flavored-markdown' | |
Plugin 'LanguageTool' | |
" Git Support | |
Plugin 'kablamo/vim-git-log' | |
Plugin 'gregsexton/gitv' | |
Plugin 'tpope/vim-fugitive' | |
"Plugin 'jaxbot/github-issues.vim' | |
" PHP Support | |
Plugin 'phpvim/phpcd.vim' | |
Plugin 'tobyS/pdv' | |
" Erlang Support | |
Plugin 'vim-erlang/vim-erlang-tags' | |
Plugin 'vim-erlang/vim-erlang-runtime' | |
Plugin 'vim-erlang/vim-erlang-omnicomplete' | |
Plugin 'vim-erlang/vim-erlang-compiler' | |
" Elixir Support | |
Plugin 'elixir-lang/vim-elixir' | |
Plugin 'avdgaag/vim-phoenix' | |
Plugin 'mmorearty/elixir-ctags' | |
Plugin 'mattreduce/vim-mix' | |
Plugin 'BjRo/vim-extest' | |
Plugin 'frost/vim-eh-docs' | |
Plugin 'slashmili/alchemist.vim' | |
Plugin 'tpope/vim-endwise' | |
Plugin 'jadercorrea/elixir_generator.vim' | |
" Elm Support | |
Plugin 'lambdatoast/elm.vim' | |
" Theme / Interface | |
Plugin 'AnsiEsc.vim' | |
Plugin 'ryanoasis/vim-devicons' | |
Plugin 'vim-airline/vim-airline' | |
Plugin 'vim-airline/vim-airline-themes' | |
Plugin 'sjl/badwolf' | |
Plugin 'tomasr/molokai' | |
Plugin 'morhetz/gruvbox' | |
Plugin 'zenorocha/dracula-theme', {'rtp': 'vim/'} | |
Plugin 'junegunn/limelight.vim' | |
Plugin 'mkarmona/colorsbox' | |
Plugin 'romainl/Apprentice' | |
Plugin 'Lokaltog/vim-distinguished' | |
Plugin 'chriskempson/base16-vim' | |
Plugin 'w0ng/vim-hybrid' | |
Plugin 'AlessandroYorba/Sierra' | |
Plugin 'daylerees/colour-schemes' | |
Plugin 'effkay/argonaut.vim' | |
Plugin 'ajh17/Spacegray.vim' | |
Plugin 'atelierbram/Base2Tone-vim' | |
Plugin 'colepeters/spacemacs-theme.vim' | |
" OSX stupid backspace fix | |
set backspace=indent,eol,start | |
call vundle#end() " required | |
filetype plugin indent on " required | |
"""" END Vundle Configuration | |
""""""""""""""""""""""""""""""""""""" | |
" Configuration Section | |
""""""""""""""""""""""""""""""""""""" | |
" Show linenumbers | |
set number | |
set ruler | |
" Set Proper Tabs | |
set tabstop=4 | |
set shiftwidth=4 | |
set smarttab | |
set expandtab | |
" Always display the status line | |
set laststatus=2 | |
" Enable Elite mode, No ARRRROWWS!!!! | |
let g:elite_mode=1 | |
" Enable highlighting of the current line | |
set cursorline | |
" Theme and Styling | |
set t_Co=256 | |
set background=dark | |
if (has("termguicolors")) | |
set termguicolors | |
endif | |
let base16colorspace=256 " Access colors present in 256 colorspace | |
colorscheme spacegray | |
" colorscheme spacemacs-theme | |
let g:spacegray_underline_search = 1 | |
let g:spacegray_italicize_comments = 1 | |
" Vim-Airline Configuration | |
let g:airline#extensions#tabline#enabled = 1 | |
let g:airline_powerline_fonts = 1 | |
let g:airline_theme='hybrid' | |
let g:hybrid_custom_term_colors = 1 | |
let g:hybrid_reduced_contrast = 1 | |
" Syntastic Configuration | |
set statusline+=%#warningmsg# | |
set statusline+=%{SyntasticStatuslineFlag()} | |
set statusline+=%* | |
let g:syntastic_always_populate_loc_list = 1 | |
let g:syntastic_auto_loc_list = 1 | |
let g:syntastic_check_on_open = 1 | |
" let g:syntastic_check_on_wq = 0 | |
" let g:syntastic_enable_elixir_checker = 1 | |
" let g:syntastic_elixir_checkers = ["elixir"] | |
" Neomake settings | |
autocmd! BufWritePost * Neomake | |
let g:neomake_elixir_enabled_makers = ['mix', 'credo', 'dogma'] | |
" Vim-PDV Configuration | |
let g:pdv_template_dir = $HOME ."/.vim/bundle/pdv/templates_snip" | |
" Markdown Syntax Support | |
augroup markdown | |
au! | |
au BufNewFile,BufRead *.md,*.markdown setlocal filetype=ghmarkdown | |
augroup END | |
" Github Issues Configuration | |
let g:github_access_token = "e6fb845bd306a3ca7f086cef82732d1d5d9ac8e0" | |
" Vim-Alchemist Configuration | |
let g:alchemist#elixir_erlang_src = "/Users/amacgregor/Projects/Github/alchemist-source" | |
let g:alchemist_tag_disable = 1 | |
" Vim-Supertab Configuration | |
let g:SuperTabDefaultCompletionType = "<C-X><C-O>" | |
" Settings for Writting | |
let g:pencil#wrapModeDefault = 'soft' " default is 'hard' | |
let g:languagetool_jar = '/opt/languagetool/languagetool-commandline.jar' | |
" Vim-pencil Configuration | |
augroup pencil | |
autocmd! | |
autocmd FileType markdown,mkd call pencil#init() | |
autocmd FileType text call pencil#init() | |
augroup END | |
" Vim-UtilSnips Configuration | |
" Trigger configuration. Do not use <tab> if you use https://github.com/Valloric/YouCompleteMe. | |
let g:UltiSnipsExpandTrigger="<tab>" | |
let g:UltiSnipsJumpForwardTrigger="<c-b>" | |
let g:UltiSnipsJumpBackwardTrigger="<c-z>" | |
let g:UltiSnipsEditSplit="vertical" " If you want :UltiSnipsEdit to split your window. | |
" Vim-Test Configuration | |
let test#strategy = "vimux" | |
" Neocomplete Settings | |
let g:acp_enableAtStartup = 0 | |
let g:neocomplete#enable_at_startup = 1 | |
let g:neocomplete#enable_smart_case = 1 | |
let g:neocomplete#sources#syntax#min_keyword_length = 3 | |
" Define dictionary. | |
let g:neocomplete#sources#dictionary#dictionaries = { | |
\ 'default' : '', | |
\ 'vimshell' : $HOME.'/.vimshell_hist', | |
\ 'scheme' : $HOME.'/.gosh_completions' | |
\ } | |
" Define keyword. | |
if !exists('g:neocomplete#keyword_patterns') | |
let g:neocomplete#keyword_patterns = {} | |
endif | |
let g:neocomplete#keyword_patterns['default'] = '\h\w*' | |
function! s:my_cr_function() | |
return (pumvisible() ? "\<C-y>" : "" ) . "\<CR>" | |
" For no inserting <CR> key. | |
"return pumvisible() ? "\<C-y>" : "\<CR>" | |
endfunction | |
" Close popup by <Space>. | |
"inoremap <expr><Space> pumvisible() ? "\<C-y>" : "\<Space>" | |
" AutoComplPop like behavior. | |
"let g:neocomplete#enable_auto_select = 1 | |
" Enable omni completion. | |
autocmd FileType css setlocal omnifunc=csscomplete#CompleteCSS | |
autocmd FileType html,markdown setlocal omnifunc=htmlcomplete#CompleteTags | |
autocmd FileType javascript setlocal omnifunc=javascriptcomplete#CompleteJS | |
autocmd FileType python setlocal omnifunc=pythoncomplete#Complete | |
autocmd FileType xml setlocal omnifunc=xmlcomplete#CompleteTags | |
" Enable heavy omni completion. | |
if !exists('g:neocomplete#sources#omni#input_patterns') | |
let g:neocomplete#sources#omni#input_patterns = {} | |
endif | |
"let g:neocomplete#sources#omni#input_patterns.php = '[^. \t]->\h\w*\|\h\w*::' | |
"let g:neocomplete#sources#omni#input_patterns.c = '[^.[:digit:] *\t]\%(\.\|->\)' | |
"let g:neocomplete#sources#omni#input_patterns.cpp = '[^.[:digit:] *\t]\%(\.\|->\)\|\h\w*::' | |
" For perlomni.vim setting. | |
" https://github.com/c9s/perlomni.vim | |
let g:neocomplete#sources#omni#input_patterns.perl = '\h\w*->\h\w*\|\h\w*::' | |
" Elixir Tagbar Configuration | |
let g:tagbar_type_elixir = { | |
\ 'ctagstype' : 'elixir', | |
\ 'kinds' : [ | |
\ 'f:functions', | |
\ 'functions:functions', | |
\ 'c:callbacks', | |
\ 'd:delegates', | |
\ 'e:exceptions', | |
\ 'i:implementations', | |
\ 'a:macros', | |
\ 'o:operators', | |
\ 'm:modules', | |
\ 'p:protocols', | |
\ 'r:records', | |
\ 't:tests' | |
\ ] | |
\ } | |
" Fzf Configuration | |
" This is the default extra key bindings | |
let g:fzf_action = { | |
\ 'ctrl-t': 'tab split', | |
\ 'ctrl-x': 'split', | |
\ 'ctrl-v': 'vsplit' } | |
" Default fzf layout | |
" - down / up / left / right | |
let g:fzf_layout = { 'down': '~40%' } | |
" In Neovim, you can set up fzf window using a Vim command | |
let g:fzf_layout = { 'window': 'enew' } | |
let g:fzf_layout = { 'window': '-tabnew' } | |
" Customize fzf colors to match your color scheme | |
let g:fzf_colors = | |
\ { 'fg': ['fg', 'Normal'], | |
\ 'bg': ['bg', 'Normal'], | |
\ 'hl': ['fg', 'Comment'], | |
\ 'fg+': ['fg', 'CursorLine', 'CursorColumn', 'Normal'], | |
\ 'bg+': ['bg', 'CursorLine', 'CursorColumn'], | |
\ 'hl+': ['fg', 'Statement'], | |
\ 'info': ['fg', 'PreProc'], | |
\ 'prompt': ['fg', 'Conditional'], | |
\ 'pointer': ['fg', 'Exception'], | |
\ 'marker': ['fg', 'Keyword'], | |
\ 'spinner': ['fg', 'Label'], | |
\ 'header': ['fg', 'Comment'] } | |
" Enable per-command history. | |
" CTRL-N and CTRL-P will be automatically bound to next-history and | |
" previous-history instead of down and up. If you don't like the change, | |
" explicitly bind the keys to down and up in your $FZF_DEFAULT_OPTS. | |
let g:fzf_history_dir = '~/.local/share/fzf-history' | |
""""""""""""""""""""""""""""""""""""" | |
" Mappings configurationn | |
""""""""""""""""""""""""""""""""""""" | |
map <C-n> :NERDTreeToggle<CR> | |
map <C-m> :TagbarToggle<CR> | |
" Omnicomplete Better Nav | |
inoremap <expr> <c-j> ("\<C-n>") | |
inoremap <expr> <c-k> ("\<C-p>") | |
" Neocomplete Plugin mappins | |
inoremap <expr><C-g> neocomplete#undo_completion() | |
inoremap <expr><C-l> neocomplete#complete_common_string() | |
" Recommended key-mappings. | |
" <CR>: close popup and save indent. | |
inoremap <silent> <CR> <C-r>=<SID>my_cr_function()<CR> | |
" <TAB>: completion. | |
inoremap <expr><TAB> pumvisible() ? "\<C-n>" : "\<TAB>" | |
" <C-h>, <BS>: close popup and delete backword char. | |
inoremap <expr><C-h> neocomplete#smart_close_popup()."\<C-h>" | |
inoremap <expr><BS> neocomplete#smart_close_popup()."\<C-h>" | |
" Mapping selecting Mappings | |
nmap <leader><tab> <plug>(fzf-maps-n) | |
xmap <leader><tab> <plug>(fzf-maps-x) | |
omap <leader><tab> <plug>(fzf-maps-o) | |
" Shortcuts | |
nnoremap <Leader>o :Files<CR> | |
nnoremap <Leader>O :CtrlP<CR> | |
nnoremap <Leader>w :w<CR> | |
" Insert mode completion | |
imap <c-x><c-k> <plug>(fzf-complete-word) | |
imap <c-x><c-f> <plug>(fzf-complete-path) | |
imap <c-x><c-j> <plug>(fzf-complete-file-ag) | |
imap <c-x><c-l> <plug>(fzf-complete-line) | |
" Vim-Test Mappings | |
nmap <silent> <leader>t :TestNearest<CR> | |
nmap <silent> <leader>T :TestFile<CR> | |
nmap <silent> <leader>a :TestSuite<CR> | |
nmap <silent> <leader>l :TestLast<CR> | |
nmap <silent> <leader>g :TestVisit<CR> | |
" Vim-PDV Mappings | |
autocmd FileType php inoremap <C-p> <ESC>:call pdv#DocumentWithSnip()<CR>i | |
autocmd FileType php nnoremap <C-p> :call pdv#DocumentWithSnip()<CR> | |
autocmd FileType php setlocal omnifunc=phpcd#CompletePHP | |
" Disable arrow movement, resize splits instead. | |
if get(g:, 'elite_mode') | |
nnoremap <Up> :resize +2<CR> | |
nnoremap <Down> :resize -2<CR> | |
nnoremap <Left> :vertical resize +2<CR> | |
nnoremap <Right> :vertical resize -2<CR> | |
endif | |
map <silent> <LocalLeader>ws :highlight clear ExtraWhitespace<CR> | |
" Advanced customization using autoload functions | |
inoremap <expr> <c-x><c-k> fzf#vim#complete#word({'left': '15%'}) | |
" Vim-Alchemist Mappings | |
autocmd FileType elixir nnoremap <buffer> <leader>h :call alchemist#exdoc()<CR> | |
autocmd FileType elixir nnoremap <buffer> <leader>d :call alchemist#exdef()<CR> |
Each plugin in this setup is separated in the following categories:
Utility
This kind of a miscellaneous category and is comprised of plugins used to enhance or change the behaivour of core vim; the most useful important ones are:
- Nerdtree: It gives you easy access to the file system in the form of a directory tree on the left side of the screen, as well provides shortcuts for filesystem manipulation(create, delete, move files and directories)
- Tagbar: Quick tag browser for the current file, a must have if you are using any kind of ctags like exuberant-tags.
- FZF: Fuzzy finder, another handy utility for finding files and commands.
- Neocomplete: Vim Autocomplete on steroids.
Generic Programming Support
These plugins fall directly on the category of programming and are used my all or most of the programming languages that I currently have setup:
- Exuberant-Ctags: tags are named definitions of classes, functions, abstract types and so on; adding support to Vim gives you some of that ‘magic’ IDE code navigation functionality.
- Syntastic: THE syntax checking plugin for Vim, if you are familiar with the way that code inspections work on Jetbrains and similar IDEs, syntastic will make feel right at home.
- Vim-autoclose: Automatically closes a character that could/should have a matching closing counterpart, like () “” [] {} and so on.
Markdown/Writing
As I mentioned Vim is my go to editor for drafting new posts be it books, blogs or random angry letters. From this particular section only language tools deserves a special shot-out as it makes for a great Grammar checker directly from inside Vim.
Erlang/Elixir/PHP/Elm Support
When it comes down to the individual language support there isn’t really much to highlight other than I’ve tried (and somewhat failed) to keep the plugins to a minimum and focus only on the essential language support.
So far elixir is winning the battle in terms of plugins as I’ve added additional functionality like the ability to run tests and generate content from inside Vim, I have yet to decide if I’m keeping all the plugins for it.
Git Support
Standard git support that I’m afraid I rarely use, I find myself going directly back to the shell and doing the git workflow outside of Vim, so I’m open to suggestions and to hear what everyone else is using in terms of setup.
Themes and Interfaces
Ok this is a big one but mostly because I keep forgetting to remove unused themes and colorschemes, let’s highlight the important ones:
- Vimarline: Lean and mean status/tabline for Vim; it also looks cool as fuck.
- Vim-Devicons: Because not only atom gets all the fancy icons on the sidebar, highly recommended to if you are using nerdtree.
The remaining parts of the configuration file are either plugin configuration or personal key re-mappings that I use for quality of like; I’ve done my best to document and segment each section so it should be easy enough to understand what each setting is doing.
For anyone getting starting with Vim, I do want to bring special attention to the following:
" Disable arrow movement, resize splits instead.
if get(g:, 'elite_mode')
nnoremap <Up> :resize +2<CR>
nnoremap <Down> :resize -2<CR>
nnoremap <Left> :vertical resize +2<CR>
nnoremap <Right> :vertical resize -2<CR>
endif
As there is no quickest way to force one-self to use the home row for navigation.
Final Remarks
I hope that for some of you this post has been helpful for some, and I’m far from done with my current setup, in that sense this is very much a toy that I continue playing on a nearly daily basis; now that being said and in order to get back to the original premise of the post; it is indeed a very powerful toy, specially when combined with other tools like Tmux, here is a sneak peak of my Elixir ‘IDE’ powered by Vim and Tmux:
If you want to know more about my setup, or want to share yours please leave a comment below.