Minimal (neo)vim with VSCode like setup
Tags: vim, beginners, tutorial, vscode
Published: 2021-12-26 07:39:09

I wanted to share a quick little setup to get up and running quickly with vim as an IDE.  I don't really like vim and IDE in the same sentence, but I do have to admit that having an IDE that runs in the terminal and loads fast is pretty sweet.
[header image](https://github.com/mkrl/misbrands/blob/master/vscode.svg)

```sh
~/.vimrc

~/.vim
├── _lsp.vim
├── _options.vim
├── _plugins.vim
├── _theme.vim
```

## Requirements

[nodejs](https://nodejs.org/en/download/)

## create ~/.vim directory
First we need a place to store a few files.  I am going to put them in `${HOME}/.vim/` so I will make that directory.

## _theme.vim (optional)
Lets create a file to store theme settings.  I am going to put it in `${HOME}/.vim/_theme.vim`.

```viml
syntax enable
" set t_Co=16 " uncomment to force ANSI 16 colors

if has('gui_running')
    set background=light
else
    set background=dark
endif

colorscheme gruvbox8

```

## _options.vim (optional)
Lets create a file to store vim options.  I am going to put it in `${HOME}/.vim/_options.vim`.  I tried to document the options below.  These are optional, I borrowed some of this information from [LunarVims default settings](https://www.lunarvim.org/configuration/01-settings.html). Most options can be prefixed with `no` to disable the behavior.

```viml
let mapleader="\ " " set leader key to space

set clipboard="" " don't use clipboard
set cmdheight=1 " more space in the neovim command line for
set colorcolumn="99999" " fixes indentline for now
set completeopt=menuone,noselect
set conceallevel=0 " so that `` is visible in markdown files
set cursorline " highlight the current line
set expandtab " convert tabs to spaces
set fileencoding="utf-8" " the encoding written to a file
set foldexpr="" " set to "nvim_treesitter#foldexpr()" for treesitter based folding
set hidden " required to keep multiple buffers and open multiple buffers
set hlsearch " highlight all matches on previous search pattern
set ignorecase " ignore case in search patterns
set laststatus=2 " hide statusline
set list " show hidden chars
set listchars=tab:│\ ,trail:·,nbsp:+
set nobackup " creates a backup file
set norelativenumber " set relative numbered lines
set nospell " disable spell checking
set noswapfile " creates a swapfile
set nowritebackup " if a file is being edited by another program (or was written to file while editing with another program) it is not allowed to be edited
set number " set numbered lines
set numberwidth=1 " set number column width to 2 {default 4}
set pumheight=10 " pop up menu height
set scrolloff=4 " is one of my fav
set shiftwidth=2 " the number of spaces inserted for each indentation
set showmode " shows " INSERT " in cmdline
set showtabline=2 " always show tabs
set sidescrolloff=4
set signcolumn="yes" " always show the sign column otherwise it would shift the text each time
set smartcase " smart case
set smartindent " make indenting smarter again
set spelllang="en" " language for spell checking
set splitbelow " force all horizontal splits to go below current window
set splitright " force all vertical splits to go to the right of current window
set tabstop=2 " insert 2 spaces for a tab
set timeoutlen=500 " timeout length
set title " set the title of window to the value of the titlestring
set titlestring="%<%F - vim" " what the title of the window will be set to
set undodir="/undo" .. "/undo"
set undofile " enable persistent undo
set updatetime=300 " faster completion
set wrap " display lines as one long line
```

## _plugins.vim

Lets create a file to manage vim plugins.  I am going to put it in `${HOME}/.vim/_plugins.vim`. For this tutorial we are using [vim-plug](https://github.com/junegunn/vim-plug).  

- Note fancy things are happening in the first 2 blocks of code that will auto install `vim-plug` and auto install missing plugins. (`curl` is required for this magic to happen)

- Note 3 plugins are being installed here:
  1. coc.nvim (LSP IDE magic)
  2. vim-gruvbox8 (colorscheme)
  3. vim-commentary (nice shortcuts for code commenting)

```viml
" https://github.com/junegunn/vim-plug/wiki/tips#automatic-installation

" Install vim-plug if not found
if empty(glob('~/.vim/autoload/plug.vim'))
   silent !curl -fLo ~/.vim/autoload/plug.vim --create-dirs
     \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
   endif

" Run PlugInstall if there are missing plugins
autocmd VimEnter * if len(filter(values(g:plugs), '!isdirectory(v:val.dir)'))
 \| PlugInstall --sync | source $MYVIMRC
 \| endif

" Plugins
call plug#begin()
  Plug 'neoclide/coc.nvim', {'branch': 'release'}
  Plug 'lifepillar/vim-gruvbox8'
  Plug 'tpope/vim-commentary'
call plug#end()
```
Most used shortcut key for `vim-commentary`

|Shortcut     |Description           |
|-------------|----------------------|
| `gcc`       | comment / uncomment  |

## _lsp.vim

Lets create a file to manage vim lsp servers.  I am going to put it in `${HOME}/.vim/_lsp.vim`. This is where we will configure [coc.nvim](https://github.com/neoclide/coc.nvim).

From the [example](https://github.com/neoclide/coc.nvim#example-vim-configuration).  The following has been slightly modified moving some options to _options.vim, and configuring the statusline to be as close to vanilla vim with some lsp information.

```viml
" 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

" Use <c-space> to trigger completion.
if has('nvim')
  inoremap <silent><expr> <c-space> coc#refresh()
else
  inoremap <silent><expr> <c-@> coc#refresh()
endif

" 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 `[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)

" GoTo code navigation.
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 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

" Highlight the symbol and its references when holding the cursor.
" autocmd CursorHold * silent call CocActionAsync('highlight')

" Symbol renaming.
nmap <leader>rn <Plug>(coc-rename)

" Formatting selected code.
xmap <leader>f  <Plug>(coc-format-selected)
nmap <leader>f  <Plug>(coc-format-selected)

augroup mygroup
  autocmd!
  " Setup formatexpr specified filetype(s).
  autocmd FileType typescript,json setl formatexpr=CocAction('formatSelected')
  " Update signature help on jump placeholder.
  autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp')
augroup end

" Applying codeAction to the selected region.
" Example: `<leader>aap` for current paragraph
xmap <leader>a  <Plug>(coc-codeaction-selected)
nmap <leader>a  <Plug>(coc-codeaction-selected)

" Remap keys for applying codeAction to the current buffer.
nmap <leader>ac  <Plug>(coc-codeaction)
" Apply AutoFix to problem on the current line.
nmap <leader>qf  <Plug>(coc-fix-current)

" Run the Code Lens action on the current line.
nmap <leader>cl  <Plug>(coc-codelens-action)

" Map function and class text objects
" NOTE: Requires 'textDocument.documentSymbol' support from the language server.
xmap if <Plug>(coc-funcobj-i)
omap if <Plug>(coc-funcobj-i)
xmap af <Plug>(coc-funcobj-a)
omap af <Plug>(coc-funcobj-a)
xmap ic <Plug>(coc-classobj-i)
omap ic <Plug>(coc-classobj-i)
xmap ac <Plug>(coc-classobj-a)
omap ac <Plug>(coc-classobj-a)

" Remap <C-f> and <C-b> for scroll float windows/popups.
if has('nvim-0.4.0') || has('patch-8.2.0750')
  nnoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? coc#float#scroll(1) : "\<C-f>"
  nnoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? coc#float#scroll(0) : "\<C-b>"
  inoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? "\<c-r>=coc#float#scroll(1)\<cr>" : "\<Right>"
  inoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? "\<c-r>=coc#float#scroll(0)\<cr>" : "\<Left>"
  vnoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? coc#float#scroll(1) : "\<C-f>"
  vnoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? coc#float#scroll(0) : "\<C-b>"
endif

" Use CTRL-S for selections ranges.
" Requires 'textDocument/selectionRange' support of language server.
nmap <silent> <C-s> <Plug>(coc-range-select)
xmap <silent> <C-s> <Plug>(coc-range-select)

" Add `:Format` command to format current buffer.
command! -nargs=0 Format :call CocAction('format')

" Add `:Fold` command to fold current buffer.
command! -nargs=? Fold :call     CocAction('fold', <f-args>)

" Add `:OR` command for organize imports of the current buffer.
command! -nargs=0 OR   :call     CocActionAsync('runCommand', 'editor.action.organizeImport')

" Add (Neo)Vim's native statusline support.
" NOTE: Please see `:h coc-status` for integrations with external plugins that
" provide custom statusline: lightline.vim, vim-airline.
set statusline=\ %<%f\ %h%m%r
set statusline+=%=%-10.50{ShowCocStatus()}\ %-.(%l,%c%V%)\ %P

function! ShowCocStatus() 
  return get(g:, 'coc_status', '')
endfunc

" Mappings for CoCList
" Show all diagnostics.
nnoremap <silent><nowait> <space>a  :<C-u>CocList diagnostics<cr>
" Manage extensions.
nnoremap <silent><nowait> <space>e  :<C-u>CocList extensions<cr>
" Show commands.
nnoremap <silent><nowait> <space>c  :<C-u>CocList commands<cr>
" Find symbol of current document.
nnoremap <silent><nowait> <space>o  :<C-u>CocList outline<cr>
" Search workspace symbols.
nnoremap <silent><nowait> <space>s  :<C-u>CocList -I symbols<cr>
" Do default action for next item.
nnoremap <silent><nowait> <space>j  :<C-u>CocNext<CR>
" Do default action for previous item.
nnoremap <silent><nowait> <space>k  :<C-u>CocPrev<CR>
" Resume latest coc list.
nnoremap <silent><nowait> <space>p  :<C-u>CocListResume<CR>
```

There is a lot going on in there.  This makes it so we get (tab) completion on typing, and setups tons of shortcuts for quickly doing things.  Look for `nmap` for all the settings. Here are a few of the very basics.

|Shortcut     |Description |
|-------------|------------|
| `gd`        | go to def  |
| `K`         | show hover |
| `leader rn` | rename     |
| `leader f`  | call format|

- Note some languages require formatters to be installed first.  As an example in `python` the default formatter using `coc-pyright` server is `autopep8`.  In order to use this formatter `pip install autopep8` etc...

## vimrc
Now lets bring it all together. Open `${HOME}/.vimrc` (I am not sure where this lives in windows sorry) and add the following.

```viml
source ~/.vim/_options.vim
source ~/.vim/_plugins.vim
source ~/.vim/_lsp.vim
source ~/.vim/_theme.vim
```

## Installing servers
Lets try installing a `coc.nvim` extension.  Quick [link to some extenstions](https://github.com/neoclide/coc.nvim/wiki/Using-coc-extensions#implemented-coc-extensions). For the purpose of this post lets install `coc-pyright` and try it out with a python file.

### Install extenstion

Lets create a simple python file. `vim main.py`

- Note line 4 has an error, and once we are setup with `coc.nvim` we should see it.

```py
#!/usr/bin/env python

def echo(msg):
    print msg

if __name__ == "__main__":
    echo("Hello World!")
```

- Install `coc-pyright` `:CocInstall coc-pyright`
- Save the file `:wq`

Next time the file is open it is expected to see an error indicated in line 4. Move the cursor to <u>msg</u> and the following should pop up.

![example screen shot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3emaewd2jw6f77ss98if.png)
 

## Conclusion

If everything is working as planned you have successfully setup `coc.nvim` and are now off to the races to further configure things.  Happy hacking.