Skip to main content

Neovim

NeoVim is an extended version of Vim, the well-known text editor, designed to provide a more powerful and user-friendly experience for coding and text editing. It retains the classic modal editing features of Vim while introducing enhancements such as a built-in terminal, asynchronous job control, and better scriptability. This makes NeoVim highly efficient for programming, with support for numerous programming languages and a vast ecosystem of plugins. It's particularly favored by developers who require a highly customizable and efficient editor for coding, server administration, and automation tasks. The integration of modern GUI features, along with its traditional command-line interface, broadens its appeal to both new and experienced users who seek a versatile, lightweight, and powerful text-editing tool.

Build from Source

To install the latest version of neovim, you'll need to build the application from source. The instructions provided require neovim 0.9.x. Fortunately the process is fairly straightforward, provided the prerequisite steps are followed. The following instructions are slightly modified steps sourced from the official Neovim GitHub repository.

Build Prerequisites

Ubuntu

sudo apt-get install ninja-build gettext cmake unzip curl

MacOS

  1. Install Xcode Command Line Tools: xcode-select --install
  2. Install Homebrew
  3. Install Neovim build dependencies:
brew install ninja cmake gettext curl

Quickstart

  1. git clone https://github.com/neovim/neovim
  2. cd neovim && make CMAKE_BUILD_TYPE=RelWithDebInfo
    • If you want the stable release, also run git checkout stable.
  3. sudo make install
    • Default install location is /usr/local
    • On Debian/Ubuntu, instead of installing files directly with sudo make install, you can run cd build && cpack -G DEB && sudo dpkg -i nvim-linux64.deb to build DEB-package and install it. This should help ensuring the clean removal of installed files.

Notes:

  • From the repository's root directory, running make will download and build all the needed dependencies and put the nvim executable in build/bin.
  • After building, you can run the nvim executable without installing it by running VIMRUNTIME=runtime ./build/bin/nvim.

Check Neovim Version

nvim --version

NVIM v0.9.5
Build type: RelWithDebInfo
LuaJIT 2.1.1692716794

Neovim RC Configuration

These instructions are based on the 0 to LSP : Neovim RC From Scratch YouTube video from ThePrimeagen. Remember to like, comment, and subscribe ;)

Directory Structure and init.lua

  1. Create the ~/.config/nvim/ directory for configuration settings
mkdir ~/.config/nvim && cd ~/.config/nvim
  1. Launch nvim and create the init.lua file.
nvim .
  • The default netrw directory listing is launched.
" ============================================================================
" Netrw Directory Listing (netrw v171)
" /home/reece/.config/nvim
" Sorted by name
" Sort sequence: [\/]$,\<core\%(\.\d\+\)\=\>,\.h$,\.c$,\.cpp$,\~\=\*$,*,\.o$,\.obj$,\.i
" Quick Help: <F1>:help -:go up dir D:delete R:rename s:sort-by x:special
" ==============================================================================
../
./
  • Type the % moniker to create a new file and make init.lua
  • Within init.lua, add a basic print statement:
print("hello")
  1. From netrw, create a new directory named lua to store custom configuration.
  • Type the d moniker to create the directory
  • Create another directory with the name to specify your configuration. In my case this will be ~/.config/nvim/lua/reece/
  1. From within your custom directory, create another init.lua file.
~/.config/nvim/lua/reece/init.lua
print("Hello from reece")
  1. Require your custom directory in the ~/.config/nvim/init.lua file. The init.lua file is automatically included.
~/.config/nvim/init.lua
require("reece")
print("hello")
  1. Quit nvim and reload your shell. e.g. zsh
  • Observe when relaunching nvim . that your print statements are now shown.
nvim .

hello from reece
hello
Press ENTER or type command to continue

The First Remap

  1. Create a file named remap.lua within the custom configuration directory.
  • Set configure vim.g.mapleader and create the first vim.keymap.set
~/.config/nvim/lua/reece/remap.lua
vim.g.mapleader = " "
vim.keymap.set("n", "<leader>pv", vim.cmd.Ex)
  • The "n" means, "in Normal mode"
  • Test the file by executing :so to source the file.
  • Execute <leader>pv and you should be returned to Netrw using vim's Ex Mode
  1. Require the remap.lua file in the init.lua file in the custom configuration directory.
~/.config/nvim/lua/reece/init.lua
require("reece.remap")
print("hello from reece")
  • Note: You do not need to include the .lua file extension when requiring the file.

Plugin Manager (Packer)

  1. Clone the wbthomason/packer.nvim repo.
git clone --depth 1 https://github.com/wbthomason/packer.nvim\
~/.local/share/nvim/site/pack/packer/start/packer.nvim
  1. Create the packer.lua file within the custom configuration directory.
~/.config/nvim/lua/reece/packer.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
  • Source the file using :so and then execute :PackerSync to install the dependencies.
  1. Install the telescope.nvim plugin for fuzzy finding.
use {
'nvim-telescope/telescope.nvim', tag = '0.1.5',
-- or , branch = '0.1.x',
requires = { {'nvim-lua/plenary.nvim'} }
}
  • Update the packer.lua file with the telescope plugin
