In this article I'm going to show how you can go from a fresh install of NeoVim (nvim as shorthand) to a ready-for-code setup.
First of all you need to have nvim installed, check out their installation guide on Github for the steps. Since the project is growing fast, consider installing the latest stable version to get the new features.
A bit of context
Once you have it installed, let's learn some basic concepts of how the nvim configuration works.
NeoVim will load the user configs from a init.lua
(or init.vim
) file located at one of those folders:
- In Unix:
~/.config/nvim/
- In Windows:
~/AppData/Local/nvim/
- Or if the env $XDG_CONFIG_HOME is set:
$XDG_CONFIG_HOME/nvim/
Here is the files structure that we are going to use:
Copy1. 2└── nvim/ 3 ├── init.lua 4 ├── lua/ 5 │ └── custom/ 6 │ └── init.lua 7 └── after/ 8 └── plugin 9
The file init.lua
is our startup file, the whole config that we are going to do will be sourced from here.
At the folder lua
will be placed our .lua files and modules. Each subfolder can have an init.lua
file that will be used as an startpoint of the module (like an index.html
).
We are going to create one module called custom
, containing all of our main config files, like keymaps and plugins management.
The folder after/plugin
will contain files that will be automatically sourced by nvim after the file init.lua
finishes its execution. Here we will do some post configurations like enabling and customizing plugins.
NeoVim 🤝 Lua
NeoVim include support to be configured using Lua, you can require any fie or module placed under the lua
folder. The vim
module is automatically required and available globally at any lua file. Check the nvim docs for a full list of functions and modules that can be used.
With that in mind lets add some initial config creating the lua/custom/set.lua
:
Copy1vim.opt.nu = true 2vim.opt.errorbells = false 3vim.opt.incsearch = true 4vim.opt.hidden = true 5vim.opt.wrap = false 6vim.opt.modifiable = true 7vim.opt.inccommand = "split" 8vim.opt.clipboard = "unnamedplus" 9 10vim.opt.autoindent = true 11vim.opt.smartindent = true 12vim.opt.smarttab = true 13vim.opt.expandtab = true 14vim.opt.shiftwidth = 2 15vim.opt.softtabstop = 2 16vim.opt.tabstop = 2 17 18vim.g.mapleader = ' ' 19
With this piece of code we are setting some global configs like enabling line numnbers to be shown, configuring indentation and setting the mapleader to <space>
.
Now we need to source the set.lua
file into the root init.lua
creating the file lua/custom/init.lua
:
Copy1require("custom.set") 2
And also adding the require to the custom module at the root init.lua
:
Copy1require("custom") 2
Plugins
Since nvim is basically a text editor, we do not have features such code completion or an git integration out of the box.
To get those features working at our editor we can use some plugins. We are going to use packer.nvim to help us manage our plugins.
The main plugins we are going to install are:
- morhetz/gruvbox: an AWESOME colorscheme.
- nvim-lualine/lualine.nvim: help us to configure the statusline.
- kyazdani42/nvim-tree.lua: a file explorer.
- junegunn/fzf.vim: integration between the fzf command line tool with nvim. It will help us finding files and text into our projects(and also much more).
- tpope/vim-fugitive: a git wrapper full of cool features.
- mhinz/vim-signify: show git diff on the sign column of the editor.
- neoclide/coc.nvim: an extension host that will handle the management of our language servers and help us on the code completion and much more.
- dense-analysis/ale: a lint engine, we can make it communicate with coc.nvim
Lets add packer and install those plugins creating the file lua/custom/plugins.lua
:
Copy1vim.cmd [[packadd packer.nvim]] 2 3return require('packer').startup(function() 4 use 'wbthomason/packer.nvim' 5 6 use 'morhetz/gruvbox' 7 use 'kyazdani42/nvim-web-devicons' 8 use 'kyazdani42/nvim-tree.lua' 9 use 'nvim-lualine/lualine.nvim' 10 11 use {'junegunn/fzf', dir = '~/.fzf', run = './install --all' } 12 use 'junegunn/fzf.vim' 13 14 use 'tpope/vim-fugitive' 15 use 'mhinz/vim-signify' 16 17 use 'dense-analysis/ale' 18 use { 'neoclide/coc.nvim', branch = 'release' } 19end) 20
Don't forget to add the require to the plugins file at the lua/custom/init.lua
file:
Copy1require("custom.set") 2require("custom.plugins") 3
We also need to add some post config at the after/plugins
folder:
-
Configuring the colorscheme at
after/plugin/color.lua
Copy1vim.cmd("syntax enable") 2vim.opt.background = "dark" 3 4vim.cmd("colorscheme gruvbox") 5
-
Setting up coc.nvim at
after/plugin/completion.lua
Copy1-- Add your extensions here. 2-- Check the list of the available ones here: https://github.com/neoclide/coc.nvim/wiki/Using-coc-extensions#implemented-coc-extensions 3vim.g.coc_global_extensions = { 4} 5 6-- This will enable code completion to be triggered using the <tab> key 7vim.cmd([[ 8 function! s:check_back_space() abort 9 let col = col('.') - 1 10 return !col || getline('.')[col - 1] =~# '\s' 11 endfunction 12 13 inoremap <silent><expr> <TAB> 14 \ coc#pum#visible() ? coc#_select_confirm() : 15 \ coc#expandableOrJumpable() ? 16 \ "\<C-r>=coc#rpc#request('doKeymap', ['snippets-expand-jump',''])\<CR>" : 17 \ <SID>check_back_space() ? "\<TAB>" : 18 \ coc#refresh() 19]]) 20 21vim.g.coc_snippet_next = '<tab>' 22
-
Customizing fzf interface at
after/plugin/finder.lua
Copy1vim.g.fzf_layout = { 2 window = { 3 width = 0.7, 4 height = 0.4 5 } 6} 7 8vim.fn.setenv("FZF_DEFAULT_OPTS", "--reverse") 9
-
Setup lualine at
after/plugin/lines.lua
Copy1require('lualine').setup { 2 options = { 3 theme = 'gruvbox-material', 4 refresh = { 5 statusline = 1000, 6 tabline = 1000, 7 winbar = 1000, 8 }, 9 }, 10 sections = { 11 lualine_a = {'mode'}, 12 lualine_b = {'branch', 'diff'}, 13 lualine_c = {'filename'}, 14 lualine_x = {'encoding', 'filetype'}, 15 lualine_y = {}, 16 lualine_z = {'location'} 17 }, 18 tabline = { 19 lualine_a = {'tabs'}, 20 lualine_b = {'filename'}, 21 lualine_c = {}, 22 lualine_x = {}, 23 lualine_y = {}, 24 lualine_z = {} 25 }, 26 extensions = { 27 'nvim-tree', 28 'fugitive', 29 'man', 30 }, 31} 32
-
Setup nvim-tree at
after/plugin/tree.lua
Copy1require("nvim-tree").setup() 2
Remaps
Another key point of building your nvim config is customizing the keybindings. To help on the remapping process we are going to reuse this snnippet from theprimeagen dotfiles. Let's place it at the file lua/custom/keymap.lua
:
Copy1local M = {} 2 3local function bind(op, outer_opts) 4 outer_opts = outer_opts or { noremap = true } 5 6 return function(lhs, rhs, opts) 7 opts = vim.tbl_extend("force", 8 outer_opts, 9 opts or {} 10 ) 11 12 vim.api.nvim_set_keymap(op, lhs, rhs, opts) 13 end 14end 15 16M.nmap = bind("n", {noremap = false}) 17M.nnoremap = bind("n") 18M.vnoremap = bind("v") 19M.xnoremap = bind("x") 20M.inoremap = bind("i") 21 22return M 23
We are defining a set of functions to support all kinds of vim mappings.
Now let's create the lua/custom/remap.lua
with some usefull bindings:
Copy1local keymap = require("custom.keymap") 2local nnoremap = keymap.nnoremap 3local vnoremap = keymap.vnoremap 4local inoremap = keymap.inoremap 5local xnoremap = keymap.xnoremap 6local nmap = keymap.nmap 7 8nnoremap("Q", "<nop>") 9nmap('<esc>', ':noh <CR>') 10 11-- Add custom mappings here 12
And again, update the file lua/custom/remap.lua
with require to the remap module:
Copy1require("custom.set") 2require("custom.plugins") 3require("custom.remap") 4
Wrapping up
Now you have an NeoVim installation configured and ready to start coding.
At my personal dotfiles I dig a little bit deeper into adding more plugins and remaps, so feel free to clone the repo and get some customization insights.
Also check out ThePrimeagen youtube channnel to learn more about vim and programming in general.