Switching to Neovim
TL;DR
brew install neovim # install brew
git clone https://github.com/xuganyu96/xuganyu96.github.io.git
ln $(pwd)/xuganyu96.github.io/neovim ~/.config/nvim # link personal config
git clone --depth 1 https://github.com/wbthomason/packer.nvim\
~/.local/share/nvim/site/pack/packer/start/packer.nvim # download Packer
sudo npm install -g pyright # install Python language server
Launch nvim, call :PackerSync to install plugins, then restart nvim.
If the icons are not showing up, go to Nerd Fonts to get patched fonts
Introduction
Through college and my first job I have used a number of text editor and/or IDE’s: from Sublime to IntelliJ, to PyCharm, and VSCode. In late 2020 after watching a number of Youtube videos evangelizing Vim, I decided to give it a go and loved being able to do everything while keeping my hands on the home row at all times.
However, Vim is not perfect. For one, my instance of Vim, installed through brew on a 2019 16-inch MacBook pro (my work laptop), could not render underscore or undercurl correctly. In addition, language support in Vim, for which I chose CoC + PyRight, takes jumping through a lot of hoops to set up correctly, and even then the linting and diagnostics don’t work exactly the way I want.
After watching this video, I am convinced to switch again from vanilla Vim to Neovim. While Neovim is a fork of Vim that keeps the majority of the source code, the setup process is significantly different, especially given that I want to use Lua instead of Vimscript as the scripting language. This post documents the process by which I learned the basics of Neovim configuration using Lua, as well as my choices of plugins and default settings.
Installing Neovim
Neovim can be easily installed through brew on MacOS:
brew install neovim
nvim --version # I got v0.7.2, which has a native lsp client
Config location
With MacOS, Neovim first looks in ~/.config/nvim directory for configuration files. The first script that loads can be either init.lua or init.vim, although I am switching from Vim to Neovim for the Lua support, so init.vim it is.
With Lua, we can separate configurations that serve different purposes into different files. The Lua scripts will be placed in ~/.config/nvim/lua and accessed by the initialization script using the require keyword:
print("Hello, world!")
require("plugins") -- runs ~/.config/nvim/lua/plugins.lua
~/.config/nvim/lua/plugins.lua is where we will specify the plugins.
Plugin manager
Follow the instruction on Packer’s website:
git clone --depth 1 https://github.com/wbthomason/packer.nvim\
~/.local/share/nvim/site/pack/packer/start/packer.nvim
At this point Packer is not recognized by Neovim yet (Packer commands don’t work). We can fix that by adding the following lines to ~/.config/nvim/lua/plugins.lua
-- This file can be loaded by calling `lua require('plugins')` from your init.vim
-- Only required if you have packer configured as `opt`
vim.cmd [[packadd packer.nvim]]
return require('packer').startup(function(use)
-- Packer can manage itself
use 'wbthomason/packer.nvim'
end)
Now when we restart Neovim, the packer commands should be recognized, although since we have not specified any plugins, the commands won’t do anything
One can wipe the entire Packer installation clean by deleting Neovim’s data directory at ~/.local/share/nvim
First plugin: file tree
Let’s try our first plugin: a file tree!
First add the following lines to plugins.lua:
use 'kyazdani42/nvim-tree.lua' -- skipping the fancy stuff
After that, restart Neovim and run the :PackerSync command. A panel should pop up indicating that the installation is completed.
Add the following line to init.lua
require("nvim-tree").setup()
Now when Noevim starts, it will automatically load and initialize the file tree plugin. At this point, the commands should work, like :NvimTreeToggle.
To uninstall the plugin, first remove the use statement from plugins.lua. Restart Neovim and run :PackerClean to uninstall the file tree plugin.
Commands
Vim itself already supports custom commands. For example, to shorthand the :NvimTreeToggle command, you can define:
command TT NvimTreeToggle
To invoke the declaration of custom command in Lua, you can use the global vim object to invoke the vim.cmd function.
For now, I will organize all custom commands into a single lua/custom-commands.lua module, in which I will add the first batch of commands:
vim.cmd("command TT NvimTreeToggle")
vim.cmd("command TF NvimTreeFocus")
Of course, to load the custom-commands.lua, I will add the following line to init.lua:
require("custom-commands")
Custom key maps
If the shorthand commands still aren’t fast enough, I can try declaring custom key maps (keys or key combinations).
There are a variety of commands in Vim that can be used to map key combinations. For simplicity, I will only use nnoremap at this moment. According to :help nnoremap, this command is understood as follows:
- The first
nstands for “Normal mode”; there are other commands likevnoremapfor visual mode,xnoremapfor Ex mode, etc. noremapmeans that the target of the map cannot be used to map to a second target. This prevents recursive key maps.
For this time, I will organize all key maps into a single custom-keymaps.lua and add it to init.lua:
vim.g.mapleader = " " -- the <leader> key referenced below
vim.keymap.set("n", "<Leader>t", ":NvimTreeToggle<CR>")
vim.keymap.set("n", "<Leader>f", ":NvimTreeFocus<CR>")
More plugins
Let’s add plugins that I used in vanilla Vim and add more flares to my Neovim
First, nvim-tree among other plugins make extensive use of icons that are not readily available in MacOS. This can be patched by downloading a font from NerdFonts, installing it, and configuring the terminal to use that font. I chose JetBrainsMonoNL NF.
(Fancy) file tree
I will add the icons to the file tree:
use {
'kyazdani42/nvim-tree.lua',
requires = {
'kyazdani42/nvim-web-devicons', -- optional, for file icons
},
tag = 'nightly' -- optional, updated every week. (see issue #1193)
}
Status line
A status line is used to display
- Vim mode
- git branch
- show column/row number
use {
'nvim-lualine/lualine.nvim',
requires = { 'kyazdani42/nvim-web-devicons', opt = true }
}
Then add the initialization requirements to init.lua
require('lualine').setup {options = { theme = 'gruvbox' }}
Color scheme
Gruvbox is the greatest color scheme of all times
use "morhetz/gruvbox"
After installing the plugin, we need to activate it using Vim’s command colorscheme gruvbox. This will be organized into lua/bruce/colors.lua. We can also set the background color between dark and light, which will also be organized there:
-- lua/bruce/colors.lua
vim.cmd("colorscheme gruvbox")
vim.cmd("set bg=bark")
Language support
For langauge support I want to be able to
- Given a keyword, go to the place where it is defined
- Given a keyword, find places where it is referenced
- Autocompletion
- Preview the signature of a method
Documentation can be found with :help lsp
Per :help lsp-quickstart:
- Install
neovim/nvim-lspconfigplugin - Install some language server. Per documentation of the plugin above, I will go with pyright (written with Node.js):
sudo npm install -g pyright
There is a recommended configuration on neovim/nvim-lspconfig, which I will place into lua/lsp.lua and source in init.lua:
require("lsp")