snacks.nvim notify 模块详解:面向 Neovim 原生 vim.notify 的轻量工具函数集
2026/9/16 17:20:27 网站建设 项目流程

snacks.nvim notify 模块详解:面向 Neovim 原生 vim.notify 的轻量工具函数集

【免费下载链接】snacks.nvim🍿 A collection of QoL plugins for Neovim项目地址: https://gitcode.com/GitHub_Trending/sn/snacks.nvim

本文围绕 snacks.nvim 的 notify 模块 展开,它定位为"一组与 Neovim 原生vim.notify协作的工具函数"(见 README 功能表),与负责浮窗渲染的 notifier 模块 是兄弟模块。读完本文,你将掌握Snacks.notify系列 API 的每个函数签名、once等选项的底层行为,并能把它直接接入自己的插件或配置中,替代裸vim.notify调用。

模块定位:notify 与 notifier 的分工

在 snacks.nvim 中,通知相关能力被拆成了两个独立模块,理解二者分工是正确使用的前提:

模块描述渲染器
notify面向vim.notify工具函数(薄封装)不关心渲染,委托给 Neovim 的vim.notify
notifier漂亮的vim.notify实现(浮窗渲染器)自带 compact / minimal / fancy / history 四种样式

从源码看,lua/snacks/notify.lua#L9-L11 中M.meta.desc明确写着"Utility functions to work with Neovim'svim.notify",即它不做任何 UI 渲染,只是帮你把消息、级别、选项组织好再交给vim.notify;而真正的界面层是 lua/snacks/notifier.lua,它通过覆盖vim.notify提供浮窗效果(见下文"与 notifier 的联动"一节)。

两者还能无缝协作:当你启用了 notifier 后,Snacks.notify传入的选项(titleicontimeoutidhlkeepstyle等)会原样透传给 notifier 渲染,因为 notify 的类型别名直接复用了 notifier 的通知选项类型。

核心 API:五个函数签名全解析

原文档(docs/notify.md)定义的类型与全部函数如下,所有函数均支持msg: string|string[],即既可以传单条字符串,也可以传字符串数组(数组会自动按行拼接,见源码分析)。

Snacks.notify(msg, opts)

---@type fun(msg: string|string[], opts?: snacks.notify.Opts) Snacks.notify(msg, opts)

