
Neovim
- 260 installs
- 6 repo stars
- Updated July 22, 2026
- julianobarbosa/claude-code-skills
neovim is a Claude Code skill that drives Neovim sessions for fast terminal editing, LSP-aware refactors, buffer navigation, and plugin-aware changes for developers who work without leaving the shell.
About
neovim is a Claude Code skill that drives Neovim sessions from the agent for terminal-native editing workflows. The skill enables fast buffer navigation, LSP-aware refactors, and plugin-aware file changes while developers remain inside Claude Code’s shell-centric loop instead of switching to a GUI editor. Reach for it when optimizing a terminal-only setup, executing multi-file refactors through Neovim commands, or leveraging existing Neovim plugins during agent-assisted edits. It bridges Claude Code orchestration with Neovim’s modal editing, LSP diagnostics, and ecosystem plugins. Use when the user’s workflow centers on nvim in tmux or SSH environments where GUI editors are unavailable or undesirable.
- Modal Vim editing inside agent workflows
- Buffer- and project-aware navigation
- LSP and plugin integration guidance
- Terminal-native refactor and patch flows
- Config and keymap assistance for Neovim
Neovim by the numbers
- 260 all-time installs (skills.sh)
- +7 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #184 of 550 CLI & Terminal skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/julianobarbosa/claude-code-skills --skill neovimAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 260 |
|---|---|
| repo stars | ★ 6 |
| Last updated | July 22, 2026 |
| Repository | julianobarbosa/claude-code-skills ↗ |
How do you control Neovim from Claude Code?
Drive Neovim sessions from Claude Code for fast terminal editing, LSP-aware refactors, buffer navigation, and plugin-aware changes without leaving the shell workflow.
Who is it for?
Terminal-centric developers who edit in Neovim daily and want Claude Code to orchestrate nvim sessions with LSP and plugins.
Skip if: Developers using VS Code or JetBrains IDEs exclusively who do not run Neovim as their primary editor.
When should I use this skill?
The user edits in Neovim, requests nvim commands, or needs LSP-aware refactors executed inside a terminal session.
What you get
Neovim buffer edits, LSP refactor results, plugin-driven file changes, and terminal-native navigation without exiting the shell workflow.
- nvim buffer edits
- LSP refactor results
- plugin-driven file changes
Files
Neovim Configuration Skill
A comprehensive guide for working with this modular, performance-optimized Neovim configuration built on lazy.nvim.
Quick Reference
| Metric | Value |
|---|---|
| Plugin Manager | lazy.nvim |
| Total Plugins | 82 |
| Target Startup | <50ms |
| Module Pattern | M.setup() |
| Leader Key | <Space> |
Architecture Overview
~/.config/nvim/
├── init.lua # Entry point
├── lua/
│ ├── config/ # Core configuration (11 modules)
│ │ ├── lazy.lua # Plugin manager bootstrap
│ │ ├── options.lua # Vim options
│ │ ├── keymaps.lua # Key bindings
│ │ ├── autocmds.lua # Autocommands
│ │ └── performance.lua # Startup optimization
│ ├── plugins/specs/ # Plugin specs (9 categories)
│ │ ├── core.lua # Foundation (plenary, nui, devicons)
│ │ ├── ui.lua # UI (lualine, bufferline, noice)
│ │ ├── editor.lua # Editor (autopairs, flash, harpoon)
│ │ ├── lsp.lua # LSP (lspconfig, mason, conform)
│ │ ├── git.lua # Git (fugitive, gitsigns, diffview)
│ │ ├── ai.lua # AI (copilot, ChatGPT)
│ │ ├── debug.lua # DAP (nvim-dap, dap-ui)
│ │ ├── tools.lua # Tools (telescope, neo-tree)
│ │ └── treesitter.lua # Syntax (treesitter, textobjects)
│ ├── kickstart/ # Kickstart-derived modules
│ └── utils/ # Utility functions
└── lazy-lock.json # Plugin version lockStandard Module Pattern
All configuration modules follow the M.setup() pattern:
local M = {}
M.setup = function()
-- Configuration logic here
end
return MPlugin Management (lazy.nvim)
Adding a New Plugin
Add to the appropriate category file in lua/plugins/specs/:
-- lua/plugins/specs/tools.lua
return {
-- Existing plugins...
{
"author/plugin-name",
event = "VeryLazy", -- Loading strategy
dependencies = { "dep/name" }, -- Required plugins
opts = {
-- Plugin options
},
config = function(_, opts)
require("plugin-name").setup(opts)
end,
},
}Loading Strategies
| Strategy | When to Use | Example |
|---|---|---|
lazy = true | Default, load on demand | Most plugins |
event = "VeryLazy" | After UI loads | UI enhancements |
event = "BufReadPre" | When opening files | Treesitter, gitsigns |
event = "InsertEnter" | When typing | Completion, autopairs |
cmd = "CommandName" | On command invocation | Heavy tools |
ft = "filetype" | For specific filetypes | Language plugins |
keys = {...} | On keypress | Motion plugins |
Plugin Commands
| Command | Description |
|---|---|
:Lazy | Open lazy.nvim dashboard |
:Lazy sync | Update and install plugins |
:Lazy profile | Show startup time analysis |
:Lazy clean | Remove unused plugins |
:Lazy health | Check plugin health |
LSP Configuration
See references/lsp.md for complete LSP reference.
LSP Stack
mason.nvim (installer)
├── mason-lspconfig.nvim → nvim-lspconfig
├── mason-tool-installer.nvim (auto-install)
└── mason-nvim-dap.nvim → nvim-dap
nvim-lspconfig
├── blink.cmp (completion)
├── conform.nvim (formatting)
├── nvim-lint (linting)
└── trouble.nvim (diagnostics)Adding an LSP Server
-- In lua/plugins/specs/lsp.lua, add to mason-tool-installer list:
ensure_installed = {
"lua_ls",
"pyright",
"your_new_server", -- Add here
}
-- Configure in lspconfig setup:
servers = {
your_new_server = {
settings = {
-- Server-specific settings
},
},
}LSP Keybindings
| Key | Action |
|---|---|
gd | Go to definition |
gr | Go to references |
gI | Go to implementation |
gD | Go to declaration |
K | Hover documentation |
<leader>rn | Rename symbol |
<leader>ca | Code action |
<leader>D | Type definition |
<leader>ds | Document symbols |
<leader>ws | Workspace symbols |
Keybindings
See references/keybindings.md for complete reference.
Core Navigation
| Key | Action |
|---|---|
<C-h/j/k/l> | Window navigation |
<S-h> / <S-l> | Previous/next buffer |
<leader>sf | Search files |
<leader>sg | Search by grep |
<leader><space> | Search buffers |
\\ | Toggle Neo-tree |
Adding Keybindings
-- In lua/config/keymaps.lua M.setup():
vim.keymap.set('n', '<leader>xx', function()
-- Your action
end, { desc = 'Description for which-key' })
-- Or in a plugin spec:
keys = {
{ "<leader>xx", "<cmd>Command<CR>", desc = "Description" },
}Debugging (DAP)
See references/debugging.md for complete reference.
DAP Keybindings
| Key | Action |
|---|---|
<F5> | Continue/Start debugging |
<F10> | Step over |
<F11> | Step into |
<F12> | Step out |
<leader>b | Toggle breakpoint |
<leader>B | Conditional breakpoint |
Adding a Debug Adapter
-- In lua/plugins/specs/debug.lua
local dap = require("dap")
dap.adapters.your_adapter = {
type = "executable",
command = "path/to/adapter",
}
dap.configurations.your_filetype = {
{
type = "your_adapter",
request = "launch",
name = "Launch",
program = "${file}",
},
}Performance Optimization
Startup Optimization Layers
| Layer | Technique | Savings |
|---|---|---|
| 1 | vim.loader.enable() | ~50ms |
| 2 | Skip vim._defaults | ~180ms |
| 3 | Disable providers | ~10ms |
| 4 | Disable builtins | ~20ms |
| 5 | Deferred config | ~30ms |
| 6 | Event-based loading | Variable |
Profiling Startup
:Lazy profileDeferred Loading Pattern
-- In init.lua
vim.defer_fn(function()
require('config.options').setup()
require('config.keymaps').setup()
require('config.autocmds').setup()
end, 0)Common Tasks
Adding an Autocommand
-- In lua/config/autocmds.lua M.setup():
vim.api.nvim_create_autocmd("FileType", {
pattern = { "markdown", "text" },
callback = function()
vim.opt_local.wrap = true
vim.opt_local.spell = true
end,
})Adding Vim Options
-- In lua/config/options.lua M.setup():
vim.opt.your_option = valueCreating a Utility Function
-- In lua/utils/init.lua
local M = {}
M.your_function = function(args)
-- Implementation
end
return M
-- Usage: require('utils').your_function(args)Plugin Categories
Core (4 plugins)
plenary.nvim, nui.nvim, nvim-web-devicons, lazy.nvim
UI (11 plugins)
tokyonight, alpha-nvim, lualine, bufferline, noice, nvim-notify, which-key, indent-blankline, mini.indentscope, fidget, nvim-scrollbar
Editor (13 plugins)
nvim-autopairs, flash.nvim, clever-f, nvim-spectre, grug-far, harpoon, persistence, smartyank, vim-sleuth, vim-illuminate, tabular, todo-comments, toggleterm
LSP (12 plugins)
nvim-lspconfig, mason, mason-lspconfig, mason-tool-installer, lazydev, luvit-meta, SchemaStore, conform, nvim-lint, trouble, blink.cmp/nvim-cmp, LuaSnip
Git (7 plugins)
vim-fugitive, vim-rhubarb, gitsigns, diffview, vim-flog, git-conflict, octo
AI (3 plugins)
copilot.vim, ChatGPT.nvim, mcphub.nvim
Debug (8 plugins)
nvim-dap, nvim-dap-ui, nvim-dap-virtual-text, nvim-dap-python, nvim-dap-go, mason-nvim-dap, telescope-dap, nvim-nio
Tools (14 plugins)
telescope, telescope-fzf-native, telescope-ui-select, neo-tree, oil.nvim, nvim-bqf, rest.nvim, vim-dadbod, vim-dadbod-ui, vim-dadbod-completion, iron.nvim, markdown-preview, nvim-puppeteer, obsidian.nvim
Treesitter (3 plugins)
nvim-treesitter, nvim-treesitter-context, nvim-treesitter-textobjects
Troubleshooting
| Issue | Solution |
|---|---|
| Plugins not loading | :Lazy sync |
| LSP not starting | :LspInfo, :Mason |
| Icons missing | Install a Nerd Font |
| Slow startup | :Lazy profile |
| Treesitter errors | :TSUpdate |
| Keybinding conflicts | :verbose map <key> |
Health Check
:checkhealthDebug Logging
-- Temporarily add to plugin config:
log_level = vim.log.levels.DEBUG,Resources
References
- references/configuration.md - Core configuration options
- references/plugins.md - All 82 plugins detailed
- references/plugin-deepdives.md - In-depth plugin guides
- references/lsp.md - LSP server configuration
- references/keybindings.md - Complete keybinding reference
- references/debugging.md - DAP debugging guide
- references/performance.md - Optimization techniques
- references/tools.md - CLI tools, utilities, and workflows
- references/troubleshooting.md - Common issues and solutions
- references/migration-0.11.md - Neovim 0.11 migration guide
---
Gotchas
- LSP attaches on FileType, not BufRead: Buffers opened before plugin spec evaluation get no LSP.
:LspInfoshows nothing — open a new buffer of the same filetype or:editto retrigger the autocommand. - `lazy-lock.json` silently pins everything:
:Lazy syncwill not update plugins unless the lock entry is removed or:Lazy updateis run explicitly. Sync only installs missing plugins and removes orphans. - `vim.defer_fn(..., 0)` runs after UIEnter but before FileType: Config loaded this way misses the first buffer's filetype event. Move keymaps and options out of
defer_fnif first-buffer integrations break. - Mason installs to `~/.local/share/nvim/mason/bin/`, not `$PATH`: External tools that invoke formatters or linters from the shell will not find Mason-installed binaries unless you prepend that path explicitly.
- `event = "VeryLazy"` defers until after UI is ready: Plugins that intercept startup behavior (sessions, dashboards, colorschemes) must use
lazy = falsewithpriority = 1000— VeryLazy is too late. - Treesitter parsers compile against the installed Neovim ABI: After a Neovim upgrade,
:TSUpdateis mandatory or you will see "Impossible pattern" errors with no obvious cause.
Configuration Reference
Complete reference for core Neovim configuration options.
Entry Point (init.lua)
The startup sequence:
-- 1. Enable module caching
vim.loader.enable()
-- 2. Disable built-in plugins for performance
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
-- ... (16 built-ins disabled)
-- 3. Bootstrap phase
require('config.compat') -- Compatibility layer
require('config.lazy') -- Plugin manager
-- 4. Deferred phase (non-blocking)
vim.defer_fn(function()
require('config.options').setup()
require('config.keymaps').setup()
require('config.autocmds').setup()
end, 0)Options (lua/config/options.lua)
Essential Options
-- Line numbers
vim.opt.number = true
vim.opt.relativenumber = true
-- Indentation
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
vim.opt.expandtab = true
vim.opt.smartindent = true
-- Search
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.hlsearch = true
vim.opt.incsearch = true
-- Visual
vim.opt.termguicolors = true
vim.opt.signcolumn = "yes"
vim.opt.cursorline = true
vim.opt.scrolloff = 8
vim.opt.sidescrolloff = 8
-- Behavior
vim.opt.mouse = "a"
vim.opt.clipboard = "unnamedplus"
vim.opt.undofile = true
vim.opt.swapfile = false
vim.opt.backup = false
-- Splits
vim.opt.splitright = true
vim.opt.splitbelow = true
-- Performance
vim.opt.updatetime = 250
vim.opt.timeoutlen = 300Adding New Options
-- In lua/config/options.lua, inside M.setup():
vim.opt.your_option = value
-- For buffer-local options:
vim.opt_local.wrap = true
-- For global variables:
vim.g.some_plugin_setting = "value"Lazy.nvim Configuration (lua/config/lazy.lua)
require("lazy").setup({
spec = {
{ import = "plugins.specs.core" },
{ import = "plugins.specs.ui" },
{ import = "plugins.specs.editor" },
{ import = "plugins.specs.lsp" },
{ import = "plugins.specs.git" },
{ import = "plugins.specs.ai" },
{ import = "plugins.specs.debug" },
{ import = "plugins.specs.tools" },
{ import = "plugins.specs.treesitter" },
{ import = "kickstart.plugins" },
},
defaults = {
lazy = true, -- Lazy load by default
version = false, -- Use latest commits
},
install = {
colorscheme = { "tokyonight", "habamax" },
},
checker = {
enabled = true, -- Check for updates
notify = false, -- Don't notify on startup
},
performance = {
cache = {
enabled = true,
ttl = 3600 * 24 * 7, -- 1 week cache
},
rtp = {
disabled_plugins = {
"gzip", "matchit", "matchparen", "netrwPlugin",
"tarPlugin", "tohtml", "tutor", "zipPlugin",
},
},
},
})Leaders (lua/config/leaders.lua)
vim.g.mapleader = " " -- Space as leader
vim.g.maplocalleader = "\\" -- Backslash as local leaderAutocommands (lua/config/autocmds.lua)
Common Autocommand Patterns
-- Highlight on yank
vim.api.nvim_create_autocmd("TextYankPost", {
callback = function()
vim.highlight.on_yank({ timeout = 200 })
end,
})
-- Filetype-specific settings
vim.api.nvim_create_autocmd("FileType", {
pattern = { "markdown", "text" },
callback = function()
vim.opt_local.wrap = true
vim.opt_local.spell = true
end,
})
-- Auto-resize splits
vim.api.nvim_create_autocmd("VimResized", {
callback = function()
vim.cmd("tabdo wincmd =")
end,
})
-- Remove trailing whitespace on save
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*",
callback = function()
local save_cursor = vim.fn.getpos(".")
vim.cmd([[%s/\s\+$//e]])
vim.fn.setpos(".", save_cursor)
end,
})Creating Autocommand Groups
local group = vim.api.nvim_create_augroup("MyAutoGroup", { clear = true })
vim.api.nvim_create_autocmd("BufEnter", {
group = group,
pattern = "*.lua",
callback = function()
-- Lua-specific setup
end,
})Performance (lua/config/performance.lua)
Disabled Built-in Plugins
local disabled = {
"gzip", "zip", "zipPlugin", "tar", "tarPlugin",
"getscript", "getscriptPlugin", "vimball", "vimballPlugin",
"2html_plugin", "logipat", "rrhelper", "spellfile_plugin",
"matchit", "matchparen", "netrw", "netrwPlugin",
}
for _, plugin in pairs(disabled) do
vim.g["loaded_" .. plugin] = 1
endDisabled Providers
-- Disable unused language providers
vim.g.loaded_node_provider = 0
vim.g.loaded_perl_provider = 0
vim.g.loaded_ruby_provider = 0GC Optimization
-- Aggressive GC during startup
collectgarbage("setstepmul", 200)
-- Relax after startup
vim.api.nvim_create_autocmd("User", {
pattern = "VeryLazy",
callback = function()
collectgarbage("setstepmul", 100)
end,
})Compatibility (lua/config/compat.lua)
Provides shims for deprecated functions:
-- vim.tbl_islist → vim.islist
if vim.islist then
vim.tbl_islist = vim.islist
end
-- vim.tbl_flatten (deprecated in 0.11)
if not vim.tbl_flatten then
vim.tbl_flatten = function(t)
return vim.iter(t):flatten():totable()
end
endConstants (lua/config/constants.lua)
Centralized configuration values:
return {
-- UI
border = "rounded",
icons = {
diagnostics = {
Error = " ",
Warn = " ",
Info = " ",
Hint = " ",
},
},
-- Paths
paths = {
cache = vim.fn.stdpath("cache"),
data = vim.fn.stdpath("data"),
config = vim.fn.stdpath("config"),
},
}Debugging Reference
Complete guide for debugging with DAP (Debug Adapter Protocol) in this Neovim configuration.
DAP Stack Overview
nvim-dap (Core DAP client)
├── nvim-dap-ui (UI panels)
├── nvim-dap-virtual-text (Inline variable values)
├── nvim-nio (Async IO for dap-ui)
└── Language-specific adapters
├── nvim-dap-python
├── nvim-dap-go
└── mason-nvim-dap (Adapter installer)Keybindings
| Key | Action | Description |
|---|---|---|
<F5> | Continue | Start/continue debugging |
<F10> | Step Over | Execute current line |
<F11> | Step Into | Step into function |
<F12> | Step Out | Step out of function |
<leader>b | Toggle Breakpoint | Set/remove breakpoint |
<leader>B | Conditional Breakpoint | Breakpoint with condition |
<leader>lp | Log Point | Set log point message |
<leader>dr | REPL | Open DAP REPL |
<leader>dl | Run Last | Repeat last debug session |
<leader>dh | Hover | Show variable value |
<leader>dp | Preview | Preview variable in popup |
<leader>df | Frames | List stack frames |
<leader>ds | Scopes | List variable scopes |
DAP UI Layout
┌─────────────────────────────────────────────────────────────┐
│ Scopes (Variables) │ Breakpoints │ Stacks │ Watches │
├─────────────────────────────────────────────────────────────┤
│ │
│ Source Code │
│ (with virtual text) │
│ │
├─────────────────────────────────────────────────────────────┤
│ REPL │
│ Console │
└─────────────────────────────────────────────────────────────┘Python Debugging
Prerequisites
# Install debugpy via Mason
:Mason
# Search for "debugpy" and install
# Or via pip
pip install debugpyConfiguration
-- lua/plugins/specs/debug.lua
{
"mfussenegger/nvim-dap-python",
ft = "python",
dependencies = { "mfussenegger/nvim-dap" },
config = function()
require("dap-python").setup("python")
end,
}Debug Configurations
-- Automatically provided by dap-python:
-- 1. Launch file
-- 2. Launch file with arguments
-- 3. Attach remote
-- 4. Run doctests in file
-- Add custom configuration:
require("dap").configurations.python = {
{
type = "python",
request = "launch",
name = "Django",
program = "${workspaceFolder}/manage.py",
args = { "runserver", "--noreload" },
django = true,
},
{
type = "python",
request = "launch",
name = "Flask",
module = "flask",
args = { "run", "--no-debugger" },
env = { FLASK_APP = "app.py" },
},
}Usage
1. Open Python file 2. Set breakpoints with <leader>b 3. Press <F5> to start debugging 4. Select configuration from menu
Go Debugging
Prerequisites
# Install delve via Mason
:Mason
# Search for "delve" and install
# Or via go
go install github.com/go-delve/delve/cmd/dlv@latestConfiguration
-- lua/plugins/specs/debug.lua
{
"leoluz/nvim-dap-go",
ft = "go",
dependencies = { "mfussenegger/nvim-dap" },
opts = {
dap_configurations = {
{
type = "go",
name = "Attach remote",
mode = "remote",
request = "attach",
},
},
delve = {
build_flags = "",
},
},
}Debug Configurations
-- Automatically provided:
-- 1. Debug (compile and run)
-- 2. Debug test (current file)
-- 3. Debug test (current function)
-- Add custom:
require("dap").configurations.go = {
{
type = "go",
name = "Debug Package",
request = "launch",
program = "${fileDirname}",
},
{
type = "go",
name = "Debug with Args",
request = "launch",
program = "${file}",
args = function()
return vim.split(vim.fn.input("Args: "), " ")
end,
},
}JavaScript/TypeScript Debugging
Prerequisites
# Install js-debug-adapter via Mason
:Mason
# Search for "js-debug-adapter"Configuration
require("dap").adapters["pwa-node"] = {
type = "server",
host = "localhost",
port = "${port}",
executable = {
command = "node",
args = {
require("mason-registry").get_package("js-debug-adapter"):get_install_path()
.. "/js-debug/src/dapDebugServer.js",
"${port}",
},
},
}
require("dap").configurations.javascript = {
{
type = "pwa-node",
request = "launch",
name = "Launch file",
program = "${file}",
cwd = "${workspaceFolder}",
},
{
type = "pwa-node",
request = "attach",
name = "Attach",
processId = require("dap.utils").pick_process,
cwd = "${workspaceFolder}",
},
}
require("dap").configurations.typescript = require("dap").configurations.javascriptAdding Custom Debug Adapters
Step 1: Define Adapter
local dap = require("dap")
dap.adapters.my_adapter = {
type = "executable", -- or "server"
command = "/path/to/adapter",
args = { "--port", "${port}" },
}
-- For server adapters:
dap.adapters.my_server_adapter = {
type = "server",
host = "127.0.0.1",
port = "${port}",
executable = {
command = "/path/to/adapter",
args = { "${port}" },
},
}Step 2: Define Configurations
dap.configurations.my_filetype = {
{
type = "my_adapter",
request = "launch", -- or "attach"
name = "Launch Program",
program = "${file}",
cwd = "${workspaceFolder}",
args = {},
env = {},
stopOnEntry = false,
},
}Step 3: Set Filetype
-- In ftplugin/my_filetype.lua or via autocmd
vim.api.nvim_create_autocmd("FileType", {
pattern = "my_filetype",
callback = function()
-- Filetype-specific debug config
end,
})DAP UI Configuration
{
"rcarriga/nvim-dap-ui",
dependencies = { "mfussenegger/nvim-dap", "nvim-neotest/nvim-nio" },
opts = {
icons = { expanded = "▾", collapsed = "▸", current_frame = "→" },
mappings = {
expand = { "<CR>", "<2-LeftMouse>" },
open = "o",
remove = "d",
edit = "e",
repl = "r",
toggle = "t",
},
layouts = {
{
elements = {
{ id = "scopes", size = 0.25 },
{ id = "breakpoints", size = 0.25 },
{ id = "stacks", size = 0.25 },
{ id = "watches", size = 0.25 },
},
position = "left",
size = 40,
},
{
elements = {
{ id = "repl", size = 0.5 },
{ id = "console", size = 0.5 },
},
position = "bottom",
size = 10,
},
},
floating = {
border = "rounded",
mappings = { close = { "q", "<Esc>" } },
},
},
config = function(_, opts)
local dap, dapui = require("dap"), require("dapui")
dapui.setup(opts)
-- Auto open/close UI
dap.listeners.after.event_initialized["dapui_config"] = function()
dapui.open()
end
dap.listeners.before.event_terminated["dapui_config"] = function()
dapui.close()
end
dap.listeners.before.event_exited["dapui_config"] = function()
dapui.close()
end
end,
}Virtual Text Configuration
{
"theHamsta/nvim-dap-virtual-text",
opts = {
enabled = true,
enabled_commands = true,
highlight_changed_variables = true,
highlight_new_as_changed = false,
show_stop_reason = true,
commented = false,
virt_text_pos = "eol", -- or "inline"
all_frames = false,
virt_lines = false,
virt_text_win_col = nil,
},
}Breakpoints
Types of Breakpoints
local dap = require("dap")
-- Regular breakpoint
dap.toggle_breakpoint()
-- Conditional breakpoint
dap.set_breakpoint(vim.fn.input("Condition: "))
-- Log point (prints message without stopping)
dap.set_breakpoint(nil, nil, vim.fn.input("Log message: "))
-- Hit count breakpoint
dap.set_breakpoint(nil, vim.fn.input("Hit count: "))Breakpoint Signs
vim.fn.sign_define("DapBreakpoint", {
text = "●",
texthl = "DapBreakpoint",
linehl = "",
numhl = "",
})
vim.fn.sign_define("DapBreakpointCondition", {
text = "◆",
texthl = "DapBreakpointCondition",
})
vim.fn.sign_define("DapLogPoint", {
text = "◆",
texthl = "DapLogPoint",
})
vim.fn.sign_define("DapStopped", {
text = "→",
texthl = "DapStopped",
linehl = "DapStoppedLine",
})REPL Commands
Inside the DAP REPL:
| Command | Description |
|---|---|
.exit | Close REPL |
.c | Continue |
.n | Step over |
.s | Step into |
.o | Step out |
.up | Go up stack frame |
.down | Go down stack frame |
.scopes | Print scopes |
.threads | Print threads |
.frames | Print frames |
Troubleshooting
| Issue | Solution |
|---|---|
| Adapter not found | Check Mason installation, verify path |
| Breakpoint not hit | Ensure source maps, check file paths |
| UI not opening | Check dap listeners are configured |
| Variables not showing | Ensure stopped at breakpoint, check scopes |
| Can't attach | Verify process is running with debug flag |
Debug Logging
-- Enable DAP logging
require("dap").set_log_level("TRACE")
-- View log
:lua print(vim.fn.stdpath("cache") .. "/dap.log")Check DAP Status
-- Show current session info
:lua print(vim.inspect(require("dap").session()))
-- List breakpoints
:lua print(vim.inspect(require("dap.breakpoints").get()))Keybindings Reference
Complete reference for all keybindings in this Neovim configuration.
Leader Key
| Key | Function |
|---|---|
<Space> | Leader key |
\ | Local leader |
Navigation
Window Navigation
| Key | Action | Mode |
|---|---|---|
<C-h> | Move to left window | Normal |
<C-j> | Move to lower window | Normal |
<C-k> | Move to upper window | Normal |
<C-l> | Move to right window | Normal |
Buffer Navigation
| Key | Action | Mode |
|---|---|---|
<S-h> | Previous buffer | Normal |
<S-l> | Next buffer | Normal |
<leader>bd | Delete buffer | Normal |
<leader>ba | Delete all buffers except current | Normal |
Harpoon (Quick Files)
| Key | Action | Mode |
|---|---|---|
<leader>a | Add file to harpoon | Normal |
<C-e> | Toggle harpoon menu | Normal |
<C-1> - <C-4> | Navigate to file 1-4 | Normal |
Jumps
| Key | Action | Mode |
|---|---|---|
<C-d> | Scroll down (centered) | Normal |
<C-u> | Scroll up (centered) | Normal |
n | Next search result (centered) | Normal |
N | Previous search result (centered) | Normal |
[d | Previous diagnostic | Normal |
]d | Next diagnostic | Normal |
[c | Previous git change | Normal |
]c | Next git change | Normal |
Search (Telescope)
| Key | Action | Mode |
|---|---|---|
<leader>sf | Search files | Normal |
<leader>sg | Search by grep | Normal |
<leader>sw | Search current word | Normal |
<leader>sh | Search help tags | Normal |
<leader>sk | Search keymaps | Normal |
<leader>sd | Search diagnostics | Normal |
<leader>sr | Resume last search | Normal |
<leader>ss | Search Telescope builtins | Normal |
<leader>sn | Search Neovim config files | Normal |
<leader><space> | Search buffers | Normal |
<leader>/ | Fuzzy search in buffer | Normal |
<leader>? | Search recent files | Normal |
<leader>gf | Search git files | Normal |
LSP
Navigation
| Key | Action | Mode |
|---|---|---|
gd | Go to definition | Normal |
gr | Go to references | Normal |
gI | Go to implementation | Normal |
gD | Go to declaration | Normal |
<leader>D | Type definition | Normal |
<leader>ds | Document symbols | Normal |
<leader>ws | Workspace symbols | Normal |
Actions
| Key | Action | Mode |
|---|---|---|
K | Hover documentation | Normal |
<C-k> | Signature help | Insert |
<leader>rn | Rename symbol | Normal |
<leader>ca | Code action | Normal, Visual |
<leader>cf | Format buffer | Normal |
Git
Gitsigns
| Key | Action | Mode |
|---|---|---|
<leader>hs | Stage hunk | Normal |
<leader>hr | Reset hunk | Normal |
<leader>hS | Stage buffer | Normal |
<leader>hR | Reset buffer | Normal |
<leader>hu | Undo stage hunk | Normal |
<leader>hp | Preview hunk | Normal |
<leader>hb | Blame line | Normal |
<leader>hd | Diff against index | Normal |
<leader>hD | Diff against last commit | Normal |
<leader>tb | Toggle line blame | Normal |
<leader>td | Toggle deleted | Normal |
Text Objects (Git)
| Key | Action | Mode |
|---|---|---|
ih | Inner hunk | Operator-pending, Visual |
ah | Around hunk | Operator-pending, Visual |
Fugitive
| Key | Action | Mode |
|---|---|---|
:Git | Git status | Command |
:Gdiffsplit | Diff current file | Command |
:Gread | Checkout file | Command |
:Gwrite | Stage file | Command |
Editing
Basic
| Key | Action | Mode |
|---|---|---|
<Esc> | Clear search highlights | Normal |
jk | Exit insert mode | Insert |
<C-s> | Save file | Normal, Insert |
<leader>q | Quit | Normal |
<leader>Q | Force quit | Normal |
<leader>wa | Save all | Normal |
Text Manipulation
| Key | Action | Mode |
|---|---|---|
J | Move selection down | Visual |
K | Move selection up | Visual |
< | Indent left (keep selection) | Visual |
> | Indent right (keep selection) | Visual |
Clipboard
| Key | Action | Mode |
|---|---|---|
<leader>y | Yank to system clipboard | Normal, Visual |
<leader>Y | Yank line to clipboard | Normal |
<leader>d | Delete to void register | Normal, Visual |
<leader>p | Paste without yanking | Visual |
<C-c> | Copy to clipboard | Visual |
<C-v> | Paste from clipboard | Normal, Insert |
Commenting
| Key | Action | Mode |
|---|---|---|
gcc | Toggle line comment | Normal |
gbc | Toggle block comment | Normal |
gc | Comment motion | Normal, Visual |
gb | Block comment motion | Normal, Visual |
gco | Add comment below | Normal |
gcO | Add comment above | Normal |
gcA | Add comment at end of line | Normal |
Surround (mini.surround)
| Key | Action | Mode |
|---|---|---|
sa | Add surrounding | Normal, Visual |
sd | Delete surrounding | Normal |
sr | Replace surrounding | Normal |
File Explorer (Neo-tree)
| Key | Action | Mode |
|---|---|---|
\ or \\ | Toggle Neo-tree | Normal |
<leader>e | Focus Neo-tree | Normal |
Inside Neo-tree
| Key | Action |
|---|---|
? | Show help |
<CR> | Open file/folder |
s | Open in horizontal split |
v | Open in vertical split |
t | Open in new tab |
a | Add file/folder |
d | Delete |
r | Rename |
y | Copy path |
x | Cut |
p | Paste |
c | Copy |
R | Refresh |
H | Toggle hidden files |
Completion (blink.cmp with super-tab preset)
Using super-tab preset - Tab/S-Tab for menu navigation and snippet jumping.
| Key | Action | Mode |
|---|---|---|
<Tab> | Next item / Jump to next snippet placeholder | Insert, Select |
<S-Tab> | Previous item / Jump to previous snippet placeholder | Insert, Select |
<CR> | Confirm selection | Insert |
<C-Space> | Show/toggle completion menu | Insert |
<C-e> | Close completion | Insert |
<C-b> | Scroll docs up | Insert |
<C-f> | Scroll docs down | Insert |
Note: When no completion menu is visible, Tab inserts a normal tab character.
Debugging (DAP)
| Key | Action | Mode |
|---|---|---|
<F5> | Continue/Start | Normal |
<F10> | Step over | Normal |
<F11> | Step into | Normal |
<F12> | Step out | Normal |
<leader>b | Toggle breakpoint | Normal |
<leader>B | Conditional breakpoint | Normal |
<leader>lp | Log point | Normal |
<leader>dr | Open REPL | Normal |
<leader>dl | Run last | Normal |
<leader>dh | Hover variables | Normal |
Terminal
| Key | Action | Mode |
|---|---|---|
<C-\> | Toggle terminal | Normal |
<Esc><Esc> | Exit terminal mode | Terminal |
<C-h/j/k/l> | Navigate from terminal | Terminal |
Testing (Neotest)
| Key | Action | Mode |
|---|---|---|
<leader>tt | Run nearest test | Normal |
<leader>tT | Run all tests in file | Normal |
<leader>tr | Run test suite | Normal |
<leader>tl | Run last test | Normal |
<leader>ts | Toggle test summary | Normal |
<leader>to | Show test output | Normal |
Flash (Motion)
| Key | Action | Mode |
|---|---|---|
s | Flash jump | Normal |
S | Flash treesitter | Normal |
r | Remote flash | Operator-pending |
Treesitter
| Key | Action | Mode |
|---|---|---|
<C-space> | Increment selection | Normal |
<BS> | Decrement selection | Visual |
Text Objects
| Key | Action | Mode |
|---|---|---|
af | Around function | Operator-pending, Visual |
if | Inner function | Operator-pending, Visual |
ac | Around class | Operator-pending, Visual |
ic | Inner class | Operator-pending, Visual |
]f | Next function start | Normal |
[f | Previous function start | Normal |
]c | Next class start | Normal |
[c | Previous class start | Normal |
Adding Custom Keybindings
In config/keymaps.lua
-- Inside M.setup()
vim.keymap.set('n', '<leader>xx', function()
-- Your action here
end, { desc = 'Description for which-key' })In Plugin Spec
{
"plugin/name",
keys = {
{ "<leader>xx", "<cmd>Command<CR>", desc = "Description" },
{ "<leader>xy", function() ... end, desc = "Lua function" },
},
}Buffer-Local Keybindings
vim.api.nvim_create_autocmd("FileType", {
pattern = "markdown",
callback = function()
vim.keymap.set('n', '<leader>mp', '<cmd>MarkdownPreview<CR>', {
buffer = true,
desc = "Markdown Preview",
})
end,
})Discovering Keybindings
| Command | Description |
|---|---|
<leader>sk | Search keymaps with Telescope |
:map | List all mappings |
:nmap <leader> | List leader mappings |
:verbose map <key> | Show where mapping is defined |
Press <leader> and wait | Which-key popup |
LSP Configuration Reference
Complete guide for configuring Language Server Protocol in this Neovim setup.
LSP Stack Overview
mason.nvim (Package Manager)
├── mason-lspconfig.nvim → nvim-lspconfig (LSP servers)
├── mason-tool-installer.nvim (Auto-install tools)
└── mason-nvim-dap.nvim → nvim-dap (Debug adapters)
nvim-lspconfig (LSP Client)
├── blink.cmp / nvim-cmp (Completion)
├── conform.nvim (Formatting)
├── nvim-lint (Linting)
└── trouble.nvim (Diagnostics UI)Installing LSP Servers
Via Mason (Recommended)
:MasonThen search and install servers interactively, or:
Via Configuration
-- In lua/plugins/specs/lsp.lua
{
"WhoIsSethDaniel/mason-tool-installer.nvim",
opts = {
ensure_installed = {
-- LSP Servers
"lua_ls",
"pyright",
"tsserver",
"gopls",
"rust_analyzer",
"yamlls",
"jsonls",
-- Formatters
"stylua",
"prettierd",
"ruff",
"gofumpt",
-- Linters
"eslint_d",
"luacheck",
"shellcheck",
},
},
}Configuring LSP Servers
Basic Server Configuration
-- In lua/plugins/specs/lsp.lua
local servers = {
lua_ls = {
settings = {
Lua = {
workspace = { checkThirdParty = false },
telemetry = { enable = false },
diagnostics = {
globals = { "vim" },
},
},
},
},
pyright = {
settings = {
python = {
analysis = {
typeCheckingMode = "basic",
autoSearchPaths = true,
useLibraryCodeForTypes = true,
},
},
},
},
gopls = {
settings = {
gopls = {
analyses = {
unusedparams = true,
},
staticcheck = true,
gofumpt = true,
},
},
},
tsserver = {},
jsonls = {},
yamlls = {},
}Server with Custom on_attach
{
"neovim/nvim-lspconfig",
config = function()
local lspconfig = require("lspconfig")
local capabilities = require("cmp_nvim_lsp").default_capabilities()
local on_attach = function(client, bufnr)
-- Buffer-local keymaps
local map = function(keys, func, desc)
vim.keymap.set("n", keys, func, { buffer = bufnr, desc = desc })
end
map("gd", vim.lsp.buf.definition, "[G]oto [D]efinition")
map("gr", vim.lsp.buf.references, "[G]oto [R]eferences")
map("gI", vim.lsp.buf.implementation, "[G]oto [I]mplementation")
map("<leader>D", vim.lsp.buf.type_definition, "Type [D]efinition")
map("<leader>rn", vim.lsp.buf.rename, "[R]e[n]ame")
map("<leader>ca", vim.lsp.buf.code_action, "[C]ode [A]ction")
map("K", vim.lsp.buf.hover, "Hover Documentation")
-- Highlight references under cursor
if client.server_capabilities.documentHighlightProvider then
vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
buffer = bufnr,
callback = vim.lsp.buf.document_highlight,
})
vim.api.nvim_create_autocmd("CursorMoved", {
buffer = bufnr,
callback = vim.lsp.buf.clear_references,
})
end
end
for server, config in pairs(servers) do
config.capabilities = capabilities
config.on_attach = on_attach
lspconfig[server].setup(config)
end
end,
}LSP Keybindings
| Key | Action | Description |
|---|---|---|
gd | vim.lsp.buf.definition | Go to definition |
gr | vim.lsp.buf.references | List references |
gI | vim.lsp.buf.implementation | Go to implementation |
gD | vim.lsp.buf.declaration | Go to declaration |
K | vim.lsp.buf.hover | Hover documentation |
<C-k> | vim.lsp.buf.signature_help | Signature help (insert) |
<leader>D | vim.lsp.buf.type_definition | Type definition |
<leader>rn | vim.lsp.buf.rename | Rename symbol |
<leader>ca | vim.lsp.buf.code_action | Code actions |
<leader>ds | vim.lsp.buf.document_symbol | Document symbols |
<leader>ws | vim.lsp.buf.workspace_symbol | Workspace symbols |
[d | vim.diagnostic.goto_prev | Previous diagnostic |
]d | vim.diagnostic.goto_next | Next diagnostic |
Completion (blink.cmp)
Keymap Presets
blink.cmp provides keymap presets for common configurations:
| Preset | Navigation | Description |
|---|---|---|
default | <C-n>/<C-p> | Classic Vim-style navigation |
super-tab | <Tab>/<S-Tab> | Tab navigates items and snippet placeholders |
enter | <C-n>/<C-p> | Enter confirms, Tab for snippets only |
Current Configuration (super-tab)
{
"saghen/blink.cmp",
version = "*",
event = "InsertEnter",
dependencies = { "rafamadriz/friendly-snippets" },
opts = {
keymap = { preset = "super-tab" }, -- Tab/S-Tab navigation
sources = {
default = { "lsp", "path", "snippets", "buffer" },
},
completion = {
menu = { auto_show = true },
documentation = { auto_show = true },
},
},
}super-tab Preset Keybindings
| Key | Action |
|---|---|
<Tab> | Select next item / Jump to next snippet placeholder |
<S-Tab> | Select previous item / Jump to previous snippet placeholder |
<CR> | Accept completion |
<C-Space> | Show/toggle completion menu |
<C-e> | Hide completion menu |
<C-b> | Scroll documentation up |
<C-f> | Scroll documentation down |
Custom Keymap Configuration
For custom keymaps instead of a preset:
opts = {
keymap = {
["<C-space>"] = { "show", "show_documentation", "hide_documentation" },
["<C-e>"] = { "hide" },
["<CR>"] = { "accept", "fallback" },
["<Tab>"] = { "select_next", "snippet_forward", "fallback" },
["<S-Tab>"] = { "select_prev", "snippet_backward", "fallback" },
["<C-b>"] = { "scroll_documentation_up", "fallback" },
["<C-f>"] = { "scroll_documentation_down", "fallback" },
},
}Formatting (conform.nvim)
{
"stevearc/conform.nvim",
event = "BufWritePre",
cmd = { "ConformInfo" },
opts = {
formatters_by_ft = {
lua = { "stylua" },
python = { "ruff_format" },
javascript = { "prettierd", "prettier" },
typescript = { "prettierd", "prettier" },
javascriptreact = { "prettierd", "prettier" },
typescriptreact = { "prettierd", "prettier" },
json = { "prettierd" },
yaml = { "prettierd" },
markdown = { "prettierd" },
go = { "gofumpt", "goimports" },
rust = { "rustfmt" },
sh = { "shfmt" },
},
format_on_save = {
timeout_ms = 500,
lsp_fallback = true,
},
},
keys = {
{
"<leader>cf",
function()
require("conform").format({ async = true, lsp_fallback = true })
end,
desc = "[C]ode [F]ormat",
},
},
}Linting (nvim-lint)
{
"mfussenegger/nvim-lint",
event = { "BufReadPre", "BufNewFile" },
config = function()
local lint = require("lint")
lint.linters_by_ft = {
javascript = { "eslint_d" },
typescript = { "eslint_d" },
python = { "ruff" },
lua = { "luacheck" },
sh = { "shellcheck" },
markdown = { "markdownlint" },
}
vim.api.nvim_create_autocmd({ "BufWritePost", "BufReadPost", "InsertLeave" }, {
callback = function()
lint.try_lint()
end,
})
end,
}Diagnostics Configuration
vim.diagnostic.config({
virtual_text = {
prefix = "●",
severity = { min = vim.diagnostic.severity.WARN },
},
signs = {
text = {
[vim.diagnostic.severity.ERROR] = " ",
[vim.diagnostic.severity.WARN] = " ",
[vim.diagnostic.severity.INFO] = " ",
[vim.diagnostic.severity.HINT] = " ",
},
},
underline = true,
update_in_insert = false,
severity_sort = true,
float = {
focusable = true,
border = "rounded",
source = "always",
},
})Trouble.nvim (Diagnostics Viewer)
{
"folke/trouble.nvim",
cmd = { "Trouble" },
opts = {},
keys = {
{ "<leader>xx", "<cmd>Trouble diagnostics toggle<cr>", desc = "Diagnostics" },
{ "<leader>xX", "<cmd>Trouble diagnostics toggle filter.buf=0<cr>", desc = "Buffer Diagnostics" },
{ "<leader>cs", "<cmd>Trouble symbols toggle<cr>", desc = "Symbols" },
{ "<leader>xL", "<cmd>Trouble loclist toggle<cr>", desc = "Location List" },
{ "<leader>xQ", "<cmd>Trouble qflist toggle<cr>", desc = "Quickfix List" },
},
}Adding a New LSP Server
1. Install via Mason:
:Mason
" Search for and install the server2. Add to auto-install list:
-- In mason-tool-installer opts
ensure_installed = {
"your_server",
}3. Configure the server:
local servers = {
your_server = {
settings = {
-- Server-specific settings
},
filetypes = { "your_filetype" },
root_dir = lspconfig.util.root_pattern(".git", "package.json"),
},
}4. Restart Neovim or run:
:LspRestartTroubleshooting
| Issue | Solution |
|---|---|
| LSP not starting | :LspInfo, check server is installed via :Mason |
| No completions | Check :LspInfo for active clients |
| Formatting not working | :ConformInfo, verify formatter installed |
| Diagnostics not showing | :lua vim.diagnostic.setloclist() |
| Wrong root directory | Check root_dir in server config |
Debug Commands
:LspInfo " Show active LSP clients
:LspLog " Open LSP log file
:LspRestart " Restart LSP clients
:Mason " Open Mason installer
:ConformInfo " Show formatter info
:lua vim.lsp.set_log_level("debug") " Enable debug loggingNeovim 0.11 Migration Guide
Guide for migrating this configuration to Neovim 0.11 and beyond.
Version Requirements
| Component | Minimum | Recommended |
|---|---|---|
| Neovim | 0.9.0 | 0.11+ |
| lazy.nvim | 10.0 | Latest |
| Treesitter | 0.9 | Latest |
Breaking Changes in 0.11
1. vim.tbl_flatten Deprecated
Old:
local flat = vim.tbl_flatten({ { 1, 2 }, { 3, 4 } })New:
local flat = vim.iter({ { 1, 2 }, { 3, 4 } }):flatten():totable()Compatibility Shim (in lua/config/compat.lua):
if not vim.tbl_flatten then
vim.tbl_flatten = function(t)
return vim.iter(t):flatten():totable()
end
end2. vim.tbl_islist → vim.islist
Old:
if vim.tbl_islist(t) thenNew:
if vim.islist(t) thenCompatibility Shim:
if vim.islist and not vim.tbl_islist then
vim.tbl_islist = vim.islist
end3. vim.lsp.buf.formatting Removed
Old:
vim.lsp.buf.formatting()
vim.lsp.buf.formatting_sync()New:
vim.lsp.buf.format()
vim.lsp.buf.format({ async = false }) -- For sync4. vim.diagnostic.disable/enable Signature Change
Old:
vim.diagnostic.disable(bufnr)
vim.diagnostic.enable(bufnr)New:
vim.diagnostic.enable(false, { bufnr = bufnr })
vim.diagnostic.enable(true, { bufnr = bufnr })5. vim.lsp.get_active_clients → vim.lsp.get_clients
Old:
local clients = vim.lsp.get_active_clients()
local clients = vim.lsp.get_active_clients({ bufnr = 0 })New:
local clients = vim.lsp.get_clients()
local clients = vim.lsp.get_clients({ bufnr = 0 })6. Inlay Hints API Change
Old (0.10):
vim.lsp.inlay_hint(bufnr, true)New (0.11+):
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
-- Or globally
vim.lsp.inlay_hint.enable(true)---
New Features in 0.11
1. Native Snippets
Neovim 0.11 includes native snippet support:
-- No need for LuaSnip for basic snippets
vim.snippet.expand("function ${1:name}(${2:args})\n\t${0}\nend")
-- Jump between placeholders
vim.snippet.jump(1) -- Next
vim.snippet.jump(-1) -- Previous
-- Check if in snippet
vim.snippet.active({ direction = 1 })2. Enhanced LSP Defaults
Default LSP keymaps are now built-in:
-- These are now defaults (can be disabled)
vim.g.lsp_default_keymaps = true -- Enabled by default
-- Default mappings:
-- grn - vim.lsp.buf.rename()
-- gra - vim.lsp.buf.code_action()
-- grr - vim.lsp.buf.references()
-- gri - vim.lsp.buf.implementation()
-- gO - vim.lsp.buf.document_symbol()
-- <C-s> - vim.lsp.buf.signature_help() (insert mode)3. vim.iter Improvements
-- Filter and map
local result = vim.iter({ 1, 2, 3, 4, 5 })
:filter(function(x) return x % 2 == 0 end)
:map(function(x) return x * 2 end)
:totable()
-- { 4, 8 }
-- Flatten nested tables
local flat = vim.iter({ { 1, 2 }, { 3, 4 } }):flatten():totable()
-- { 1, 2, 3, 4 }
-- Find first match
local first_even = vim.iter({ 1, 3, 4, 5 }):find(function(x)
return x % 2 == 0
end)
-- 44. Terminal Improvements
-- New terminal highlights
vim.api.nvim_set_hl(0, "TermCursor", { bg = "#ffffff" })
vim.api.nvim_set_hl(0, "TermCursorNC", { bg = "#666666" })5. Improved vim.ui
-- vim.ui.open for system open
vim.ui.open("https://neovim.io")
vim.ui.open("/path/to/file.pdf")---
Plugin Compatibility
Plugins That Need Updates for 0.11
| Plugin | Issue | Solution |
|---|---|---|
| nvim-cmp | Snippet API | Update to latest |
| LuaSnip | Optional now | Can use native snippets |
| null-ls | Archived | Use conform.nvim + nvim-lint |
| lspconfig | API changes | Update to latest |
Recommended Updates
1. Replace null-ls:
-- Before (null-ls)
null_ls.builtins.formatting.stylua
null_ls.builtins.diagnostics.eslint
-- After (conform + nvim-lint)
formatters_by_ft = { lua = { "stylua" } }
linters_by_ft = { javascript = { "eslint_d" } }2. Use native snippets or update LuaSnip:
-- Native snippets
vim.keymap.set({ "i", "s" }, "<Tab>", function()
if vim.snippet.active({ direction = 1 }) then
return "<cmd>lua vim.snippet.jump(1)<cr>"
else
return "<Tab>"
end
end, { expr = true })---
Migration Checklist
Before Upgrading
- [ ] Backup current configuration
- [ ] Check plugin compatibility
- [ ] Review breaking changes above
- [ ] Update lazy.nvim to latest
Update Steps
1. Update Neovim:
brew upgrade neovim
# or
brew install neovim --HEAD2. Update plugins:
:Lazy sync3. Check for deprecation warnings:
:checkhealth4. Apply compatibility shims:
-- In lua/config/compat.lua
require("config.compat")5. Test critical features:
- LSP completions
- Formatting
- Diagnostics
- Treesitter highlighting
- DAP debugging
After Upgrading
- [ ] Run
:checkhealth - [ ] Verify LSP works (
:LspInfo) - [ ] Check completions
- [ ] Test formatting
- [ ] Verify Treesitter (
:TSInstallInfo)
---
Compatibility Layer
Full compatibility module for gradual migration:
-- lua/config/compat.lua
local M = {}
M.setup = function()
-- vim.tbl_islist → vim.islist
if vim.islist and not vim.tbl_islist then
vim.tbl_islist = vim.islist
elseif vim.tbl_islist and not vim.islist then
vim.islist = vim.tbl_islist
end
-- vim.tbl_flatten (deprecated in 0.11)
if not vim.tbl_flatten then
vim.tbl_flatten = function(t)
return vim.iter(t):flatten():totable()
end
end
-- vim.tbl_add_reverse_lookup (deprecated)
if not vim.tbl_add_reverse_lookup then
vim.tbl_add_reverse_lookup = function(t)
for k, v in pairs(t) do
t[v] = k
end
return t
end
end
-- vim.lsp.get_active_clients → vim.lsp.get_clients
if vim.lsp.get_clients and not vim.lsp.get_active_clients then
vim.lsp.get_active_clients = vim.lsp.get_clients
end
-- Check Neovim version
local version = vim.version()
if version.major == 0 and version.minor >= 11 then
-- 0.11+ specific setup
-- Disable default LSP keymaps if you have custom ones
-- vim.g.lsp_default_keymaps = false
end
end
return M---
Feature Flags
Use feature detection instead of version checks:
-- Check for native snippets
local has_native_snippets = vim.snippet ~= nil
-- Check for new inlay hints API
local has_new_inlay_hints = vim.lsp.inlay_hint and vim.lsp.inlay_hint.enable
-- Check for vim.iter
local has_iter = vim.iter ~= nil
-- Use feature flags
if has_native_snippets then
-- Use native snippets
else
-- Use LuaSnip
end---
Deprecated Features to Avoid
| Deprecated | Replacement |
|---|---|
vim.tbl_flatten() | vim.iter():flatten():totable() |
vim.tbl_islist() | vim.islist() |
vim.lsp.buf.formatting() | vim.lsp.buf.format() |
vim.lsp.buf.range_formatting() | vim.lsp.buf.format({ range = ... }) |
vim.lsp.get_active_clients() | vim.lsp.get_clients() |
vim.lsp.buf_get_clients() | vim.lsp.get_clients({ bufnr = 0 }) |
vim.diagnostic.disable() | vim.diagnostic.enable(false, opts) |
vim.lsp.diagnostic | vim.diagnostic |
---
Testing Configuration
# Test with specific Neovim version
nvim --version
# Test startup
nvim --startuptime /tmp/startup.log -c "q"
cat /tmp/startup.log
# Test health
nvim --headless -c "checkhealth" -c "qa!" 2>&1 | head -100
# Test with clean config
nvim --clean
# Test specific plugin
nvim --cmd "lua vim.g.test_mode = true" -c "Lazy load telescope.nvim"Performance Optimization Reference
Complete guide for optimizing Neovim startup and runtime performance.
Target Metrics
| Metric | Target | Measure With |
|---|---|---|
| Startup Time | <50ms | :Lazy profile |
| Plugin Count | 82 | :Lazy |
| Lazy Loaded | ~95% | :Lazy |
| Memory (startup) | <50MB | :lua print(collectgarbage("count")) |
Startup Optimization Layers
Layer 1: Module Caching (~50ms savings)
-- init.lua (first line)
vim.loader.enable()This caches Lua bytecode for faster subsequent loads.
Layer 2: Skip vim._defaults (~180ms savings)
-- init.lua
vim.g.skip_defaults = true -- Skip default vim settingsLayer 3: Disable Providers (~10ms savings)
-- lua/config/performance.lua
vim.g.loaded_node_provider = 0
vim.g.loaded_perl_provider = 0
vim.g.loaded_ruby_provider = 0Layer 4: Disable Built-in Plugins (~20ms savings)
local disabled_builtins = {
"gzip",
"zip",
"zipPlugin",
"tar",
"tarPlugin",
"getscript",
"getscriptPlugin",
"vimball",
"vimballPlugin",
"2html_plugin",
"logipat",
"rrhelper",
"spellfile_plugin",
"matchit",
"matchparen",
"netrw",
"netrwPlugin",
}
for _, plugin in pairs(disabled_builtins) do
vim.g["loaded_" .. plugin] = 1
endLayer 5: Deferred Configuration (~30ms savings)
-- init.lua
vim.defer_fn(function()
require('config.options').setup()
require('config.keymaps').setup()
require('config.autocmds').setup()
end, 0)Layer 6: Event-Based Plugin Loading (Variable)
| Loading Strategy | When Loaded | Best For |
|---|---|---|
lazy = true | On demand | Default for all plugins |
event = "VeryLazy" | After UI | UI enhancements |
event = "BufReadPre" | Opening files | Treesitter, gitsigns |
event = "InsertEnter" | Start typing | Completion, autopairs |
cmd = "Command" | On command | Heavy tools |
ft = "filetype" | On filetype | Language plugins |
keys = {...} | On keypress | Motion plugins |
Lazy.nvim Performance Config
require("lazy").setup({
-- Plugin specs...
}, {
defaults = {
lazy = true, -- Lazy load by default
version = false, -- Use latest commits
},
performance = {
cache = {
enabled = true,
ttl = 3600 * 24 * 7, -- 1 week cache
},
reset_packpath = true,
rtp = {
reset = true,
disabled_plugins = {
"gzip", "matchit", "matchparen", "netrwPlugin",
"tarPlugin", "tohtml", "tutor", "zipPlugin",
},
},
},
})Profiling Tools
Lazy Profile
:Lazy profileShows:
- Total startup time
- Time per plugin
- Loading order
- Event triggers
Startup Time Breakdown
# From command line
nvim --startuptime startup.log
# View results
nvim startup.logMemory Usage
-- Current memory (KB)
:lua print(collectgarbage("count"))
-- Force garbage collection
:lua collectgarbage("collect")
:lua print(collectgarbage("count"))Plugin Loading
:Lazy " Show all plugins and status
:Lazy health " Check lazy.nvim health
:Lazy profile " Detailed timingGC Optimization
-- lua/config/performance.lua
M.setup = function()
-- Aggressive GC during startup
collectgarbage("setstepmul", 200)
-- Relax GC after startup
vim.api.nvim_create_autocmd("User", {
pattern = "VeryLazy",
callback = function()
collectgarbage("setstepmul", 100)
end,
})
endPlugin-Specific Optimizations
Treesitter
{
"nvim-treesitter/nvim-treesitter",
event = { "BufReadPost", "BufNewFile" },
opts = {
highlight = {
enable = true,
disable = function(lang, buf)
-- Disable for large files
local max_filesize = 100 * 1024 -- 100 KB
local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
if ok and stats and stats.size > max_filesize then
return true
end
end,
},
},
}Telescope
{
"nvim-telescope/telescope.nvim",
cmd = "Telescope", -- Only load on command
dependencies = {
{
"nvim-telescope/telescope-fzf-native.nvim",
build = "make", -- Compiled for performance
},
},
}LSP
-- Debounce diagnostics
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
update_in_insert = false, -- Don't update in insert mode
virtual_text = {
severity = { min = vim.diagnostic.severity.WARN },
},
}
)Completion
{
"saghen/blink.cmp",
event = "InsertEnter", -- Only on insert
opts = {
trigger = {
completion = {
debounce_ms = 100, -- Debounce completions
},
},
},
}Runtime Optimizations
Reduce Redraws
vim.opt.lazyredraw = true -- Don't redraw during macrosSyntax Optimization
vim.opt.synmaxcol = 240 -- Only highlight first 240 columnsFold Optimization
-- Use treesitter folding with fallback
vim.opt.foldmethod = "expr"
vim.opt.foldexpr = "nvim_treesitter#foldexpr()"
vim.opt.foldlevelstart = 99 -- Start with all folds openTimeout Optimization
vim.opt.updatetime = 250 -- Faster CursorHold
vim.opt.timeoutlen = 300 -- Faster key sequenceLarge File Handling
vim.api.nvim_create_autocmd("BufReadPre", {
callback = function(args)
local ok, stats = pcall(vim.loop.fs_stat, args.file)
if ok and stats and stats.size > 1024 * 1024 then -- 1MB
-- Disable expensive features
vim.cmd("syntax off")
vim.opt_local.foldmethod = "manual"
vim.opt_local.spell = false
vim.opt_local.swapfile = false
vim.opt_local.undofile = false
end
end,
})Monitoring Performance
Create Performance Report
local function performance_report()
local stats = {
startup_time = vim.fn.reltimefloat(vim.fn.reltime(vim.g.start_time)) * 1000,
memory = collectgarbage("count"),
buffers = #vim.fn.getbufinfo({ buflisted = 1 }),
windows = #vim.api.nvim_list_wins(),
plugins = require("lazy").stats().count,
loaded = require("lazy").stats().loaded,
}
print(string.format([[
Performance Report:
Startup: %.2fms
Memory: %.2f KB
Buffers: %d
Windows: %d
Plugins: %d/%d loaded
]], stats.startup_time, stats.memory, stats.buffers,
stats.windows, stats.loaded, stats.plugins))
end
vim.api.nvim_create_user_command("PerformanceReport", performance_report, {})Troubleshooting Slow Startup
1. Profile with Lazy:
:Lazy profile2. Check for slow plugins: Look for plugins taking >10ms in profile
3. Verify lazy loading:
:LazyCheck plugins show "not loaded" until used
4. Check startup log:
nvim --startuptime /tmp/startup.log && cat /tmp/startup.log5. Identify culprits:
- Look for
require()calls in init.lua - Check for synchronous operations
- Look for large file reads
Best Practices
1. Always lazy load - Set lazy = true as default 2. Use events wisely - VeryLazy for UI, BufReadPre for editing 3. Defer non-critical - Use vim.defer_fn() for setup that can wait 4. Profile regularly - Check :Lazy profile after changes 5. Avoid sync operations - Use async where possible 6. Limit startup plugins - Only core/theme need to load at start 7. Cache aggressively - Let vim.loader do its job
Plugin Deep-Dives
In-depth configuration guides for the most important plugins.
lazy.nvim (Plugin Manager)
Architecture
~/.local/share/nvim/lazy/ # Plugin install location
├── lazy.nvim/ # Self-managed
├── plenary.nvim/
├── telescope.nvim/
└── ...
~/.config/nvim/lazy-lock.json # Version lock fileAdvanced Configuration
require("lazy").setup({
spec = { import = "plugins.specs" },
defaults = {
lazy = true,
version = false,
},
install = {
missing = true,
colorscheme = { "tokyonight", "habamax" },
},
checker = {
enabled = true,
concurrency = 4,
notify = false,
frequency = 3600,
},
change_detection = {
enabled = true,
notify = false,
},
performance = {
cache = { enabled = true },
reset_packpath = true,
rtp = {
reset = true,
disabled_plugins = { "netrw", "netrwPlugin" },
},
},
ui = {
border = "rounded",
icons = {
loaded = "●",
not_loaded = "○",
},
},
})Plugin Spec Options
| Option | Type | Description |
|---|---|---|
enabled | boolean/function | Enable/disable plugin |
cond | boolean/function | Conditional loading |
dependencies | string/table | Required plugins |
init | function | Runs before loading |
opts | table/function | Options for setup() |
config | function/true | Configuration function |
build | string/function | Build command |
branch | string | Git branch |
tag | string | Git tag |
version | string | Semver version |
pin | boolean | Don't update |
priority | number | Load priority (higher first) |
Loading Triggers
-- Event-based
event = "VeryLazy"
event = { "BufReadPre", "BufNewFile" }
event = "InsertEnter"
event = "CmdlineEnter"
-- Command-based
cmd = "Telescope"
cmd = { "Git", "Gdiffsplit" }
-- Filetype-based
ft = "lua"
ft = { "python", "javascript" }
-- Key-based
keys = {
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find Files" },
{ "<leader>fg", mode = { "n", "v" }, "<cmd>Telescope live_grep<cr>" },
}
-- Module-based (loads when require() is called)
-- Automatic for lazy = true plugins---
Telescope.nvim (Fuzzy Finder)
Core Configuration
{
"nvim-telescope/telescope.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
{ "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
"nvim-telescope/telescope-ui-select.nvim",
},
opts = {
defaults = {
prompt_prefix = " ",
selection_caret = " ",
path_display = { "truncate" },
sorting_strategy = "ascending",
layout_config = {
horizontal = {
prompt_position = "top",
preview_width = 0.55,
},
vertical = { mirror = false },
width = 0.87,
height = 0.80,
preview_cutoff = 120,
},
mappings = {
i = {
["<C-j>"] = "move_selection_next",
["<C-k>"] = "move_selection_previous",
["<C-n>"] = "cycle_history_next",
["<C-p>"] = "cycle_history_prev",
["<C-c>"] = "close",
["<CR>"] = "select_default",
["<C-x>"] = "select_horizontal",
["<C-v>"] = "select_vertical",
["<C-t>"] = "select_tab",
["<C-u>"] = "preview_scrolling_up",
["<C-d>"] = "preview_scrolling_down",
},
n = {
["q"] = "close",
["<Esc>"] = "close",
},
},
},
pickers = {
find_files = {
hidden = true,
find_command = { "fd", "--type", "f", "--strip-cwd-prefix" },
},
live_grep = {
additional_args = function()
return { "--hidden" }
end,
},
buffers = {
show_all_buffers = true,
sort_lastused = true,
mappings = {
i = { ["<C-d>"] = "delete_buffer" },
},
},
},
extensions = {
fzf = {
fuzzy = true,
override_generic_sorter = true,
override_file_sorter = true,
case_mode = "smart_case",
},
["ui-select"] = {
require("telescope.themes").get_dropdown(),
},
},
},
config = function(_, opts)
local telescope = require("telescope")
telescope.setup(opts)
telescope.load_extension("fzf")
telescope.load_extension("ui-select")
end,
}Custom Pickers
-- Find in Neovim config
vim.keymap.set("n", "<leader>sn", function()
require("telescope.builtin").find_files({
cwd = vim.fn.stdpath("config"),
})
end, { desc = "Search Neovim config" })
-- Search TODOs
vim.keymap.set("n", "<leader>st", function()
require("telescope.builtin").grep_string({
search = "TODO|FIXME|HACK|NOTE",
use_regex = true,
})
end, { desc = "Search TODOs" })
-- Custom picker
local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local my_picker = function(opts)
opts = opts or {}
pickers.new(opts, {
prompt_title = "My Picker",
finder = finders.new_table({
results = { "item1", "item2", "item3" },
}),
sorter = conf.generic_sorter(opts),
}):find()
end---
nvim-lspconfig (LSP Client)
Server Configuration Pattern
local lspconfig = require("lspconfig")
local capabilities = require("cmp_nvim_lsp").default_capabilities()
-- Shared on_attach
local on_attach = function(client, bufnr)
-- Keymaps
local map = function(keys, func, desc)
vim.keymap.set("n", keys, func, { buffer = bufnr, desc = desc })
end
map("gd", vim.lsp.buf.definition, "Go to Definition")
map("gr", vim.lsp.buf.references, "Go to References")
map("K", vim.lsp.buf.hover, "Hover")
map("<leader>rn", vim.lsp.buf.rename, "Rename")
map("<leader>ca", vim.lsp.buf.code_action, "Code Action")
-- Highlight references
if client.server_capabilities.documentHighlightProvider then
vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
buffer = bufnr,
callback = vim.lsp.buf.document_highlight,
})
vim.api.nvim_create_autocmd("CursorMoved", {
buffer = bufnr,
callback = vim.lsp.buf.clear_references,
})
end
-- Inlay hints (Neovim 0.10+)
if client.server_capabilities.inlayHintProvider then
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
end
end
-- Configure servers
local servers = {
lua_ls = {
settings = {
Lua = {
runtime = { version = "LuaJIT" },
workspace = {
checkThirdParty = false,
library = vim.api.nvim_get_runtime_file("", true),
},
diagnostics = { globals = { "vim" } },
telemetry = { enable = false },
hint = { enable = true },
},
},
},
pyright = {
settings = {
python = {
analysis = {
typeCheckingMode = "basic",
autoSearchPaths = true,
diagnosticMode = "workspace",
useLibraryCodeForTypes = true,
},
},
},
},
gopls = {
settings = {
gopls = {
analyses = { unusedparams = true, shadow = true },
staticcheck = true,
gofumpt = true,
hints = {
assignVariableTypes = true,
compositeLiteralFields = true,
constantValues = true,
functionTypeParameters = true,
parameterNames = true,
rangeVariableTypes = true,
},
},
},
},
tsserver = {
settings = {
typescript = {
inlayHints = {
includeInlayParameterNameHints = "all",
includeInlayFunctionParameterTypeHints = true,
includeInlayVariableTypeHints = true,
},
},
},
},
}
for server, config in pairs(servers) do
config.capabilities = capabilities
config.on_attach = on_attach
lspconfig[server].setup(config)
endCustom Language Server
-- For non-Mason servers
lspconfig.my_custom_server.setup({
cmd = { "/path/to/server" },
filetypes = { "myfiletype" },
root_dir = lspconfig.util.root_pattern(".git", "setup.py"),
settings = {},
})---
nvim-treesitter (Syntax)
Full Configuration
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
event = { "BufReadPost", "BufNewFile" },
dependencies = {
"nvim-treesitter/nvim-treesitter-textobjects",
"nvim-treesitter/nvim-treesitter-context",
},
opts = {
ensure_installed = {
"bash", "c", "cpp", "go", "lua", "python", "rust",
"javascript", "typescript", "tsx", "json", "yaml",
"html", "css", "markdown", "markdown_inline",
"vim", "vimdoc", "query", "regex",
},
auto_install = true,
highlight = {
enable = true,
disable = function(lang, buf)
local max_filesize = 100 * 1024
local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
if ok and stats and stats.size > max_filesize then
return true
end
end,
additional_vim_regex_highlighting = false,
},
indent = { enable = true },
incremental_selection = {
enable = true,
keymaps = {
init_selection = "<C-space>",
node_incremental = "<C-space>",
scope_incremental = false,
node_decremental = "<bs>",
},
},
textobjects = {
select = {
enable = true,
lookahead = true,
keymaps = {
["af"] = "@function.outer",
["if"] = "@function.inner",
["ac"] = "@class.outer",
["ic"] = "@class.inner",
["aa"] = "@parameter.outer",
["ia"] = "@parameter.inner",
["ai"] = "@conditional.outer",
["ii"] = "@conditional.inner",
["al"] = "@loop.outer",
["il"] = "@loop.inner",
},
},
move = {
enable = true,
set_jumps = true,
goto_next_start = {
["]f"] = "@function.outer",
["]c"] = "@class.outer",
["]a"] = "@parameter.inner",
},
goto_next_end = {
["]F"] = "@function.outer",
["]C"] = "@class.outer",
},
goto_previous_start = {
["[f"] = "@function.outer",
["[c"] = "@class.outer",
["[a"] = "@parameter.inner",
},
goto_previous_end = {
["[F"] = "@function.outer",
["[C"] = "@class.outer",
},
},
swap = {
enable = true,
swap_next = { ["<leader>a"] = "@parameter.inner" },
swap_previous = { ["<leader>A"] = "@parameter.inner" },
},
},
},
config = function(_, opts)
require("nvim-treesitter.configs").setup(opts)
end,
}Custom Queries
-- Create custom highlight query
-- ~/.config/nvim/after/queries/lua/highlights.scm
;; extends
(function_call
name: (identifier) @function.builtin
(#eq? @function.builtin "require"))---
gitsigns.nvim (Git Integration)
Full Configuration
{
"lewis6991/gitsigns.nvim",
event = { "BufReadPre", "BufNewFile" },
opts = {
signs = {
add = { text = "▎" },
change = { text = "▎" },
delete = { text = "" },
topdelete = { text = "" },
changedelete = { text = "▎" },
untracked = { text = "▎" },
},
signcolumn = true,
numhl = false,
linehl = false,
word_diff = false,
watch_gitdir = { interval = 1000, follow_files = true },
attach_to_untracked = true,
current_line_blame = false,
current_line_blame_opts = {
virt_text = true,
virt_text_pos = "eol",
delay = 500,
ignore_whitespace = false,
},
current_line_blame_formatter = "<author>, <author_time:%Y-%m-%d> - <summary>",
sign_priority = 6,
update_debounce = 100,
status_formatter = nil,
max_file_length = 40000,
preview_config = {
border = "rounded",
style = "minimal",
relative = "cursor",
row = 0,
col = 1,
},
on_attach = function(bufnr)
local gs = package.loaded.gitsigns
local function map(mode, l, r, opts)
opts = opts or {}
opts.buffer = bufnr
vim.keymap.set(mode, l, r, opts)
end
-- Navigation
map("n", "]c", function()
if vim.wo.diff then return "]c" end
vim.schedule(function() gs.next_hunk() end)
return "<Ignore>"
end, { expr = true, desc = "Next hunk" })
map("n", "[c", function()
if vim.wo.diff then return "[c" end
vim.schedule(function() gs.prev_hunk() end)
return "<Ignore>"
end, { expr = true, desc = "Previous hunk" })
-- Actions
map("n", "<leader>hs", gs.stage_hunk, { desc = "Stage hunk" })
map("n", "<leader>hr", gs.reset_hunk, { desc = "Reset hunk" })
map("v", "<leader>hs", function()
gs.stage_hunk({ vim.fn.line("."), vim.fn.line("v") })
end, { desc = "Stage hunk" })
map("v", "<leader>hr", function()
gs.reset_hunk({ vim.fn.line("."), vim.fn.line("v") })
end, { desc = "Reset hunk" })
map("n", "<leader>hS", gs.stage_buffer, { desc = "Stage buffer" })
map("n", "<leader>hu", gs.undo_stage_hunk, { desc = "Undo stage" })
map("n", "<leader>hR", gs.reset_buffer, { desc = "Reset buffer" })
map("n", "<leader>hp", gs.preview_hunk, { desc = "Preview hunk" })
map("n", "<leader>hb", function()
gs.blame_line({ full = true })
end, { desc = "Blame line" })
map("n", "<leader>tb", gs.toggle_current_line_blame, { desc = "Toggle blame" })
map("n", "<leader>hd", gs.diffthis, { desc = "Diff this" })
map("n", "<leader>hD", function()
gs.diffthis("~")
end, { desc = "Diff ~" })
map("n", "<leader>td", gs.toggle_deleted, { desc = "Toggle deleted" })
-- Text object
map({ "o", "x" }, "ih", ":<C-U>Gitsigns select_hunk<CR>", { desc = "Select hunk" })
end,
},
}---
blink.cmp (Completion)
Configuration
{
"saghen/blink.cmp",
version = "*",
event = "InsertEnter",
dependencies = {
"rafamadriz/friendly-snippets",
"L3MON4D3/LuaSnip",
},
opts = {
keymap = {
preset = "default",
["<C-space>"] = { "show", "show_documentation", "hide_documentation" },
["<C-e>"] = { "hide", "fallback" },
["<CR>"] = { "accept", "fallback" },
["<Tab>"] = { "select_next", "snippet_forward", "fallback" },
["<S-Tab>"] = { "select_prev", "snippet_backward", "fallback" },
["<Up>"] = { "select_prev", "fallback" },
["<Down>"] = { "select_next", "fallback" },
["<C-p>"] = { "select_prev", "fallback" },
["<C-n>"] = { "select_next", "fallback" },
["<C-b>"] = { "scroll_documentation_up", "fallback" },
["<C-f>"] = { "scroll_documentation_down", "fallback" },
},
appearance = {
use_nvim_cmp_as_default = true,
nerd_font_variant = "mono",
},
sources = {
default = { "lsp", "path", "snippets", "buffer" },
cmdline = {},
},
completion = {
accept = { auto_brackets = { enabled = true } },
menu = {
border = "rounded",
draw = {
columns = {
{ "kind_icon" },
{ "label", "label_description", gap = 1 },
},
},
},
documentation = {
auto_show = true,
auto_show_delay_ms = 200,
window = { border = "rounded" },
},
ghost_text = { enabled = true },
},
signature = { enabled = true },
snippets = {
expand = function(snippet)
require("luasnip").lsp_expand(snippet)
end,
active = function(filter)
if filter and filter.direction then
return require("luasnip").jumpable(filter.direction)
end
return require("luasnip").in_snippet()
end,
jump = function(direction)
require("luasnip").jump(direction)
end,
},
},
}---
neo-tree.nvim (File Explorer)
Full Configuration
{
"nvim-neo-tree/neo-tree.nvim",
branch = "v3.x",
cmd = "Neotree",
dependencies = {
"nvim-lua/plenary.nvim",
"nvim-tree/nvim-web-devicons",
"MunifTanjim/nui.nvim",
},
keys = {
{ "\\", "<cmd>Neotree toggle<cr>", desc = "Toggle Explorer" },
{ "<leader>e", "<cmd>Neotree focus<cr>", desc = "Focus Explorer" },
{ "<leader>ge", "<cmd>Neotree git_status<cr>", desc = "Git Explorer" },
{ "<leader>be", "<cmd>Neotree buffers<cr>", desc = "Buffer Explorer" },
},
opts = {
close_if_last_window = true,
popup_border_style = "rounded",
enable_git_status = true,
enable_diagnostics = true,
sort_case_insensitive = true,
default_component_configs = {
indent = {
with_expanders = true,
expander_collapsed = "",
expander_expanded = "",
},
icon = {
folder_closed = "",
folder_open = "",
folder_empty = "",
},
modified = { symbol = "●" },
git_status = {
symbols = {
added = "",
modified = "",
deleted = "✖",
renamed = "",
untracked = "",
ignored = "",
unstaged = "",
staged = "",
conflict = "",
},
},
},
window = {
position = "left",
width = 35,
mappings = {
["<space>"] = "none",
["<CR>"] = "open",
["o"] = "open",
["s"] = "open_split",
["v"] = "open_vsplit",
["t"] = "open_tabnew",
["a"] = { "add", config = { show_path = "relative" } },
["A"] = "add_directory",
["d"] = "delete",
["r"] = "rename",
["y"] = "copy_to_clipboard",
["x"] = "cut_to_clipboard",
["p"] = "paste_from_clipboard",
["c"] = "copy",
["m"] = "move",
["q"] = "close_window",
["R"] = "refresh",
["?"] = "show_help",
["<"] = "prev_source",
[">"] = "next_source",
["H"] = "toggle_hidden",
["/"] = "fuzzy_finder",
["f"] = "filter_on_submit",
["<C-x>"] = "clear_filter",
["[g"] = "prev_git_modified",
["]g"] = "next_git_modified",
},
},
filesystem = {
bind_to_cwd = false,
follow_current_file = { enabled = true },
use_libuv_file_watcher = true,
filtered_items = {
visible = false,
hide_dotfiles = false,
hide_gitignored = true,
hide_by_name = { ".git", "node_modules", "__pycache__" },
never_show = { ".DS_Store" },
},
},
buffers = {
follow_current_file = { enabled = true },
group_empty_dirs = true,
},
git_status = {
window = { position = "float" },
},
},
}Plugin Reference
Complete reference for all 82 plugins organized by category.
Plugin Spec Structure
{
"author/plugin-name", -- GitHub short URL
version = "*", -- Use latest stable (or false for HEAD)
enabled = true, -- Enable/disable plugin
cond = function() end, -- Conditional loading
dependencies = {}, -- Required plugins
init = function() end, -- Run before loading
opts = {}, -- Options passed to setup()
config = function(_, opts) -- Configuration function
require("plugin").setup(opts)
end,
-- Loading triggers (pick one)
lazy = true, -- Default lazy
event = "VeryLazy", -- On event
cmd = "CommandName", -- On command
ft = "filetype", -- On filetype
keys = { ... }, -- On keypress
}Core Plugins
plenary.nvim
Lua utility library required by many plugins.
{ "nvim-lua/plenary.nvim", lazy = true }nui.nvim
UI component library for neo-tree, noice, etc.
{ "MunifTanjim/nui.nvim", lazy = true }nvim-web-devicons
File icons support.
{
"nvim-tree/nvim-web-devicons",
lazy = true,
opts = { default = true },
}UI Plugins
tokyonight.nvim
Colorscheme.
{
"folke/tokyonight.nvim",
lazy = false,
priority = 1000,
opts = { style = "night" },
config = function(_, opts)
require("tokyonight").setup(opts)
vim.cmd.colorscheme("tokyonight")
end,
}lualine.nvim
Status line.
{
"nvim-lualine/lualine.nvim",
event = "VeryLazy",
opts = {
options = {
theme = "tokyonight",
component_separators = "|",
section_separators = "",
},
},
}bufferline.nvim
Buffer tabs.
{
"akinsho/bufferline.nvim",
event = "VeryLazy",
opts = {
options = {
diagnostics = "nvim_lsp",
offsets = {
{ filetype = "neo-tree", text = "File Explorer" },
},
},
},
}noice.nvim
Enhanced UI for messages, cmdline, popupmenu.
{
"folke/noice.nvim",
event = "VeryLazy",
dependencies = { "MunifTanjim/nui.nvim", "rcarriga/nvim-notify" },
opts = {
lsp = {
override = {
["vim.lsp.util.convert_input_to_markdown_lines"] = true,
["vim.lsp.util.stylize_markdown"] = true,
["cmp.entry.get_documentation"] = true,
},
},
presets = {
command_palette = true,
lsp_doc_border = true,
},
},
}which-key.nvim
Keybinding hints.
{
"folke/which-key.nvim",
event = "VeryLazy",
opts = {
plugins = { spelling = true },
},
config = function(_, opts)
local wk = require("which-key")
wk.setup(opts)
wk.register({
["<leader>"] = {
c = { name = "+code" },
g = { name = "+git" },
s = { name = "+search" },
-- Add more groups
},
})
end,
}Editor Plugins
flash.nvim
Enhanced motion.
{
"folke/flash.nvim",
event = "VeryLazy",
opts = {},
keys = {
{ "s", function() require("flash").jump() end, desc = "Flash" },
{ "S", function() require("flash").treesitter() end, desc = "Flash Treesitter" },
},
}harpoon
Quick file navigation.
{
"ThePrimeagen/harpoon",
branch = "harpoon2",
dependencies = { "nvim-lua/plenary.nvim" },
keys = {
{ "<leader>a", function() require("harpoon"):list():add() end },
{ "<C-e>", function() require("harpoon").ui:toggle_quick_menu(require("harpoon"):list()) end },
},
}nvim-autopairs
Auto bracket pairing.
{
"windwp/nvim-autopairs",
event = "InsertEnter",
opts = {
check_ts = true,
fast_wrap = {},
},
}toggleterm.nvim
Terminal integration.
{
"akinsho/toggleterm.nvim",
cmd = "ToggleTerm",
keys = { { "<C-\\>", "<cmd>ToggleTerm<cr>" } },
opts = {
size = 20,
direction = "float",
float_opts = { border = "rounded" },
},
}LSP Plugins
nvim-lspconfig
LSP configuration.
{
"neovim/nvim-lspconfig",
event = { "BufReadPre", "BufNewFile" },
dependencies = {
"williamboman/mason.nvim",
"williamboman/mason-lspconfig.nvim",
},
config = function()
-- See references/lsp.md for full config
end,
}mason.nvim
LSP/DAP/Linter installer.
{
"williamboman/mason.nvim",
cmd = "Mason",
opts = {
ui = { border = "rounded" },
},
}conform.nvim
Code formatting.
{
"stevearc/conform.nvim",
event = { "BufWritePre" },
cmd = { "ConformInfo" },
opts = {
formatters_by_ft = {
lua = { "stylua" },
python = { "ruff_format" },
javascript = { "prettierd", "prettier" },
typescript = { "prettierd", "prettier" },
json = { "prettierd" },
yaml = { "prettierd" },
markdown = { "prettierd" },
},
format_on_save = {
timeout_ms = 500,
lsp_fallback = true,
},
},
}trouble.nvim
Diagnostics viewer.
{
"folke/trouble.nvim",
cmd = { "TroubleToggle", "Trouble" },
opts = {},
keys = {
{ "<leader>xx", "<cmd>Trouble diagnostics toggle<cr>" },
{ "<leader>xX", "<cmd>Trouble diagnostics toggle filter.buf=0<cr>" },
},
}Git Plugins
gitsigns.nvim
Git signs in gutter.
{
"lewis6991/gitsigns.nvim",
event = { "BufReadPre", "BufNewFile" },
opts = {
signs = {
add = { text = "▎" },
change = { text = "▎" },
delete = { text = "" },
},
on_attach = function(buffer)
-- See references/keybindings.md for git keymaps
end,
},
}vim-fugitive
Git commands.
{
"tpope/vim-fugitive",
cmd = { "Git", "G", "Gdiffsplit", "Gread", "Gwrite", "Ggrep", "GMove", "GDelete", "GBrowse" },
}diffview.nvim
Diff viewer.
{
"sindrets/diffview.nvim",
cmd = { "DiffviewOpen", "DiffviewFileHistory" },
opts = {},
}AI Plugins
copilot.vim
GitHub Copilot.
{
"github/copilot.vim",
event = "InsertEnter",
config = function()
vim.g.copilot_no_tab_map = true
vim.keymap.set("i", "<C-J>", 'copilot#Accept("\\<CR>")', {
expr = true,
replace_keycodes = false,
})
end,
}ChatGPT.nvim
ChatGPT integration.
{
"jackMort/ChatGPT.nvim",
cmd = { "ChatGPT", "ChatGPTActAs", "ChatGPTEditWithInstructions" },
dependencies = {
"MunifTanjim/nui.nvim",
"nvim-lua/plenary.nvim",
"nvim-telescope/telescope.nvim",
},
opts = {
api_key_cmd = "pass show api/openai",
},
}Debug Plugins
nvim-dap
Debug adapter protocol.
{
"mfussenegger/nvim-dap",
dependencies = {
"rcarriga/nvim-dap-ui",
"theHamsta/nvim-dap-virtual-text",
"nvim-neotest/nvim-nio",
},
keys = {
{ "<F5>", function() require("dap").continue() end },
{ "<F10>", function() require("dap").step_over() end },
{ "<F11>", function() require("dap").step_into() end },
{ "<F12>", function() require("dap").step_out() end },
{ "<leader>b", function() require("dap").toggle_breakpoint() end },
},
}nvim-dap-ui
DAP UI.
{
"rcarriga/nvim-dap-ui",
dependencies = { "mfussenegger/nvim-dap", "nvim-neotest/nvim-nio" },
opts = {},
config = function(_, opts)
local dap, dapui = require("dap"), require("dapui")
dapui.setup(opts)
dap.listeners.after.event_initialized["dapui_config"] = function()
dapui.open()
end
dap.listeners.before.event_terminated["dapui_config"] = function()
dapui.close()
end
end,
}Tools Plugins
telescope.nvim
Fuzzy finder.
{
"nvim-telescope/telescope.nvim",
cmd = "Telescope",
dependencies = {
"nvim-lua/plenary.nvim",
{ "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
},
keys = {
{ "<leader>sf", "<cmd>Telescope find_files<cr>" },
{ "<leader>sg", "<cmd>Telescope live_grep<cr>" },
{ "<leader><space>", "<cmd>Telescope buffers<cr>" },
{ "<leader>sh", "<cmd>Telescope help_tags<cr>" },
},
opts = {
defaults = {
mappings = {
i = {
["<C-j>"] = "move_selection_next",
["<C-k>"] = "move_selection_previous",
},
},
},
},
}neo-tree.nvim
File explorer.
{
"nvim-neo-tree/neo-tree.nvim",
branch = "v3.x",
cmd = "Neotree",
dependencies = {
"nvim-lua/plenary.nvim",
"nvim-tree/nvim-web-devicons",
"MunifTanjim/nui.nvim",
},
keys = {
{ "\\", "<cmd>Neotree toggle<cr>" },
{ "<leader>e", "<cmd>Neotree focus<cr>" },
},
opts = {
filesystem = {
follow_current_file = { enabled = true },
use_libuv_file_watcher = true,
},
},
}Treesitter Plugins
nvim-treesitter
Syntax parsing.
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
event = { "BufReadPost", "BufNewFile" },
opts = {
ensure_installed = {
"lua", "python", "javascript", "typescript", "go",
"json", "yaml", "markdown", "markdown_inline",
"bash", "vim", "vimdoc", "query",
},
highlight = { enable = true },
indent = { enable = true },
incremental_selection = {
enable = true,
keymaps = {
init_selection = "<C-space>",
node_incremental = "<C-space>",
node_decremental = "<bs>",
},
},
},
config = function(_, opts)
require("nvim-treesitter.configs").setup(opts)
end,
}nvim-treesitter-textobjects
Enhanced text objects.
{
"nvim-treesitter/nvim-treesitter-textobjects",
dependencies = "nvim-treesitter/nvim-treesitter",
opts = {
textobjects = {
select = {
enable = true,
lookahead = true,
keymaps = {
["af"] = "@function.outer",
["if"] = "@function.inner",
["ac"] = "@class.outer",
["ic"] = "@class.inner",
},
},
move = {
enable = true,
goto_next_start = {
["]f"] = "@function.outer",
["]c"] = "@class.outer",
},
goto_previous_start = {
["[f"] = "@function.outer",
["[c"] = "@class.outer",
},
},
},
},
}Tools & Utilities Reference
Command-line tools, Neovim utilities, and AI-assisted workflow patterns for this configuration.
Neovim Built-in Tools
Health Checks
:checkhealth " Full system health check
:checkhealth lazy " Lazy.nvim health
:checkhealth mason " Mason health
:checkhealth lspconfig " LSP configuration health
:checkhealth treesitter " Treesitter healthLSP Tools
:LspInfo " Show active LSP clients
:LspLog " Open LSP log file
:LspRestart " Restart all LSP clients
:LspStop " Stop all LSP clients
:LspStart " Start LSP for current buffer
" Debug
:lua vim.lsp.set_log_level("debug")
:lua print(vim.lsp.get_log_path())Mason (Package Manager)
:Mason " Open Mason UI
:MasonInstall <pkg> " Install package
:MasonUninstall <pkg> " Uninstall package
:MasonUpdate " Update all packages
:MasonLog " Open Mason logLazy.nvim
:Lazy " Open Lazy dashboard
:Lazy sync " Update and install plugins
:Lazy update " Update plugins
:Lazy clean " Remove unused plugins
:Lazy profile " Show startup profile
:Lazy health " Check Lazy health
:Lazy log " Show plugin changelogTreesitter
:TSInstall <lang> " Install parser
:TSUpdate " Update all parsers
:TSInstallInfo " Show installed parsers
:TSModuleInfo " Show module status
:InspectTree " Show syntax tree
:Inspect " Show highlight groups under cursorTelescope
:Telescope " Open Telescope
:Telescope find_files " Find files
:Telescope live_grep " Search in files
:Telescope help_tags " Search help
:Telescope keymaps " Search keybindings
:Telescope commands " Search commands
:Telescope diagnostics " Search diagnosticsCLI Tools
Required
| Tool | Purpose | Install |
|---|---|---|
git | Plugin management | System package manager |
ripgrep | Telescope grep | brew install ripgrep |
fd | Telescope find | brew install fd |
Optional
| Tool | Purpose | Install |
|---|---|---|
node | Copilot, markdown-preview | brew install node |
python | Python LSP, DAP | brew install python |
go | Go LSP, DAP | brew install go |
lua-language-server | Lua LSP | Via Mason |
stylua | Lua formatter | Via Mason |
Performance Tools
# Startup time analysis
nvim --startuptime /tmp/startup.log
cat /tmp/startup.log | sort -k2 -n -r | head -20
# Memory usage
nvim --cmd 'lua print(collectgarbage("count"))' --cmd 'q'
# Profile with verbose
nvim -V10/tmp/nvim.logLua Utilities
Debug Helpers
-- Print table contents
:lua print(vim.inspect(some_table))
-- Get current buffer info
:lua print(vim.inspect(vim.api.nvim_buf_get_name(0)))
-- List all keymaps
:lua print(vim.inspect(vim.api.nvim_get_keymap("n")))
-- Check loaded modules
:lua print(vim.inspect(package.loaded))
-- Reload module
:lua package.loaded["module.name"] = nil
:lua require("module.name")LSP Utilities
-- Get active clients
:lua print(vim.inspect(vim.lsp.get_active_clients()))
-- Get client capabilities
:lua print(vim.inspect(vim.lsp.get_active_clients()[1].server_capabilities))
-- Format document
:lua vim.lsp.buf.format({ async = true })
-- Get diagnostics
:lua print(vim.inspect(vim.diagnostic.get(0)))Treesitter Utilities
-- Get current node type
:lua print(vim.treesitter.get_node():type())
-- Get parser info
:lua print(vim.inspect(vim.treesitter.get_parser():lang()))
-- Query highlights
:lua print(vim.inspect(vim.treesitter.get_captures_at_cursor(0)))AI Integration
ChatGPT.nvim Commands
:ChatGPT " Open ChatGPT
:ChatGPTActAs " Act as a persona
:ChatGPTEditWithInstructions " Edit with AI
:ChatGPTRun <action> " Run specific actionActions: grammar_correction, translate, keywords, docstring, add_tests, optimize_code, summarize, fix_bugs, explain_code, roxygen_edit, code_readability_analysis
Copilot Commands
:Copilot enable " Enable Copilot
:Copilot disable " Disable Copilot
:Copilot status " Check status
:Copilot panel " Open suggestion panel
:Copilot setup " Configure CopilotMCP Hub (mcphub.nvim)
:MCPHub " Open MCP HubProvides integration with Model Context Protocol servers.
Claude Code Workflow Tips
Working with This Configuration
When modifying this Neovim configuration with Claude Code:
1. Plugin Changes:
Edit lua/plugins/specs/<category>.lua
Then run :Lazy sync in Neovim2. Keymap Changes:
Edit lua/config/keymaps.lua
Then source with :source % or restart3. LSP Changes:
Edit lua/plugins/specs/lsp.lua
Then :LspRestart or restart Neovim4. Test Changes:
# Quick syntax check
luacheck lua/
# Full test
nvim --headless -c "checkhealth" -c "q"Common Tasks
Add a new plugin:
-- In appropriate lua/plugins/specs/*.lua
{
"author/plugin-name",
event = "VeryLazy",
opts = {},
}Add a keybinding:
-- In lua/config/keymaps.lua M.setup()
vim.keymap.set('n', '<leader>xx', function()
-- action
end, { desc = 'Description' })Add an LSP server:
-- In mason-tool-installer ensure_installed
"server_name",
-- In servers config
server_name = { settings = {} },Add a formatter:
-- In conform.nvim formatters_by_ft
filetype = { "formatter_name" },Validation Commands
# Check Lua syntax
luacheck lua/
# Validate config loads
nvim --headless -c "lua require('config.lazy')" -c "q"
# Run health checks
nvim --headless -c "checkhealth" -c "qa!" 2>&1
# Profile startup
nvim --startuptime /tmp/startup.log -c "q" && cat /tmp/startup.logExternal Tool Integration
Git Integration
| Command | Description |
|---|---|
:Git | Full Git interface (fugitive) |
:Git blame | Line-by-line blame |
:Git log | Commit history |
:DiffviewOpen | Visual diff viewer |
:DiffviewFileHistory | File history |
Database (vim-dadbod)
:DB <connection> " Connect to database
:DBUI " Open database UIHTTP Client (rest.nvim)
:Rest run " Execute HTTP request under cursorMarkdown Preview
:MarkdownPreview " Open preview in browser
:MarkdownPreviewStop " Stop previewTerminal (toggleterm)
:ToggleTerm " Toggle terminal
:TermExec cmd="command" " Execute command in terminal
" Keybinding: <C-\>File Management
Neo-tree
:Neotree toggle " Toggle file tree
:Neotree reveal " Reveal current file
:Neotree float " Floating file tree
:Neotree buffers " Show buffers
:Neotree git_status " Show git statusOil.nvim
:Oil " Open parent directory as bufferHarpoon
" Add file: <leader>a
" Toggle menu: <C-e>
" Navigate: <C-1> through <C-4>Search & Replace
Telescope
:Telescope live_grep " Search in files
:Telescope grep_string " Search word under cursorSpectre
:Spectre " Open search/replace panelGrug-far
:GrugFar " Find and replace across filesQuickfix & Location List
:copen " Open quickfix
:cclose " Close quickfix
:cnext " Next item
:cprev " Previous item
:cdo <cmd> " Execute on each item
:lopen " Open location list
:lnext " Next location
:lprev " Previous locationBQF (Better Quickfix)
Provides enhanced quickfix with preview, fzf integration, and more.
Troubleshooting Guide
Comprehensive solutions for common Neovim configuration issues.
Quick Diagnostics
:checkhealth " Full system check
:Lazy " Plugin status
:LspInfo " LSP status
:Mason " Installed tools
:messages " Recent messages/errors---
Startup Issues
Neovim Won't Start
Symptom: Error on launch, blank screen, or crash
Solutions:
# 1. Start with minimal config
nvim --clean
# 2. Start without plugins
nvim -u NONE
# 3. Check for syntax errors
nvim --startuptime /tmp/startup.log
cat /tmp/startup.log | grep -i error
# 4. Verbose mode
nvim -V10/tmp/nvim.log
cat /tmp/nvim.log | grep -i errorSlow Startup
Symptom: >100ms startup time
Diagnosis:
:Lazy profileSolutions:
1. Check plugin load times - Look for plugins >10ms 2. Verify lazy loading - Ensure lazy = true is set 3. Check event triggers - Use appropriate events 4. Disable heavy startup plugins:
{
"plugin/name",
event = "VeryLazy", -- Defer loading
}Plugin Installation Fails
Symptom: Lazy.nvim can't install plugins
Solutions:
# 1. Clear plugin cache
rm -rf ~/.local/share/nvim/lazy
# 2. Clear lazy-lock.json
rm ~/.config/nvim/lazy-lock.json
# 3. Check network
curl -I https://github.com
# 4. Reinstall
nvim --headless "+Lazy! sync" +qa---
LSP Issues
LSP Not Starting
Symptom: No completions, no diagnostics
Diagnosis:
:LspInfo
:LspLogSolutions:
1. Server not installed:
:Mason
" Search and install the server2. Wrong filetype:
:set filetype?
:set filetype=python " Manually set3. Root directory not detected:
-- Add root markers
root_dir = lspconfig.util.root_pattern(".git", "setup.py", "pyproject.toml")4. Check server logs:
:LspLogCompletions Not Working
Symptom: No autocompletion popup
Solutions:
1. Check completion source:
:lua print(vim.inspect(require("cmp").get_config().sources))2. Verify LSP is attached:
:LspInfo3. Check capabilities:
-- Ensure capabilities are passed
local capabilities = require("cmp_nvim_lsp").default_capabilities()
lspconfig.server.setup({ capabilities = capabilities })4. Trigger manually:
Press <C-Space> in insert modeFormatting Not Working
Symptom: :lua vim.lsp.buf.format() does nothing
Solutions:
1. Check conform.nvim:
:ConformInfo2. Verify formatter installed:
:Mason
" Search for stylua, prettierd, etc.3. Check formatter config:
formatters_by_ft = {
lua = { "stylua" },
python = { "ruff_format" },
}4. Format manually:
:lua require("conform").format({ async = true })Diagnostics Not Showing
Symptom: No error/warning highlights
Solutions:
1. Check diagnostics exist:
:lua print(vim.inspect(vim.diagnostic.get(0)))2. Check virtual text config:
vim.diagnostic.config({
virtual_text = true,
signs = true,
underline = true,
})3. Check severity filter:
-- May be filtering warnings
virtual_text = {
severity = { min = vim.diagnostic.severity.ERROR }, -- Too strict?
}---
Plugin Issues
Plugin Not Loading
Symptom: Plugin commands/features unavailable
Diagnosis:
:Lazy
" Check if plugin shows "not loaded"Solutions:
1. Trigger loading event:
-- Plugin loads on BufReadPre but you haven't opened a file
event = "VeryLazy" -- Change to VeryLazy for immediate load2. Check dependencies:
dependencies = { "nvim-lua/plenary.nvim" } -- Missing?3. Force load:
:Lazy load plugin-namePlugin Conflicts
Symptom: Unexpected behavior, errors mentioning multiple plugins
Solutions:
1. Check load order:
priority = 1000 -- Higher loads first2. Check for overlapping keymaps:
:verbose map <key>3. Disable one plugin:
{ "plugin/name", enabled = false }Telescope Errors
Symptom: Telescope commands fail
Solutions:
1. Install dependencies:
brew install ripgrep fd2. Rebuild fzf-native:
:Lazy build telescope-fzf-native.nvim3. Check picker config:
:Telescope find_files find_command=fdTreesitter Errors
Symptom: Highlighting broken, parser errors
Solutions:
1. Update parsers:
:TSUpdate2. Reinstall specific parser:
:TSInstall! lua3. Check parser status:
:TSInstallInfo4. Disable for large files:
highlight = {
disable = function(lang, buf)
local max_filesize = 100 * 1024
local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
return ok and stats and stats.size > max_filesize
end,
}---
UI Issues
Icons Not Displaying
Symptom: Boxes or question marks instead of icons
Solutions:
1. Install Nerd Font:
brew install --cask font-jetbrains-mono-nerd-font2. Configure terminal to use Nerd Font
3. Verify font in terminal:
:echo &guifontColors Wrong
Symptom: Wrong or missing colors
Solutions:
1. Enable true color:
vim.opt.termguicolors = true2. Check colorscheme loaded:
:colorscheme3. Check terminal supports true color:
echo $TERM
# Should be xterm-256color or similarNoice.nvim Errors
Symptom: cmdline or messages not working
Solutions:
1. Check dependencies:
dependencies = { "MunifTanjim/nui.nvim", "rcarriga/nvim-notify" }2. Reset noice:
:Noice dismiss
:Noice disable
:Noice enable3. Check presets:
presets = {
command_palette = true,
lsp_doc_border = true,
}---
Debugging Issues
DAP Not Connecting
Symptom: F5 does nothing, no debug UI
Solutions:
1. Check adapter installed:
:Mason
" Look for debugpy, delve, etc.2. Check configuration:
:lua print(vim.inspect(require("dap").configurations))3. Enable DAP logging:
require("dap").set_log_level("TRACE")
-- Check ~/.cache/nvim/dap.logBreakpoints Not Hit
Symptom: Breakpoints set but not stopping
Solutions:
1. Verify source mapping - Ensure paths match 2. Check breakpoint set:
:lua print(vim.inspect(require("dap.breakpoints").get()))3. Use log point to debug:
<leader>lp " Set log point---
Git Issues
Gitsigns Not Showing
Symptom: No git signs in gutter
Solutions:
1. Check if in git repo:
git status2. Check gitsigns status:
:Gitsigns debug_messages3. Refresh:
:Gitsigns refreshFugitive Commands Fail
Symptom: :Git errors
Solutions:
1. Check git available:
which git2. Check git config:
git config --list---
Performance Issues
High Memory Usage
Symptom: Neovim using >500MB RAM
Solutions:
1. Check memory:
:lua print(collectgarbage("count") .. " KB")2. Force garbage collection:
:lua collectgarbage("collect")3. Check large buffers:
:ls4. Close unused buffers:
:bd [buffer_number]Lag When Typing
Symptom: Input delay
Solutions:
1. Disable heavy plugins in insert mode:
event = "BufReadPost" -- Not InsertEnter2. Reduce updatetime:
vim.opt.updatetime = 2503. Disable LSP in insert:
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, {
update_in_insert = false,
}
)---
Reset & Recovery
Full Reset
# Backup first!
mv ~/.config/nvim ~/.config/nvim.bak
mv ~/.local/share/nvim ~/.local/share/nvim.bak
mv ~/.local/state/nvim ~/.local/state/nvim.bak
mv ~/.cache/nvim ~/.cache/nvim.bak
# Fresh start
git clone <your-config-repo> ~/.config/nvim
nvimClear Caches Only
rm -rf ~/.cache/nvim
rm -rf ~/.local/state/nvim
rm ~/.config/nvim/lazy-lock.jsonDisable All Plugins Temporarily
nvim -u NONE
# or
nvim --clean---
Getting Help
1. Check health: :checkhealth 2. Read logs: :messages, :LspLog 3. Search keymaps: :Telescope keymaps 4. Check verbose: :verbose set option? 5. Plugin docs: :help plugin-name
Related skills
FAQ
What does the neovim skill control?
The neovim skill drives Neovim sessions from Claude Code for buffer navigation, LSP-aware refactors, and plugin-aware edits. Developers use it to stay in terminal workflows without switching to a GUI editor.
When is the neovim skill most useful?
The neovim skill fits terminal-centric developers on SSH or tmux setups. Invoke it when edits should run through nvim commands, LSP actions, or existing Neovim plugins orchestrated by the agent.