nvim / documentation

English translation of the original guide. For the latest source edits, open the Markdown on GitHub ↗.

Neovim Config

A modular Neovim configuration built around lazy.nvim, with a minimal bootstrap, plugin specifications organized by purpose, and a clear separation between:

  • core modules 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:

  1. load myconf.core
  2. bootstrap of lazy.nvim and import of plugin specs
  3. apply theme and palette
  4. 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:

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.nvim is missing from stdpath("data"), it is cloned with Git.
  • The runtime path is updated.
  • lazy.nvim imports plugin specifications from myconf.plugins and myconf.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, cmd or keys for lazy-loading when possible
  • dependencies declared locally in the spec
  • keep opts or config in the plugin specification file

Examples:

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 (dark or light)
  • 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 nvim

External binaries can also be overridden with optional environment variables:

NVIM_ZOXIDE_BIN=/custom/bin/zoxide \
NVIM_SIOYEK_BIN=/custom/bin/sioyek \
nvim

In 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

UI And Theme

Editing

LSP

How lazy.nvim Works in This Setup

Here lazy.nvim does three things:

  1. plugin manager bootstrap
  2. import of all plugin specs
  3. actual lazy-loading via event, ft, cmd and keys

Important setup choices:

  • checker.enabled = true: Check for updates plugins
  • change_detection.notify = false: no spam by notifications when you edit config files
  • lazy-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:

  1. create a file in lua/myconf/plugins/ or lua/myconf/plugins/lsp/
  2. return a spec lazy.nvim
  3. always prefer a lazy trigger (event, ft, cmd, keys) if the plugin does not It is used for startup
  4. 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:

  • Mason installs servers and tools
  • lspconfig decides 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:

  1. add the package in ensure_installed of mason-lspconfig if you want to auto-install it
  2. add the server name to the servers list
  3. create an override in server_overrides only if needed filetypes, cmd, settings or root_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 = true provides a fallback to LSP formatting.

To add a formatter:

  1. add the tool to mason-tool-installer if you want auto-install it
  2. add the filetype -> formatter pair in formatters_by_ft
  3. 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 on BufEnter, BufWritePost, InsertLeave
  • manual trigger on <leader>ll

To add a linter:

  1. add the tool to mason-tool-installer if it needs to be self-installed
  2. record the filetype in linters_by_ft

Useful Configuration Commands

  • :ConfigEdit: opens a config file with selector
  • :ConfigVars: opens lua/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-lua requires the fzf system binary in PATH; telescope-fzf-native is a thing separate and only covers the fuzzy extension of Telescope
  • tokyonight is 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:

  • jdtls requires python3 and a java runtime; this setup requires at least Java 21
  • prettier and eslint_d require node
  • latexindent is installed by Mason, and this setup passes ~/.config/latexindent/config.yaml to 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
  • core modules without hidden side effects: better setup() 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:

  1. install the hard requirements in the section above
  2. place this config in ~/.config/nvim
  3. verify that ~/.config/theme/colors.lua exists
  4. open nvim
  5. run :Lazy sync
  6. open :Mason and check that the required servers and tools are installed

Future Improvements

Possible future improvements include:

  • clarify whether fzf-lua should remain as an alternative to Telescope or if it should be reduced
  • document any file dependencies ~/.config/theme/colors.lua if it becomes shared between multiple dotfiles