Plugins with Purpose
A Neovim configuration can easily drown in plugins. This setup takes the opposite approach: every plugin must solve a problem that cannot be addressed with a few lines of Lua. The result is a lean dashboard where Treesitter provides structural understanding, nvim‑cmp offers intelligent completion, LuaSnip gives programmable snippets, and bufferline keeps tabs visible without visual clutter.
In this final article we open lua/plugins/core.lua – the single file that
defines the entire plugin set. We’ll examine why each plugin was chosen, how it’s configured,
and how it integrates with the custom functions and snippets we’ve built throughout the series.
The Complete Plugin Specification
Below is the full plugins/core.lua file, broken into sections for discussion.
Themes: Nordic and Tokyonight
Two themes are loaded with lazy = false and high priority, ensuring they are
available when colors.lua runs its scheduled setup. Both are configured with
transparent backgrounds – Nordic via its transparent option, Tokyonight via
transparent = true. The fallback logic (Article #5) means the editor is never
without a colourscheme.
{
"AlexvZyl/nordic.nvim",
lazy = false,
priority = 1000,
config = function()
require("nordic").setup({
transparent = {
bg = true,
float = true,
},
})
require("nordic").load()
end,
},
{
"folke/tokyonight.nvim",
lazy = false,
priority = 1000,
opts = {
style = "storm",
transparent = true,
styles = {
comments = { italic = true },
keywords = { italic = true },
},
},
},
Telescope: Fuzzy Everything
Telescope is loaded on the Telescope command (not at startup) and provides
three essential fuzzy finders: files, live grep, and buffers. The keymaps
(<leader>ff, <leader>fg, <leader>fb)
are defined in its config callback.
{
"nvim-telescope/telescope.nvim",
dependencies = { "nvim-lua/plenary.nvim" },
cmd = "Telescope",
config = function()
local builtin = require("telescope.builtin")
vim.keymap.set("n", "ff", builtin.find_files)
vim.keymap.set("n", "fg", builtin.live_grep)
vim.keymap.set("n", "fb", builtin.buffers)
vim.keymap.set("n", "fh", builtin.help_tags)
end,
},
Treesitter: The Backbone
Treesitter provides structural syntax highlighting and indentation. It’s loaded on
BufReadPost and BufNewFile – never at startup – and a minimal
set of parsers is installed. The highlight and indent modules
are enabled; both are non‑negotiable for accurate editing.
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
event = { "BufReadPost", "BufNewFile" },
config = function()
require("nvim-treesitter.configs").setup({
ensure_installed = {
"lua", "python", "javascript", "html", "css", "bash", "c", "cpp", "json", "yaml",
},
highlight = { enable = true },
indent = { enable = true },
})
end,
},
nvim-cmp and LuaSnip: The Completion Engine
Completion is the most complex subsystem. nvim-cmp is loaded on
InsertEnter and configured with four sources: LSP, LuaSnip, buffer text,
and file paths. The snippet expansion is handled by LuaSnip; the Tab key
is overloaded to both cycle completion items and expand/advance snippets.
Critically, after cmp.setup(), the configuration loads the snippet files
we analysed in Article #4 – config.tex_snippets and
config.html_snippets – ensuring snippets are registered before the user
starts typing.
{
"hrsh7th/nvim-cmp",
dependencies = {
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-buffer",
"hrsh7th/cmp-path",
"saadparwaiz1/cmp_luasnip",
"L3MON4D3/LuaSnip",
},
event = "InsertEnter",
config = function()
local cmp = require("cmp")
local luasnip = require("luasnip")
vim.o.completeopt = "menu,menuone,noselect"
cmp.setup({
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
mapping = cmp.mapping.preset.insert({
[''] = cmp.mapping.complete(),
[''] = cmp.mapping.confirm({ select = true }),
[''] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end, { "i", "s" }),
[''] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
elseif luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end, { "i", "s" }),
}),
sources = cmp.config.sources({
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
{ name = 'buffer' },
{ name = 'path' },
}),
})
-- Load LaTeX snippets
require("config.tex_snippets")
-- Load HTML snippets
require("config.html_snippets")
end,
},
Mason: LSP Without the Pain
Mason manages LSP servers, linters, and formatters. It’s loaded with build = ":MasonUpdate"
to ensure the registry is up‑to‑date. The configuration is minimal – just
require("mason").setup(). The LSP servers are currently commented out in
this configuration, but the infrastructure is ready to be uncommented when needed.
Keeping Mason in the plugin list even without active LSP servers means
:MasonInstall is always available.
{
"williamboman/mason.nvim",
build = ":MasonUpdate",
config = function() require("mason").setup() end,
},
Bufferline: Visible Tabs, Minimalist
Bufferline shows open buffers as tabs. It’s configured to be as minimal as possible:
no close icons, no diagnostics, no numbers, just separator characters between buffer
names. always_show_bufferline = true prevents it from disappearing when
only one buffer is open.
{
"akinsho/bufferline.nvim",
version = "*",
config = function()
require("bufferline").setup({
options = {
mode = "buffers",
show_buffer_close_icons = false,
show_close_icon = false,
show_tab_indicators = false,
truncate_names = false,
diagnostics = false,
indicator = { style = "none" },
separator_style = { "|", "|" },
always_show_bufferline = true,
numbers = "none",
},
})
end,
},
The Plugin Philosophy: What’s Missing
Equally important is what’s not in this configuration:
- No file‑tree plugin – ranger (Article #6) and Telescope cover file navigation without a permanent sidebar.
- No statusline plugin – Neovim’s built‑in statusline is sufficient, and custom highlights keep it readable.
- No Git gutter – Git operations are handled in the terminal, keeping the editor free of constant change‑tracking visual noise.
- No AI completion – completion is limited to deterministic sources: LSP, snippets, buffer words, and file paths.
Every omission is a deliberate choice. Each plugin that is included must justify itself every time the editor starts, and none is loaded until needed.
How the Dashboard Comes Together
When Neovim starts:
init.luasets options, loads lazy.nvim, and schedules the colour setup.- Lazy.nvim parses
plugins/core.luaand begins loading plugins by their lazy‑load triggers (events, commands, keys). - The scheduled callback applies the theme, defines global keymaps and commands.
- When a buffer is opened, Treesitter attaches, the appropriate ftplugin fires, and snippets register for that filetype.
- On
InsertEnter, nvim‑cmp activates with all snippet and LSP sources ready.
At no point does the user wait for a progress bar. The entire plugin set, once cached, starts in under 100 milliseconds. The dashboard is ready before you can think about it.
Closing the Series
Over seven articles, we’ve dissected a Neovim configuration from the bootstrapping layer
through filetype engines, automation functions, snippet systems, visual theming, keymaps,
and finally the plugin ecosystem. The configuration is fully reproducible: clone the
repository, run :Lazy sync, and every file, every function, and every colour
is exactly as intended.
The real takeaway is not the specific keybindings or colours, but the engineering mindset: treat your editor as a programmable platform, not a settings file. Build functions, not just mappings. Prefer determinism over convenience defaults. And always be able to explain why every plugin is there.
This configuration will continue to evolve, but the principles documented here are timeless. Happy engineering.