| name | nvim-config |
| description | Configure Neovim editor with init.lua, plugin management, LSP setup, keymaps, and options. Use when setting up Neovim, adding plugins, configuring language servers, or customizing keybindings. Keywords: neovim, nvim, init.lua, vim, plugin, LSP, keymap, configure, vimscript |
Neovim Configuration Skill
Manages Neovim configuration through Lua-based init files and modular configuration structure.
Important Context
This dotfiles repository has a symlinked Neovim configuration:
- Configuration location:
config/nvim/
- Symlinked to:
~/.config/nvim/ (XDG standard)
- All changes are applied globally to the Neovim installation
Current state: Configuration directory exists but is empty (ready for initial setup)
Neovim Configuration Structure
Modern Lua-Based Configuration (Recommended)
config/nvim/
├── init.lua # Main entry point
├── lua/
│ ├── config/
│ │ ├── options.lua # Editor options (tab size, line numbers, etc.)
│ │ ├── keymaps.lua # Custom keybindings
│ │ ├── autocmds.lua # Autocommands
│ │ └── lazy.lua # Plugin manager setup (lazy.nvim)
│ └── plugins/
│ ├── lsp.lua # LSP configuration
│ ├── treesitter.lua # Syntax highlighting
│ ├── telescope.lua # Fuzzy finder
│ └── ... # Other plugin configs
└── after/
└── plugin/ # Plugin-specific overrides
Legacy Vimscript (For Vim Compatibility)
config/nvim/
├── init.vim # Main entry point (Vimscript)
├── plugin/ # Plugin configurations
└── after/plugin/ # Late-loading configs
Note: Use either init.lua OR init.vim, not both. Lua is recommended for new configurations.
Configuration Categories
1. Editor Options
Set in lua/config/options.lua:
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.tabstop = 4
vim.opt.shiftwidth = 4
vim.opt.expandtab = true
vim.opt.smartindent = true
vim.opt.termguicolors = true
vim.opt.signcolumn = "yes"
vim.opt.wrap = false
vim.opt.cursorline = true
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.hlsearch = true
vim.opt.splitright = true
vim.opt.splitbelow = true
vim.opt.updatetime = 250
vim.opt.timeoutlen = 300
vim.opt.backup = false
vim.opt.swapfile =
vim.opt.undofile =
Access via Vimscript:
set number
set relativenumber
set tabstop=4
2. Keymaps
Set in lua/config/keymaps.lua:
vim.g.mapleader = " "
vim.g.maplocalleader = " "
local map = function(mode, lhs, rhs, opts)
opts = opts or {}
opts.silent = opts.silent ~= false
vim.keymap.set(mode, lhs, rhs, opts)
end
map("n", "<leader>w", "<cmd>w<cr>", { desc = "Save file" })
map("n", "<leader>q", "<cmd>q<cr>", { desc = "Quit" })
map("n", "<Esc>", "<cmd>nohlsearch<cr>", { desc = "Clear highlights" })
map("n", "<C-h>", "<C-w>h", { desc = "Move to left window" })
map("n", "<C-j>", "<C-w>j", { desc = "Move to bottom window" })
map("n", "<C-k>", "<C-w>k", { desc = "Move to top window" })
map("n", "<C-l>", "<C-w>l", { desc = "Move to right window" })
map("n", "<C-Up>", "<cmd>resize +2<cr>", { desc = "Increase height" })
map("n", "<C-Down>", , { desc = })
map(, , , { desc = })
map(, , , { desc = })
map(, , , { desc = })
map(, , , { desc = })
map(, , )
map(, , )
map(, , , { desc = })
map(, , , { desc = })
map(, , , { desc = })
map(, , , { desc = })
Mapping modes:
n - Normal mode
i - Insert mode
v - Visual mode
x - Visual block mode
t - Terminal mode
c - Command mode
3. Plugin Management (lazy.nvim)
Set in lua/config/lazy.lua:
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable",
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
require("lazy").setup({
spec = {
{ import = "plugins" },
},
defaults = {
lazy = false,
version = false,
},
install = { colorscheme = { "habamax" } },
checker = { enabled = true },
performance = {
rtp = {
disabled_plugins = {
"gzip",
"tarPlugin",
"tohtml",
"tutor",
"zipPlugin",
},
},
},
})
Plugin specification format:
return {
"plugin-author/plugin-name",
dependencies = { "dependency-plugin" },
config = function()
end,
keys = {
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find files" },
},
event = "VeryLazy",
cmd = "PluginCommand",
ft = "filetype",
}
4. LSP Configuration
Set in lua/plugins/lsp.lua:
return {
"neovim/nvim-lspconfig",
dependencies = {
"williamboman/mason.nvim",
"williamboman/mason-lspconfig.nvim",
"hrsh7th/cmp-nvim-lsp",
},
config = function()
require("mason").setup()
require("mason-lspconfig").setup({
ensure_installed = {
"lua_ls",
"rust_analyzer",
"gopls",
"pyright",
"ts_ls",
},
})
local on_attach = function(client, bufnr)
local map = function(keys, func, desc)
vim.keymap.set("n", keys, func, { buffer = bufnr, desc = "LSP: " .. desc })
end
map("gd", vim.lsp.buf.definition, "Goto Definition")
map("gr", vim.lsp.buf.references, "Goto References")
map("gI", vim.lsp.buf.implementation, "Goto Implementation")
map("<leader>D", vim.lsp.buf.type_definition, )
map(, vim.lsp.buf., )
map(, vim.lsp.buf.code_action, )
map(, vim.lsp.buf.hover, )
capabilities = ().default_capabilities()
lspconfig = ()
lspconfig.lua_ls.setup({
on_attach = on_attach,
capabilities = capabilities,
settings = {
Lua = {
diagnostics = { globals = { } },
workspace = { checkThirdParty = },
telemetry = { enable = },
},
},
})
lspconfig.rust_analyzer.setup({
on_attach = on_attach,
capabilities = capabilities,
settings = {
[] = {
cargo = { allFeatures = },
checkOnSave = { command = },
},
},
})
lspconfig.gopls.setup({
on_attach = on_attach,
capabilities = capabilities,
settings = {
gopls = {
analyses = {
unusedparams = ,
},
staticcheck = ,
},
},
})
,
}
5. Autocommands
Set in lua/config/autocmds.lua:
vim.api.nvim_create_autocmd("TextYankPost", {
callback = function()
vim.highlight.on_yank()
end,
})
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*.go",
callback = function()
vim.lsp.buf.format()
end,
})
vim.api.nvim_create_autocmd("FileType", {
pattern = { "help", "qf", "man", "notify" },
callback = function(event)
vim.bo[event.buf].buflisted = false
vim.keymap.set("n", "q", "<cmd>close<cr>", { buffer = event.buf, silent = true })
end,
})
vim.api.nvim_create_autocmd("BufReadPost", {
callback = function()
local mark = vim.api.nvim_buf_get_mark(0, '"')
if mark[1] > 0 and mark[1] <= vim.api.nvim_buf_line_count(0) then
vim.api.nvim_win_set_cursor(0, mark)
end
end,
})
Main Entry Point (init.lua)
Minimal structure:
vim.g.mapleader = " "
vim.g.maplocalleader = " "
require("config.options")
require("config.keymaps")
require("config.autocmds")
require("config.lazy")
Essential Plugins
Core Plugins
1. Color Scheme:
return {
"folke/tokyonight.nvim",
lazy = false,
priority = 1000,
config = function()
vim.cmd([[colorscheme tokyonight-night]])
end,
}
2. Treesitter (Syntax Highlighting):
return {
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
config = function()
require("nvim-treesitter.configs").setup({
ensure_installed = { "lua", "vim", "rust", "go", "python", "typescript" },
highlight = { enable = true },
indent = { enable = true },
})
end,
}
3. Telescope (Fuzzy Finder):
return {
"nvim-telescope/telescope.nvim",
dependencies = { "nvim-lua/plenary.nvim" },
keys = {
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find files" },
{ "<leader>fg", "<cmd>Telescope live_grep<cr>", desc = "Live grep" },
{ "<leader>fb", "<cmd>Telescope buffers<cr>", desc = "Buffers" },
},
}
4. Completion (nvim-cmp):
return {
"hrsh7th/nvim-cmp",
dependencies = {
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-buffer",
"hrsh7th/cmp-path",
"L3MON4D3/LuaSnip",
},
config = function()
local cmp = require("cmp")
cmp.setup({
mapping = cmp.mapping.preset.insert({
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<C-Space>"] = cmp.mapping.complete(),
["<CR>"] = cmp.mapping.confirm({ select = true }),
}),
sources = {
{ name = "nvim_lsp" },
{ name = "buffer" },
{ name = "path" },
},
})
end,
}
Workflow Instructions
Initial Setup (Empty Configuration)
Create minimal init.lua:
vim.g.mapleader = " "
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.expandtab = true
vim.opt.shiftwidth = 4
vim.opt.tabstop = 4
Or create full modular structure:
- Create
init.lua with module imports
- Create
lua/config/ directory with options, keymaps, autocmds
- Create
lua/plugins/ directory for plugin specs
- Set up lazy.nvim plugin manager
Modifying Existing Configuration
Always start by reading:
Read config/nvim/init.lua
Read config/nvim/lua/config/options.lua
Read config/nvim/lua/config/keymaps.lua
Use Edit tool for precise changes:
- Read current configuration
- Identify exact section to modify
- Use Edit to replace specific content
- Preserve Lua syntax and structure
Adding Plugins
Create new plugin spec:
return {
"author/plugin-name",
dependencies = {},
config = function()
end,
}
Adding Keymaps
Append to keymaps.lua:
map("n", "<leader>gg", "<cmd>LazyGit<cr>", { desc = "Open LazyGit" })
Configuring LSP Servers
Add to mason ensure_installed:
ensure_installed = {
"lua_ls",
"rust_analyzer",
"new_server_name",
}
Add server-specific configuration:
lspconfig.new_server.setup({
on_attach = on_attach,
capabilities = capabilities,
settings = {
},
})
Common Configuration Tasks
1. Set Theme
return {
"catppuccin/nvim",
name = "catppuccin",
priority = 1000,
config = function()
vim.cmd([[colorscheme catppuccin-mocha]])
end,
}
2. Configure Tab Behavior
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
vim.opt.expandtab = true
3. Enable Format on Save
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*",
callback = function()
vim.lsp.buf.format()
end,
})
4. Add File Explorer (neo-tree)
return {
"nvim-neo-tree/neo-tree.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"nvim-tree/nvim-web-devicons",
"MunifTanjim/nui.nvim",
},
keys = {
{ "<leader>e", "<cmd>Neotree toggle<cr>", desc = "Toggle file explorer" },
},
}
5. Configure Go Development
ensure_installed = { "gopls" }
lspconfig.gopls.setup({
on_attach = on_attach,
capabilities = capabilities,
settings = {
gopls = {
gofumpt = true,
analyses = {
unusedparams = true,
shadow = true,
},
staticcheck = true,
},
},
})
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*.go",
callback = function()
vim.lsp.buf.format()
end,
})
Best Practices
- Modular structure - Separate options, keymaps, plugins into distinct files
- Lazy loading - Use lazy.nvim to load plugins on demand
- Descriptive keymaps - Always include
desc for which-key integration
- LSP-first - Use LSP for language features instead of custom scripts
- Persistent undo - Enable
undofile for undo history across sessions
- Leader key - Use space as leader for custom mappings
- Performance - Disable unused built-in plugins in lazy.nvim config
Neovim-Specific Features
Lua API advantages:
- Better performance than Vimscript
- Native async support
- Cleaner syntax for configuration
- First-class LSP support
XDG Base Directory:
- Config:
~/.config/nvim/
- Data:
~/.local/share/nvim/
- State:
~/.local/state/nvim/
- Cache:
~/.cache/nvim/
Built-in LSP:
- No plugin required for basic LSP functionality
- Use
vim.lsp.buf.* for LSP operations
- Configure servers via
nvim-lspconfig
Troubleshooting
Check Configuration Location
:echo stdpath('config') " Should show ~/.config/nvim
Reload Configuration
:source $MYVIMRC " Reload init file
:Lazy sync " Update plugins
Check Plugin Status
:Lazy " Open plugin manager UI
:checkhealth " Run health checks
LSP Issues
:LspInfo " Check attached servers
:Mason " Manage LSP servers
:LspRestart " Restart LSP servers
Verify Symlink
ls -la ~/.config/nvim
Quick Reference
Current Configuration:
- Location:
config/nvim/ (symlinked to ~/.config/nvim/)
- State: Empty (ready for initial setup)
- Format: Lua-based (recommended)
Standard Init Structure:
init.lua → Main entry point
lua/config/
├── options.lua → Editor settings
├── keymaps.lua → Keybindings
├── autocmds.lua → Autocommands
└── lazy.lua → Plugin manager
lua/plugins/
├── colorscheme.lua → Theme
├── lsp.lua → LSP configuration
├── treesitter.lua → Syntax highlighting
└── ... → Other plugins
Essential Commands:
:Lazy - Plugin manager
:Mason - LSP server installer
:checkhealth - Diagnostic checks
:LspInfo - LSP status
:Telescope - Fuzzy finder
Useful Options:
vim.opt.number - Line numbers
vim.opt.expandtab - Spaces instead of tabs
vim.opt.shiftwidth - Indent width
vim.opt.termguicolors - True color support
vim.opt.undofile - Persistent undo