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 allowed-tools: Read, Write, Edit, Bash(cat:), Bash(nvim:)
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:
-- Basic editor settings
vim.opt.number = true -- Show line numbers
vim.opt.relativenumber = true -- Relative line numbers
vim.opt.tabstop = 4 -- Tab width
vim.opt.shiftwidth = 4 -- Indent width
vim.opt.expandtab = true -- Use spaces instead of tabs
vim.opt.smartindent = true -- Auto-indent new lines
-- UI enhancements
vim.opt.termguicolors = true -- True color support
vim.opt.signcolumn = "yes" -- Always show sign column
vim.opt.wrap = false -- No line wrapping
vim.opt.cursorline = true -- Highlight current line
-- Search settings
vim.opt.ignorecase = true -- Case-insensitive search
vim.opt.smartcase = true -- Case-sensitive if uppercase used
vim.opt.hlsearch = true -- Highlight search results
-- Split behavior
vim.opt.splitright = true -- Vertical splits go right
vim.opt.splitbelow = true -- Horizontal splits go below
-- Performance
vim.opt.updatetime = 250 -- Faster completion
vim.opt.timeoutlen = 300 -- Faster key sequence timeout
-- Backup and undo
vim.opt.backup = false -- No backup files
vim.opt.swapfile = false -- No swap files
vim.opt.undofile = true -- Persistent undo
Access via Vimscript:
set number
set relativenumber
set tabstop=4
2. Keymaps
Set in lua/config/keymaps.lua:
-- Set leader key
vim.g.mapleader = " " -- Space as leader key
vim.g.maplocalleader = " "
-- Helper function for mappings
local map = function(mode, lhs, rhs, opts)
opts = opts or {}
opts.silent = opts.silent ~= false
vim.keymap.set(mode, lhs, rhs, opts)
end
-- General mappings
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" })
-- Window navigation
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" })
-- Window resizing
map("n", "<C-Up>", "<cmd>resize +2<cr>", { desc = "Increase height" })
map("n", "<C-Down>", "<cmd>resize -2<cr>", { desc = "Decrease height" })
map("n", "<C-Left>", "<cmd>vertical resize -2<cr>", { desc = "Decrease width" })
map("n", "<C-Right>", "<cmd>vertical resize +2<cr>", { desc = "Increase width" })
-- Buffer navigation
map("n", "<S-h>", "<cmd>bprevious<cr>", { desc = "Previous buffer" })
map("n", "<S-l>", "<cmd>bnext<cr>", { desc = "Next buffer" })
-- Better indenting
map("v", "<", "<gv")
map("v", ">", ">gv")
-- Move lines
map("n", "<A-j>", "<cmd>m .+1<cr>==", { desc = "Move line down" })
map("n", "<A-k>", "<cmd>m .-2<cr>==", { desc = "Move line up" })
map("v", "<A-j>", ":m '>+1<cr>gv=gv", { desc = "Move selection down" })
map("v", "<A-k>", ":m '<-2<cr>gv=gv", { desc = "Move selection up" })
Mapping modes:
n- Normal modei- Insert modev- Visual modex- Visual block modet- Terminal modec- Command mode
3. Plugin Management (lazy.nvim)
Set in lua/config/lazy.lua:
-- Bootstrap lazy.nvim
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)
-- Configure plugins
require("lazy").setup({
spec = {
{ import = "plugins" }, -- Import all plugin specs from lua/plugins/
},
defaults = {
lazy = false, -- Load plugins immediately
version = false, -- Use latest git commit
},
install = { colorscheme = { "habamax" } },
checker = { enabled = true }, -- Check for updates
performance = {
rtp = {
disabled_plugins = {
"gzip",
"tarPlugin",
"tohtml",
"tutor",
"zipPlugin",
},
},
},
})
Plugin specification format:
return {
"plugin-author/plugin-name",
dependencies = { "dependency-plugin" },
config = function()
-- Plugin configuration
end,
keys = {
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find files" },
},
event = "VeryLazy", -- Lazy load on event
cmd = "PluginCommand", -- Lazy load on command
ft = "filetype", -- Lazy load on filetype
}
4. LSP Configuration
Set in lua/plugins/lsp.lua:
return {
"neovim/nvim-lspconfig",
dependencies = {
"williamboman/mason.nvim", -- LSP installer
"williamboman/mason-lspconfig.nvim", -- Mason-lspconfig bridge
"hrsh7th/cmp-nvim-lsp", -- LSP completion source
},
config = function()
-- Mason setup (LSP installer)
require("mason").setup()
require("mason-lspconfig").setup({
ensure_installed = {
"lua_ls", -- Lua
"rust_analyzer", -- Rust
"gopls", -- Go
"pyright", -- Python
"ts_ls", -- TypeScript
},
})
-- LSP keymaps (set on attach)
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, "Type Definition")
map("<leader>rn", vim.lsp.buf.rename, "Rename")
map("<leader>ca", vim.lsp.buf.code_action, "Code Action")
map("K", vim.lsp.buf.hover, "Hover Documentation")
end
-- Capabilities (for completion)
local capabilities = require("cmp_nvim_lsp").default_capabilities()
-- Server configurations
local lspconfig = require("lspconfig")
lspconfig.lua_ls.setup({
on_attach = on_attach,
capabilities = capabilities,
settings = {
Lua = {
diagnostics = { globals = { "vim" } },
workspace = { checkThirdParty = false },
telemetry = { enable = false },
},
},
})
lspconfig.rust_analyzer.setup({
on_attach = on_attach,
capabilities = capabilities,
settings = {
["rust-analyzer"] = {
cargo = { allFeatures = true },
checkOnSave = { command = "clippy" },
},
},
})
lspconfig.gopls.setup({
on_attach = on_attach,
capabilities = capabilities,
settings = {
gopls = {
analyses = {
unusedparams = true,
},
staticcheck = true,
},
},
})
end,
}
5. Autocommands
Set in lua/config/autocmds.lua:
-- Highlight on yank
vim.api.nvim_create_autocmd("TextYankPost", {
callback = function()
vim.highlight.on_yank()
end,
})
-- Auto-format on save
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*.go",
callback = function()
vim.lsp.buf.format()
end,
})
-- Close certain filetypes with 'q'
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,
})
-- Restore cursor position
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:
-- Set leader keys before loading plugins
vim.g.mapleader = " "
vim.g.maplocalleader = " "
-- Load core configurations
require("config.options")
require("config.keymaps")
require("config.autocmds")
require("config.lazy") -- Plugin manager setup
Essential Plugins
Core Plugins
1. Color Scheme:
-- lua/plugins/colorscheme.lua
return {
"folke/tokyonight.nvim",
lazy = false,
priority = 1000,
config = function()
vim.cmd([[colorscheme tokyonight-night]])
end,
}
2. Treesitter (Syntax Highlighting):
-- lua/plugins/treesitter.lua
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):
-- lua/plugins/telescope.lua
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):
-- lua/plugins/completion.lua
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:
-- config/nvim/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.luawith 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:
-- config/nvim/lua/plugins/myplugin.lua
return {
"author/plugin-name",
dependencies = {},
config = function()
-- Plugin setup
end,
}
Adding Keymaps
Append to keymaps.lua:
-- Add to lua/config/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 new server
}
Add server-specific configuration:
lspconfig.new_server.setup({
on_attach = on_attach,
capabilities = capabilities,
settings = {
-- Server-specific settings
},
})
Common Configuration Tasks
1. Set Theme
-- lua/plugins/colorscheme.lua
return {
"catppuccin/nvim",
name = "catppuccin",
priority = 1000,
config = function()
vim.cmd([[colorscheme catppuccin-mocha]])
end,
}
2. Configure Tab Behavior
-- lua/config/options.lua
vim.opt.tabstop = 2 -- Tab width
vim.opt.shiftwidth = 2 -- Indent width
vim.opt.expandtab = true -- Use spaces
3. Enable Format on Save
-- lua/config/autocmds.lua
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*",
callback = function()
vim.lsp.buf.format()
end,
})
4. Add File Explorer (neo-tree)
-- lua/plugins/neo-tree.lua
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 gopls is installed
ensure_installed = { "gopls" }
-- Add Go-specific settings
lspconfig.gopls.setup({
on_attach = on_attach,
capabilities = capabilities,
settings = {
gopls = {
gofumpt = true, -- Use gofumpt formatter
analyses = {
unusedparams = true,
shadow = true,
},
staticcheck = true,
},
},
})
-- Auto-format Go files on save
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
descfor which-key integration - LSP-first - Use LSP for language features instead of custom scripts
- Persistent undo - Enable
undofilefor 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 # Should point to dotfiles/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 numbersvim.opt.expandtab- Spaces instead of tabsvim.opt.shiftwidth- Indent widthvim.opt.termguicolors- True color supportvim.opt.undofile- Persistent undo