积累沉淀

待山花烂漫,化茧成蝶

NeoVim 使用手册

前言: Neovim 是基于 Vim 的一个现代化分支(fork),它的定位是“hyperextensible Vim-based text editor”——一个基于 Vim、具备超强扩展能力的文本编辑器。 简单来说,Neovim 不是对 Vim 的重写,而是延续和扩展。它保留了 Vim 的核心操作哲学——高效、快速、键盘驱动、极简,同时通过架构层面的革新,让这个经典的编辑器能够更好地适应现代开发需求。


NeoVim 使用手册

轻量 C++ Vibe Coding 配置 | lazy.nvim + 原生 LSP + clangd + Telescope + Oil

配置文件:模块化结构,~/.config/nvim/init.lua (引导) + lua/config/*.lua (选项/按键/autocmd) + lua/plugins/lsp.lua (LSP)

AI 辅助由 Claude Code CLI(接入 DeepSeek)在 tmux pane 中独立运行,未集成到 nvim 内部。


目录


全局选项

选项 说明
number true 显示绝对行号
relativenumber false 关闭相对行号
shiftwidth / tabstop / softtabstop 4 缩进宽度 4 空格
expandtab true 按 Tab 插入 4 个空格
mouse a 所有模式下启用鼠标
updatetime 250 光标空闲 250ms 触发 CursorHold
signcolumn yes 始终显示标记列(git/diagnostic 标记)
termguicolors true 24-bit 真彩色
ignorecase / smartcase true 搜索智能大小写
wrap false 长行不换行
scrolloff 4 光标距屏幕边缘保留 4 行
laststatus 3 全局状态栏(v0.10+ 新默认值)
completeopt menu,menuone,noselect,popup 原生补全弹窗配置
showmatch / matchtime true / 5 括号匹配高亮,持续 50ms
undofile true 持久化撤销(关闭文件后可重新 undo)
splitright true 垂直分屏在右侧打开
splitbelow true 水平分屏在下方打开
confirm true 未保存退出时弹确认对话框
autowrite true 切换 buffer 时自动保存

状态栏格式:%<%f %h%m%r %= %l:%c %P(文件名 · 标记 · 行:列 · 百分比)

配色:Neovim 内置 habamax,零依赖。


核心快捷键

通用操作

按键 模式 功能
jj 插入 快速退出插入模式(替代 <Esc>
<C-s> 普通 / 插入 保存文件
<C-j> / <C-k> 普通 快速滚动 5 行
<Esc> 普通 清除搜索高亮
tn / tp 普通 下一个 / 上一个标签页
- 普通 打开 Oil 文件浏览器
<leader>v 普通 垂直分屏
<leader>s 普通 水平分屏
<leader>tv 普通 在右侧打开 terminal
<leader>ts 普通 在下方打开 terminal
<leader>tq Terminal 退出 Terminal-Job 模式
i Terminal-Normal 回到 Terminal-Job(继续向 shell 输入)
<C-w>h/j/k/l 普通/Terminal-Normal 窗口间移动
<C-w>q 普通/Terminal-Normal 关闭当前窗口
<leader>H/J/K/L 普通 调整窗口大小(同 tmux C-b 字母)

可视模式

按键 功能
<C-c> 复制选中文本到系统剪贴板
< / > 缩进后保持选中状态

搜索导航

按键 功能
n / N 下一个 / 上一个搜索结果(光标居中)
* / # 向前 / 向后搜索光标下单词(光标居中)
gD 跳转到全局声明(光标居中)
gV 重新选中刚才粘贴的文本

Built-in Terminal

Neovim 内置终端模拟器,:terminal 可直接在当前窗口打开 shell。

⚠️ 已知问题mouse=a + neovim v0.12 在切换到 terminal 时可能停留在 Insert mode 而非 Terminal-Job mode,导致按键触发 E21: Cannot make changes, 'modifiable' is off 错误。已在 autocmds.lua 中通过 TermOpen 自动修复。

打开终端

按键 功能
<leader>tv 右侧垂直分屏打开 terminal
<leader>ts 下方水平分屏打开 terminal
:terminal 在当前窗口打开 terminal(覆盖当前 buffer)

推荐使用 <leader>tv / <leader>ts 分屏打开,这样原文件保留在另一窗口,更方便切换和关闭。

模式切换

terminal buffer 有三种模式:

模式 进入方式 说明
Terminal-Job 打开 terminal 时自动进入 按键直接发送给 shell(正常的终端交互)
Terminal-Normal Ctrl+\ Ctrl+n<leader>tq 类似 Normal mode,可以移动光标、切换窗口、复制文本
Normal (Insert) 偶发 bug 导致 ❌ 此模式下按键触发 E21 错误

快捷键

按键 模式 功能
<leader>tv Normal 在右侧垂直分屏打开 terminal
<leader>ts Normal 在下方水平分屏打开 terminal
<leader>tq Terminal 退出 Terminal-Job,进入 Terminal-Normal
i Terminal-Normal 回到 Terminal-Job(继续向 shell 输入)

Terminal-Normal 模式下的操作

<leader>tqCtrl+\ Ctrl+n 进入 Terminal-Normal 后:

操作 按键
窗口间移动 Ctrl+w h/j/k/l
关闭 terminal 窗口 Ctrl+w q
切回上一个 buffer(原文件) Ctrl+6Ctrl+o
按名称切换 buffer :b <filename>
复制 terminal 输出 v 进入 visual mode 选择,y 复制
回到 Terminal-Job ia

典型工作流

1
2
3
4
5
6
7
1. 正在编辑 vim.md
2. <leader>tv ← 右侧打开 terminal(vim.md 保留在左边)
3. ls / git status / make ← 在 terminal 中敲命令
4. <leader>tq ← 退出 Terminal-Job
5. Ctrl+w h ← 切回左边 vim.md 窗口继续编辑
6. Ctrl+w l ← 切回右边 terminal 继续敲命令
7. Ctrl+w q ← 关闭 terminal 窗口

插件一览

插件 用途 加载方式
nvim-treesitter 语义级语法高亮 启动加载
snacks.nvim 缩进线 + 作用域文本对象 + 大文件保护 启动加载
telescope.nvim 模糊搜索文件/内容/符号 调用 :Telescope 命令时
oil.nvim 目录文件浏览器 -
gitsigns.nvim 行号左侧 Git 变更标记 打开文件时
vim-tmux-navigator nvim ↔ tmux 无缝导航 <leader>h/j/k/l
nvim-surround 包围字符操作(ys/ds/cs) ys/ds/cs
peek.nvim Markdown 浏览器实时预览 <leader>p
zellij-nav.nvim Zellij pane 导航(仅 Windows) <leader>h/j/k/l

无 nvim-lspconfig、nvim-cmp、LuaSnip。LSP 使用 Neovim v0.12 原生 vim.lsp.config() API,补全使用原生 vim.lsp.completion


Telescope — 模糊搜索

Telescope 是你在 Vibe Coding 中最高频使用的工具,用于快速定位文件和代码。

快捷键

按键 功能 说明
<leader>ff Find Files 按文件名搜索(基于 fd)
<leader>fw Live Grep 按文件内容搜索(基于 ripgrep)
<leader>fb Buffers 在已打开的 buffer 间切换
gr LSP References 查看光标下符号的所有引用位置

常用操作

按下快捷键后,底部会出现 Telescope 输入框:

1
2
3
4
5
6
7
Find Files              (按 <leader>ff)
─────────────────────────────────────
> main_wi ← 输入关键词,实时过滤
src/main_window.cpp
src/main_window.h
tests/test_main.cpp
─────────────────────────────────────
操作 按键
上下选择结果 <C-n> / <C-p> 或方向键
打开选中文件 <CR>
预览文件内容 自动显示在右侧预览窗
在新的垂直分屏打开 <leader>v
在新的水平分屏打开 <leader>s
在新 tab 打开 <C-t>
关闭 Telescope <Esc><C-c>

技巧:Live Grep 支持正则搜索,输入 class.*Window 可以搜索包含该模式的代码行。


Oil — 文件浏览器

Oil 用纯 vim buffer 的方式展示目录,你在里面操作文件就像编辑文本一样自然。不使用侧边栏式的文件树(nvim-tree)。

打开

按键 功能
- 打开 Oil,浏览当前文件所在目录

界面

1
2
3
4
5
6
7
8
9
10
❯ oil:///home/user/project/src
──────────────────────────────────
.cache/
build/
main.cpp
main.h
utils/
string_helpers.cpp
string_helpers.h
──────────────────────────────────

这就是一个普通的 vim buffer,所有操作都和编辑文本一致。

导航操作

按键 功能
j / k 上下移动
<CR> 打开光标下的文件/目录
- 返回上级目录
~ 跳转到 home 目录
g~ 切换到当前目录的 tab 作用域
<C-l> 刷新当前目录列表
g\ 跳转到回收站目录

打开文件方式

按键 功能
<CR> 当前窗口打开
<leader>v 垂直分屏打开
<leader>s 水平分屏打开
<C-t> 新标签页打开
<C-p> 预览窗口打开/关闭
gx 系统默认程序打开

文件操作

按键 功能
dd 删除文件/目录
yy 复制文件/目录
p 粘贴文件/目录
r 重命名文件/目录
gs 更改排序方式
g. 切换显示隐藏文件
<C-c> 关闭 Oil 并恢复原 buffer
g? 显示全部默认键位帮助

新建文件/目录

把光标停在一个目录上,按 oa 进入插入模式,输入:

1
2
new_file.cpp          ← 新建文件
new_directory/ ← 末尾加 / 表示新建目录

<Esc> 回到普通模式,Oil 会自动创建对应的文件/目录。

设计理念:Oil 会把你的文件编辑操作(dd/yy/p/r)直接映射为文件系统操作。比 nvim-tree 更快更轻量。


原生 LSP 补全

使用 Neovim v0.12 内置的 vim.lsp.completion,无外部补全插件。进入插入模式后自动弹出补全建议。

补全来源

来源 说明
LSP (clangd) 语义补全(类型精准、函数签名)
原生 omnifunc vim.lsp.omnifunc(自动设置)

操作

1
2
3
4
5
6
7
8
9
main_window.cpp
────────────────────────────────────
auto win = std::
[ LSP ]─────────── ← 自动弹出
std::string ← <Tab>/<S-Tab> 选择
std::vector
std::cout
std::unique_ptr
────────────────────────────────────
操作 按键
选择下一项 <Tab>
选择上一项 <S-Tab>
确认选择 <CR>
继续输入(不选择) 直接输入字符

LSP / clangd — C++ 语言服务

使用 Neovim v0.12 原生 vim.lsp.config() / vim.lsp.enable() API 配置 clangd,无需 nvim-lspconfig。

准备工作

项目根目录下要有 compile_commands.json

1
2
3
4
5
# cmake 配置时生成
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

# 在项目根目录建立软链接(推荐)
ln -s build/compile_commands.json .

LSP 快捷键

按键 功能
K 悬停查看:浮动窗口显示类型和文档
gd 跳转到定义
gD 跳转到声明
gi 跳转到实现
gr 查找引用(用 Telescope 展示所有引用位置)
<F2> 重命名符号:修改所有引用处
<F4> 代码操作:显示 clangd 建议的修复
[d 跳转到上一个诊断
]d 跳转到下一个诊断
<leader>e 浮动显示诊断详情:查看当前行错误的完整信息
<leader>cl 运行 CodeLens:执行 clangd 提供的可操作提示

什么是 CodeLens? clangd 在代码行上方显示的内联可操作提示,例如 ▶ Run test0 references3 derived classes 等。按 <leader>cl 即可执行光标所在行对应的操作,无需手动敲命令。

内联提示(Inlay Hints)

自动启用。在代码中显示推导类型:

1
auto it = vec.begin();    // → auto it = /* std::vector<int>::iterator */ vec.begin();

