T 教程 Tutorials

Neovim 精通:Lua API、插件骨架、性能与远程工作流

中文长文:Lua API 与 autocmd、keymap 设计、自定义 plugin 骨架、性能 profiling、远程编辑,以及与 iTerm2/tmux 的配合。

教程

接 入门 与 实战:本篇面向「配置已能干活、想掌控扩展点」的读者——读懂 Lua API、写可靠 autocmd / keymap、搭最小插件、会 profiling,并把 nvim 嵌进 iTerm2 + tmux / 远程场景。

Lua API 速览

Neovim 把编辑器状态暴露给 Lua。最常用:

API用途
vim.opt / vim.o / vim.bo / vim.wo选项(全局 / buffer / window)
vim.g / vim.b / vim.w / vim.t全局与作用域变量
vim.keymap.set键位(替代 nvim_set_keymap)
vim.api.nvim_*底层 API:缓冲、窗口、命令、autocmd
vim.fn.*调用 Vimscript 函数
vim.cmd()执行 Ex 命令字符串
vim.uv / vim.looplibuv 异步(文件、定时器、进程)
vim.schedule把回调排回主循环(API 安全)

示例:读当前文件与改一行选项:

local buf = vim.api.nvim_get_current_buf()
local name = vim.api.nvim_buf_get_name(buf)
vim.bo[buf].filetype = 'python'

