Neovim 0.12 新特性详解与配置实战

Neovim 0.12.0 于 2026 年 3 月 29 日正式发布,代号 “The year of Nvim OOTB”——开箱即用的 Neovim 之年。这个版本的核心叙事不再只是"新功能列表",而是:更多编辑器能力现在不需要插件栈就能获得

本文将从特性解读出发,带你一步步编写适配 0.12 的配置文件。


一、重量级新特性 #

1.1 内置插件管理器 vim.pack #

这是 0.12 最具里程碑意义的新增。Neovim 终于有了自己的插件管理器,不再必须依赖 Packer、Lazy 等第三方方案。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
-- init.lua 或 lua/config/pack.lua
vim.pack.add({
  -- 直接用 GitHub URL
  'https://github.com/sainnhe/gruvbox-material',
  'https://github.com/nvim-treesitter/nvim-treesitter',

  -- 指定版本锁定
  { src = 'https://github.com/neovim/nvim-lspconfig', version = 'v2.0.0' },

  -- 自定义插件名(避免长 URL 重复引用)
  { src = 'https://github.com/user/long-repo-name', name = 'my-plugin' },
})

关键细节:

方面说明
存储位置site/pack/core/opt/(data path 下)
锁文件~/.config/nvim/nvim-pack-lock.json,建议纳入 git
更新vim.pack.update() 打开确认 buffer 审阅变更
删除vim.pack.del({'old-plugin'})
限制基于 Git;不是懒加载框架;不会自动校验已下载插件的版本一致性

更新操作变体:

1
2
3
4
vim.pack.update({ 'nvim-treesitter' })              -- 只更新指定插件
vim.pack.update(nil, { force = true })               -- 强制更新,跳过确认
vim.pack.update(nil, { offline = true })             -- 离线模式
vim.pack.update(nil, { target = 'lockfile' })        -- 锁定到 lockfile 版本

安装后钩子通过 PackChanged 事件实现:

1
2
3
4
5
6
7
vim.api.nvim_create_autocmd('PackChanged', {
  callback = function(ev)
    if ev.data.spec.name == 'nvim-treesitter' then
      vim.system({ 'make' }, { cwd = ev.data.path })
    end
  end,
})

1.2 内置插入模式自动补全 #

0.12 新增 'autocomplete' 选项,开启后无需 nvim-cmp 等插件即可获得基本补全:

1
2
3
4
vim.o.autocomplete = true
vim.o.pumborder  = 'rounded'   -- 补全菜单边框样式
vim.o.pummaxwidth = 40          -- 补全菜单最大宽度
vim.o.completeopt = 'menu,menuone,noselect,nearest'

'complete' 选项新增:

  • F{func} — 用指定 Lua 函数补全
  • F — 用 'completefunc' 补全
  • o — 用 'omnifunc' 补全
  • {flag}^<limit> — 限制每个源的匹配数,如 .^5,t^3,w

'completeopt' 新增 nearest 标志,按距离光标远近排序。

1.3 LSP 支持大幅扩展 #

0.12 让内置 LSP 更加"开箱即用":

推荐配置模式(不再依赖 nvim-lspconfig):

1
2
3
4
5
6
vim.lsp.config['lua_ls'] = {
  cmd = { 'lua-language-server' },
  filetypes = { 'lua' },
  root_markers = { { '.luarc.json', '.luarc.jsonc' }, '.git' },
}
vim.lsp.enable('lua_ls')

新增 LSP 能力:

能力说明
selectionRangev_an 向外选、v_in 向内选(无 Treesitter 时回退到此)
inlineCompletion行内补全
documentLinkgx 打开光标处的文档链接
semanticTokens/range仅请求可视区域 token,性能优化
diagnosticpull 诊断(含增量 id)
codeLensCode Lens 以虚拟行显示

内置默认映射:

按键功能
graCode Actions
griImplementations
grnRename
grrReferences
grtType Definition
gODocument Symbols
Ctrl-S(Insert)Signature Help