可随时开关:

1
:lua vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled(), nil)

Treesitter — 语法高亮

使用 Neovim 原生 vim.treesitter.start() 进行语法高亮,nvim-treesitter 插件仅用于自动安装和更新解析器。

已安装的语言

c, cpp, lua, vim, vimdoc, markdown, javascript, typescript, tsx, python, bash, rust, java, qmljs

管理解析器

1
2
:TSUpdate          " 更新所有解析器
:TSInstall rust " 手动安装其他语言

snacks.nvim — 缩进线与作用域

snacks.nvim 提供三个已启用的模块:

indent — 缩进线

光标所在作用域显示 虚线缩进指示器,其余行不绘制(only_scope),保持界面干净。

1
2
3
4
5
6
7
if a then          ╎
if b then ╎╎
if c then ╎╎╎
return ╎╎╎
end ╎╎█ ← 光标在这里,此层缩进线高亮
end ╎╎
end ╎

scope — 语义文本对象

基于 treesitter 的 Vim 文本对象,兼容标准 {operator}{a|i}{text-object} 语法:

文本对象 含义 示例
iif / aif if 块内部/整体 diif 删内部、daif 删整体
ifun / afun 函数内部/整体 cifun 改函数体
iclass / aclass 类内部/整体 viclass 选中类内所有代码
iloop / aloop 循环内部/整体 diloop 删循环体