~/.config/nvim/lua/reece/packer.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'

use {
'nvim-telescope/telescope.nvim', tag = '0.1.5',
-- or , branch = '0.1.x',
requires = { {'nvim-lua/plenary.nvim'} }
}
end
  1. Add key remaps for telescope
  • Create the telescope.lua file in the nvim/after/plugin/ directory.
~/.config/nvim/after/plugin/telescope.lua
local builtin = require('telescope.builtin')
vim.keymap.set('n', '<leader>pf', builtin.find_files, {})
vim.keymap.set('n', '<C-p>', builtin.git_files, {})
vim.keymap.set('n', '<leader>ps', function()
builtin.grep_string({ search = vim.fn.input("grep > ") })
end)
vim.keymap.set('n', '<leader>vh', builtin.help_tags, {})
  • <leader>pf = "Project Files" - uses telescope to search for files in the project
  • <C-p> = "Ctrl+P" - uses telescope to search for files tracked only by git.
  • <leader>ps = "Project Search" - uses ripgrep to grep for files in the current working directory tree.
  • <leader>vh = "Vim Help" - opens the vim help window for references.
note

The BurntSushi/ripgrep tool is required to utilze the grep function. Install it by following the directions in the README.md.

Colorscheme

This step is entirely optional, but is provided as part of the YouTube tutorial follow along.

  1. Update the packer.lua file in the custom config directory with the rose-pine colorscheme.
~/.config/nvim/lua/reece/packer.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'

use {
'nvim-telescope/telescope.nvim', tag = '0.1.5',
-- or , branch = '0.1.x',
requires = { {'nvim-lua/plenary.nvim'} }
}

use({
'rose-pine/neovim',
as = 'rose-pine',
config = function()
vim.cmd('colorscheme rose-pine')
end
})
end
  1. Modify the color scheme according to personal preferences with a colors.lua after plugin file.
~/.config/nvim/after/plugin/colors.lua
require('rose-pine').setup({
disable_background = true
})

function ColorMyPencils(color)
color = color or "rose-pine"
vim.cmd.colorscheme(color)

vim.api.nvim_set_hl(0, "Normal", { bg = "none" })
vim.api.nvim_set_hl(0, "NormalFloat", { bg = "none" })

end

ColorMyPencils()

More Plugins

Treesitter

Tree-sitter is a parser generator tool and an incremental parsing library. It can build a concrete syntax tree for a source file and efficiently update the syntax tree as the source file is edited. Tree-sitter aims to be:

  • General enough to parse any programming language
  • Fast enough to parse on every keystroke in a text editor
  • Robust enough to provide useful results even in the presence of syntax errors
  • Dependency-free so that the runtime library (which is written in pure C) can be embedded in any application

via: tree-sitter

  1. Add the treesitter plugin to the packer.lua file in the custom configuration directory
~/.config/nvim/lua/reece/packer.lua
use('nvim-treesitter/nvim-treesitter', { run = ':TSUpdate' })
  • Remember to run :PackerSync to install the plugin after making changes.
  1. Create the treesitter.lua file in the after plugin file.
~/.config/nvim/after/plugin/treesitter.lua
require'nvim-treesitter.configs'.setup {
-- A list of parser names, or "all"
ensure_installed = { "vimdoc", "javascript", "typescript", "c", "lua", "rust" },

-- Install parsers synchronously (only applied to `ensure_installed`)
sync_install = false,

-- Automatically install missing parsers when entering buffer
-- Recommendation: set to false if you don't have `tree-sitter` CLI installed locally
auto_install = true,

highlight = {
-- `false` will disable the whole extension
enable = true,

-- Setting this to true will run `:h syntax` and tree-sitter at the same time.
-- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
-- Using this option may slow down your editor, and you may see some duplicate highlights.
-- Instead of true it can also be a list of languages
additional_vim_regex_highlighting = false,
},
}

Harpoon

  1. Add the harpoon plugin to the packer.lua file in the custom configuration directory
~/.config/nvim/lua/reece/packer.lua
use('theprimeagen/harpoon')
  1. Create the harpoon.lua file in the after plugin file.
~/.config/nvim/after/plugin/harpoon.lua
local mark = require("harpoon.mark")
local ui = require("harpoon.ui")

vim.keymap.set("n", "<leader>a", mark.add_file)
vim.keymap.set("n", "<C-e>", ui.toggle_quick_menu)

vim.keymap.set("n", "<C-h>", function() ui.nav_file(1) end)
vim.keymap.set("n", "<C-t>", function() ui.nav_file(2) end)
vim.keymap.set("n", "<C-n>", function() ui.nav_file(3) end)
vim.keymap.set("n", "<C-s>", function() ui.nav_file(4) end)

Undotree

  1. Add the undotree plugin to the packer.lua file in the custom configuration directory
~/.config/nvim/lua/reece/packer.lua
use('mbbill/undotree')
  1. Create the undotree.lua file in the after plugin file.
~/.config/nvim/after/plugin/undotree.lua
vim.keymap.set("n", "<leader>u", vim.cmd.UndotreeToggle)

