$ terminals _

init.lua 配置指南

Neovim 现代化 Lua 配置方案,基于 lazy.nvim 构建高效开发环境

简介

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.lualua/plugins/ 目录等模块化结构
  • 延迟加载:lazy.nvim 支持 eventcmdftkeys 等条件加载方式,合理使用可显著加快启动速度
  • LSP 服务器管理:运行 :Mason 打开图形界面,可以搜索、安装、更新所有可用的 LSP 服务器和工具
  • Treesitter 调试:运行 :InspectTree 可查看当前文件的语法树结构,有助于理解高亮和缩进行为
  • 快捷键查看:安装 which-key.nvim 插件后,按下 Leader 键会弹出可用快捷键提示面板
  • 配置即代码:将整个 ~/.config/nvim/ 目录纳入 Git 管理,方便在多台机器间同步配置