nvim-lua-plugin

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 等文件结构与代码。

3Star
0Fork
更新于 1/22/2026
SKILL.md
readonly只读
name
nvim-lua-plugin
description

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

关键决策(先推理,再写代码)

  1. 识别需求类型(决定入口与加载时机)

    • 需要用户显式调用的功能(命令/映射)?
    • 需要跟随事件触发(autocmd)?
    • 只对特定 filetype 生效?
    • 是否有插件自有 UI buffer/窗口?
    • 启动性能是否敏感?(通常敏感)
  2. 先查权威来源(避免网上文章过时)

    • 插件开发::h lua-plugin
    • Lua 使用::h lua-guide
    • API::h api
    • 自检::h health-dev
    • vimdoc::h help-writing:h :helptags
  3. 选择目录与加载模型(默认推荐)

    • plugin/<name>.lua:只注册入口(commands/keymaps/autocmd),保持极小。
    • lua/<name>/...:放实现模块;在入口回调里 require() 实现模块(隐式 lazy-loading)。
    • filetype 相关:用 ftplugin/<ft>.lua 或 autocmd(参考 :h lua-plugin-filetype)。
  4. 选择对外接口形态(减少冲突/提升可组合性)

    • 默认优先:<Plug> 映射 + 用户自定义 vim.keymap.set():h lua-plugin-keymaps)。
    • 其次:用户命令 nvim_create_user_command()
    • 最灵活:导出 require('<name>').action(opts) 形式的 Lua API(适合参数组合很多)。
  5. 决定配置与初始化的拆分

    • 推荐: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):

  1. 每个 test 文件返回一个 test set(MiniTest.new_set()
  2. MiniTest.new_child_neovim() 启动隔离的 child 进程
  3. 在 child 里加载插件、执行操作、用 MiniTest.expect.* 断言
  4. 本地 :lua MiniTest.run() 或 CI nvim --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

coding-agent

179Kdev-codegen

Run Codex CLI, Claude Code, OpenCode, or Pi Coding Agent via background process for programmatic control.

openclaw avataropenclaw
获取
add-uint-support

add-uint-support

97Kdev-codegen

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 avatarpytorch
获取
at-dispatch-v2

at-dispatch-v2

97Kdev-codegen

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 avatarpytorch
获取
skill-writer

skill-writer

97Kdev-codegen

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 avatarpytorch
获取

Implements JavaScript classes in C++ using JavaScriptCore. Use when creating new JS classes with C++ bindings, prototypes, or constructors.

oven-sh avataroven-sh
获取

Creates JavaScript classes using Bun's Zig bindings generator (.classes.ts). Use when implementing new JS APIs in Zig with JSC integration.

oven-sh avataroven-sh
获取