Vim Fugitive

  1. Add the vim-fugitive plugin to the packer.lua file in the custom configuration directory.
~/.config/nvim/lua/reece/packer.lua
use('mbbill/undotree')
  1. Create the fugitive.lua file in the after plugin file.
~/.config/nvim/after/plugin/fugitive.lua
vim.keymap.set("n", "<leader>gs", vim.cmd.Git)

LSP (Language Server Protocol)

The Language Server Protocol (LSP) is a standardized protocol used to provide programming language-specific features to code editors and Integrated Development Environments (IDEs). Its purpose is to separate the language-specific processing (like code completion, diagnostics, definitions) from the editor's interface. By doing this, LSP allows a single language server to be reused in multiple development tools, eliminating the need to implement and update specific language capabilities for each editor. This results in consistent and high-quality developer tools and experiences across different platforms and editors.

  1. Add the lsp-zero plugin to the packer.lua file in the custom configuration directory.
~/.config/nvim/lua/reece/packer.lua
use {
'VonHeikemen/lsp-zero.nvim',
branch = 'v3.x',
requires = {
--- Manage the language servers from neovim
{'williamboman/mason.nvim'},
{'williamboman/mason-lspconfig.nvim'},

-- LSP Support
{'neovim/nvim-lspconfig'},
-- Autocompletion
{'hrsh7th/nvim-cmp'},
{'hrsh7th/cmp-nvim-lsp'},
{'L3MON4D3/LuaSnip'},
}
}
  1. Create the lsp.lua file in the after plugin file.
~/.config/nvim/after/plugin/lsp.lua
local lsp_zero = require('lsp-zero')

lsp_zero.on_attach(function(client, bufnr)
-- see :help lsp-zero-keybindings
-- to learn the available actions
lsp_zero.default_keymaps({buffer = bufnr})
end)

require('mason').setup({})
require('mason-lspconfig').setup({
ensure_installed = {
'tsserver',
'eslint',
'lua_ls',
'rust_analyzer',
},
handlers = {
lsp_zero.default_setup,
lua_ls = function()
local lua_opts = lsp_zero.nvim_lua_ls()
require('lspconfig').lua_ls.setup(lua_opts)
end,
},
})

Editor Settings

  1. Create a set.lua file within the custom configuration directory.
~/.config/nvim/lua/reece/set.lua
vim.opt.guicursor = ""

vim.opt.nu = true
vim.opt.relativenumber = true

vim.opt.tabstop = 4
vim.opt.softtabstop = 4
vim.opt.shiftwidth = 4
vim.opt.expandtab = true

vim.opt.smartindent = true

vim.opt.wrap = false

vim.opt.swapfile = false
vim.opt.backup = false
vim.opt.undodir = os.getenv("HOME") .. "/.vim/undodir"
vim.opt.undofile = true

vim.opt.hlsearch = false
vim.opt.incsearch = true

vim.opt.termguicolors = true

vim.opt.scrolloff = 8
vim.opt.signcolumn = "yes"
vim.opt.isfname:append("@-@")

vim.opt.updatetime = 50

vim.opt.colorcolumn = "80"
  1. Require the set file from within the init.lua file in the custom configuration directory.
~/.config/nvim/lua/reece/init.lua
require("reece.remap")
require("reece.set")

More Remaps

  1. Update the remap.lua file with the following keymaps.
~/.config/nvim/lua/reece/remap.lua
vim.g.mapleader = " "
vim.keymap.set("n", "<leader>pv", vim.cmd.Ex)

vim.keymap.set("v", "J", ":m '>+1<CR>gv=gv")
vim.keymap.set("v", "K", ":m '<-2<CR>gv=gv")

vim.keymap.set("n", "J", "mzJ`z")
vim.keymap.set("n", "<C-d>", "<C-d>zz")
vim.keymap.set("n", "<C-u>", "<C-u>zz")
vim.keymap.set("n", "n", "nzzzv")
vim.keymap.set("n", "N", "Nzzzv")

-- greatest remap ever
vim.keymap.set("x", "<leader>p", [["_dP]])

-- next greatest remap ever : asbjornHaland
vim.keymap.set({ "n", "v" }, "<leader>y", [["+y]])
vim.keymap.set("n", "<leader>Y", [["+Y]])

vim.keymap.set({ "n", "v" }, "<leader>d", [["_d]])

vim.keymap.set("n", "Q", "<nop>")

vim.keymap.set("n", "<leader>f", vim.lsp.buf.format)

vim.keymap.set("n", "<C-k>", "<cmd>cnext<CR>zz")
vim.keymap.set("n", "<C-j>", "<cmd>cprev<CR>zz")
vim.keymap.set("n", "<leader>k", "<cmd>lnext<CR>zz")
vim.keymap.set("n", "<leader>j", "<cmd>lprev<CR>zz")

vim.keymap.set("n", "<leader>s", [[:%s/\<<C-r><C-w>\>/<C-r><C-w>/gI<Left><Left><Left>]])
vim.keymap.set("n", "<leader>x", "<cmd>!chmod +x %<CR>", { silent = true })