My personal Neovim configuration
- Easily Extendable and Configurable: Modular and simple directory structure.
- Intelligent Autocompletion: Powered by
blink.cmp(Rust fuzzy matching) andLuaSnip. - Git Integration: Visualize git changes with
gitsignsand TUI access vialazygit. - Language Support: LSP configuration with
nvim-lspconfigfor Go, TypeScript, Python, Shell, JSON, YAML, Docker (Dockerfiles through dockerfile-language-server, compose files through yamlls), Terraform, SQL, and Lua. Mason installs the native servers; the Node-based ones come from the system package manager (on Arch, no npm needed). - Formatting and Linting:
conform.nvimfor formatters with LSP fallback and format-on-save,nvim-lintfor additional linting (selene, hadolint, shellcheck, yamllint, golangci-lint, ruff, tflint). - File Management:
Neo-treeas the file explorer andTelescopewith fzf-native for fuzzy finding. - Syntax Highlighting: Built-in Neovim 0.12 treesitter highlighting via
vim.treesitter.start(). - Navigation:
flash.nvimfor labeled jump navigation,nvim-surroundfor surround operations. - DevOps Tools: TUI access to Docker via
lazydocker. - Modern UI: Catppuccin theme,
lualine,trouble.nvimfor diagnostics, and a modernized command palette/messaging system withnoice.nvim. - Session Management:
auto-sessionpersists sessions on exit; restore from the alpha dashboard'slbutton (no auto-restore on startup so the dashboard always shows first).
This configuration targets Arch Linux. Other Linux distributions work on a best-effort basis through the bootstrap script, which lists the optional tools it cannot install. macOS and Windows are not supported.
One pacman command installs everything except hadolint (see below). The bootstrap script (Method B) installs the same packages for you, except the Nerd Font, and picks the clipboard tool from your session; if you install manually (Method A), run the command yourself. On an X11 session, replace wl-clipboard with xclip in the command:
sudo pacman -S --needed neovim git ripgrep base-devel curl unzip wl-clipboard nodejs go \
bash-language-server dockerfile-language-server vscode-json-languageserver pyright \
typescript-language-server yaml-language-server \
stylua shfmt prettier python-black go-tools terraform \
selene shellcheck yamllint tflint golangci-lint ruff \
lazygit lazydocker fortune-mod ttf-jetbrains-mono-nerdRun sudo -k afterwards, so the cached sudo credentials do not reach the plugin builds on Neovim's first launch (the bootstrap script does this itself).
hadolint (Dockerfile linter) is only in the AUR. Review the PKGBUILD, then install it with your AUR helper, or let Mason install it from inside Neovim with :MasonInstall hadolint:
yay -S hadolint-bin- Core: Neovim 0.12+, Git, and ripgrep (Telescope live grep).
- Build tools:
base-develprovides the C compiler andmakethat telescope-fzf-native and LuaSnip compile with. Mason downloads withcurland extracts withunzip. - Clipboard:
wl-clipboard(Wayland) orxclip(X11). Without one, yanks do not reach the system clipboard. - Runtimes: Node.js runs the Node-based language servers (npm is not needed). Mason uses Go to build
goplsandsqls. - Language servers:
- Sources: Mason installs the native servers (
gopls,lua_ls,sqls,terraformls) the first time you open a file. The Node-based servers (bashls,dockerls,jsonls,pyright,ts_ls,yamlls) come from the system packages above and are enabled only when installed. "System" means the first matching binary on Neovim'sPATH, whatever installed it. Mason copies of these servers are never enabled automatically:lsp.lua's checks decide, though a Mason copy still runs if it is the one onPATH(see Upgrading). docker-language-server is not used: it sends telemetry, crash reports, and Dockerfile image names to Docker by default. :lsp enable: a repo'snode_modulescopies of the Node-based servers are never used, not even when you run:lsp enable <name>for one of them yourself. A bare:lsp enable(no name) enables every lspconfig server for the buffer's filetype, and some of those prefer a repo'snode_modules/.bin. The known lspconfig servers that would run a repo's code with tools this config installs never start, not even then:tflint(plugins named in a repo's.tflint.hcl),golangci_lint_ls(a repo's.golangci.yml),terraform_lspandtofu_ls(Terraform providers), andesbonioandtvm_ffi_navigator(a repo's Python modules throughpython -m).ts_ls: enabled only with a real TypeScript package: Arch'stypescript(a dependency oftypescript-language-server), or the package yourtsserverbelongs to; a version manager's shim does not count. The package is checked again each time a buffer needsts_ls, so once it is removed,ts_lsstarts for no further buffers. Its automatic type acquisition is off, so tsserver never runs npm to download@typespackages.lua_ls: declines its workspace plugin prompts, so a repo's.luarc.jsonplugin never loads. That answer also declines its auto-require prompts: accepting a completion for a module the file does not require yet inserts only the name, without arequireline. Its plugin approvals live inlua_ls/trustedunder Neovim's log directory (~/.local/state/nvim), which starts empty and which this config never adds to.- terraform-ls: when the
terraformCLI is installed, terraform-ls stays off until you allow its directory, because it runs provider binaries from.terraformdirectories. Neovim never prompts for this: run:TerraformTrustfrom a file buffer under the directory (a new file must be saved first). The directory is the nearest ancestor with.terraform(else.git, else the file's own directory), and trusting it covers the.terraformdirectories below it.:TerraformTrustrefuses/, your home directory and its parents, world-writable directories, and directories owned by another user. terraform-ls starts only in directories you own that are not world-writable, so:TerraformTrust!overrides a refusal only for those (your home directory, for example), and trusting such a directory covers every project below it. A file whose path has a$NAMEthat Neovim would expand (a directory literally named$HOME, for example) never starts terraform-ls. To revoke, run:trust ++remove <directory>, then:lsp stop terraformls.
- Sources: Mason installs the native servers (
- No phoning home: terraform's update check is off (
CHECKPOINT_DISABLE), and yamlls and jsonls never download schemas, so SchemaStore and remote$schemaURLs give no completion or validation (local schema files still work). terraform-ls may still look up public module metadata on registry.terraform.io for registrymodulesources; this config has no switch for that. - Formatters (
conform.nvim): stylua, shfmt, prettier, black, goimports (go-tools), gofmt (ships withgo), andterraform fmt. prettier always runs the system binary, but it still reads a repo's prettier config, and a JavaScript config or a plugin listed in it can run code. - Linters (
nvim-lint): selene, shellcheck, yamllint, tflint, golangci-lint, ruff, and hadolint. Run through nvim-lint, tflint and golangci-lint never run plugins a repo names: tflint uses this config'stflint.hcland plugins fromtflint-plugins/in Neovim's data directory (~/.local/share/nvim), never a repo's (seetflint.hclfor installing one), and golangci-lint runs with--no-config, so repos'.golangci.ymlfiles are ignored. Their language servers (tflint,golangci_lint_ls) never start. - TUI tools: lazygit (
<leader>gtt) and lazydocker (<leader>dc).fortuneputs a random quote on the dashboard. - Nerd Font: icons need a Nerd Font. JetBrainsMono Nerd Font is recommended; set it as your terminal's font.
Missing formatters, linters, and Node-based servers are skipped quietly inside Neovim, so you can install only what your work needs. One deliberate exception: terraform-ls warns when the terraform CLI is missing.
Older versions installed ansible-language-server, docker-language-server, dockerfile-language-server, docker-compose-language-service, and sqlls through Mason. lsp.lua keeps Mason from enabling any of them, but they still take disk space, and a Mason dockerfile-language-server comes first on Neovim's PATH, so it runs instead of the pacman one. Remove them: :MasonUninstall errors on a package that is not installed, so run this inside Neovim instead; it removes only the ones that are present (restart Neovim afterwards):
:lua for _, n in ipairs { "ansible-language-server", "docker-language-server", "dockerfile-language-server", "docker-compose-language-service", "sqlls" } do local p = require("mason-registry").get_package(n) if p:is_installed() then p:uninstall() end end
If an older install also put bash-language-server, json-lsp, pyright, typescript-language-server, or yaml-language-server in Mason, those copies come first on Neovim's PATH, so lsp.lua runs them instead of the pacman packages. Remove them from :Mason to use the system ones.
Older versions let lua_ls keep its plugin approvals in Mason's lua-language-server/libexec/log/trusted (under ~/.local/share/nvim/mason/packages/). This config no longer reads that file; delete it if you like.
Before proceeding, back up your existing Neovim configuration:
mv ~/.config/nvim ~/.config/nvim_backup
mv ~/.local/share/nvim ~/.local/share/nvim_backup
mv ~/.local/state/nvim ~/.local/state/nvim_backup
mv ~/.cache/nvim ~/.cache/nvim_backup- Install the packages under Requirements. On Arch, the pacman command there includes Neovim 0.12+; elsewhere, use your distribution's package manager.
- Clone the repository directly into your Neovim configuration directory:
git clone https://github.com/jkleinne/nvim ~/.config/nvim
The nvim-install.sh script detects your distribution and installs Neovim, build tools, a clipboard tool, runtimes (Node, Go), the Node-based language servers (Arch only), formatters, linters, TUI tools, and clones the configuration. On Arch it installs the same packages as the command under Requirements, except the Nerd Font (install it with sudo pacman -S ttf-jetbrains-mono-nerd). Run it as your normal user; it calls sudo when it needs to. It asks before it changes anything, so it needs a terminal to answer from: piped into bash (as in curl ... | bash), it stops at the first question.
Supported platforms: Arch Linux (pacman) is first-class. Debian/Ubuntu (apt), Fedora/RHEL (dnf), and Alpine (apk) are best-effort.
git clone https://github.com/jkleinne/nvim ~/.config/nvim
cd ~/.config/nvim
./nvim-install.shThe script will:
- On Arch, upgrade the system first (
pacman -Syu, with pacman's own confirmation) when anything needs installing, since installing against a newer package database without upgrading is a partial upgrade - Install Neovim 0.12+ (prefers your system package manager, falls back to the GitHub release tarball)
- Ask before moving an existing config that is not this repo aside, and move it only after the clone succeeds
- Install build dependencies, a clipboard tool, runtimes, formatters, linters, TUI tools, and on Arch the Node-based language servers
- On best-effort platforms, install the user-level tools their repos lack (
go install,uv tool install) last, after forgetting any cached sudo credentials - Forget cached sudo credentials, including ones from before the run (such as a
sudo pacman -Syou just ran), before it clones the config and launches Neovim, which installs plugins and runs their build steps; with passwordless sudo (NOPASSWD), forgetting the ticket protects nothing - Detect your shell and update PATH if needed (bash, zsh, fish)
- Skip anything already installed
- Stop if a required package (build tools, core tools, runtimes) fails, and list optional tools it could not install in the summary at the end (best-effort platforms get manual steps there)
Run ./nvim-install.sh --help for details.
The installer's tests run with bash tests/install-script.test.sh (the scripts that run the installer's main for them, their shared helpers and stub commands, and the reader that gets the server list through nvim --clean, are in tests/lib/; the suite refuses to run when its temp dir is on a noexec mount, so point TMPDIR elsewhere then); set RUN_FULL=1 on a fully provisioned Arch machine to include an end-to-end run (it runs sudo -k itself first, so no cached sudo ticket reaches the run, and it needs network: Neovim's first launch installs the plugins into scratch directories), and REQUIRE_ALL=1 to fail instead of skipping when the root-guard test cannot run (it needs fakeroot or unshare -r). The LSP and linter settings that keep repo code from running and tools from downloading anything are tested with nvim --clean --headless -l tests/editor-config.test.lua (no plugins or network needed).
Open Neovim. The lazy.nvim plugin manager will automatically start installing the plugins, including compiling telescope-fzf-native and LuaSnip components. Mason installs the native language servers the first time you open a file.
nvimAfter the plugins are installed, run :checkhealth to ensure all dependencies are met and identify any issues.
:checkhealth
The configuration files are organized as follows:
~/.config/nvim/
├── lua/ # Lua configuration directory
│ ├── lsp_gates/ # Server list, TypeScript and terraform-ls trust checks used by plugins/lsp.lua
│ ├── plugins/ # Individual plugin configurations
│ │ ├── lsp.lua
│ │ ├── telescope.lua
│ │ └── ... (additional plugins)
│ ├── mappings.lua # Key mappings and shortcuts
│ └── settings.lua # General settings and options
├── init.lua # Main entry point
├── lazy-lock.json # Lock file for lazy.nvim
├── nvim-install.sh # Bootstrap script (Arch Linux; other distros best-effort)
├── tests/ # Installer and editor config tests
├── tflint.hcl # tflint config used instead of repos' own
└── ...
The leader key is set to Space.
| Keybinding | Description |
|---|---|
<C-e> |
Toggle Neo-tree file explorer |
<TAB> |
Cycle to the next buffer |
<S-TAB> |
Cycle to the previous buffer |
<leader>bd |
Close the current buffer (Bdelete) |
s |
Flash jump (labeled jump to any visible location) |
S |
Flash treesitter jump (select treesitter nodes) |
| Keybinding | Description |
|---|---|
<leader>tf |
Find files in the current working directory |
<leader>tg |
Live grep (search text) in the current working directory |
<leader>tb |
List open buffers |
<leader>th |
Search help tags |
<leader>ts |
Search LuaSnip snippets |
| Keybinding | Description |
|---|---|
<C-w>t |
Open a new tab |
<leader>bn |
Open a new empty buffer (:enew) |
<leader>h |
Open a terminal in a horizontal split below |
<leader>v |
Open a terminal in a vertical split to the right |
<Esc><Esc> |
Exit terminal mode (double-tap; single Esc preserved for TUIs) |
<C-h> / <C-j> / <C-k> / <C-l> |
From terminal mode, jump to the left/lower/upper/right window |
| Keybinding | Description |
|---|---|
<leader>gtt |
Open LazyGit in a terminal buffer |
<leader>dc |
Open LazyDocker in a terminal buffer |
These are active when an LSP server is attached to a buffer.
| Keybinding | Description |
|---|---|
gd |
Go to definition |
K |
Hover documentation |
gi |
Go to implementation |
gr |
Go to references |
<C-k> |
Signature help |
<leader>D |
Go to type definition |
<leader>rn |
Rename symbol |
<leader>ca |
Code actions |
<leader>f |
Format buffer (conform.nvim with LSP fallback) |
gl |
Open floating diagnostics |
[d |
Go to previous diagnostic |
]d |
Go to next diagnostic |
<leader>q |
Send diagnostics to location list |
<leader>wa |
Add workspace folder |
<leader>wr |
Remove workspace folder |
<leader>wl |
List workspace folders |
| Keybinding | Description |
|---|---|
<leader>xx |
Toggle workspace diagnostics |
<leader>xd |
Toggle buffer diagnostics |
<leader>xl |
Toggle location list |
<leader>xq |
Toggle quickfix list |
| Keybinding | Description |
|---|---|
gcc |
Toggle line comment (built-in) |
gc |
Toggle comment (visual mode, built-in) |
cs"' |
Change surrounding " to ' (nvim-surround) |
ds( |
Delete surrounding ( (nvim-surround) |
ysiw] |
Add [] around word (nvim-surround) |
Here is a list of the key plugins included in this configuration:
- alpha-nvim: Startup screen.
- auto-session: Saves sessions on exit; restore on demand from the alpha dashboard (
l). - blink.cmp: Autocompletion engine with Rust fuzzy matcher.
- bufferline.nvim: Manage open buffers as tabs.
- catppuccin/nvim: Soothing pastel theme (Mocha flavor).
- conform.nvim: Formatter runner with LSP fallback and format-on-save.
- flash.nvim: Labeled jump navigation.
- gitsigns.nvim: Git decorations in the gutter.
- kubectl.nvim: Kubernetes integration. Disabled: set
enabled = trueinlua/plugins/kubectl.luato restore it and<leader>k(lazy.nvim reinstalls it at the locked commit on the next start). - lazydev.nvim: Lazy-loaded Neovim API type information and completions for
lua_ls. - mason.nvim: Installs the native language servers, plus anything you add with
:Mason. - neo-tree.nvim: File explorer.
- noice.nvim: Modernizes the UI for messages, cmdline, and popups.
- nvim-lint: Async linting for Lua, Dockerfile, shell, YAML, Go, Python, Terraform.
- nvim-surround: Surround operations (change, delete, add).
- telescope.nvim: Fuzzy finder with fzf-native sorting.
- todo-comments.nvim: Highlight and search TODO/FIXME/HACK comments.
- trouble.nvim: Diagnostics and quickfix UI.
- which-key.nvim: Displays a popup with possible keybindings.
For a complete list, refer to the lua/plugins folder in the repository.
- Themes: Change the colorscheme or flavor by modifying the
lua/plugins/colorscheme.luafile. - Keybindings: Update or add new keybindings in the
lua/mappings.luafile. - Plugins: Add or remove plugins in the
lua/plugins/directory.lazy.nvimwill manage them automatically upon restart. - Language Servers: Change the server lists in
lua/lsp_gates/servers.luaand server settings inlua/plugins/lsp.lua, or install interactively using:Mason. - Formatters: Configure formatters per filetype in
lua/plugins/conform.lua. - Linters: Configure linters per filetype in
lua/plugins/lint.lua.
This project is licensed under the MIT License

