My basic Neovim setup
Tags: webdev, neovim, vim, programming
Published: 2021-11-09 19:24:01


In the last few articles, I commented on [why](https://dev.to/elvessousa/why-i-switched-from-vscode-to-neovim-1da4) I adopted Neovim as my main editor and went through the [basics](https://dev.to/elvessousa/vim-basics-for-beginners-5aln) for using the program. However, it does not come with plugins or nice looks by default. Here, I show you how I set up my Neovim.

The programming languages I use in my daily life are JavaScript (JSX and TypeScript), CSS, Sass and lately I'm studying Python, too. Therefore, my configuration supports these technologies.

![Neovim](https://i.imgur.com/9VeS1of.png)
_Neovim as is on first launch_

### About my choices for the configuration

As you can imagine, Neovim is configured by text files.

There are two ways to configure it: using the Lua language, or VimScript, created for Vim. Here I will use the last one, as it works for both Vim and Neovim. So I kill two birds with one stone.

I have no experience with Lua, and at the moment I have no plans on learning the language just to configure the application. I'm aware that there are more modern ways to do this, but I prefer to keep my settings concise.

At the end of this guide, you will not have a tangle of files and directories, as you may have seen elsewhere, but rather a single file, just enough for comfortable use.

### Setting options

As with most software, there is a way to edit options and preferences. These can be tested with the command `:set <option-name>`. Try opening an instance of Vim/Neovim now and type `:set number`, you'll notice that the line numbers will appear on the side. The problem with this is that these commands will have to be repeated each time you reopen the program.

### Saving settings

To keep these settings saved, just write these commands in a file in your user folder. The name and location will vary depending on whether you use Vim or Neovim:

- **Vim**: `<username>/.vimrc`
- **Neovim**: `<username>/.config/nvim/init.vim`

If these files/directories do not exist in your home folder, just create them manually. Remember that files starting with "." are hidden by default on Unix systems (Linux, BSDs and macOS). Make sure you are showing hidden files in your file manager or terminal.

### Basic options in the configuration file

Before we get into more complex things like extensions and their settings, let's stick to the basics. There are many options to tinker with, however, the ones I put below are the ones I currently use. Note that in the file will have the commands that would have to be typed every time the editor opened.

```vim
" Options
set background=dark
set clipboard=unnamedplus
set completeopt=noinsert,menuone,noselect
set cursorline
set hidden
set inccommand=split
set mouse=a
set number
set relativenumber
set splitbelow splitright
set title
set ttimeoutlen=0
set wildmenu

" Tabs size
set expandtab
set shiftwidth=2
set tabstop=2
```

Here is the explanation of each command above:

- `background=dark`: apply the color set for dark screens. Not just the background of the screen, as it might look.
- `clipboard=unnamedplus`: enables the clipboard between Vim/Neovim and other applications.
- `cursorline`: highlights the current line in the editor.
- `completeopt`: modifies the auto-complete menu to behave more like an IDE.
- `hidden`: hide unused buffers[^1].
- `inccommand=split`: show replacements in a split screen, before applying to the file.
- `mouse=a`: allows the use of the mouse in the editor.
- `number`: shows line numbers.
- `relativenumber`: show line numbers starting from the current one. Useful for using multiline commands.
- `splitbelow splitright`: changes split screen behavior with the command `:split` (split horizontally) and `:vsplit` (vertically). In this case, the screens will always split below the current screen and to the right.
- `title`: show the file title
- `ttimeoutlen=0`: time in milliseconds to run commands.
- `wildmenu`: shows a more advanced menu for auto-completion suggestions.
- `expandtab`: transforms tabs into spaces.
- `shiftwidth=2`: number of spaces for indentation.
- `tabstop=2`: number of spaces for tabs.

### Syntax

To add automatic syntax support for open files:

```vim
filetype plugin indent on
syntax on
```

### Color support

To enable 256 colors on the terminal:

```vim
set t_Co=256
```

Below is a logic I made for Vim to detect if the system supports a greater number of colors. Note that I made a conditional to detect if the terminal emulator is Apple's one. For some reason the colors behave differently in the versions I used.

```vim
" True color if available
let term_program=$TERM_PROGRAM

" Check for conflicts with Apple Terminal app
if term_program !=? 'Apple_Terminal'
	set termguicolors
else
	if $TERM !=? 'xterm-256color'
		set termguicolors
	endif
endif
```

If you don't use the Apple terminal, ignore that part and just add the code inside the `else`.

### Support for true italics

If you're like me and like fonts with true italic support, add these lines:

```vim
" Italics
let &t_ZH="\e[3m"
let &t_ZR="\e[23m"
```

Don't ask me what these codes there mean. All I know is that a terminal with italic support will recognize them as italic.

---

### NetRW: Vim's default file manager

_Notice: If you already know a little about Vim and are not interested in NetRW, skip this part._

![NetRW](https://i.imgur.com/QCh5fZ5.png)
_NetRW as it is_

If you've ever used an IDE in your life, you might have noticed that there's always a tree on the side with the project files. Vim/Neovim use NetRW by default, which brings this same function.

When opening Vim or Neovim using the `[n]vim .` command to open the project directory, the file listing will appear inside it. It can also be opened using the `:Ex` ,`:Vex` or `:Sex` commands.

You can use it with the default settings, but personally, some things bother me, so I used the following settings:

```vim
" File browser
let g:netrw_banner=0
let g:netrw_liststyle=0
let g:netrw_browse_split=4
let g:netrw_altv=1
let g:netrw_winsize=25
let g:netrw_keepdir=0
let g:netrw_localcopydircmd='cp -r'
```

Here's the explanation:

- `g:netrw_banner=0`: hides the top banner that appears by default.
- `g:netrw_liststyle=0`: change the display of files.
  - `0` shows only one directory at a time.
  - `1` shows file data.
  - `2` shows files in columns.
  - `3` shows as a tree where open directories are expanded.
- `g:netrw_browse_split=4`: changes how files are opened.
  - `1` opens files in a horizontal split.
  - `2` opens in a vertical split.
  - `3` opens in a new tab.
  - `4` opens in a previous window, avoiding the creation of more divisions.
- `g:netrw_altv=1`: switches the NetRW display to the left.
- `g:netrw_winsize=25`: limits window size to 25% of available screen space.
- `g:netrw_keepdir=0`: keeps the directory you accessed previously.
- `g:netrw_localcopydircmd`: modifies the command used to copy files. By default, NetRW just copies empty folders. To change this, I set the default command to `cp -r` so that the copy occurs recursively.

To make creating files easier, I've added more settings:

```vim
" Create file without opening buffer
function! CreateInPreview()
  let l:filename = input('please enter filename: ')
  execute 'silent !touch ' . b:netrw_curdir.'/'.l:filename
  redraw!
endfunction

" Netrw: create file using touch instead of opening a buffer
function! Netrw_mappings()
  noremap <buffer>% :call CreateInPreview()<cr>
endfunction

augroup auto_commands
	autocmd filetype netrw call Netrw_mappings()
augroup END
```

This prevents NetRW from opening an empty screen just to create a file.

If you want to use NetRW, I believe this is the minimum needed to use it comfortably.

### NetRW and its problems

There are several criticisms of NetRW for the way it causes a buffer mess. From what I've researched, it seems that using `g:netrw_liststyle=3` tree mode tends to make this behavior worse. Errors are random, making fixing it a difficult process, and updating only NetRW is more complex than it should be.

That's why there are several alternatives to NetRW, and the most famous one is NERDTree, which I use in my main configuration.

---

## Plugins

![NetRW](https://i.imgur.com/YpJyJnt.png)
_Neovim using the tweaks we made so far_

### Plugin manager setup

Finally, it's time to configure the plugins. Unlike VS Code or other editors, there are several options for extension installers: Neobundle, Vundle, Vim-Plug, etc. Here I will use the last one, which seems to be the most used nowadays. To install it, open a terminal and run the following command:

For Vim:

```bash
$ curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
```

For Neovim:

```bash
$ sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \
       https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
```

These commands above do all the necessary to have Vim-Plug available in your editor.

### Installing plugins

Vim-Plug reads a part of your configuration file to find the extension in Github and install it in the editor. This part is delimited by the following structure:

```vim
call plug#begin()
	Plug '<github-user>/<repo-name>'
call plug#end()
```

You can add multiple extensions at once, just add multiple `Plug` as desired. There is a website that catalogs these plugins for easy discovery, if you want to visit. It's called Vim Awesome. The link is at the end of the article.

### Plugins I use

Here's what I use on my Vim/Neovim:

```vim
call plug#begin()
	" Appearance
	Plug 'vim-airline/vim-airline'
	Plug 'ryanoasis/vim-devicons'

	" Utilities
	Plug 'sheerun/vim-polyglot'
	Plug 'jiangmiao/auto-pairs'
	Plug 'ap/vim-css-color'
	Plug 'preservim/nerdtree'

	" Completion / linters / formatters
	Plug 'neoclide/coc.nvim',  {'branch': 'master', 'do': 'yarn install'}
	Plug 'plasticboy/vim-markdown'

	" Git
	Plug 'airblade/vim-gitgutter'
call plug#end()
```

- **Vim Airline**: modifies the status bar to make it more pleasant than the default one.
- **Vim Devicons**: shows icons in the interface, based on icon fonts[^2].
- **Vim Polyglot**: syntax highlighting for several programming languages.
- **Auto Pairs**: automatically closes parentheses, square brackets and braces when typing.
- **Vim CSS Color**: shows colors (hex, RGB, HSL) directly in the code.
- **NERDTree**: sidebar to access project files. Replaces the NetRW I mentioned earlier.
- **CoC (Conquer of Completion)**: provides auto-completion, very similar to what is available in VS Code. I'll go into details about it later.
- **Vim Markdown**: better support for Markdown file syntax.
- **Vim GitGutter**: Shows Git changes in open files.

Writing these lines with addresses is not enough to install extensions. First, you must save the configuration file and restart Vim in order for the editor to comply with the newly added settings. After that, run the `:PlugInstall` command.

![Vim Plug](https://i.imgur.com/a3ZXjUJ.png)
_Vim Plug_

A window will open and show the installation process for each extension. After installation, you will have to restart Vim one more time to use the new extensions.

To uninstall, remove the line of the plugin you want to get rid of from the configuration file and after restarting the editor, run the command `:PlugClean`.

---

## Color schemes

If there's something critical to a good development experience, it's a good color scheme. These are installed in the same way as extensions.

First, add the line with the path to the theme's Github repository:

```vim
call plug#begin()
	Plug 'morhetz/gruvbox'
call plug#end()
```

To use the theme, add the command `colorscheme gruvbox` in the configuration file. This will be enough to get rid of that black screen and stark colors that come by default.

### Color scheme I use

![Sobrio and NetRW](https://i.imgur.com/57T9ab6.png)
_Sobrio: the theme I use in my daily life_

Despite setting the example with the GruvBox theme, this is not the theme I use. Color schemes are very subjective things and as I used this theme, I ended up getting a little bored with it. The same happened with others. Since I couldn't find one I liked, I made my own. It's called Sobrio. If you want to test it, just follow the same recipe above, changing only the repository:

```vim
call plug#begin()
	Plug 'elvessousa/sobrio'
call plug#end()
```

Then just add `colorscheme sobrio` and restart the editor. Please feel free to leave your comment and suggestion about it. I would like to know your opinion. If you want to know more about it, the link for the website is at the end of the article.

---

## Plugins configuration

### Devicons

Devicons is an extension that adds icons to Vim's interface. In order to work, it is necessary to have fonts with icon support installed on the system.

Look for "NERD Fonts" on the internet, and you'll find plenty of them. The way to install fonts on the system varies a lot depending on the operating system, and I won't go into details here. Download it, install and apply the font for your terminal emulator. Other than that, no other configuration is required.

### Airline

![Airline](https://i.imgur.com/8CcRTC2.jpg)

Little to set up here. Basically it's saying which theme to use and if you want to use "Powerline" mode, which are these lines with arrow shapes.

```vim
let g:airline_theme='sobrio'
let g:airline_powerline_fonts = 1
let g:airline#extensions#tabline#enabled = 1
```

The last option above shows enables Airline for the tab bar.

### NERDTree

NERDTree is the replacement for NetRW. By default, it works fine. I just added a line to show hidden files by default.

```vim
" File browser
let NERDTreeShowHidden=1
```

You know, I like to see `.env` or `.gitignore` files without having to type `I` first.

![Sobrio para Vim](https://i.imgur.com/pWiMQB0.png)
_Neovim com as configurações até agora_

### CTRLP

This plugin enables file search in your project's folder. It is activated by the same keyboard shortcut that gives its name. If you've ever used VS Code, you will get used to it very quickly. The only setting I made was this one:

```vim
let g:ctrlp_user_command = ['.git/', 'git --git-dir=%s/.git ls-files -oc --exclude-standard']
```

Putting the line above into plain English: CTRLP will ignore every file or directory mentioned inside your project's `.gitignore` file.

### Vim Markdown

If you don't use Markdown, skip this part.

As I use the Markdown format a lot to write on my blog, I installed this extension to improve the display of this type of file on my Neovim.

```vim
" Disable math tex conceal feature
let g:tex_conceal = ''
let g:vim_markdown_math = 1

" Markdown
let g:vim_markdown_folding_disabled = 1
let g:vim_markdown_frontmatter = 1
let g:vim_markdown_conceal = 0
let g:vim_markdown_fenced_languages = ['tsx=typescriptreact']
```

I use YAML headers (Frontmatter) at the top so that Gatsby does its magic. Without this plugin the frontmatter and code blocks syntax is not recognized.

### Coc: Conquer of Completion

This is the plugin that requires the most configuration. Conquer of Completion works as a server that provides auto-completion and code correction tools, working similar to VS Code. It supports extensions too, and these are responsible for adding support for the desired programming languages.

The weakness of CoC is that it is based on NodeJS, so the dependencies of installed packages will take up a certain amount of disk space, like all Node projects. Of the ones I've tested so far, it has met my expectations the most, with the least configuration.

Installing extensions is simple: just run the command `:CocInstall <extension-name>` and the features will be available for you to use.

Here are the extensions I use:

- **coc-tsserver**: support for JavaScript, TypeScript and React.
- **coc-css**: support for CSS, Sass and SCSS.
- **coc-eslint**: lints JavaScript and TypeScript files.
- **coc-emmet**: adds shortcuts to write less in HTML, CSS, Sass, SCSS and extended JavaScript files (JSX,TSX)
- **coc-pyright**: Python support.
- **coc-prettier**: formats the code according to well accepted standards in the community.

To install them all at once, just run the command:

```vim
:CocInstall coc-tsserver coc-css coc-eslint coc-emmet coc-pyright coc-prettier
```

### Prettier with coc-prettier

```vim
" Language server stuff
command! -nargs=0 Prettier :call CocAction('runCommand', 'prettier.formatFile')
```

The command above will run Prettier and format the file whenever you save the file. This helps a lot, as I spend less time pressing `Tab` to indent code.

### Coc settings in JSON

Apart from the `init.vim` or `.vimrc` we've used so far, I also use a separate file called `coc-settings.json`. With it it is possible to modify the behavior of your extensions.

```json
{
  "coc.preferences.formatOnSaveFiletypes": [
    "css",
    "sass",
    "scss",
    "markdown",
    "html",
    "json",
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "python"
  ],
  "python.formatting.provider": "black",
  "python.formatting.blackPath": "~/.local/bin/black",
  "prettier.singleQuote": true,
  "javascript.autoClosingTags": true,
  "typescript.autoClosingTags": true,
  "emmet.includeLanguages": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "html"
  ],
  "codeLens.enable": false
}
```

Here is the explanation of the settings above:

1. Configure CoC to format some file types when saving.
2. Configure Prettier to prefer single quotes in formatting.
3. Inform which code formatter for python, "black" in this case, and passed the path to black on my system.
4. Enable auto-close for extended JavaScript tags (JSX)
5. Enable Emmet for more file formats.
6. Disable Code Lens, as it crashed occasionally.

Note that if you don't use Python, just ignore the settings for it. For the Python formatter to work, it must be installed on the system:

```shell
$ pip install black
```

---

## Keyboard shortcuts

As with other programs, there is a way to configure keyboard shortcuts. This can be done for each editor mode. The possibilities are many. To add a keyboard shortcut in Vim, just add the expression:

```vim
[first-letter-of-target-mode]noremap <key-or-letters> :Command
```

The strange word `noremap` comes from _"**No**n **Re**cursive **Map**ping"_, "Non-Recursive Mapping". This means that the created shortcut will not overwrite another similar shortcut, avoiding conflicts.

As I'm a bit minimalist, I didn't add many shortcuts.

```vim
" Normal mode remappings
nnoremap <C-q> :q!<CR>
nnoremap <F4> :bd<CR>
nnoremap <F5> :NERDTreeToggle<CR>
nnoremap <F6> :sp<CR>:terminal<CR>

" Tabs
nnoremap <S-Tab> gT
nnoremap <Tab> gt
nnoremap <silent> <S-t> :tabnew<CR>
```

Here is the explanation of the above mappings:

- `Ctrl+q`: closes the open screen.
- `F4`: closes the open file.
- `F5`: shows or hides NERDTree.
- `F6`: opens a terminal in a lower split window.
- `Shift+Tab`: switch to the previous tab.
- `Shift+t`: creates a tab.
- `Tab`: switch to the next open tab.

Note that there are several `<CR>`. These represent all the times you would have to press `Enter` in command mode. **CR** is the abbreviation for _"Carriage Return"_: a term from the time when typewriters were used. Today, we understand it as the `Enter` or `Return` key.

All shortcuts I used are for normal mode, so `nnoremap` is on every line. If it was for insert mode it would be `inoremap`, `vnoremap` for visual mode and so on.

### Automatic commands

To automate some things, Vim makes it possible to define what it calls `autocmd`: automatic commands performed on certain events.

```vim
" Auto Commands
augroup auto_commands
	autocmd BufWrite *.py call CocAction('format')
	autocmd FileType scss setlocal iskeyword+=@-@
augroup END
```

Above, I defined two actions: one to format the Python code when saving the file, and another to resolve an inconsistency I had with SCSS files.

---

## Wrap up

Without a doubt, this was the longest article I've made so far, but if you followed the instructions, you have a very functional editor now. There are other plugins, but as you get used to it and build confidence, you will test out others better suitable for your needs.

Ideally, try to keep the init file simple. Too much extensions can affect performance. I use other settings that I haven't detailed in this article, as they are not important for most cases. I'm providing the link to my **dotfiles** repository for you to check at the end of this article if you want.

See you in the next article!

### Links

- [This article in Portuguese](https://blog.elvessousa.com.br/post/vim-parte-3)
- [My dotfiles](https://github.com/elvessousa/.dotfiles)
- [Sobrio theme](https://sobrio.elvessousa.com.br)
- [Vim Awesome](https://vimawesome.com)

[^1]: **Buffer:** A buffer is an area of Vim's memory used to hold text read from a file.
[^2]: **Icon fonts:** A collection of vector icons avaible as font.

---

If this article helped you in some way, consider [donating](https://www.buymeacoffee.com/elvessousa). This will help me to create more content like this!