最通用的入口,等价于Snacks.notify.notify(msg, opts)。它是模块的默认调用方式——模块本身被setmetatable成了可调用对象,__call直接转发到t.notify(...)(lua/snacks/notify.lua#L3-L7),所以Snacks.notify("hi")Snacks.notify.notify("hi")效果完全一致。

Snacks.notify.notify(msg, opts)

---@param msg string|string[] ---@param opts? snacks.notify.Opts Snacks.notify.notify(msg, opts)

底层实现函数,无预设级别(级别由opts.level决定或交给vim.notify默认值)。

三个便捷级别函数

---@param msg string|string[] ---@param opts? snacks.notify.Opts Snacks.notify.error(msg, opts) Snacks.notify.info(msg, opts) Snacks.notify.warn(msg, opts)

三个函数分别预设vim.log.levels.ERRORINFOWARN级别。从源码看(lua/snacks/notify.lua#L29-L43),它们并非独立实现,而是统一委托给M.notify,并用vim.tbl_extend("keep", { level = ... }, opts or {})合并选项——注意是"keep"语义:用户在opts中显式传入的level会覆盖预设级别,因此Snacks.notify.error("msg", { level = vim.log.levels.WARN })会以 WARN 级别发出。

snacks.notify.Opts类型:从 notifier 继承的完整选项表

原文档给出了类型别名:

---@alias snacks.notify.Opts snacks.notifier.Notif.opts|{once?: boolean}

Snacks.notify的选项 =notifier 通知的全部选项 + 一个专属的once开关。前者定义在 lua/snacks/notifier.lua#L25-L37,字段如下:

字段类型说明
idnumber\|string通知 ID,用于替换已有通知(详见下文示例)
msgstring通知正文(通常由调用方直接传参,无需手动填)
levelnumber\|snacks.notifier.level级别,数字或"trace"\|"debug"\|"info"\|"warn"\|"error"字符串
titlestring标题
iconstring图标(覆盖默认级别图标)
timeoutnumber\|boolean超时毫秒数;0false表示一直保留直到手动关闭,true表示使用全局默认值
ftstring通知窗口的文件类型
keepfun(notif): boolean自定义"保持显示"判定函数,返回true则通知不被超时关闭
stylesnacks.notifier.style渲染样式:compact/minimal/fancy,也支持自定义渲染函数
optsfun(notif)动态选项回调,每次渲染前调用,可动态修改图标等(如 LSP 进度动画)
hlsnacks.notifier.hl高亮覆盖:title/icon/border/footer/msg
historyboolean是否写入历史记录,默认写入

而 notify 模块独有的once选项,会让底层切换到 Neovim 的去重通知API(源码依据见下节)。

源码级机制:notify 到底做了什么

lua/snacks/notify.lua#L17-L25 的M.notify实现非常精炼,只有几行,却包含了四个值得注意的行为:

function M.notify(msg, opts) opts = opts or {} local notify = vim[opts.once and "notify_once" or "notify"] --[[@as fun(...)]] notify = vim.in_fast_event() and vim.schedule_wrap(notify) or notify msg = type(msg) == "table" and table.concat(msg, "\n") or msg --[[@as string]] msg = vim.trim(msg) opts.title = opts.title or "Snacks" return notify(msg, opts.level, opts) end
  1. oncevim.notify_once:当opts.once为真时,调用 Neovim 的vim.notify_once(同名消息去重,同一时刻重复内容只通知一次);否则调用vim.notify。这是 notify 模块相对 notifier 最大的"附加价值"。
  2. fast event 安全:若调用发生在 fast event 上下文(如部分 autocmd 回调),vim.in_fast_event()为真,会用vim.schedule_wrap包裹,避免在受限上下文中直接触发 UI 操作。
  3. 数组消息自动拼接msg为字符串数组时用"\n"连接成多行文本,方便一次性输出多行内容。
  4. 默认标题与裁剪:对消息做vim.trim去掉首尾空白;未指定title时默认设为"Snacks"。这也是为什么你在 Snacks 自己的模块里看到大量{ title = "Snacks Picker" }之类的写法——它们是在覆盖这个默认值,例如 lua/snacks/picker/actions.lua#L349 的Snacks.notify.warn("Only open buffers can be deleted", { title = "Snacks Picker" })

调用后返回notify(msg, opts.level, opts)的结果(即通知 ID,可复用于替换/隐藏),因此Snacks.notifyvim.notify在返回值上保持兼容。

与 notifier 的联动:启用后 vim.notify 被整体接管

虽然 notify 模块本身不渲染,但当你同时启用 notifier 时(README 安装表中 notifier 标记为‼️,表示需要 setup),lua/snacks/init.lua#L219-L224 会在Snacks.setup()中把全局vim.notify替换为Snacks.notifier.notify

if M.config.notifier.enabled then vim.notify = function(msg, level, o) vim.notify = Snacks.notifier.notify return Snacks.notifier.notify(msg, level, o) end end

这意味着:启用 notifier 后,任何插件调用vim.notify(...)都会走 Snacks 的浮窗渲染;而Snacks.notify调用链(Snacks.notifyvim.notify→ 已被覆盖的Snacks.notifier.notify)则天然获得同样的效果。notifier 的全局默认配置定义在 lua/snacks/notifier.lua#L112-L144,包括timeout = 3000(默认 3 秒)、level = vim.log.levels.TRACE(TRACE 为最低显示门槛,所有通知仍写入历史)、sort = { "level", "added" }(先按级别再按时间排序)、top_down = true(自上而下排列)等,可通过opts = { notifier = { ... } }覆盖。

级别字符串与数字之间的映射由 lua/snacks/notifier.lua#L238-L246 维护:TRACE/DEBUG/INFO/WARN/ERROR数字对应"trace"/"debug"/"info"/"warn"/"error"字符串,未识别值一律归一为"info"(normlevel),因此Snacks.notify.info/warn/error传入的数字级别不会丢失。

实战示例:把 Snacks.notify 用起来

替换已有通知(进度类消息的标准做法)

notify 的id选项继承自 notifier,同 ID 的通知会原地替换而非叠加。这来自 notifier 的N:add实现(lua/snacks/notifier.lua#L395-L403):当opts.id已存在于队列时,复用旧的added时间戳并保留窗口,只更新内容。典型用法:

for i = 1, 10 do vim.defer_fn(function() Snacks.notify("Hello " .. i, { id = "test" }) end, i * 500) end

每次调用都会替换 ID 为"test"的那条通知,而不是堆出 10 条。你也可以直接使用Snacks.notify的返回值作为 ID。

带级别的快捷调用

Snacks.notify.error("保存失败:磁盘空间不足", { title = "My Plugin" }) Snacks.notify.warn("文件已被外部修改", { title = "My Plugin", timeout = 8000 }) Snacks.notify.info({ "第一行", "第二行", "第三行" }) -- 数组自动拼接为多行

once:去重提示

-- 快速事件上下文也安全;同内容短时间内只提示一次 Snacks.notify("配置已热重载", { once = true, level = vim.log.levels.INFO })

动态 LSP 进度条

opts字段支持函数形式,在每次渲染前动态改写通知属性,适合做转圈动画(完整示例见 notifier 文档):

local spinner = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" } vim.api.nvim_create_autocmd("LspProgress", { callback = function(ev) vim.notify(vim.lsp.status(), "info", { id = "lsp_progress", title = "LSP Progress", opts = function(notif) notif.icon = ev.data.params.value.kind == "end" and "✓ " or spinner[math.floor(vim.uv.hrtime() / (1e6 * 80)) % #spinner + 1] end, }) end, })

这里直接使用vim.notify也能生效——因为 notifier 已接管了它,而opts字段正是由 notifier 的 N:render 在每次渲染前调用。

总结与进一步阅读

Snacks.notify的价值在于:它把"与 Neovim 原生vim.notify打交道"的常见痛点(fast event 安全、数组消息、默认标题、去重、级别预设)收敛成一组稳定的工具函数,且选项体系与 notifier 完全打通——不启用 notifier 时它是薄封装,启用 notifier 时它自动获得漂亮的浮窗渲染。对插件作者来说,用Snacks.notify.error/warn/info代替裸vim.notify几乎零成本。

  • 类型与函数签名原始定义:docs/notify.md
  • 实现源码:lua/snacks/notify.lua、lua/snacks/notifier.lua
  • 渲染器配置与示例:docs/notifier.md
  • 全局安装与启用方式:README.md

【免费下载链接】snacks.nvim🍿 A collection of QoL plugins for Neovim项目地址: https://gitcode.com/GitHub_Trending/sn/snacks.nvim

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询