简介
Neovim 是 Vim 的现代化分支,从 0.5 版本开始全面支持 Lua 作为一等配置语言,带来了更强大的插件生态和更直观的配置体验。相比传统的 VimScript,Lua 配置更易读、性能更高,且可以直接调用 Neovim 的全部 API。
本文提供一份基于 lazy.nvim 插件管理器的完整 init.lua 配置,涵盖基础选项、键位映射、插件安装与配置。配置文件位于 ~/.config/nvim/init.lua,适合作为单文件起点,后续可按需拆分为模块化结构。
完整配置
-- ============================================================
-- ~/.config/nvim/init.lua — Neovim 配置文件
-- 文档: https://neovim.io/doc/user/lua-guide.html
-- ============================================================
-- ---------- 1. 基础选项 ----------
local opt = vim.opt
-- 行号显示
opt.number = true -- 显示行号
opt.relativenumber = true -- 显示相对行号(方便跳转)
-- 缩进设置
opt.tabstop = 4 -- Tab 显示宽度为 4 个空格
opt.shiftwidth = 4 -- 自动缩进宽度为 4
opt.expandtab = true -- 将 Tab 转换为空格
opt.smartindent = true -- 智能缩进(根据语法自动调整)
opt.autoindent = true -- 继承上一行的缩进
-- 搜索设置
opt.ignorecase = true -- 搜索时忽略大小写
opt.smartcase = true -- 如果包含大写字母则区分大小写
opt.hlsearch = true -- 高亮搜索结果
opt.incsearch = true -- 实时显示匹配
-- 外观设置
opt.termguicolors = true -- 启用 24 位真彩色
opt.signcolumn = "yes" -- 始终显示符号列(避免文本跳动)
opt.cursorline = true -- 高亮当前行
opt.scrolloff = 8 -- 光标距离顶部/底部至少保留 8 行
opt.sidescrolloff = 8 -- 水平滚动时保留 8 列
opt.wrap = false -- 禁用自动换行
opt.colorcolumn = "100" -- 在第 100 列显示参考线
-- 分屏行为
opt.splitbelow = true -- 水平分屏时新窗口在下方
opt.splitright = true -- 垂直分屏时新窗口在右侧
-- 文件处理
opt.swapfile = false -- 禁用交换文件
opt.backup = false -- 禁用备份文件
opt.undofile = true -- 启用持久化撤销(重启后仍可撤销)
opt.undodir = vim.fn.stdpath("data") .. "/undo"
-- 性能与体验
opt.updatetime = 250 -- 降低 CursorHold 触发延迟(毫秒)
opt.timeoutlen = 300 -- 按键序列超时时间(毫秒)
opt.mouse = "a" -- 启用全模式鼠标支持
opt.clipboard = "unnamedplus" -- 使用系统剪贴板
opt.completeopt = { "menu", "menuone", "noselect" } -- 补全菜单行为
opt.conceallevel = 0 -- 不隐藏 Markdown 等格式字符
-- 填充字符(去除窗口分隔线的波浪号)
opt.fillchars = { eob = " ", fold = " ", diff = "╱" }
-- ---------- 2. Leader 键设置 ----------
-- 必须在加载插件之前设置 Leader 键
vim.g.mapleader = " " -- 空格键作为 Leader
vim.g.maplocalleader = "," -- 逗号作为 Local Leader
-- ---------- 3. 基础键位映射 ----------
local map = vim.keymap.set
-- 用 jk 退出插入模式(比 Esc 更快)
map("i", "jk", "<Esc>", { desc = "退出插入模式" })
-- 窗口导航(Ctrl + h/j/k/l)
map("n", "<C-h>", "<C-w>h", { desc = "移至左侧窗口" })
map("n", "<C-j>", "<C-w>j", { desc = "移至下方窗口" })
map("n", "<C-k>", "<C-w>k", { desc = "移至上方窗口" })
map("n", "<C-l>", "<C-w>l", { desc = "移至右侧窗口" })
-- 窗口大小调整
map("n", "<C-Up>", ":resize +2<CR>", { desc = "增大窗口高度" })
map("n", "<C-Down>", ":resize -2<CR>", { desc = "减小窗口高度" })
map("n", "<C-Left>", ":vertical resize -2<CR>", { desc = "减小窗口宽度" })
map("n", "<C-Right>", ":vertical resize +2<CR>", { desc = "增大窗口宽度" })
-- 缓冲区导航
map("n", "<S-h>", ":bprevious<CR>", { desc = "上一个缓冲区" })
map("n", "<S-l>", ":bnext<CR>", { desc = "下一个缓冲区" })
map("n", "<leader>bd", ":bdelete<CR>", { desc = "关闭缓冲区" })
-- 在可视模式下移动选中行
map("v", "J", ":m '>+1<CR>gv=gv", { desc = "下移选中行" })
map("v", "K", ":m '<-2<CR>gv=gv", { desc = "上移选中行" })
-- 清除搜索高亮
map("n", "<leader>h", ":nohlsearch<CR>", { desc = "清除搜索高亮" })
-- 保存与退出快捷键
map("n", "<leader>w", ":w<CR>", { desc = "保存文件" })
map("n", "<leader>q", ":q<CR>", { desc = "退出" })
map("n", "<leader>Q", ":qa!<CR>", { desc = "强制退出所有" })
-- 保持光标居中
map("n", "<C-d>", "<C-d>zz", { desc = "向下半页并居中" })
map("n", "<C-u>", "<C-u>zz", { desc = "向上半页并居中" })
map("n", "n", "nzzzv", { desc = "下一个匹配并居中" })
map("n", "N", "Nzzzv", { desc = "上一个匹配并居中" })
-- ---------- 4. 自动命令 ----------
local augroup = vim.api.nvim_create_augroup("UserConfig", { clear = true })
-- 高亮复制的文本(闪烁提示)
vim.api.nvim_create_autocmd("TextYankPost", {
group = augroup,
callback = function()
vim.highlight.on_yank({ higroup = "IncSearch", timeout = 200 })
end,
})
-- 打开文件时恢复上次编辑位置
vim.api.nvim_create_autocmd("BufReadPost", {
group = augroup,
callback = function()
local mark = vim.api.nvim_buf_get_mark(0, '"')
local lcount = vim.api.nvim_buf_line_count(0)
if mark[1] > 0 and mark[1] <= lcount then
pcall(vim.api.nvim_win_set_cursor, 0, mark)
end
end,
})
-- ---------- 5. 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)
-- ---------- 6. 插件列表与配置 ----------
require("lazy").setup({
-- ===== 配色方案 =====
{
"folke/tokyonight.nvim",
lazy = false, -- 立即加载(不延迟)
priority = 1000, -- 最高优先级,确保最先加载
config = function()
require("tokyonight").setup({
style = "night", -- 风格: storm | moon | night | day
transparent = false, -- 背景透明
terminal_colors = true, -- 终端颜色跟随主题
})
vim.cmd.colorscheme("tokyonight")
end,
},
-- ===== 文件搜索(Telescope)=====
{
"nvim-telescope/telescope.nvim",
branch = "0.1.x",
dependencies = {
"nvim-lua/plenary.nvim",
{
"nvim-telescope/telescope-fzf-native.nvim",
build = "make", -- 编译 C 扩展以获得更快的模糊匹配
},
},
config = function()
local telescope = require("telescope")
telescope.setup({
defaults = {
-- 搜索时忽略的路径模式
file_ignore_patterns = { "node_modules", ".git/", "dist/" },
-- 布局策略
layout_strategy = "horizontal",
layout_config = { prompt_position = "top" },
sorting_strategy = "ascending",
},
})
telescope.load_extension("fzf")
-- Telescope 快捷键
local builtin = require("telescope.builtin")
map("n", "<leader>ff", builtin.find_files, { desc = "搜索文件" })
map("n", "<leader>fg", builtin.live_grep, { desc = "全局搜索文本" })
map("n", "<leader>fb", builtin.buffers, { desc = "搜索缓冲区" })
map("n", "<leader>fh", builtin.help_tags, { desc = "搜索帮助文档" })
map("n", "<leader>fr", builtin.oldfiles, { desc = "最近打开的文件" })
map("n", "<leader>fc", builtin.commands, { desc = "搜索命令" })
end,
},
-- ===== 语法高亮(Treesitter)=====
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate", -- 安装后自动更新解析器
config = function()
require("nvim-treesitter.configs").setup({
-- 自动安装以下语言的解析器
ensure_installed = {
"lua", "vim", "vimdoc", "query",
"javascript", "typescript", "tsx",
"python", "rust", "go",
"html", "css", "json", "yaml", "toml",
"bash", "markdown", "markdown_inline",
},
auto_install = true, -- 打开文件时自动安装缺失的解析器
highlight = {
enable = true, -- 启用语法高亮
},
indent = {
enable = true, -- 基于 Treesitter 的智能缩进
},
incremental_selection = {
enable = true,
keymaps = {
init_selection = "<C-space>", -- 开始选择
node_incremental = "<C-space>", -- 扩大选区
scope_incremental = false,
node_decremental = "<bs>", -- 缩小选区
},
},
})
end,
},
-- ===== LSP 配置(语言服务器)=====
{
"neovim/nvim-lspconfig",
dependencies = {
"williamboman/mason.nvim", -- LSP 服务器安装管理
"williamboman/mason-lspconfig.nvim", -- mason 与 lspconfig 桥接
},
config = function()
-- Mason:自动安装 LSP 服务器
require("mason").setup({
ui = { border = "rounded" },
})
require("mason-lspconfig").setup({
-- 确保以下 LSP 服务器已安装
ensure_installed = {
"lua_ls", -- Lua
"ts_ls", -- TypeScript/JavaScript
"pyright", -- Python
"rust_analyzer", -- Rust
"gopls", -- Go
},
})
-- LSP 附加时设置快捷键
vim.api.nvim_create_autocmd("LspAttach", {
group = augroup,
callback = function(event)
local opts = { buffer = event.buf }
map("n", "gd", vim.lsp.buf.definition, vim.tbl_extend("force", opts, { desc = "跳转到定义" }))
map("n", "gD", vim.lsp.buf.declaration, vim.tbl_extend("force", opts, { desc = "跳转到声明" }))
map("n", "gr", vim.lsp.buf.references, vim.tbl_extend("force", opts, { desc = "查找引用" }))
map("n", "gi", vim.lsp.buf.implementation, vim.tbl_extend("force", opts, { desc = "跳转到实现" }))
map("n", "K", vim.lsp.buf.hover, vim.tbl_extend("force", opts, { desc = "悬浮文档" }))
map("n", "<leader>rn", vim.lsp.buf.rename, vim.tbl_extend("force", opts, { desc = "重命名符号" }))
map("n", "<leader>ca", vim.lsp.buf.code_action, vim.tbl_extend("force", opts, { desc = "代码操作" }))
map("n", "<leader>e", vim.diagnostic.open_float, vim.tbl_extend("force", opts, { desc = "浮窗诊断" }))
map("n", "[d", vim.diagnostic.goto_prev, vim.tbl_extend("force", opts, { desc = "上一个诊断" }))
map("n", "]d", vim.diagnostic.goto_next, vim.tbl_extend("force", opts, { desc = "下一个诊断" }))
end,
})
-- 配置诊断显示样式
vim.diagnostic.config({
virtual_text = { prefix = "●" }, -- 行尾虚拟文本前缀
signs = true,
underline = true,
update_in_insert = false, -- 插入模式下不更新诊断
float = { border = "rounded" },
})
-- LSP 服务器单独配置
local lspconfig = require("lspconfig")
-- Lua(Neovim 开发)
lspconfig.lua_ls.setup({
settings = {
Lua = {
runtime = { version = "LuaJIT" },
workspace = {
checkThirdParty = false,
library = vim.api.nvim_get_runtime_file("", true),
},
telemetry = { enable = false },
},
},
})
-- TypeScript / JavaScript
lspconfig.ts_ls.setup({})
-- Python
lspconfig.pyright.setup({})
-- Rust
lspconfig.rust_analyzer.setup({})
-- Go
lspconfig.gopls.setup({})
end,
},
-- ===== 自动补全(nvim-cmp)=====
{
"hrsh7th/nvim-cmp",
dependencies = {
"hrsh7th/cmp-nvim-lsp", -- LSP 补全源
"hrsh7th/cmp-buffer", -- 缓冲区文本补全
"hrsh7th/cmp-path", -- 文件路径补全
"L3MON4D3/LuaSnip", -- 代码片段引擎
"saadparwaiz1/cmp_luasnip", -- LuaSnip 补全源
"rafamadriz/friendly-snippets", -- 常用代码片段集合
},
config = function()
local cmp = require("cmp")
local luasnip = require("luasnip")
-- 加载 VS Code 风格的代码片段
require("luasnip.loaders.from_vscode").lazy_load()
cmp.setup({
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
-- 补全窗口样式
window = {
completion = cmp.config.window.bordered(),
documentation = cmp.config.window.bordered(),
},
-- 补全快捷键
mapping = cmp.mapping.preset.insert({
["<C-b>"] = cmp.mapping.scroll_docs(-4), -- 向上翻文档
["<C-f>"] = cmp.mapping.scroll_docs(4), -- 向下翻文档
["<C-Space>"] = cmp.mapping.complete(), -- 手动触发补全
["<C-e>"] = cmp.mapping.abort(), -- 关闭补全菜单
["<CR>"] = cmp.mapping.confirm({ select = true }), -- 确认选择
-- Tab / Shift+Tab 在补全项和片段之间导航
["<Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end, { "i", "s" }),
["<S-Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
elseif luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end, { "i", "s" }),
}),
-- 补全源优先级(从上到下)
sources = cmp.config.sources({
{ name = "nvim_lsp" }, -- LSP 补全(最高优先级)
{ name = "luasnip" }, -- 代码片段
{ name = "buffer" }, -- 缓冲区文本
{ name = "path" }, -- 文件路径
}),
})
end,
},
-- ===== 状态栏(Lualine)=====
{
"nvim-lualine/lualine.nvim",
dependencies = { "nvim-tree/nvim-web-devicons" }, -- 文件类型图标
config = function()
require("lualine").setup({
options = {
theme = "tokyonight", -- 跟随主题配色
component_separators = "|", -- 组件分隔符
section_separators = { left = "", right = "" }, -- 区块分隔符
globalstatus = true, -- 全局状态栏(所有窗口共享)
},
sections = {
lualine_a = { "mode" }, -- 当前模式
lualine_b = { "branch", "diff", "diagnostics" }, -- Git 与诊断
lualine_c = { { "filename", path = 1 } }, -- 文件名(含相对路径)
lualine_x = { "encoding", "fileformat", "filetype" },
lualine_y = { "progress" }, -- 进度百分比
lualine_z = { "location" }, -- 行:列
},
})
end,
},
-- ===== 其他实用插件 =====
-- 自动配对括号
{
"windwp/nvim-autopairs",
event = "InsertEnter",
config = true,
},
-- Git 标记(左侧符号列显示增删改状态)
{
"lewis6991/gitsigns.nvim",
config = function()
require("gitsigns").setup({
signs = {
add = { text = "│" },
change = { text = "│" },
delete = { text = "_" },
topdelete = { text = "‾" },
changedelete = { text = "~" },
},
})
end,
},
-- 注释切换(gcc 切换行注释,gc 切换块注释)
{
"numToStr/Comment.nvim",
config = true,
},
}, {
-- lazy.nvim 自身配置
ui = { border = "rounded" },
checker = { enabled = true, notify = false }, -- 自动检查插件更新
performance = {
rtp = {
-- 禁用不需要的内置插件以加快启动
disabled_plugins = {
"gzip", "matchit", "matchparen",
"netrwPlugin", "tarPlugin", "tohtml",
"tutor", "zipPlugin",
},
},
},
})
配置说明
基础选项
vim.opt 用于设置 Neovim 的各类选项,等价于 VimScript 中的 set 命令。相对行号配合 j/k 跳转非常高效,例如 8j 向下跳 8 行。undofile 启用持久化撤销历史,即使关闭文件再打开也能继续撤销。
插件管理
lazy.nvim 是目前 Neovim 社区最流行的插件管理器,支持延迟加载、依赖管理、构建步骤等特性。按 <leader>l(空格+l)可打开 lazy.nvim 的管理界面,查看插件状态、更新或清理。
LSP 与补全
Mason 负责自动下载和管理 LSP 服务器二进制文件,无需手动安装。nvim-cmp 提供补全菜单,整合了 LSP、代码片段、缓冲区文本和文件路径四种补全源,按优先级排列。
Telescope 搜索
Telescope 是 Neovim 生态中最强大的模糊搜索工具,支持文件、文本、缓冲区、Git 提交等多种搜索目标。配合 fzf-native 扩展可获得更快的排序性能。
常用技巧
- 健康检查:运行
:checkhealth可诊断 Neovim 环境问题(缺少依赖、配置错误等) - 模块化拆分:随着配置增长,可将
init.lua拆分为lua/config/options.lua、lua/plugins/目录等模块化结构 - 延迟加载:lazy.nvim 支持
event、cmd、ft、keys等条件加载方式,合理使用可显著加快启动速度 - LSP 服务器管理:运行
:Mason打开图形界面,可以搜索、安装、更新所有可用的 LSP 服务器和工具 - Treesitter 调试:运行
:InspectTree可查看当前文件的语法树结构,有助于理解高亮和缩进行为 - 快捷键查看:安装 which-key.nvim 插件后,按下 Leader 键会弹出可用快捷键提示面板
- 配置即代码:将整个
~/.config/nvim/目录纳入 Git 管理,方便在多台机器间同步配置