
nvim-lua-plugin
Neovim Lua 插件作者工作流与规约(repo-only)。当需要设计/实现/重构 Neovim Lua 插件时使用,涉及 plugin/ 与 lua/ 的加载结构、隐式 lazy-loading(延迟 require)、用户命令与 autocmd、Plug 映射与 keymap 策略、配置与初始化拆分(setup 仅覆盖默认值)、vimdoc(doc/*.txt + :helptags)、health checks(lua/name/health.lua + :checkhealth)以及使用 mini.test 的自动化测试。适用于修改或生成 plugin/*.lua、lua/**、doc/*.txt、tests/*.lua 等文件结构与代码。
Neovim Lua 插件作者工作流与规约(repo-only)。当需要设计/实现/重构 Neovim Lua 插件时使用,涉及 plugin/ 与 lua/ 的加载结构、隐式 lazy-loading(延迟 require)、用户命令与 autocmd、Plug 映射与 keymap 策略、配置与初始化拆分(setup 仅覆盖默认值)、vimdoc(doc/*.txt + :helptags)、health checks(lua/name/health.lua + :checkhealth)以及使用 mini.test 的自动化测试。适用于修改或生成 plugin/*.lua、lua/**、doc/*.txt、tests/*.lua 等文件结构与代码。
Nvim Lua Plugin
Overview
这个 skill 帮助你以“贴近 Neovim 官方最佳实践”的方式开发 Lua 插件:在不牺牲启动性能的前提下,提供清晰的对外接口(命令/<Plug>/Lua API)、可维护的目录结构、可诊断的健康检查、可阅读的 vimdoc,以及使用 mini.test 的自动化测试。
快速开始(最小结构 + 最小入口)
最小目录结构:
plugin/<name>.lua
lua/<name>/init.lua
lua/<name>/config.lua
doc/<name>.txt
最小入口(plugin/<name>.lua):
vim.api.nvim_create_user_command("<Name>Do", function(opts)
require("<name>").do_action(opts)
end, { nargs = "*" })
最小实现(lua/<name>/init.lua):
local M = {}
function M.do_action(opts)
-- ... 实际逻辑
end
return M
关键决策(先推理,再写代码)
-
识别需求类型(决定入口与加载时机)
- 需要用户显式调用的功能(命令/映射)?
- 需要跟随事件触发(autocmd)?
- 只对特定 filetype 生效?
- 是否有插件自有 UI buffer/窗口?
- 启动性能是否敏感?(通常敏感)
-
先查权威来源(避免网上文章过时)
- 插件开发:
:h lua-plugin - Lua 使用:
:h lua-guide - API:
:h api - 自检:
:h health-dev - vimdoc:
:h help-writing、:h :helptags
- 插件开发:
-
选择目录与加载模型(默认推荐)
plugin/<name>.lua:只注册入口(commands/keymaps/autocmd),保持极小。lua/<name>/...:放实现模块;在入口回调里require()实现模块(隐式 lazy-loading)。- filetype 相关:用
ftplugin/<ft>.lua或 autocmd(参考:h lua-plugin-filetype)。
-
选择对外接口形态(减少冲突/提升可组合性)
- 默认优先:
<Plug>映射 + 用户自定义vim.keymap.set()(:h lua-plugin-keymaps)。 - 其次:用户命令
nvim_create_user_command()。 - 最灵活:导出
require('<name>').action(opts)形式的 Lua API(适合参数组合很多)。
- 默认优先:
-
决定配置与初始化的拆分
- 推荐:
setup(opts)只 merge 默认配置;初始化逻辑放在plugin//ftplugin/。 - 例外:需要显式 opt-in 或初始化高度可定制时,才考虑合并到
setup()(:h lua-plugin-init)。
- 推荐:
实施步骤(详细步骤与检查点)
Step 0:收集最小上下文(避免盲写)
至少确认这些信息(写在注释/设计草稿里即可):
- 插件名(决定
lua/<name>/、help tags 前缀、:checkhealth <name>) - 入口:命令名/
<Plug>名称/autocmd 事件/文件类型 - 配置项与默认值(必须项/可选项)
- 外部依赖(其它插件/系统命令/可选依赖)
- 性能约束(启动期是否允许 I/O/扫描?通常不允许)
Step 1:设计目录结构与入口(默认模板)
推荐结构(来自 :h lua-plugin / :h lua-guide):
plugin/<name>.lua 启动时执行:只注册 commands/keymaps/autocmd
lua/<name>/init.lua 模块入口:导出公共 API
lua/<name>/config.lua 默认配置 + setup(opts)
lua/<name>/actions.lua 具体功能实现
lua/<name>/health.lua :checkhealth 支持(可选但推荐)
doc/<name>.txt vimdoc
关键点:plugin/<name>.lua 顶层避免 require('<name>...') 重模块;把 require() 移到回调里(:h lua-plugin-defer-require)。
Step 2:实现入口(命令 / <Plug> / autocmd)
命令
用 vim.api.nvim_create_user_command()(:h nvim_create_user_command())。回调内部再 require() 实现模块:
- 好:命令回调里
local m = require('<name>') - 避免:
plugin/<name>.lua顶层local m = require('<name>')
<Plug> 映射
遵循 :h lua-plugin-keymaps:插件作者提供 <Plug>(...),用户用 vim.keymap.set() 绑定到自己喜欢的按键。
autocmd
用 vim.api.nvim_create_autocmd()(:h nvim_create_autocmd())。对于 filetype 特化,也可以用 ftplugin/<ft>.lua。
Step 3:配置与校验(既稳又不打扰用户)
setup(opts)只覆盖默认配置,避免做昂贵动作(I/O、扫描、创建大量 autocmd/keymap)。- 轻量校验用
vim.validate()(:h vim.validate())。 - “未知字段/拼写错误”这类深度校验可放到 health check,减少运行期开销(
:h health-dev)。
对比示例(不推荐 vs 推荐):
-- 不推荐:setup 里注册大量 autocmd/keymap
function M.setup(opts)
M.config = vim.tbl_deep_extend("force", defaults, opts or {})
vim.api.nvim_create_autocmd("BufEnter", { callback = M.on_buf_enter })
end
-- 推荐:setup 只合并配置,注册放在 plugin/ 或 ftplugin/
function M.setup(opts)
M.config = vim.tbl_deep_extend("force", defaults, opts or {})
end
Step 4:可观测性与错误处理
- 用
vim.notify()/vim.notify_once()给用户明确且可操作的提示(:h vim.notify())。 - 对可选依赖用
pcall(require, ...)兜底;错误信息应包含“如何修复”。 - 开发/排障时:
- 用
:messages查看启动期输出 - 用
:restart快速观察改动效果(参考:h lua-plugin-troubleshooting) - 用
nvim --startuptime <file>量化插件启动影响(参考:h --startuptime)
- 用
Step 5:Health checks(推荐)
为插件提供 lua/<name>/health.lua(或 lua/<name>/health/init.lua),返回带 check() 的表(:h health-dev)。
检查范围建议:
- 配置是否有效(类型/范围/未知字段)
- 外部依赖是否存在
- 初始化是否正确(例如避免重复初始化)
Step 6:vimdoc(强烈建议)
提供 doc/<name>.txt 并遵循 :h help-writing:
- 首行
*<name>.txt*+ 简述 - tags 用
*tag*,交叉引用用|tag| - 代码块用
>/<(也可>lua/>vim)
提醒用户运行 :helptags 为 doc 生成 tags(:h :helptags)。
Step 7:版本与弃用
遵循 :h lua-plugin-versioning:
- 用 SemVer 表达破坏性变更
- 弃用用
vim.deprecate()(:h vim.deprecate())或---@deprecate注解提前告知
Step 8:测试(推荐 mini.test)
为插件添加自动化测试可以大幅提升可维护性。推荐使用 mini.test(见 references/testing-mini-test.md):
为什么选 mini.test:
- 与 Neovim 深度集成,支持 child Neovim 进程隔离测试
- 支持 test set、hooks、参数化与过滤
- 内置 screenshot/reference 测试,适合 UI 插件
推荐目录结构:
tests/
test_smoke.lua -- 冒烟测试
test_commands.lua -- 命令/API 测试
scripts/
minimal_init.lua -- 测试用最小配置(加载 mini.test + 你的插件)
minitest.lua -- 可选:项目特定测试脚本
核心工作流(详见 references/testing-mini-test.md):
- 每个 test 文件返回一个 test set(
MiniTest.new_set()) - 用
MiniTest.new_child_neovim()启动隔离的 child 进程 - 在 child 里加载插件、执行操作、用
MiniTest.expect.*断言 - 本地
:lua MiniTest.run()或 CInvim --headless ...
最小运行示例(与 scripts/minimal_init.lua 配合):
nvim --headless -u scripts/minimal_init.lua -c "lua MiniTest.run()" -c "qa"
常用断言见 :h MiniTest.expect。
规约(30%:必须/禁止/建议)
MUST
plugin/<name>.lua只做注册入口(commands/keymaps/autocmd),保持极小(:h lua-plugin-lazy)。- 在
plugin/<name>.lua避免顶层加载重模块;把require()放进回调(:h lua-plugin-defer-require)。 - 若插件有外部依赖或复杂配置,提供
health.lua以支持:checkhealth(:h health-dev)。 - 如果发布给用户使用,提供 vimdoc(
:h lua-plugin-doc、:h help-writing)。
AVOID
- 自动创建大量全局 keymap(容易冲突);优先
<Plug>/命令/Lua API(:h lua-plugin-keymaps)。 - 启动期做 I/O/扫描/遍历项目文件等昂贵动作;把成本推迟到用户触发时(
:h lua-plugin-lazy)。 - 在
setup()里混入大量初始化逻辑,导致用户必须显式调用才能“默认可用”(除非确实要 opt-in)(:h lua-plugin-init)。
SHOULD
- 用
vim.validate()做轻量参数校验;昂贵校验放到 health check(:h vim.validate()、:h health-dev)。 - 关键错误用
vim.notify()给出可操作的修复建议(:h vim.notify())。 - 采用 LuaLS 注解(LuaCATS/EmmyLua)提高类型安全(
:h lua-plugin-type-safety)。 - 为核心功能写自动化测试(推荐
mini.test,见 Step 8)。
资源索引(按需阅读/复制)
references/
references/nvim-lua-plugin-guidelines.md:快速索引references/api_reference.md:完整指南(按 help tags 组织)references/nvim-lua-api-cheatsheet.md:按任务组织的 API 速查references/vimdoc-template.md:vimdoc 最小模板references/testing-mini-test.md:mini.test 测试框架速用指南
assets/
assets/plugin-skeleton/:可复制的插件骨架(目录树 + 文件模板)assets/minimal-repro-config/:最小复现用的init.lua模板(可配合nvim --clean -u <file>)assets/mini-test-skeleton/:mini.test 测试骨架(tests/+scripts/minimal_init.lua)
You Might Also Like
Related Skills

coding-agent
Run Codex CLI, Claude Code, OpenCode, or Pi Coding Agent via background process for programmatic control.
openclaw
add-uint-support
Add unsigned integer (uint) type support to PyTorch operators by updating AT_DISPATCH macros. Use when adding support for uint16, uint32, uint64 types to operators, kernels, or when user mentions enabling unsigned types, barebones unsigned types, or uint support.
pytorch
at-dispatch-v2
Convert PyTorch AT_DISPATCH macros to AT_DISPATCH_V2 format in ATen C++ code. Use when porting AT_DISPATCH_ALL_TYPES_AND*, AT_DISPATCH_FLOATING_TYPES*, or other dispatch macros to the new v2 API. For ATen kernel files, CUDA kernels, and native operator implementations.
pytorch
skill-writer
Guide users through creating Agent Skills for Claude Code. Use when the user wants to create, write, author, or design a new Skill, or needs help with SKILL.md files, frontmatter, or skill structure.
pytorch
implementing-jsc-classes-cpp
Implements JavaScript classes in C++ using JavaScriptCore. Use when creating new JS classes with C++ bindings, prototypes, or constructors.
oven-sh
implementing-jsc-classes-zig
Creates JavaScript classes using Bun's Zig bindings generator (.classes.ts). Use when implementing new JS APIs in Zig with JSC integration.
oven-sh