交互式管理::lsp 命令;健康检查::checkhealth vim.lsp

1.4 实验性 UI2 重设计 #

0.12 引入了核心消息和命令行 UI 的重设计:

1
require('vim._core.ui2').enable()

效果:

  • ✅ 消除大量 “Press ENTER” 打断
  • ✅ 命令行实时语法高亮
  • ✅ Pager 变为正式 buffer + window
  • ✅ 消除 W10 等警告延迟

目前标记为实验性,建议先体验再决定是否日常启用。


二、内置工具与新命令 #

命令说明
:Undotree内置可视化撤销树(需 :packadd nvim.undotree
:DiffTool dir1 dir2内置目录/文件对比
:restart重启 Nvim 并重连所有 UI
:connect addr动态连接到 Neovim 服务器
:uniq去重当前 buffer 行
:iput:put 但自动调整缩进
:retab -indentonly仅改首行缩进空白
:wall ++p写所有 buffer + 自动创建缺失父目录
:help!智能猜测光标处 help tag

三、性能提升与 Lua API #

3.1 性能 #

  • vim.glob.to_lpeg() 新 LPeg 实现(Peglob),复杂模式约 50% 加速
  • i_CTRL-R 插入寄存器提速 10x(改为字面插入)
  • :packadd 不再清空 Lua package path cache,改善启动时间

3.2 新 Lua API #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
-- HTTP 请求(内置!)
local res = vim.net.request('https://api.github.com/repos/neovim/neovim')
print(res.body)

-- 文件扩展名
vim.fs.ext('init.lua')        -- => 'lua'
vim.fs.ext('archive.tar.gz')  -- => 'gz'

-- 去重
vim.list.unique({ 1, 2, 2, 3, 1 })  -- => { 1, 2, 3 }

-- 二分查找
vim.list.bisect({ 10, 20, 30, 40 }, 25)  -- => 2

-- 实验性 Position/Range
local pos = vim.pos(0, 5)   -- 行 0,列 5
local rng = vim.range(pos, vim.pos(0, 10))

迭代器增强:

1
2
3
4
5
6
7
8
-- take/skip 支持谓词
vim.iter({ 1, 2, 3, 4, 5 }):take(function(x) return x < 4 end):totable()  -- {1,2,3}

-- peek(不消费)
vim.iter(range):peek()

-- unique
vim.iter({ 1, 1, 2, 3 }):unique():totable()  -- {1,2,3}

四、终端与 TUI 改进 #

  • 支持同步输出(DEC private mode 2026),减少撕裂
  • 支持 CSI 3 J 清除滚动回溯
  • nvim_open_term() 可在非空 buffer 上调用(内容通过 PTY 显示)
  • 挂起进程显示 [Process suspended],按键恢复
  • 退出显示虚拟文本 + 状态栏退出码
  • TUI 渲染 SGR dim/faint、blink、conceal、overline
  • OSC 9;4 原生进度条

五、破坏性变更(升级必看) #

这是升级最需要注意的部分:

5.1 API 重命名 #

vim.diffvim.text.diff
vim.lsp.semantic_tokens.start/stopvim.lsp.semantic_tokens.enable

5.2 诊断 Signs #

sign-define 方式彻底废弃,必须迁移到 vim.diagnostic.config()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
-- ❌ 旧方式(0.12 中无效)
vim.fn.sign_define('DiagnosticSignError', { text = 'E', texthl = 'DiagnosticError' })

-- ✅ 新方式
local sev = vim.diagnostic.severity
vim.diagnostic.config({
  severity_sort = true,
  signs = {
    text = {
      [sev.ERROR] = 'E',
      [sev.WARN]  = 'W',
      [sev.INFO]  = 'I',
      [sev.HINT]  = 'H',
    },
  },
})

vim.diagnostic.disable()is_disabled() 已移除;旧签名 vim.diagnostic.enable() 不再支持。

5.3 行为变更 #

  • i_CTRL-R 现在字面插入寄存器内容(旧行可用 <C-R>=@x
  • 'shelltemp' 默认改为 false
  • vim.treesitter.get_parser() 不再抛异常,返回 nil
  • LSP JSON nullvim.NIL(不再是 nil
  • tohtml 不再默认加载
  • Python 3.7/3.8 支持移除

六、完整配置实战 #

下面给出一个适配 0.12 的最小但实用的配置结构,让你开箱即用。

6.1 目录结构 #

~/.config/nvim/
├── init.lua
├── nvim-pack-lock.json    ← vim.pack 自动生成
└── lua/
    └── config/
        ├── init.lua        ← 入口
        ├── options.lua     ← 编辑器选项
        ├── keymaps.lua     ← 按键映射
        ├── diagnostics.lua ← 诊断配置
        ├── lsp.lua         ← LSP 配置
        ├── pack.lua        ← 插件管理
        └── ui2.lua         ← UI2(可选)

6.2 init.lua #

1
require('config')

6.3 lua/config/init.lua #

1
2
3
4
5
6
require('config.options')
require('config.pack')       -- 插件要先加载
require('config.keymaps')
require('config.diagnostics')
require('config.lsp')
-- require('config.ui2')     -- 实验性,按需启用

6.4 lua/config/options.lua #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
-- 基本选项
vim.o.number         = true
vim.o.relativenumber = true
vim.o.tabstop        = 4
vim.o.shiftwidth     = 4
vim.o.expandtab      = true
vim.o.smartindent    = true
vim.o.wrap           = false
vim.o.swapfile       = false
vim.o.undofile       = true
vim.o.ignorecase     = true
vim.o.smartcase      = true
vim.o.termguicolors  = true
vim.o.signcolumn     = 'yes'
vim.o.cursorline     = true

-- 0.12 新选项:内置自动补全
vim.o.autocomplete   = true
vim.o.pumborder      = 'rounded'
vim.o.pummaxwidth    = 40
vim.o.completeopt    = 'menu,menuone,noselect,nearest'

-- 搜索
vim.o.hlsearch       = true
vim.o.incsearch      = true

-- 默认 diffopt 已含 indent-heuristic + inline:char,可追加
vim.o.diffopt        = vim.o.diffopt .. ',inline:word'

-- shada 排除临时目录(0.12 新默认行为,此处显式声明)
vim.o.shada          = vim.o.shada .. ',/tmp/,/private/'

6.5 lua/config/pack.lua #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
vim.pack.add({
  -- 主题
  'https://github.com/sainnhe/gruvbox-material',

  -- Treesitter
  'https://github.com/nvim-treesitter/nvim-treesitter',

  -- 如仍需 nvim-lspconfig(补充内置不足的语言服务器)
  { src = 'https://github.com/neovim/nvim-lspconfig', version = 'v2.0.0' },
})

-- 安装后钩子:自动编译 Treesitter
vim.api.nvim_create_autocmd('PackChanged', {
  callback = function(ev)
    if ev.data.spec.name == 'nvim-treesitter' then
      vim.system({ 'make' }, { cwd = ev.data.path })
    end
  end,
})

6.6 lua/config/keymaps.lua #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 空格为 Leader
vim.g.mapleader = ' '

-- 0.12 已内置 LSP 映射(gra/gri/grn/grr/grt/gO/Ctrl-S)
-- 此处补充自定义映射

-- 窗口
vim.keymap.set('n', '<leader>w', '<C-w>', { desc = 'Window' })
vim.keymap.set('n', '<leader>wc', '<C-w>c', { desc = 'Close window' })

-- Buffer
vim.keymap.set('n', '<leader>bd', ':bdelete<CR>', { desc = 'Delete buffer' })

-- 诊断浮动窗口
vim.keymap.set('n', '<leader>df', vim.diagnostic.open_float, { desc = 'Diagnostic float' })
vim.keymap.set('n', '<leader>dl', vim.diagnostic.setloclist, { desc = 'Diagnostic loclist' })

-- 0.12 新命令快捷键
vim.keymap.set('n', '<leader>ur', ':Undotree<CR>', { desc = 'Undo tree' })
vim.keymap.set('n', '<leader>R', ':restart<CR>', { desc = 'Restart Nvim' })

-- Treesitter 结构选择(0.12 新增)
vim.keymap.set('v', 'an', nil, { desc = 'Select outward (TS/LSP)' })  -- 内置
vim.keymap.set('v', 'in', nil, { desc = 'Select inward (TS/LSP)' })    -- 内置

6.7 lua/config/diagnostics.lua #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
local sev = vim.diagnostic.severity

vim.diagnostic.config({
  severity_sort = true,
  update_in_insert = false,
  float = {
    border = 'rounded',
    source = true,
  },
  signs = {
    text = {
      [sev.ERROR] = '✘',
      [sev.WARN]  = '▲',
      [sev.INFO]  = '●',
      [sev.HINT]  = '○',
    },
  },
  -- 虚拟文本只显示最高级别
  virtual_lines = { current_line = true },
  underline = true,
})

-- 诊断状态可用于自定义 statusline
-- vim.diagnostic.status() 返回统计表
-- vim.lsp.status()   返回 LSP 进度

6.8 lua/config/lsp.lua #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- Python
vim.lsp.config['pyright'] = {
  cmd = { 'pyright-langserver', '--stdio' },
  filetypes = { 'python' },
  root_markers = { { 'pyproject.toml', 'setup.py' }, '.git' },
}
vim.lsp.enable('pyright')

-- Go
vim.lsp.config['gopls'] = {
  cmd = { 'gopls' },
  filetypes = { 'go', 'gomod', 'gowork' },
  root_markers = { 'go.mod', '.git' },
}
vim.lsp.enable('gopls')

-- Lua
vim.lsp.config['lua_ls'] = {
  cmd = { 'lua-language-server' },
  filetypes = { 'lua' },
  root_markers = { { '.luarc.json', '.luarc.jsonc' }, '.git' },
  settings = {
    Lua = {
      runtime = { version = 'LuaJIT' },
      diagnostics = { globals = { 'vim' } },
      workspace = { library = { vim.env.VIMRUNTIME } },
    },
  },
}
vim.lsp.enable('lua_ls')

-- 健康检查
-- :checkhealth vim.lsp

6.9 lua/config/ui2.lua(可选,实验性) #

1
2
3
require('vim._core.ui2').enable()
-- 注意:这是实验性功能,可能导致某些插件不兼容
-- 建议先用几天观察稳定性再长期启用

七、迁移 Checklist #

从旧版本升级到 0.12 时,逐一检查:

项目操作
插件管理器评估是否用 vim.pack 替代 Packer/Lazy
诊断 Signs删除所有 sign-define 代码,改用 vim.diagnostic.config()
vim.diff改为 vim.text.diff
semantic_tokensstart()/stop()enable()
vim.diagnostic.disable()移除,0.12 中已不存在
vim.treesitter.get_parser()nil 检查,不再依赖抛异常
Insert Ctrl-R如需旧行为(解释特殊字符),改用 <C-R>=@x
tohtml需手动 :packadd nvim.tohtml
shellmenu 插件已移除
'shelltemp'默认变为 false,如依赖旧行为需手动设 true

八、总结 #

Neovim 0.12 的核心故事不只是"又加了几个功能"——它让编辑器本身承担了更多原来需要插件栈才能实现的能力。内置插件管理、自动补全、LSP增强、UI重设计、诊断配置简化——五根支柱撑起了"开箱即用"的承诺。

对于轻度用户,0.12 可能意味着你可以删掉半数插件。对于深度定制用户,新 API 和行为变更需要仔细迁移,但换来的是更干净的架构和更快的启动速度。

官方参考:

  • :help news-0.12
  • :help vim.pack
  • :help lsp
  • :help diagnostic-signs
  • :help ui2
  • :help deprecated-0.12