ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

snacks.nvim notifier 完全指南:优雅的通知中心与 LSP 进度集成

snacks.nvim notifier 完全指南:优雅的通知中心与 LSP 进度集成 snacks.nvim notifier 完全指南优雅的通知中心与 LSP 进度集成【免费下载链接】snacks.nvim A collection of QoL plugins for Neovim项目地址: https://gitcode.com/GitHub_Trending/sn/snacks.nvim本篇指南围绕 snacks.nvim 的notifier模块展开系统讲解如何用一套配置接管 Neovim 原生vim.notify实现通知去重替换、历史记录回溯、以及基于 LSP 进度事件的实时状态展示。读完本文你将掌握 notifier 的全部配置项、三种内置渲染风格、五个核心 API 的用法并能从 lua/snacks/notifier.lua 源码层面理解通知从产生、排队、渲染到消亡的完整生命周期。模块定位漂亮的vim.notify替代实现在 lua/snacks/notifier.lua 中notifier 模块的自我描述是Prettyvim.notify即一个美观的 Neovim 原生通知渲染器。它的核心职责有三接管vim.notify模块启用后所有通过vim.notify()发出的消息都会自动由 notifier 渲染无需改动任何插件或既有代码维护通知历史即使通知超时消失也会被持久化到历史记录中随时可以按需回溯查看支持通知更新替换通过固定的id可以原地替换已有通知天然适配进度条、加载状态等高频更新的场景。从源码结构看lua/snacks/notifier.lua模块本身是一个可调用的 table——Snacks.notifier(...)等价于Snacks.notifier.notify(...)其中needs_setup true意味着它必须在Snacks.setup()中启用后才生效。快速开始Setup 配置notifier 的启用方式和 snacks.nvim 其他模块一致在 lazy.nvim 的opts中声明即可。留空则使用默认配置-- lazy.nvim { folke/snacks.nvim, ---type snacks.Config opts { notifier { -- your notifier configuration comes here -- or leave it empty to use the default settings -- refer to the configuration section below } } }当notifier.enabled为真时lua/snacks/init.lua 会在 setup 过程中把全局的vim.notify替换为Snacks.notifier.notifyif 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这段代码采用先包装再替换的方式确保调用链上第一个vim.notify之后的所有通知都落入 notifier 的渲染管线。这也解释了为什么启用后连:messages、其他插件甚至vim.notify_once发出的通知都会以统一风格呈现。完整配置参数解析notifier 的默认配置如下与 lua/snacks/notifier.lua 中的defaults表完全一致---class snacks.notifier.Config ---field enabled? boolean ---field keep? fun(notif: snacks.notifier.Notif): boolean # global keep function ---field filter? fun(notif: snacks.notifier.Notif): boolean # filter our unwanted notifications (return false to hide) { timeout 3000, -- default timeout in ms width { min 40, max 0.4 }, height { min 1, max 0.6 }, -- editor margin to keep free. tabline and statusline are taken into account automatically margin { top 0, right 1, bottom 0 }, padding true, -- add 1 cell of left/right padding to the notification window gap 0, -- gap between notifications sort { level, added }, -- sort by level and time -- minimum log level to display. TRACE is the lowest -- all notifications are stored in history level vim.log.levels.TRACE, icons { error  , warn  , info  , debug  , trace  , }, keep function(notif) return vim.fn.getcmdpos() 0 end, ---type snacks.notifier.style style compact, top_down true, -- place notifications from top to bottom date_format %R, -- time format for notifications -- format for footer when more lines are available -- %d is replaced with the number of lines. -- only works for styles with a border ---type string|boolean more_format ↓ %d lines , refresh 50, -- refresh at most every 50ms }各参数的作用与源码对应关系如下参数默认值说明源码依据timeout3000通知默认存活时间毫秒。单条通知可通过timeout 0或false永久驻留notifier.luawidth{ min 40, max 0.4 }通知窗口宽度。min/max为小于 1 的小数时按编辑器宽度比例计算notifier.luaheight{ min 1, max 0.6 }通知窗口高度规则同widthnotifier.luamargin{ top 0, right 1, bottom 0 }为编辑器预留的空白区域tabline 与 statusline 会自动计入无需手动处理notifier.luapaddingtrue通知窗口左右各加 1 格内边距notifier.luagap0通知之间的垂直间距notifier.luasort{ level, added }通知排序字段先按级别再按时间level排序时错误优先notifier.lualevelvim.log.levels.TRACE显示的最低日志级别。低于该级别的通知不展示但仍存入历史notifier.luaicons各级别图标表各日志级别对应的图标需 Nerd Font 支持notifier.luakeep函数全局保活函数返回true时通知不因超时消失。默认实现是vim.fn.getcmdpos() 0即输入命令时保持通知notifier.luastylecompact默认渲染风格可选compact/minimal/fancy或自定义渲染函数notifier.luatop_downtrue通知从上往下排列为false时从下往上notifier.luadate_format%R历史记录与 fancy 风格中时间戳的格式os.date格式notifier.luamore_format ↓ %d lines 内容超出高度时显示在边框 footer 的提示文案%d被替换为折叠掉的行数仅对有边框的样式生效notifier.luarefresh50队列刷新节流时间毫秒即最多每 50ms 处理一次队列notifier.lua补充说明filter与level的配合配置层面还预留了filter字段filter接收通知对象并返回布尔值返回false则该通知被隐藏但仍会写入历史。从 notifier.lua 的add实现可以看到判断链路local want numlevel(notif.level) numlevel(self.opts.level) want want and (not self.opts.filter or self.opts.filter(notif)) if not want then return notif.id end self.queue[notif.id] notif即先做级别阈值过滤再做自定义filter过滤两者都通过才进入渲染队列。通知窗口样式Stylesnotifier 依赖 snacks.win 的样式系统相关样式通过Snacks.config.style()注册见 lua/snacks/notifier.lua。你可以通过opts.styles覆盖它们完整的样式定制方式可参考 docs/styles.md。notification渲染单条通知的浮动窗口样式{ border true, zindex 100, ft markdown, wo { winblend 5, wrap false, conceallevel 2, colorcolumn , }, bo { filetype snacks_notif }, }关键点ft markdown允许消息体使用 Markdown 语法高亮conceallevel 2配合 Markdown 注入隐藏标记字符。notification_history通知历史窗口的样式{ border true, zindex 100, width 0.6, height 0.6, minimal false, title Notification History , title_pos center, ft markdown, bo { filetype snacks_notif_history, modifiable false }, wo { winhighlight Normal:SnacksNotifierHistory }, keys { q close }, }该窗口占编辑器 60% 的宽高标题居中q键关闭。注意keys { q close }声明了默认按键绑定——在历史窗口内按q即可关闭。类型系统理解通知的骨架文档中用 LuaCATS 注解定义了完整的类型体系与 notifier.lua 顶部注释一致掌握它们才能写好自定义渲染或深度集成。渲染风格别名---alias snacks.notifier.style snacks.notifier.render|compact|fancy|minimal三种内置风格compact使用窗口边框展示图标与标题minimal无边框仅图标加消息fancy接近 nvim-notify 默认的样式。通知选项Notif.opts---class snacks.notifier.Notif.opts ---field id? number|string ---field msg? string ---field level? number|snacks.notifier.level ---field title? string ---field icon? string ---field timeout? number|boolean timeout in ms. Set to 0|false to keep until manually closed ---field ft? string ---field keep? fun(notif: snacks.notifier.Notif): boolean ---field style? snacks.notifier.style ---field opts? fun(notif: snacks.notifier.Notif) -- dynamic opts ---field hl? snacks.notifier.hl -- highlight overrides ---field history? boolean几个容易忽略的字段timeout接受布尔值false或0表示永不超时需手动关闭true则回退到全局timeoutopts是一个动态回调每次重渲染前调用允许在渲染时修改通知属性如动态切换图标是 LSP 进度动画的关键hl可对标题、图标、边框、footer、消息体做高亮覆盖history false可阻止单条通知写入历史。通知对象Notif---class snacks.notifier.Notif: snacks.notifier.Notif.opts ---field id number|string ---field msg string ---field win? snacks.win ---field icon string ---field level snacks.notifier.level ---field timeout number ---field dirty? boolean ---field added number timestamp with nano precision ---field updated number timestamp with nano precision ---field shown? number timestamp with nano precision ---field hidden? number timestamp with nano precision ---field layout? { top?: number, width: number, height: number }通知对象在选项之上增加了运行时状态added/updated/shown/hidden四个纳秒精度时间戳记录了通知的生命周期节点layout记录其在屏幕上的排布位置win指向实际的浮动窗口。自定义渲染---alias snacks.notifier.render fun(buf: number, notif: snacks.notifier.Notif, ctx: snacks.notifier.ctx)---class snacks.notifier.hl ---field title string ---field icon string ---field border string ---field footer string ---field msg string---class snacks.notifier.ctx ---field opts snacks.win.Config ---field notifier snacks.notifier.Class ---field hl snacks.notifier.hl ---field ns number自定义渲染函数接收目标缓冲区、通知对象和上下文自行决定如何把消息写入缓冲区。ctx提供窗口配置、通知器实例、高亮组集合和用于nvim_buf_set_extmark的 namespace。历史查询参数---class snacks.notifier.history ---field filter? vim.log.levels|snacks.notifier.level|fun(notif: snacks.notifier.Notif): boolean ---field sort? string[] # sort fields, default: {added} ---field reverse? boolean---alias snacks.notifier.level trace|debug|info|warn|errorfilter既可以是级别字符串或数字表示不低于该级别也可以是自定义过滤函数sort默认按added排序reverse用于倒序历史窗口默认启用。模块 APInotifier 通过Snacks.notifier暴露五个接口实现见 notifier.luaSnacks.notifier()---type fun(msg: string, level?: snacks.notifier.level|number, opts?: snacks.notifier.Notif.opts): number|string Snacks.notifier()由于模块元表把__call指向notifynotifier.lua直接调用Snacks.notifier(msg)等价于发送一条 info 通知返回值是通知的id。Snacks.notifier.notify(msg, level, opts)---param msg string ---param level? snacks.notifier.level|number ---param opts? snacks.notifier.Notif.opts Snacks.notifier.notify(msg, level, opts)向队列添加一条通知返回其id。level可以是trace/debug/info/warn/error字符串或vim.log.levels数值。Snacks.notifier.get_history(opts)---param opts? snacks.notifier.history Snacks.notifier.get_history(opts)返回满足条件的历史通知数组。源码实现notifier.lua会先应用过滤再按opts.sort排序最后按reverse决定顺序。Snacks.notifier.hide(id)---param id? number|string Snacks.notifier.hide(id)隐藏指定id的通知不传参数则隐藏队列中全部通知。实现notifier.lua会关闭对应窗口并记录hidden时间戳。Snacks.notifier.show_history(opts)---param opts? snacks.notifier.history Snacks.notifier.show_history(opts)在notification_history样式的浮动窗口中展示历史记录默认reverse true最新在前。若历史窗口已打开则直接关闭notifier.lua。配套辅助工具Snacks.notifynotifier 还配套了便捷工具模块 lua/snacks/notify.lua提供Snacks.notify(msg, opts)及其warn/info/error等语义化封装支持once去重内部走vim.notify_once并自动处理vim.in_fast_event()下的调度包装适合在插件回调等场景安全调用。实战示例替换已有通知固定id即可原地替换通知也可直接用notify的返回值作为id-- to replace an existing notification just use the same id. -- you can also use the return value of the notify function as id. for i 1, 10 do vim.defer_fn(function() vim.notify(Hello .. i, info, { id test }) end, i * 500) end从源码看notifier.lua当opts.id已存在于队列时新通知会继承旧通知的added、窗口句柄和布局仅更新updated时间戳并标记dirty从而在原窗口原位刷新而不是新开窗口——这是进度类通知不闪烁的关键。此外还兼容 nvim-notify 风格的replace字段notifier.lua迁移成本很低。简单的 LSP 进度展示监听LspProgress事件用单条通知 动态opts回调实现旋转动画vim.api.nvim_create_autocmd(LspProgress, { ---param ev {data: {client_id: integer, params: lsp.ProgressParams}} callback function(ev) local spinner { ⠋, ⠙, ⠹, ⠸, ⠼, ⠴, ⠦, ⠧, ⠇, ⠏ } 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, })这里opts回调在每次渲染前被调用见 notifier.lua 的N:render通过高精度时钟vim.uv.hrtime()计算帧索引从而让图标持续旋转任务结束时替换为完成图标。高级的 LSP 进度展示同一客户端可能并发多个进度任务如同时进行 lint 和 build这个示例按ProgressToken分组管理并将所有任务消息聚合到一条通知中---type tablenumber, {token:lsp.ProgressToken, msg:string, done:boolean}[] local progress vim.defaulttable() vim.api.nvim_create_autocmd(LspProgress, { ---param ev {data: {client_id: integer, params: lsp.ProgressParams}} callback function(ev) local client vim.lsp.get_client_by_id(ev.data.client_id) local value ev.data.params.value --[[as {percentage?: number, title?: string, message?: string, kind: begin | report | end}]] if not client or type(value) ~ table then return end local p progress[client.id] for i 1, #p 1 do if i #p 1 or p[i].token ev.data.params.token then p[i] { token ev.data.params.token, msg ([%3d%%] %s%s):format( value.kind end and 100 or value.percentage or 100, value.title or , value.message and ( **%s**):format(value.message) or ), done value.kind end, } break end end local msg {} ---type string[] progress[client.id] vim.tbl_filter(function(v) return table.insert(msg, v.msg) or not v.done end, p) local spinner { ⠋, ⠙, ⠹, ⠸, ⠼, ⠴, ⠦, ⠧, ⠇, ⠏ } vim.notify(table.concat(msg, \n), info, { id lsp_progress, title client.name, opts function(notif) notif.icon #progress[client.id] 0 and  or spinner[math.floor(vim.uv.hrtime() / (1e6 * 80)) % #spinner 1] end, }) end, })该示例演示了三种进阶用法token 去重同 token 只更新不新增、done 状态剔除vim.tbl_filter移除已完成任务、多行消息聚合table.concat(msg, \n)将多个任务叠加显示配合 Markdown 加粗展示message。当所有任务完成时队列为空图标自动切换为。源码级原理解析通知生命周期add → update → layout整个渲染管线由三阶段驱动notifier.luafunction N:process() self:update() self:layout() endN:addnotifier.lua接收选项规范化级别与超时分配自增id写入history通过级别阈值和filter检查后放入queue。若当前处于阻塞模式命令输入中等见N:is_blocking则立即同步处理。N:updatenotifier.lua清理队列——判断通知是否需要保留的条件包括尚未显示、无超时、位于当前窗口/缓冲区、单条keep回调、全局keep回调、以及未超时。任一满足则保留否则调用hide。N:layoutnotifier.lua按排序结果逐个计算窗口位置。new_layout会根据top_down、margin以及 tabline/statusline 自动占据的屏幕行构造一个空闲行表为每条通知寻找可用空间空间不足时隐藏多余通知窗口尺寸变化时通过VimResized自动重排。渲染刷新机制队列处理由uv.new_timer驱动notifier.lua以opts.refresh默认 50ms为周期节流每次回调通过vim.schedule调度到主循环执行并避开搜索状态N.in_search检查命令行是否处于/或?避免打断搜索高亮遇到 E565 等错误会清空队列并打印错误而非阻塞编辑器。四种内置渲染实现N.stylesnotifier.lua中实现了四种渲染函数compact将icon title作为居中的边框标题正文按行写入缓冲区minimal去掉边框图标通过右对齐的virt_text虚拟文本呈现history为历史窗口设计逐条追加记录每行前缀由日期时间、图标、级别、标题四段虚拟文本组成fancy模仿 nvim-notify图标标题在首行左侧、时间戳右对齐第二行绘制━分隔线。自定义风格只需实现snacks.notifier.render签名并把它赋给style字段即可N:get_render会优先使用函数类型的 style见 notifier.lua。高亮体系级别图标与文字颜色由N:init中的链接表建立notifier.luaSnacksNotifierIcon{Level}链接到DiagnosticSign{Level}SnacksNotifierTitle/Border/Footer{Level}链接到Diagnostic{Level}trace/debug 则链接到NonText。你可以在 colorscheme 中直接定制这些SnacksNotifier*高亮组或用opts.hl逐条覆盖。健康检查与常见问题notifier 提供:checkhealth snacks支持M.healthnotifier.lua会发送一条checkhealth标记的通知并短暂等待据此判断 notifier 是否接管了vim.notify并正常渲染。使用中需要注意图标依赖 Nerd Font默认图标表使用 Nerd Font 字形等字体不支持时会显示为空白或方框可修改opts.icons替换level阈值影响可见性低于opts.level的通知不展示但仍进历史若希望所有通知可见保持默认TRACE即可接管时机notifier 只接管vim.notify若其他插件在Snacks.setup()之前缓存了vim.notify的引用那些调用可能绕过 notifier属正常现象。通过本文的配置与源码对照你已具备完全掌控 snacks.nvim 通知体系的能力从全局接管、参数调优到基于id的去重进度条、自定义渲染风格再到历史记录的查询与展示均可按需组合使用。【免费下载链接】snacks.nvim A collection of QoL plugins for Neovim项目地址: https://gitcode.com/GitHub_Trending/sn/snacks.nvim创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表