My init.lua for Neovim
Gradually working in explainer text as I go. Working off of multiple resources.
I’d probably tuck all of these into different Lua files if I were assembling
this like a normal project. Since I’m using Yarner to generate the
config from this Markdown file, I’ll lean on the narrative flow for as long as
possible and tangle everything to init.lua
.
It’s all the same to Neovim once it gets loaded into memory.
init.lua
global prep
Most Neovim Lua functionality is contained in the vim
module. Pull some of
the frequently used ones into the current namespace, to save a little typing
for our fingers.
vim.cmd
- vim commands (eg
cmd('pwd')
) vim.fn
- vim functions (eg
fn.buffer()
) vim.g
- a table for global variables
vim.opt
- vim options
local cmd = vim.cmd
local fn = vim.fn
local g = vim.g
local opt = vim.opt
helper functions
Conveniences I copied from other init.lua
examples.
map
Creates mappings with noremap
option enabled by default.
local function map(mode, lhs, rhs, opts)
local options = {noremap = true}
if opts then options = vim.tbl_extend('force', options, opts) end
vim.api.nvim_set_keymap(mode, lhs, rhs, options)
end
Packer bootstrap
Install the plugin manager Packer if it’s not already installed.
local install_path = fn.stdpath('data')..'/site/pack/packer/start/packer.nvim'
if fn.empty(fn.glob(install_path)) > 0 then
packer_bootstrap = fn.system({
'git',
'clone',
'--depth', '1',
'https://github.com/wbthomason/packer.nvim',
install_path
})
end
Load plugins via Packer
packer.nvim
brings the flexibility — and complexity — of Emacs
use-package
to Neovim. How cool is that?
require('packer').startup(function(use)
use "wbthomason/packer.nvim"
-- ==> Load plugins.
-- automatically set up your configuration after cloning packer.nvim
-- put this at the end after all plugins
if packer_bootstrap then
require('packer').sync()
end
end)
-- # Load plugins
-- ==> Load popup.nvim.
-- ==> Load plenary.nvim.
-- ==> Load nvim-web-devicons.
-- ==> Load nvim-treesitter.
-- ==> Load telescope.nvim.
-- ==> Load null-ls.nvim.
-- ==> Load nvim-lspconfig.
-- ==> Load which-key.nvim.
-- ==> Load nightfox.nvim.
-- ==> Load lualine.
-- ==> Load language support.
-- ==> Load Riv.
popup.nvim
-- # Load popup.nvim
use "nvim-lua/popup.nvim"
plenary.nvim
-- # Load plenary.nvim
use "nvim-lua/plenary.nvim"
nvim-web-devicons
-- # Load nvim-web-devicons
use "kyazdani42/nvim-web-devicons"
nvim-treesitter
nvim-treesitter
is an experimental feature of Neovim. Something
to do with syntax highlighting? Both it and the plugins that use it change
frequently. So I better follow the instructions about keeping everything up to
date when I sync.
-- # Load nvim-treesitter
use {
"nvim-treesitter/nvim-treesitter",
run = ":TSUpdate",
config = function()
require("nvim-treesitter.configs").setup {
ensure_installed = "all",
highlight = {
enable = true,
},
additional_vim_regex_highlighting = false,
}
local parser_config = require("nvim-treesitter.parsers").get_parser_configs()
parser_config.just = {
install_info = {
url = "https://github.com/IndianBoy42/tree-sitter-just", -- local path or git repo
files = { "src/parser.c", "src/scanner.cc" },
branch = "main",
},
maintainers = { "@IndianBoy42" },
}
end
}
telescope.nvim
telescope.nvim
is a ridiculously fancy fuzzy-finder.
-- # Load telescope.nvim
use { "nvim-telescope/telescope.nvim",
requires = { {"nvim-lua/plenary.nvim"} },
}
Global key bindings for telescope.nvim
Showing the global telescope.nvim
key bindings here, though Yarner will be
inserting them outside all this plugin definition stuff. I haven’t figured out
how to do global keybindings in a plugin setup quite yet.
-- # Add global bindings for telescope.nvim
map("n", "<leader>ff", "<cmd>lua require('telescope.builtin').find_files()<cr>")
map("n", "<leader>fg", "<cmd>lua require('telescope.builtin').live_grep()<cr>")
map("n", "<leader>fb", "<cmd>lua require('telescope.builtin').buffers()<cr>")
map("n", "<leader>fh", "<cmd>lua require('telescope.builtin').help_tags()<cr>")
null-ls.nvim
-- # Load null-ls.nvim
use {
"jose-elias-alvarez/null-ls.nvim",
config = function()
require("null-ls").setup({
sources = {
require("null-ls").builtins.formatting.stylua,
require("null-ls").builtins.code_actions.proselint,
}
})
end
}
which-key.nvim
I first bumped into the which-key
help menu in Doom
Emacs. Start a chained key binding like SPC
, a menu pops up
showing what chains are available. Indispensable there. Indispensable here.
Thank goodness folks are porting so many Emacs packages to Neovim.
-- # Load which-key.nvim
use {
"folke/which-key.nvim",
config = function()
require("which-key").setup {}
end
}
nvim-lspconfig
-- # Load nvim-lspconfig
use "neovim/nvim-lspconfig"
Stock LSP setup
-- # Use suggested config for LSP
local lsp_opts = { noremap=true, silent=true }
map('n', '<space>e', '<cmd>lua vim.diagnostic.open_float()<CR>', lsp_opts)
map('n', '[d', '<cmd>lua vim.diagnostic.goto_prev()<CR>', lsp_opts)
map('n', ']d', '<cmd>lua vim.diagnostic.goto_next()<CR>', lsp_opts)
map('n', '<space>q', '<cmd>lua vim.diagnostic.setloclist()<CR>', lsp_opts)
-- Use an on_attach function to only map the following keys
-- after the language server attaches to the current buffer
local lspconfig_on_attach = function(client, bufnr)
-- Enable completion triggered by <c-x><c-o>
vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
-- Mappings.
-- See `:help vim.lsp.*` for documentation on any of the below functions
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gd', '<cmd>lua vim.lsp.buf.definition()<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<space>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<space>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<space>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<space>D', '<cmd>lua vim.lsp.buf.type_definition()<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<space>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<space>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', lsp_opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<space>f', '<cmd>lua vim.lsp.buf.formatting()<CR>', lsp_opts)
end
-- ==> Set up individual LSP servers.
Set up LSP language servers
-- # Set up individual LSP servers
require("lspconfig").pyright.setup {
on_attach = lspconfig_on_attach,
}
require("lspconfig").tsserver.setup {
on_attach = lspconfig_on_attach,
}
nightfox.nvim
-- # Load nightfox.nvim
use "EdenEast/nightfox.nvim"
lualine.nvim
-- # Load lualine
use { 'nvim-lualine/lualine.nvim',
requires = { 'kyazdani42/nvim-web-devicons', opt = true },
config = function()
require('lualine').setup({
options = {
theme = "duskfox",
}
})
end,
}
Language support
A couple of the tools I use regularly require some special handling.
-- # Load language support
-- ==> Load nvim-nu.
-- ==> Load vim-jinja2-syntax
-- ==> Load black.
nvim-nu
nvim-nu
adds #nushell support to Neovim.
-- # Load nvim-nu
use {
"LhKipp/nvim-nu",
run = ":TSInstall nu",
config = function()
require("nu").setup{}
end
}
vim-jinja2-syntax
Vim-Jinja2-Syntax highlights the Jinja / Nunjucks / Tera family of text template languages
-- # Load vim-jinja2-syntax
use "glench/vim-jinja2-syntax"
black
Because pyright doesn’t provide formatting
-- # Load black
use { "psf/black", cmd = {"Black"}}
File hooks for black
-- # Format Python files with Black when saving
cmd[[autocmd BufWritePre *.py execute 'Black']]
Riv for knowledge management
I bounce way too much between systems. Right now I use Riv when in Neovim. What can I say? I like reStructuredText.
g:riv_file_link_style
- use Riv’s
:doc:
role instead of[[...]]
for wiki links
-- # Load Riv
use {
"Rykka/riv.vim",
config = function()
riv_main = {
path = "~/Dropbox/riv/main"
}
riv_work = {
path = "~/work/riv"
}
vim.g.riv_projects = {riv_main, riv_work}
vim.g.riv_file_link_style = 2
end
}
Global Settings
-- ==> Set colorscheme.
-- ==> Set filetype behaviors and hooks.
-- ==> Set global variables.
-- ==> Set global options.
-- ==> Configure diagnostics.
-- ==> Add global key bindings.
-- ==> Use suggested config for LSP.
Set a colorscheme
-- # Set colorscheme
cmd[[colorscheme nightfox]]
Filetype behaviors and hooks
-- # Set filetype behaviors and hooks
cmd[[filetype plugin on]]
cmd[[autocmd FileType * setlocal formatoptions-=cro]]
cmd[[autocmd FocusGained * checktime]]
-- ==> Format Python files with Black when saving.
-- ==> Recognize Astro files.
Recognize Astro files
Eventually Astro will be able to fully build my site. And I’ll be ready, thanks to treesitter. I just need to tell the editor which ones are the Astro files.
-- # Recognize Astro files
cmd[[autocmd BufEnter *.astro set ft=astro]]
A few global variables
I use SPC
as my global leader key, and the comma for my local leader. Also, I
tend to fiddle a lot with Pyenv so I set up a dedicated virtualenv for Neovim.
-- # Set global variables
g.mapleader = ' '
g.maplocalleader = ','
g.python3_host_prog = '~/.pyenv/versions/neovim/bin/python'
g.markdown_fenced_languages = {
"bash=sh",
"python",
"lua",
}
g.rst_syntax_code_list = { "python" }
Global options
-- # Set global options
opt.autoread = true
opt.background = 'dark'
opt.completeopt = {'menuone', 'noinsert', 'noselect'} -- completion options (for deoplete)
opt.cursorline = true -- highlight current line
opt.encoding = "utf-8"
opt.expandtab = true -- spaces instead of tabs
opt.hidden = true -- enable background buffers
opt.ignorecase = true -- ignore case in search
opt.joinspaces = false -- no double spaces with join
opt.list = true -- show some invisible characters
opt.maxmempattern = 1000 -- for Riv
opt.mouse = "nv" -- Enable mouse in normal and visual modes
opt.number = true -- show line numbers
opt.relativenumber = true -- number relative to current line
opt.scrolloff = 4 -- lines of context
opt.shiftround = true -- round indent
opt.shiftwidth = 2 -- size of indent
opt.sidescrolloff = 8 -- columns of context
opt.smartcase = true -- do not ignore case with capitals
opt.smartindent = true -- insert indents automatically
opt.splitbelow = true -- put new windows below current
opt.splitright = true -- put new vertical splits to right
opt.termguicolors = true -- truecolor support
opt.wildmode = {'list', 'longest'} -- command-line completion mode
opt.wrap = false -- disable line wrap
Diagnostic settings
See :help vim.diagnostic.*
for documentation on any of the below functions
-- # Configure diagnostics
vim.diagnostic.config({
virtual_text = false,
})
Global key bindings
-- # Add global key bindings
-- ==> Add global bindings for telescope.nvim.
-- ==> <bs> clears search highlights.
I described the telescope.nvim
and LSP global bindings earlier. This is where
they get used.
Then a little thing I’ve gotten used to. Let search highlight matches, but clear those highlights out when I hit Backspace.
-- # <bs> clears search highlights
map("n", "<bs>", ":nohlsearch<cr>", { silent = true })