Lazyvim is a good base for Neovim configs
2024-09-21
I have been using Neovim in different flavors for several years. I have used
basic Vim
, Neovim
, Plug
, Packer
, even aniseed
(and also it's
successor nfnl
).
Using Neovim as an IDE is not straightforward, after a basic setup there are still
a myriad of small issues. Back in the day, around Neovim 0.6, LSPs were
constantly breaking, Treesitter was always finding random ways to crash and the
APIs changed so very frequently. Over the last years, the situation has
improved. Neovim APIs have been stabilized. LSPs work better than ever, and
plugin managers have become faster and better. We probably owe this to plugin authors
like Folke, who does a great job at
standardizing the way plugins manage their APIs. He is also the author of
lazy.nvim
- a spiritual successor to Packer
which greatly improves upon it.
The best feature of lazy.nvim
is the ability to cleanly separate plugin
configurations by topic, you can declare a plugin twice, and be sure that in
the end it's parameters will be merged in a sane way.
While the situation has greatly improved, it is still far from being an "out of the box, batteries included" editor.
LazyVim is Folke's "Neovim distribution". It comes with default
options for most the popular plugins. Thanks to the use of lazy.nvim
's
configuration override mechanism, it can provide sane defaults without having
to re-export a bunch of settings.
# My Vim config
At first I discarded LazyVim as I thought it would not be for me. I find it
easier to do things from scratch, rather than undoing whatever defaults someone
thought were good. However, I was pleasantly surprised with how easy it was to
override LazyVim's built in settings. I simply went through the list of
installed plugins with :Lazy
and created a file to disable all of the ones I did not
specifically want.
return {
{ "folke/noice.nvim", enabled = false },
{ "MunifTanjim/nui.nvim", enabled = false },
{ "folke/tokyonight.nvim", enabled = false },
{ "nvimdev/dashboard-nvim", enabled = false },
{ "lucas-reineke/indent-blankline.nvim", enabled = false },
{ "folke/flash.nvim", enabled = false },
{ "folke/todo-comments.nvim", enabled = false },
{ "nvim-neo-tree/neo-tree.nvim", enabled = false },
{ "folke/persistence.nvim", enabled = false },
{ "rafamadriz/friendly-snippets", enabled = false },
{ "garymjr/nvim-snippets", enabled = false },
-- Some of these I might want
{ "folke/trouble.nvim", enabled = false },
{ "echasnovski/mini.ai", enabled = false },
{ "echasnovski/mini.icons", enabled = false },
{ "echasnovski/mini.pairs", enabled = false },
{ "stevearc/dressing.nvim", enabled = false },
{ "stevearc/conform.nvim", enabled = false },
{ "MagicDuck/grug-far.nvim", enabled = false },
{ "rcarriga/nvim-notify", enabled = false },
{ "windwp/nvim-ts-autotag", enabled = false },
{ "Bilal2453/luvit-meta", enabled = false },
}
For the options, it was the same. I just copied the options from my previous config, which are very complete.
vim.g.mapleader = " " -- Set space as the leader key
vim.g.maplocalleader = "," -- Set comma as the local leader key
vim.g.netrw_banner = 0 -- Disable netrw banner
vim.g.markdown_recommended_style = 0 -- Fix vim ignores shoftabstop in markdown
local opt = vim.opt
opt.autowrite = true -- Auto-save before certain actions
opt.exrc = true -- Allow local .vimrc files in directories
opt.completeopt = "" -- Disable built-in completion behavior
opt.conceallevel = 3 -- Hide markup characters in files like markdown
opt.confirm = true -- Confirm to save changes before closing
opt.cursorline = true -- Highlight the current line
opt.expandtab = true -- Convert tabs to spaces
opt.formatoptions = "jcroqlnt" -- Set format options for comments and text wrapping
opt.grepformat = "%f:%l:%c:%m" -- Format for showing grep results
opt.grepprg = "rg --vimgrep" -- Use ripgrep for searching
opt.ignorecase = true -- Ignore case in search patterns
opt.inccommand = "nosplit" -- Live preview of substitution changes
opt.laststatus = 0 -- Hide the status line initially
opt.mouse = "a" -- Enable mouse support
opt.number = true -- Show line numbers
opt.pumblend = 10 -- Set popup menu transparency
opt.pumheight = 10 -- Limit popup menu height
opt.relativenumber = true -- Show relative line numbers
opt.scrolloff = 4 -- Minimal lines to keep above and below cursor
opt.sessionoptions = { "buffers", "curdir", "tabpages", "winsize" } -- Session persistence settings
opt.shiftround = true -- Round indent to multiple of `shiftwidth`
opt.shiftwidth = 2 -- Number of spaces to use for indentation
opt.shortmess:append({
I = true, -- Suppress intro message
W = true, -- Suppress "written" message when saving a file
c = true -- Suppress completion messages
})
opt.showmode = false -- Don't show mode in command line (like -- INSERT --)
opt.sidescrolloff = 8 -- Columns to keep left/right of the cursor during horizontal scroll
opt.smartcase = true -- Override `ignorecase` if search contains uppercase letters
opt.smartindent = true -- Smart auto-indentation
opt.spelllang = { "en" } -- Set language for spell check to English
opt.splitbelow = true -- Force all horizontal splits to go below current window
opt.splitright = true -- Force all vertical splits to go to the right
opt.tabstop = 2 -- Number of spaces tabs count for
opt.termguicolors = true -- Enable 24-bit RGB colors in the terminal
opt.timeoutlen = 300 -- Time to wait for a mapped sequence to complete
opt.undofile = true -- Enable persistent undo
opt.undolevels = 10000 -- Maximum number of undo levels
opt.updatetime = 200 -- Faster completion (default is 4000ms)
opt.wildmode = "longest:full,full" -- Command-line completion mode
opt.winminwidth = 5 -- Minimum window width
opt.wrap = false -- Don't wrap lines by default
opt.compatible = false -- Disable 'compatible' mode to use modern features
opt.hidden = true -- Allow switching buffers without saving
opt.encoding = "utf-8" -- Set file encoding to UTF-8
opt.autoindent = true -- Copy indent from current line when starting a new line
opt.incsearch = true -- Show search matches as you type
opt.ruler = false -- Don't show ruler (line and column info)
opt.switchbuf = "usetab" -- Reuse existing tabs for switching buffers
opt.smarttab = true -- Make tab behavior context-sensitive
opt.copyindent = true -- Copy indent from previous line
opt.previewheight = 38 -- Set height for preview windows
opt.softtabstop = -1 -- Use `shiftwidth` for tab size in editing
opt.backspace = "indent,eol,start" -- Allow backspacing over everything in insert mode
opt.swapfile = false -- Disable swap file creation
opt.foldcolumn = "0" -- No fold column on the left
opt.signcolumn = "yes" -- Always show the sign column
opt.laststatus = 3 -- Global status line
opt.shell = "zsh" -- Set default shell to zsh
opt.cmdheight = 1 -- Set height of command line to 1
opt.showcmd = false -- Hide command in progress in command line
opt.cmdheight = 0 -- Hide command line when not in use
opt.splitkeep = "screen" -- Keep view stable when splitting
opt.statuscolumn = "" -- Customize status column
-- LazyVim's default: [[%!v:lua.require'lazyvim.util'.ui.statuscolumn()]]
Finally for the autocommands, I find that the default autocmds provided by LazyVim are sensible, I just added a few more on top.
-- LazyVim's pre-defined autocmds are:
-- 1. Automatically reload the file when it changes (triggered on certain events like focus gain).
-- 2. Highlights text when yanked (copied).
-- 3. Resizes all window splits if the Vim window is resized.
-- 4. Automatically returns to the last cursor location when reopening a buffer, except for certain file types (e.g., gitcommit).
-- 5. Binds <q> to close certain filetypes (like help, LSP info, and test panels) for easier quitting.
-- 6. Man files opened inline are set as unlisted to prevent clutter in buffer lists.
-- 7. Enables word wrap and spell checking for text-related filetypes (markdown, gitcommit, etc.).
-- 8. Disables JSON file concealment for better readability.
-- 9. Automatically creates missing directories when saving a file, ensuring that any intermediate directories are created if needed.
-- 10. Adds custom filetype detection logic to handle large files ("bigfile"), disables certain animations, and adjusts syntax highlighting to improve performance.
local function augroup(name)
return vim.api.nvim_create_augroup("drusk_" .. name, { clear = true })
end
-- Enable wrap and spell check for gitcommit and markdown filetypes
vim.api.nvim_create_autocmd("FileType", {
group = augroup("wrap_spell"),
pattern = { "gitcommit", "markdown" },
callback = function()
vim.opt_local.wrap = true
vim.opt_local.spell = true
end,
})
-- Disable line numbers and enter insert mode when opening a terminal
vim.api.nvim_create_autocmd("TermOpen", {
group = augroup("term_open"),
callback = function()
vim.opt_local.number = false
vim.opt_local.relativenumber = false
vim.cmd("startinsert")
end,
})
-- Set filetype to "helm" for specific YAML template and helm-related files
vim.api.nvim_create_autocmd({ "BufNewFile", "BufRead" }, {
pattern = { "*/templates/*.yaml", "*/templates/*.tpl", "*.gotmpl", "helmfile*.yaml" },
callback = function()
vim.opt_local.filetype = "helm"
end,
})
# Plus of using LazyVim
Everything seems to work and be stable. I have had no crashes or error messages thus far. I am hoping that some of the advantages of using LazyVim will be:
Avoiding the common pitfalls. Since the LazyVim community is pretty big, most things just work. Someone else has figured out how to glue plugins together, and how to fix common issues.
Better Updates. Sometimes it's easier to start from scratch rather than update a config. Yes, your package manager updates your plugins, but sometimes stuff stops working or requires a change. I hate that.
Actually good defaults. The configs are in the website, and are often exactly what I am looking for. It is very easy to override settings and it is very easy to follow.
The best lazy loading I have ever had. No weird ordering bugs.