Neovim Config
A modular Neovim configuration built around lazy.nvim, with a minimal bootstrap, plugin specifications organized by purpose, and a clear separation between:
coremodules loaded immediately- plugins loaded by
lazy.nvim - development tools managed by
Mason - Explicitly declared formatter and linter
The goal is to keep a simple entry point, small modules, and a predictable way to extend the configuration.
Bootstrap and Structure
The entrypoint is init.lua:
- load
myconf.core - bootstrap of
lazy.nvimand import of plugin specs - apply theme and palette
- activate the custom winbar
In practice the flow is this:
init.lua
├── myconf.core
│ ├── editor/options.lua
│ ├── ui/tabline.lua
│ ├── mappings/*
│ ├── cmd/*
│ └── events/*
├── myconf.lazy
│ ├── bootstrap lazy.nvim
│ ├── import myconf.plugins
│ └── import myconf.plugins.lsp
├── myconf.theme.theme
└── myconf.ui.winbar
The main directories are:
lua/configuration.lua: toggle and global setup valueslua/myconf/core: options, mappings, commands and autocmd loaded eagerlylua/myconf/plugins: General spec plugins forlazy.nvimlua/myconf/plugins/lsp: Spec plugins for LSP and Masonlua/myconf/theme: palette, theme and color utilitieslua/myconf/ui: components Custom UI outside the pluginslazy-lock.json: pinned plugin versions
How Modules Are Loaded
core modules
core modules are discovered automatically by lua/myconf/core/utils/modules.lua; they do not need to be registered one by one.
This applies to:
Every .lua file in those directories, except init.lua, is loaded automatically. If a module exports:
- If it exports a table with
setup(), that method is called. - If it exports a function, that function is called directly.
Adding a mapping or command usually only requires a new file in the appropriate directory.
Plugin with lazy.nvim
The lazy.nvim bootstrap lives in lua/myconf/lazy.lua. On the first launch:
- If
lazy.nvimis missing fromstdpath("data"), it is cloned with Git. - The runtime path is updated.
lazy.nvimimports plugin specifications frommyconf.pluginsandmyconf.plugins.lsp.
Each file in those directories returns a valid lazy.nvim plugin specification. The conventions are:
- one file per plugin or per small area
event,ft,cmdorkeysfor lazy-loading when possibledependenciesdeclared locally in the spec- keep
optsorconfigin the plugin specification file
Examples:
lua/myconf/plugins/telescope.lualua/myconf/plugins/nvim-cmp.lualua/myconf/plugins/lsp/lspconfig.lua
Key Modules
Global Config
lua/configuration.lua is
the right place for:
- toggle UI like winbar, smooth scrolling and backdrop
- small but global values such as the maximum number of splits
- choice of theme (
darkorlight) - override external binaries in
conf.external
The ui.theme() function also reads NVIM_THEME, so you can override the theme from the shell:
NVIM_THEME=light nvimExternal binaries can also be overridden with optional environment variables:
NVIM_ZOXIDE_BIN=/custom/bin/zoxide \
NVIM_SIOYEK_BIN=/custom/bin/sioyek \
nvimIn the current TeX workflow, Sioyek is used by vimtex
as a viewer. texlab remains separate and is not used as
entrypoint for preview or forward search.
Core
lua/myconf/core/editor/options.lua: basic options, filetype indentation, text wrapping, scrolloff dynamiclua/myconf/core/mappings: leader, base shortcut, split navigation, terminal mappinglua/myconf/core/cmd: custom commands like:ConfigEdit,:ReloadConfig,:CodexHere,:Finder,:Zlua/myconf/core/events: general autocmd and backdrop for some floating UIs
UI And Theme
lua/myconf/plugins/colorscheme.lua: setup oftokyonightwith custom palettelua/myconf/theme/palette.lua: converts the external palette into values used by the themelua/myconf/ui/winbar.lua: custom winbar with filename, icons and breadcrumbs LSP vianvim-naviclua/myconf/plugins/lualine.lua: statusline
Navigation And Search
lua/myconf/plugins/telescope.lua: main pickers,file_browser,zoxide,fzflua/myconf/plugins/fzf.lua:fzf-luaas a quick alternativelua/myconf/plugins/nvim-tree.lua: side explorerlua/myconf/plugins/smart-splits.lua: resize, swap buffer and integration with WezTerm
Editing
lua/myconf/plugins/treesitter.lua: parser, highlighting, incremental selectionafter/queries/ecma/textobjects.scm: custom Treesitter query for textobject on JS/TS propertieslua/myconf/plugins/nvim-cmp.lua: completion and snippetlua/myconf/plugins/conform.lua: filetype formatterlua/myconf/plugins/nvim-lint.lua: linter on autocmd
LSP
lua/myconf/plugins/lsp/mason.lua: installation of tools and language serverslua/myconf/plugins/lsp/lspconfig.lua: server enable, override and keymap onLspAttach
How lazy.nvim Works in This Setup
Here lazy.nvim does three things:
- plugin manager bootstrap
- import of all plugin specs
- actual lazy-loading via
event,ft,cmdandkeys
Important setup choices:
checker.enabled = true: Check for updates pluginschange_detection.notify = false: no spam by notifications when you edit config fileslazy-lock.json: pin installed versions
Useful commands:
:Lazy: main panel:Lazy sync: Install/update and realign plugins:Lazy restore: Reset lockfile versions
Adding a Plugin
To add a new plugin:
- create a file in
lua/myconf/plugins/orlua/myconf/plugins/lsp/ - return a spec
lazy.nvim - always prefer a lazy trigger (
event,ft,cmd,keys) if the plugin does not It is used for startup - run
:Lazy sync
Minimal example:
return {
"folke/which-key.nvim",
event = "VeryLazy",
opts = {},
}If a change is just runtime UI/theme you can often use
:ReloadConfig; if you touch plugin spec, dependencies or
bootstrap it is better to do :Lazy sync or restart Neovim.
LSP, Mason, Formatters, and Linters
LSP
This setup separates tool installation from language-server activation:
Masoninstalls servers and toolslspconfigdecides which servers to really enable and with which overrides
In lua/myconf/plugins/lsp/lspconfig.lua:
- servers are listed in
servers - the exceptions are in
server_overrides - all receive the capabilities of
cmp-nvim-lsp - LSP keymaps are registered to
LspAttach, then they are buffer-local
Add a New Language Server
Recommended flow:
- add the package in
ensure_installedofmason-lspconfigif you want to auto-install it - add the server name to the
serverslist - create an override in
server_overridesonly if neededfiletypes,cmd,settingsorroot_markers
Conceptual example:
-- mason.lua
ensure_installed = {
"ts_ls",
}
-- lspconfig.lua
local servers = {
"ts_ls",
}
local server_overrides = {
ts_ls = {
settings = {
-- ...
},
},
}The automatic_enable = false setting in mason_lspconfig.setup() is intentional. Servers are activated in lspconfig.lua, not by Mason.
Formatter
Formatting is handled by conform.nvim in lua/myconf/plugins/conform.lua.
Setup Pattern:
- map filetype -> formatter to
formatters_by_ft - any custom formatters are defined in
formatters - manual formatting on
<leader>fi lsp_fallback = trueprovides a fallback to LSP formatting.
To add a formatter:
- add the tool to
mason-tool-installerif you want auto-install it - add the filetype -> formatter pair in
formatters_by_ft - if you need custom binary or custom arguments, define it in
formatters
Linter
Linting is handled by nvim-lint in lua/myconf/plugins/nvim-lint.lua.
Setup Pattern:
- map filetype -> linter to
linters_by_ft try_lint()runs onBufEnter,BufWritePost,InsertLeave- manual trigger on
<leader>ll
To add a linter:
- add the tool to
mason-tool-installerif it needs to be self-installed - record the filetype in
linters_by_ft
Useful Configuration Commands
:ConfigEdit: opens a config file with selector:ConfigVars: openslua/configuration.lua:ReloadConfig: Reload theme and light modules:Lazy: plugin panel:Mason: tool/LSP panel
Essential Dependencies
This section distinguishes between:
- dependencies required to start and install the basic configuration
- dependencies introduced by my custom config
- dependencies introduced by active plugins
Core Setup Requirements
| Dependency | Because it's needed |
|---|---|
nvim 0.11+ |
the setup uses recent APIs like vim.lsp.config() /
vim.lsp.enable() |
git |
Initial bootstrap of lazy.nvim and installation of plugins |
make |
native plugin builds like telescope-fzf-native and
LuaSnip |
C/C++ toolchain (clang or gcc) |
Required for native builds and Treesitter parsers |
~/.config/theme/colors.lua |
requested by lua/myconf/theme/palette.lua; without this file, the theme cannot start even though lazy.nvim installs tokyonight automatically |
Dependencies Of My Config
These dependencies support custom commands and explicit choices in this configuration.
| Dependency | Where it is used | If you take it off |
|---|---|---|
zoxide |
:Z |
The custom command to jump between directories doesn't work |
codex |
:CodexHere* |
The command reports that codex is not executable. |
~/.config/latexindent/config.yaml |
TeX formatter in conform.nvim |
the configured TeX formatter does not start as expected |
mypandoc |
:PandocExport |
the custom export command does not work |
code |
:Code |
the command that opens VS Code doesn't work |
op |
:Open |
1Password CLI based command doesn't work |
tmux |
mapping <leader>lt which opens lazygit
in a new window |
that specific mapping doesn't work |
Active Plugin Dependencies
These dependencies are needed only for the enabled plugin features listed below.
Note:
fzf-luarequires thefzfsystem binary inPATH;telescope-fzf-nativeis a thing separate and only covers the fuzzy extension of Telescopetokyonightis not an external dependency to install by hand; the real dependence of the theme remains~/.config/theme/colors.lua
| Dependency | Plugins / features | If you take it off |
|---|---|---|
fd |
telescope.nvim find_files |
the file picker configured with fd stops working
correctly |
rg |
telescope.nvim live_grep /
grep_string |
you lose the ripgrep-based search part |
fzf |
ibhagwan/fzf-lua (<leader>zf,
<leader>zs, <leader>zb) |
fzf-lua does not start |
zoxide |
jvgrootveld/telescope-zoxide |
Telescope's zoxide picker doesn't work |
lazygit |
kdheepak/lazygit.nvim and git mapping |
The Git interface does not open. |
wezterm |
mrjones2014/smart-splits.nvim for external panes |
<C-S-h/j/k/l> mappings to WezTerm pane do not
they work |
latexmk |
vimtex and texlab |
skips LaTeX compilation and current TeX integration |
sioyek |
vimtex with \\lv |
the LaTeX PDF workflow loses the expected viewer |
node |
copilot.lua, CopilotChat.nvim,
prettier, eslint_d |
Copilot and part of the JS toolchain do not start |
python3 |
Mason wrapper by jdtls |
the Java server does not start |
java / JDK 21+ |
jdtls |
Java server does not start or version check fails |
latexindent |
conform.nvim to tex |
you lose the configured TeX formatter |
Platform-Specific Dependencies
Platform-specific binaries are configured through conf.external in lua/configuration.lua, with fallbacks to PATH and, where useful, common macOS locations.
| Dependency / path | Current status | Impact |
|---|---|---|
| Sioyek app bundle macOS | fallback only on macOS; on Linux sioyek is preferred by
PATH |
LaTeX PDF viewer via vimtex |
open -R |
only used on macOS inside shell.reveal_path() |
on Linux there is fallback to xdg-open, on Windows to
explorer.exe |
xdg-open |
only required on Linux for :Finder and reveal in
nvim-tree |
It is not needed on macOS or Windows |
explorer.exe |
only required on Windows for :Finder and reveal in
nvim-tree |
It is not needed on macOS or Linux |
Note About Mason
Mason installs some of the servers and tools declared in the repository, but these are distinct from the requirements for starting the basic configuration.
In this setup, at least:
- LSPs installed by Mason:
html,cssls,svelte,lua_ls,graphql,emmet_ls,prismals,pyright,gopls,jdtls,clangd,texlab - tools:
prettier,stylua,isort,black,pylint,eslint_d,clang-format,latexindent
These tools support the development workflow, but not all are required just to start Neovim. Some also require external runtimes:
jdtlsrequirespython3and ajavaruntime; this setup requires at least Java 21prettierandeslint_drequirenodelatexindentis installed by Mason, and this setup passes~/.config/latexindent/config.yamlto it
Project Conventions
When extending this configuration, follow these conventions:
- a plugin file = a clear responsibility
- small global toggles in
lua/configuration.lua, not scattered throughout the plugins coremodules without hidden side effects: bettersetup()explicit- use lazy-loading for anything that is not needed at startup
- avoid absolute platform paths unless strictly necessary
- If you add a feature with external dependencies, document it here
Quick Bootstrap
On a new machine, the minimal setup is:
- install the hard requirements in the section above
- place this config in
~/.config/nvim - verify that
~/.config/theme/colors.luaexists - open
nvim - run
:Lazy sync - open
:Masonand check that the required servers and tools are installed
Future Improvements
Possible future improvements include:
- clarify whether
fzf-luashould remain as an alternative to Telescope or if it should be reduced - document any file dependencies
~/.config/theme/colors.luaif it becomes shared between multiple dotfiles