local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
print(#lines, name)

创建用户命令:

vim.api.nvim_create_user_command('WdDate', function()
  local s = os.date('%Y-%m-%d %H:%M')
  vim.api.nvim_put({ s }, 'c', true, true)
end, { desc = 'Insert local datetime' })

通知:

vim.notify('LSP ready', vim.log.levels.INFO)

autocmd:事件驱动

用 nvim_create_augroup + nvim_create_autocmd,避免散落的 Vimscript au。

local aug = vim.api.nvim_create_augroup('WdTweaks', { clear = true })

-- 离开插入时取消搜索高亮可按需
vim.api.nvim_create_autocmd('TextYankPost', {
  group = aug,
  callback = function()
    vim.highlight.on_yank({ higroup = 'IncSearch', timeout = 120 })
  end,
})

-- 按文件类型缩进
vim.api.nvim_create_autocmd('FileType', {
  group = aug,
  pattern = { 'python', 'rust' },
  callback = function()
    vim.opt_local.shiftwidth = 4
    vim.opt_local.tabstop = 4
  end,
})

-- 终端 buffer 自动 insert
vim.api.nvim_create_autocmd('TermOpen', {
  group = aug,
  callback = function()
    vim.opt_local.number = false
    vim.opt_local.relativenumber = false
    vim.cmd.startinsert()
  end,
})

-- 保存时自动创建无尾空白(谨慎用于有意义空白的格式)
vim.api.nvim_create_autocmd('BufWritePre', {
  group = aug,
  pattern = '*',
  callback = function()
    local view = vim.fn.winsaveview()
    vim.cmd([[%s/\s\+$//e]])
    vim.fn.winrestview(view)
  end,
})

常用事件:BufWritePre、BufEnter、LspAttach、UIEnter、VimResized、FileType。

调试::autocmd / :autocmd TextYankPost 查看;clear = true 保证重载不重复注册。

Keymap 设计原则

  1. Leader 语义分层:<leader>f 找、<leader>g git、<leader>l lsp、<leader>x 诊断/列表。
  2. buffer-local 优先:LSP 映射放 LspAttach,避免全局污染。
  3. desc 必填:配合 which-key,也方便自己 :map。
  4. 少用递归:vim.keymap.set 默认 noremap 语义更安全。
  5. 插入模式映射克制:容易与输入法、补全冲突;jk→Esc 可留。
  6. 终端模式:vim.keymap.set('t', '<Esc><Esc>', [[<C-\><C-n>]]) 跳出。

模式化封装:

local M = {}

function M.map(mode, lhs, rhs, opts)
  opts = opts or {}
  opts.silent = opts.silent ~= false
  vim.keymap.set(mode, lhs, rhs, opts)
end

function M.nmap(lhs, rhs, desc)
  M.map('n', lhs, rhs, { desc = desc })
end

return M

冲突排查::verbose nmap <leader>ff 看最后定义位置。

自定义 plugin 骨架

最小插件不必发布;本地 ~/projects/wd.nvim 或配置内 lua/wd/ 即可。

作为 runtimepath 插件

~/code/wd.nvim/
  lua/
    wd/
      init.lua
      health.lua
  plugin/
    wd.lua          -- 可选:自动加载入口
  doc/
    wd.txt

lua/wd/init.lua:

local M = {}
M.config = {
  cheer = true,
}

function M.setup(opts)
  M.config = vim.tbl_deep_extend('force', M.config, opts or {})
  vim.api.nvim_create_user_command('WdHello', function()
    local msg = M.config.cheer and '🍷 Cabernet ready' or 'ok'
    vim.notify(msg)
  end, {})
end

return M

在 lazy 里:

{
  dir = vim.fn.expand('~/code/wd.nvim'),
  name = 'wd.nvim',
  opts = { cheer = true },
  config = function(_, opts)
    require('wd').setup(opts)
  end,
},

plugin/wd.lua(Vim 启动时自动 source):

-- 仅注册极轻逻辑;重逻辑放 setup
vim.api.nvim_create_user_command('WdPing', function()
  print('pong')
end, {})

健康检查 :checkhealth wd:

-- lua/wd/health.lua
local M = {}
function M.check()
  vim.health.start('wd.nvim')
  if vim.fn.has('nvim-0.9') == 1 then
    vim.health.ok('Neovim >= 0.9')
  else
    vim.health.error('Need Neovim 0.9+')
  end
end
return M

要点:setup() 幂等;默认值与用户 opts 合并;避免在 import 时创建 autocmd(放到 setup)。

性能 profiling

启动

:profile start /tmp/nvim-profile.log
:profile func *
:profile file *
" 再执行可疑操作或重启后加载
:profile stop

或启动参数:

nvim --startuptime /tmp/startup.log

看谁最慢:大插件、同步 require、过早加载的 UI。

lazy 视角

:Lazy profile

把不碍事的插件设 event = 'VeryLazy'、cmd = ...、ft = 'python'、keys = ...。

运行时卡顿

  • Treesitter:大 minified JSON 可 vim.bo.syntax = 'off' 或禁用 highlight。
  • LSP::LspStop 对比;pyright 对巨型 monorepo 要限 root_dir / exclude。
  • 状态栏轮询、git blame 实时刷新:降频或按需。
-- 大文件保护
vim.api.nvim_create_autocmd('BufReadPre', {
  callback = function(args)
    local ok, stat = pcall(vim.loop.fs_stat, args.file)
    if ok and stat and stat.size > 1024 * 1024 * 1.5 then
      vim.b.large_file = true
      vim.cmd.syntax('off')
      vim.opt_local.foldmethod = 'manual'
    end
  end,
})

远程编辑

方案 A:SSH 上直接跑 nvim

最稳。本地 tmux → ssh host → nvim。配置用 GNU stow / git bare / Ansible 同步 ~/.config/nvim。

方案 B:netrw / scp

nvim scp://user@host//var/www/app/main.py

可用但体验一般,延迟高时痛苦。

方案 C:SSHFS / mutagen / rclone mount

把远端目录挂到本地,再用本地 nvim + LSP。注意:LSP 在本地跑、文件在远端时,解释器路径与索引要想清楚;有时不如远端 nvim + 远端 pyright。

方案 D:neovim 远程 UI(进阶)

nvim --headless --listen 0.0.0.0:6666 与 nvim --server / 第三方 UI。公司网络需 SSH 隧道:

ssh -L 6666:127.0.0.1:6666 user@host

安全:勿对公网裸奔 listen。

与 iTerm2 / tmux 配合

iTerm2

  • Profiles → Colors:真彩色;Font 用等宽(JetBrains Mono / Maple Mono)。
  • Keys:把 Option 设为 Esc+,方便 Meta 映射。
  • 滚动与鼠标:set mouse=a 后,可在 nvim 里用鼠标调窗(tmux 需同步设置)。
  • 分裂窗:习惯上 tmux 管会话,nvim 管 buffer,避免两边都狂分屏。

tmux 最小友好配置

# ~/.tmux.conf
set -g mouse on
set -g default-terminal "tmux-256color"
set -as terminal-overrides ",xterm-256color:RGB"
set -s escape-time 0
set -g focus-events on
setw -g mode-keys vi

# 窗格移动与 nvim 一致的心智
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

escape-time 0 减少 Esc 延迟;focus-events 利于「自动保存 / git gutter」类插件。

剪贴板三角

iTerm2 ↔ tmux ↔ nvim:优先让 nvim unnamedplus 对接系统;tmux 可装 OSC52 或 set -g set-clipboard on。SSH 远程时 OSC52 常比抢 X11 转发更省事。

会话脚本示例

#!/usr/bin/env bash
# wd-session.sh — 数据项目一键
SESSION=analysis
PROJECT=~/work/report-q3
tmux has-session -t "$SESSION" 2>/dev/null && tmux attach -t "$SESSION" && exit
tmux new-session -d -s "$SESSION" -c "$PROJECT"
tmux send-keys -t "$SESSION":0 'source .venv/bin/activate && nvim' C-m
tmux split-window -h -t "$SESSION":0 -c "$PROJECT"
tmux send-keys -t "$SESSION":0.1 'source .venv/bin/activate && python' C-m
tmux attach -t "$SESSION"

配置维护建议

  • 配置进 git:~/.config/nvim 单独 repo,机器间 pull。
  • lazy-lock.json 提交,保证同事/另一台电脑版本一致。
  • 用 git bisect 或临时 nvim --clean 对比「是不是配置害的」。
  • 文档化自己的 leader 表(可放在本博客或 CHEATSHEET.md)。

三部曲收束

篇焦点
入门安装、模式、buffer、最小 init.lua
实战lazy、LSP、telescope、git、Python 流
精通(本篇)Lua API、autocmd、插件、性能、远程、tmux

下一步不是「更多插件」,而是:把常用操作练到不必想,并删掉一个月没用的映射。


精通是减法:知道能加什么,更知道不该加什么。

评论