My Annotated Vim CoC Config
Tags: vim
Published: 2021-04-12 16:31:13

This is extracted directly from the [CoC.nvim Example Configuration](https://github.com/neoclide/coc.nvim#example-vim-configuration). I've made some tweaks and annotated some things that work for me.

Here's the whole thing. Below this, I'll break out sections of this that I want to say a bit more about.

```vim
""""""""""""""
" CoC Config
""""""""""""""
" Use tab for trigger completion with characters ahead and navigate.
" NOTE: Use command ':verbose imap <tab>' to make sure tab is not mapped by
" other plugin before putting this into your config.
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

" Make <CR> auto-select the first completion item and notify coc.nvim to
" format on enter, <cr> could be remapped by other vim plugin
" inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm() : "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"

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

function! s:show_documentation()
  if (index(['vim','help'], &filetype) >= 0)
    execute 'h '.expand('<cword>')
  elseif (coc#rpc#ready())
    call CocActionAsync('doHover')
  else
    execute '!' . &keywordprg . " " . expand('<cword>')
  endif
endfunction

" Use `[g` and `]g` to navigate diagnostics
" Use `:CocDiagnostics` to get all diagnostics of current buffer in location list.
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)
```

I've then also installed [coc-tsserver](https://github.com/neoclide/coc-tsserver) by running:

```vim
:CocInstall coc-tsserver
```

This gives me a ton of useful support for both JavaScript and TypeScript.

### The Breakdown

This first bit is the minimal necessary config. This allows me to tab through the list. This also means `Shift-Tab` works for going backward through the list. And if you prefer, both `Ctrl-n` and `Ctrl-p` can be used to go the next and previous items respectively.

```vim
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
```

This next bit is supposed to streamline your use of CoC. It automatically highlights the top completion item. Then as soon as you hit `Enter`, it completes that item so that you can keep typing.

As you can see, I've commented it out. That is because as great as it is, it comes at an expense. First, it was often auto-completing words unexpectedly when what I was trying to do was create a new line. For instance, in Ruby files, I'd type `do` for a block and then hit enter to go the first line of the block's body. Instead, it would auto-complete `do` to `does`. Second, this overriding of the `enter` key was clashing with [`vim-endwise`](https://github.com/tpope/vim-endwise) which I depend on for Ruby development.

I say leave this out and hit `tab` once or twice to get to the completion you want.

```vim
" Make <CR> auto-select the first completion item and notify coc.nvim to
" format on enter, <cr> could be remapped by other vim plugin
" inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm() : "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
```

This next bit is wonderful for any language server you have running that provides documentation and type definitions. For instance, with TypeScript and `coc-tsserver`, I'm able to move the cursor over a variable or function call, hit `K` (`Shift-k`) and it will provide a pop up with docs and type info.

```vim
" Use K to show documentation in preview window.
nnoremap <silent> K :call <SID>show_documentation()<CR>

function! s:show_documentation()
  if (index(['vim','help'], &filetype) >= 0)
    execute 'h '.expand('<cword>')
  elseif (coc#rpc#ready())
    call CocActionAsync('doHover')
  else
    execute '!' . &keywordprg . " " . expand('<cword>')
  endif
endfunction
```

As an avid user of [`vim-unimpaired`](https://github.com/tpope/vim-unimpaired), I love these next two lines. These two bindings allow me to jump forward and backward to the next diagnostics marker. For instance, in a TypeScript file, if I change a type definition, that causes some type errors throughout the file, I can quickly jump to those spots in the file.

```vim
" Use `[g` and `]g` to navigate diagnostics
" Use `:CocDiagnostics` to get all diagnostics of current buffer in location list.
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)
```

There may be more to come, but for now this is the configuration I'm working with.

I write and [record](https://www.youtube.com/watch?v=U24ckmJZ-OI&list=PL46-cKSxMYYDgMdQZ2FRU38gaFxIBBfoa&index=5) on all things Vim. If you found this writeup useful, consider [joining my newsletter](https://crafty-builder-6996.ck.page/e169c61186) or telling me about it on [twitter](https://twitter.com/jbrancha).