用法与标准 text-object 完全一致:diifciifviif 等。

bigfile — 大文件保护

打开大文件时自动禁用 treesitter 和 indent 等重功能,避免卡顿。全自动,无需手动干预。


gitsigns — Git 变更标记

打开被 git 跟踪的文件时,行号左侧自动显示:

  • + 绿色 = 新增行
  • ~ 黄色 = 修改行
  • - 红色 = 删除行
按键 功能
]c 跳转到下一个变更块(hunk)
[c 跳转到上一个变更块
<leader>hs 暂存当前 hunk(相当于 git add -p
<leader>hr 撤销当前 hunk 的修改
<leader>hp 预览当前 hunk 的完整内容
<leader>hb 切换行级 blame 显示(查看每行的最后提交者)

peek — Markdown 预览

需要安装 deno。

按键 功能
<leader>p 打开 Markdown 实时预览(浏览器)
<leader>mp 关闭预览

C++ 工作流快捷键

按键 功能 详细说明
<F7> 头/源文件切换 main.cppmain.h 之间跳转。支持 .cpp/.cc/.cxx/.c.h/.hpp
<F5> 构建项目 保存当前文件 → 通过 tmux 向右侧 pane 发送 cmake --build build -j$(nproc)
<F6> 运行测试 保存当前文件 → 通过 tmux 向右侧 pane 发送 ctest --output-on-failure

调整 F5 发送到的 tmux pane

默认发送到右侧 pane(-t right)。如果你的 tmux 布局不同,编辑 lua/config/keymaps.lua

1
2
-- 修改 -t 后的目标:
vim.fn.system("tmux send-keys -t right '" .. cmd .. "' Enter")

Tmux 联动

通过 vim-tmux-navigator 实现 nvim 窗口和 tmux pane 之间的无缝导航。

快捷键

按键 功能
<leader>h 向左导航(nvim 窗口 / tmux pane)
<leader>j 向下导航(nvim 窗口 / tmux pane)
<leader>k 向上导航(nvim 窗口 / tmux pane)
<leader>l 向右导航(nvim 窗口 / tmux pane)

行为:如果该方向存在 nvim 窗口则切换到窗口,否则切换到 tmux pane。

Windows 下切换为 zellij-nav.nvim,快捷键相同。

推荐 tmux 布局

1
2
3
4
5
6
7
8
9
10
11
┌──────────────────────┬─────────────────────┐
│ Pane 1: Neovim │ Pane 2: 终端 │
│ (主编辑区) │ (编译 / git / │
│ │ Claude Code CLI) │
│ │ │
│ <Space>ff 搜索文件 │ F5 编译命令发到这里 │
│ <Space>fw 搜索内容 │ │
│ K/gd LSP导航 │ │
│ F7 切头源 │ │
│ - Oil文件浏览 │ │
└──────────────────────┴─────────────────────┘

插件管理

使用 lazy.nvim 管理插件。自动更新检查已禁用(避免启动干扰),手动管理更可控。

配置文件结构

1
2
3
4
5
6
7
8
9
10
~/.config/nvim/
├── init.lua # 引导文件:加载模块 + lazy.nvim + 插件声明
├── lua/
│ ├── config/
│ │ ├── options.lua # 编辑器选项、色彩方案、平台设置
│ │ ├── keymaps.lua # 所有按键映射
│ │ └── autocmds.lua # 自动命令(yank高亮、自动保存、自动mkdir)
│ └── plugins/
│ └── lsp.lua # LSP 配置(clangd)
└── lazy-lock.json # 插件版本锁定文件

常用命令

1
2
3
4
:Lazy              " 打开插件管理界面(查看状态、更新)
:Lazy sync " 同步插件(安装新添加的、移除删除的)
:Lazy update " 更新所有插件
:Lazy clean " 清理不再使用的插件目录

添加新插件

编辑 ~/.config/nvim/init.lua,在 require('lazy').setup({ ... }) 的插件列表中添加:

1
2
3
4
5
6
7
8
9
{
'作者/插件名',
cmd = { 'CmdName' }, -- 懒加载:执行此命令时加载
-- 或
event = 'InsertEnter', -- 懒加载:进入插入模式时加载
config = function()
-- 插件的配置代码
end,
},

添加后执行 :Lazy sync 安装。

移除插件

  1. init.lua 中删除对应的插件定义
  2. 重启 nvim 或执行 :Lazy sync
  3. 执行 :Lazy clean 清理残留文件

自动保存

  • 切换 buffer 时:由 autowrite = true 触发
  • 光标停止 250ms 后:由 CursorHold / CursorHoldI 事件触发

只有在文件确实有未保存修改时才写入,性能开销极低。


从零开始 nvim — 以 coronet 项目为例

本节用一个真实的 C++ 项目 coronet(C++20 协程异步 I/O 库)演示从打开项目到完成日常开发的全流程。

0. 前置准备

确保 clangd 能正常工作,需要 compile_commands.json

1
2
3
cd ~/workspace/coronet
cmake -B build -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCORONET_DEVELOPER_MODE=ON
ln -sf build/compile_commands.json .

软链接只需做一次,后续重新 cmake 配置后 compile_commands.json 自动更新。

1. 打开项目

1
2
cd ~/workspace/coronet
nvim

看到 Neovim 启动界面,状态栏显示 habamax 配色,左下角行号。左下角可以看到 coronet 目录名称。

2. Oil 浏览项目结构

- 打开 Oil,看到 coronet 项目根目录。按 j/k 上下移动光标。把光标移到 include/ 上按 <CR> 进入。

3. Telescope 快速查找文件

<Space>ff(空格松开再按 ff),底部弹出 Telescope 搜索框。输入 io_context

1
2
3
4
5
Find Files              (按 <leader>ff)
─────────────────────────────────────
> io_context
src/coronet/io_context.cpp
include/coronet/io_context.hpp

<C-n> 选择,按 <CR> 打开。

4. 全文搜索(Live Grep)

<Space>fw,输入 co_spawn,搜索所有出现位置,移动光标按 <CR> 跳转。

5. LSP 代码导航

打开 include/coronet/task.hpp。光标放到 task 类名上,按 K 查看文档悬浮窗。按 gd 跳转到定义位置,按 <C-o> 返回。按 gr 查看所有引用位置(Telescope 展示)。

操作 按键
跳转到定义 gd
查看文档/类型 K
查看符号引用 gr
返回上一位置 <C-o>
前进到下一位置 <C-i>

6. 补全体验(原生 LSP)

在文件中新建一行,输入 ctx.,自动弹出补全菜单:

1
2
3
4
5
6
7
ctx.
[ LSP ]───────────────
run()
start()
co_spawn()
stop()
────────────────────────

<Tab> 选择,按 <CR> 确认。

7. 头/源文件切换

假设你在编辑 io_context.cpp,按 <F7> 自动跳转到 io_context.hpp。再按 <F7> 跳回。

8. 重命名符号

光标放到函数名上,按 <F2>。输入新名称后按 <CR>,clangd 自动修改所有引用处。

9. scope 文本对象

在 if 块内部按 diif 删除块内所有代码,daif 删除整个 if(含 if 行)。ciif 改内部代码。详见 snacks.nvim

10. 代码诊断

行号左侧有标记列:

  • ✘ 红色 = Error
  • ✘ 黄色 = Warning

]d 跳转到下一个诊断,[d 跳转到上一个。

11. 编译项目

在 nvim 中按 <F5>,自动保存当前文件并发送编译命令到右侧 tmux pane。按 <Space>l 切换到终端查看编译输出。

12. 运行测试

<F6>,发送测试命令到右侧 tmux pane。

完整工作流小结

1
2
3
4
5
6
7
8
9
10
11
12
13
1. cd coronet/ && nvim      ← 打开项目
2. - ← Oil 浏览目录
3. <Space>ff io_context ← 找到文件
4. K gd gr ← LSP 导航
5. <F7> ← 头/源切换
6. <F2> ← 重命名符号
7. <C-s> ← 保存
8. <F5> ← 编译
9. <Space>l ← 切换到 tmux 查看输出
10. <Space>h ← 切回 nvim
11. <Space>fw error ← 搜索错误
12. 修改 → <C-s> → <F5> ← 修改 → 保存 → 重新编译
13. <F6> ← 运行测试

常见问题

clangd 不工作 / 没有补全

原因compile_commands.json 缺失。

解决

1
2
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
ln -sf build/compile_commands.json .

完成后关闭 nvim 重开,或执行 :LspRestart

Telescope 报错 “No file finder”

1
sudo apt install fd-find ripgrep

已安装的插件不生效

1
2
:Lazy sync    " 确保插件已安装
:checkhealth " 检查是否有配置错误

nvim 启动慢?

当前配置启动时间约 30ms。如果发现变慢,排查:

1
:Lazy profile

外部修改文件后刷新

1
2
:e!            " 强制重新加载当前文件
:checktime " 检查所有 buffer 的外部修改
Buy me a coffee please.