Vim with fancy code completion

2019/11/20

I finally was annoyed enough to set up proper code-completion in Vim. This is mostly a highly condensed and simplified version of a great blog post from somebody else: https://octetz.com/posts/vim-as-go-ide

The main difference is that i’m not using Neovim. The reason being that i really like gvim and all the GUI options for Neovim are either bloated Electron abominations or just looking awkward.

Quick setup

Anyway, to replicate my setup you need:

Put this in your .vimrc:

" vim-plug config
call plug#begin('~/.vim/plugged')
Plug 'fatih/vim-go', { 'do': ':GoInstallBinaries', 'for': 'go' }
Plug 'tpope/vim-surround'
Plug 'scrooloose/nerdtree', { 'on':  'NERDTreeToggle' }
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'sheerun/vim-polyglot'
Plug 'PProvost/vim-ps1'
call plug#end()

Then either open Vim and run :PlugInstall, or run it via your shell with vim +PlugInstall. The result will be the same. You’ll get some progress indicators and in the end a confirmation that all the wanted plugins are installed.

Explaination of the plugins from above:

Install language servers

coc.nvim enables you to use the very nice concept of language servers (https://en.wikipedia.org/wiki/Language_Server_Protocol). So to use that, you need to install some of them. On Arch you’d do something like

$ pacman -S bash-language-server python-language-server

go-langserver can be installed with

$ go get -u github.com/sourcegraph/go-langserver

Configure language servers

To actually configure coc.nvim to make use of some language servers run :CocConfig in your Vim. Then configure it like:

{
      "powershell.integratedConsole.showOnStartup": false,
      "diagnostic.virtualText": true,
      "languageserver": {
      "golang": {
        "command": "go-langserver",
        "filetypes": ["go"],
        "initializationOptions": {
          "gocodeCompletionEnabled": true,
          "diagnosticsEnabled": true,
          "lintTool": "golint"
        }
      }
    },
      "languageserver": {
      "bash": {
        "command": "bash-language-server",
        "args": ["start"],
        "filetypes": ["sh"],
        "ignoredRootPaths": ["~"]
      }
    },
    "languageserver": {
      "terraform": {
        "command": "terraform-lsp",
        "filetypes": ["terraform"],
        "initializationOptions": {}
      }
    }
}

And then you want to make sure some coc.nvim extensions are installed if missing. You do that with adding this to your .vimrc:

" automatically install coc extensions if they're missing
let g:coc_global_extensions=[ 'coc-powershell', 'coc-python' , 'coc-yaml']

Overall coc.nvim config

Finally, you want to properly configure coc.nvim. Just put this into your .vimrc:

" -------------------------------------------------------------------------------------------------
" coc.nvim default settings
" -------------------------------------------------------------------------------------------------

" if hidden is not set, TextEdit might fail.
set hidden
" Better display for messages
set cmdheight=2
" Smaller updatetime for CursorHold & CursorHoldI
set updatetime=300
" don't give |ins-completion-menu| messages.
set shortmess+=c
" always show signcolumns
set signcolumn=yes

" Use tab for trigger completion with characters ahead and navigate.
" Use command ':verbose imap <tab>' to make sure tab is not mapped by other plugin.
inoremap <silent><expr> <TAB>
      \ pumvisible() ? "\<C-n>" :
      \ <SID>check_back_space() ? "\<TAB>" :
      \ coc#refresh()
inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<C-h>"

function! s:check_back_space() abort
  let col = col('.') - 1
  return !col || getline('.')[col - 1]  =~# '\s'
endfunction

" Use <c-space> to trigger completion.
inoremap <silent><expr> <c-space> coc#refresh()

" Use `[c` and `]c` to navigate diagnostics
nmap <silent> [c <Plug>(coc-diagnostic-prev)
nmap <silent> ]c <Plug>(coc-diagnostic-next)

" Remap keys for gotos
nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)

" Use U to show documentation in preview window
nnoremap <silent> U :call <SID>show_documentation()<CR>

" Remap for rename current word
nmap <leader>rn <Plug>(coc-rename)

" Remap for format selected region
vmap <leader>f  <Plug>(coc-format-selected)
nmap <leader>f  <Plug>(coc-format-selected)
" Show all diagnostics
nnoremap <silent> <space>a  :<C-u>CocList diagnostics<cr>
" Manage extensions
nnoremap <silent> <space>e  :<C-u>CocList extensions<cr>
" Show commands
nnoremap <silent> <space>c  :<C-u>CocList commands<cr>
" Find symbol of current document
nnoremap <silent> <space>o  :<C-u>CocList outline<cr>
" Search workspace symbols
nnoremap <silent> <space>s  :<C-u>CocList -I symbols<cr>
" Do default action for next item.
nnoremap <silent> <space>j  :<C-u>CocNext<CR>
" Do default action for previous item.
nnoremap <silent> <space>k  :<C-u>CocPrev<CR>
" Resume latest coc list
nnoremap <silent> <space>p  :<C-u>CocListResume<CR>

This seemed to work fine for me. Have fun and enjoy your Vim on steroids :-)