ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

mini.notify 深度解析:为 Neovim 打造轻量统一的通知窗口、LSP 进度与 vim.notify 集成

2026/9/16 21:05:13 拓冰建站 浏览量
mini.notify 深度解析:为 Neovim 打造轻量统一的通知窗口、LSP 进度与 vim.notify 集成 mini.notify 深度解析为 Neovim 打造轻量统一的通知窗口、LSP 进度与 vim.notify 集成【免费下载链接】mini.nvimLibrary of 45 independent Lua modules improving Neovim experience with minimal effort项目地址: https://gitcode.com/GitHub_Trending/mi/mini.nvimmini.notify 是 mini.nvim 库中负责“显示通知”的独立模块它把一条或多条带高亮的通知集中渲染在单个浮动窗口中并提供增删改查、历史记录、LSP 进度自动展示以及自定义vim.notify()实现等一系列能力。读完本文你将掌握 mini.notify 的完整配置模型内容格式化/排序、LSP 进度、窗口参数学会通过MiniNotify全局 API 管理通知生命周期并能结合源码理解其浮动窗口渲染与调度机制从而在自己的配置中真正用起来。模块定位与核心功能mini.notify 是 mini.nvim45 个独立 Lua 模块组成的 Neovim 增强库中的一员对应仓库内的源码位于 lua/mini/notify.lua完整帮助文档位于 doc/mini-notify.txt。根据其文档核心功能可以概括为五点单窗口聚合展示在同一个浮动窗口中显示一条或多条带高亮的通知而不是像默认vim.notify()那样把消息挤到消息区message area里。通知全生命周期管理支持添加add、更新update、移除remove、清空clear。自定义vim.notify()实现调用setup()后会自动替换 Neovim 内置的vim.notify也可通过MiniNotify.make_notify()按需定制。LSP 进度自动展示自动监听 LSP$/progress消息把编译、格式化等后台任务的进度渲染成通知。历史记录所有通知都会被记录可通过MiniNotify.get_all()访问用MiniNotify.show_history()在独立缓冲区中回看。与同类插件相比mini.notify 的设计哲学是“克制”官方文档在与 j-hui/fidget.nvim、rcarriga/nvim-notify 的对比中明确说明——这些插件提供了更多的配置项和视觉效果而本模块“by design”不追求这些专注把通知这件事做好、做轻。安装与启用mini.notify 既可作为 mini.nvim 库的一部分安装推荐也可以作为独立插件安装。仓库提供main与stable两个分支main是默认推荐分支包含最新开发版本自上一个稳定版以来的改动应视为处于 beta 测试阶段stable仅在发布时更新代码经过main分支的公开测试。以下列出常见插件管理器下的安装方式任选其一使用 mini.depsNeovim 0.12 之前-- 主分支最新开发版 add(nvim-mini/mini.notify) -- stable 分支 add({ source nvim-mini/mini.notify, checkout stable })使用 lazy.nvim-- 主分支最新开发版 { nvim-mini/mini.notify, version false }, -- stable 分支 { nvim-mini/mini.notify, version * },使用 vim.packNeovim 0.12 及更新版本-- 主分支提供 mini.notify 独立仓库地址即可 vim.pack.add({ mini.notify 独立仓库地址 }) -- stable 分支 vim.pack.add({ { src mini.notify 独立仓库地址, version stable }, })无论用哪种方式关键一步是调用require(mini.notify).setup()启用功能参数可省略默认使用内置配置require(mini.notify).setup() -- 使用默认配置 -- 或 require(mini.notify).setup({}) -- 传入自定义 config 表setup()会做四件事见 lua/mini/notify.lua导出全局 Lua 表MiniNotify此后可直接用:lua MiniNotify.*调用、校验并应用配置、创建相关自动命令与默认高亮组、最后把vim.notify替换为MiniNotify.make_notify()的实现。此外setup()还会清空历史记录若想保留配置但强制清空历史可调用MiniNotify.setup(MiniNotify.config)。安装时如遇 Windows 下文件路径过长如Filename too long报错可执行git config --system core.longpaths true后重装或将插件安装到更短的路径下。默认配置速览与参数总表mini.notify 的全部配置集中在三组键中content内容管理、lsp_progressLSP 进度、window窗口选项。默认配置如下无需复制进setup()未传入时自动使用{ -- Content management content { -- Function which formats the notification message -- By default prepends message with notification time format nil, -- Function which orders notification array from most to least important -- By default orders first by level and then by update timestamp sort nil, }, -- Notifications about LSP progress lsp_progress { -- Whether to enable showing enable true, -- Notification level level INFO, -- Duration (in ms) of how long last message should be shown duration_last 1000, }, -- Window options window { -- Floating window config config {}, -- Maximum window width as share (between 0 and 1) of available columns max_width_share 0.382, -- Value of winblend option winblend 25, }, }各配置项的作用一览配置项类型默认值说明content.formatfunction或nilnil即MiniNotify.default_format接收单个通知对象返回要直接展示的字符串content.sortfunction或nilnil即MiniNotify.default_sort接收通知对象数组返回排序/过滤后的数组lsp_progress.enablebooleantrue是否把 LSP 进度渲染为通知lsp_progress.levelstringINFO进度通知使用的级别vim.log.levels的键lsp_progress.duration_lastnumber1000最后一条进度消息在屏幕上停留的毫秒数window.configtable或function{}浮动窗口配置结构同nvim_open_win()可为返回该表的函数window.max_width_sharenumber0.382窗口最大宽度占可用列数的比例0 到 1 之间window.winblendnumber25通知窗口的winblend混合/透明取值setup()会对所有配置项做严格类型校验见 lua/mini/notify.lua例如window.config必须是 table 或可调用对象lsp_progress.enable必须是 boolean非法值会直接报错。这一点在 tests/test_notify.lua 中有完整的参数化测试覆盖。另外mini.notify 支持缓冲区局部配置在vim.b.mininotify_config中放入与MiniNotify.config同结构的表即可覆盖全局配置详见MiniNotify.config文档中关于 buffer-local config 的说明。内容管理content.format 与 content.sortcontent.format与content.sort共同决定了通知“长什么样、按什么顺序出现”。自定义格式化content.formatformat接收一个通知对象结构见下文“通知数据模型”一节返回直接用于展示的字符串。默认实现MiniNotify.default_format()会为消息拼接可读的更新时间与分隔符源码见 lua/mini/notify.luaMiniNotify.default_format function(notif) local time vim.fn.strftime(%H:%M:%S, math.floor(notif.ts_update)) return string.format(%s │ %s, time, notif.msg) end也就是说默认展示形如12:34:56 │ 消息内容的格式——这在 tests/test_notify.lua 中被精确断言当ts_update对应 UTC 12:34:56 时输出12:34:56 │ Hello。自定义排序content.sortsort接收活动通知对象数组返回排序后的数组可用于调整同时显示的多条通知的优先级甚至做过滤。默认实现MiniNotify.default_sort()先按level排序ERRORWARNINFODEBUGTRACEOFF级别越高越重要级别相同再按更新时间越新越重要。源码中通过H.level_priority映射实现这一优先级见 lua/mini/notify.lua 与 lua/mini/notify.lua。注意sort的输入是应用format之前的通知对象同时default_sort不会修改输入数组tests/test_notify.lua 对此有专门测试。组合示例以下示例来自官方文档展示了同时定制两者对 LSP 进度通知直接使用原始消息不做时间前缀并按更新时间倒序展示最新的在最上面require(mini.notify).setup({ content { -- Use notification message as is for LSP progress format function(notif) if notif.data.source lsp_progress then return notif.msg end return MiniNotify.default_format(notif) end, -- Show more recent notifications first sort function(notif_arr) table.sort( notif_arr, function(a, b) return a.ts_update b.ts_update end ) return notif_arr end, }, })从源码看MiniNotify.refresh()中渲染前会先应用sort再应用format且两者均可被校验排序输出必须是合法通知数组、格式化输出必须是字符串否则报错见 lua/mini/notify.lua。LSP 进度通知lsp_progressmini.notify 会自动监听 LSP 的$/progress消息把“编译中 0/1 (0%)”之类的后台进度渲染成通知。它的实现方式是每个进度使用一条持续更新的通知而不是反复新增删除源码见 lua/mini/notify.lua 的H.lsp_progress_handler。相关配置lsp_progress.enable默认true是否展示 LSP 进度。注意它必须在setup()调用期间为true才能在当前会话中注册 handler运行中可通过修改该字段开关。测试 tests/test_notify.lua 验证了false/true切换时窗口的消失与重现。lsp_progress.level默认INFO进度通知使用的级别会传入MiniNotify.add()。lsp_progress.duration_last默认1000进度结束后最后一条消息继续展示的毫秒数用于减少“闪一下就没”的突兀感。实现细节上有几点值得注意均有源码与测试支撑尊重既有 handlersetup()通过vim.schedule()延迟注册降低启动时加载vim.lsp的开销并把原有的$/progresshandler 缓存为$/progress before mini.notify每次新消息先调用原 handler、再处理自己的逻辑因此不会破坏LspProgress事件等其他监听方tests/test_notify.lua 验证了原 handler 被持续调用。数据字段约定所有 LSP 进度通知都会在data中写入四个字段——source lsp_progress、client_name客户端名、context最近一次请求上下文ctx、response最近一次响应result。消息拼装默认消息格式为客户端名: 标题 消息 (百分比%)百分比在end阶段强制记为 100title只在begin阶段缓存因为 LSP 规范中后续 report 可能不再携带。进度更新复用同一条通知通过lsp_progress_id由 buffer 号、客户端名、token 拼接识别同一进度用MiniNotify.update()原地更新消息保证历史记录里每个进度只占一条tests/test_notify.lua 断言历史记录长度恒为 1。错误兜底LSP 返回err时会以ERROR级别调用vim.notify()展示错误详情。通知窗口windowwindow配置组控制浮动窗口的外观与位置。window.config位置与样式的完全控制window.config可以是一个与nvim_open_win()结构相同的表也可以是一个可调用对象函数函数会收到“已包含通知内容的缓冲区 id”作为参数并返回窗口配置表。默认值将通知展示在右上角具体为width适配缓冲区内容但上限为max_width_share比例的columns想突破宽度上限可在window.config函数里自行计算尺寸。height在默认宽度下适配内容并开启wrap换行。anchor/col/rowNE、columns、0 或 1取决于是否存在 tabline。bordersingle。zindex999尽量置顶。title Notifications 。focusablefalse避免干扰窗口导航。上述默认值在源码H.window_compute_config中构建并会进一步做安全修正根据 border 是否存在减去 2 的偏移、限制在max_height/max_width内、标题超宽时以…截断见 lua/mini/notify.lua。官方文档给出了把通知挪到右下角的示例local win_config function() local has_statusline vim.o.laststatus 0 local pad vim.o.cmdheight (has_statusline and 1 or 0) return { anchor SE, col vim.o.columns, row vim.o.lines - pad } end require(mini.notify).setup({ window { config win_config } })max_width_share 与 winblendwindow.max_width_share默认0.382窗口最大宽度占可用列数的比例应为 0不含到 1 之间的数。源码在计算宽度时会做math.min(math.max(share, 0), 1)的钳制lua/mini/notify.lua测试 tests/test_notify.lua 验证了 0.75、1、越界值 10 和 0 的行为。window.winblend默认25通知窗口的winblend值实现半透明效果窗口开启时会应用winblend并同时设置winhighlight NormalFloat:MiniNotifyNormal,FloatBorder:MiniNotifyBorder,FloatTitle:MiniNotifyTitlelua/mini/notify.lua。窗口行为上还有两个经过测试保证的细节通知窗口只出现在当前 tabpage切换标签页后会在对应 tabpage 重新展示tests/test_notify.luaVimResized与TabEnter事件会触发MiniNotify.refresh()重新计算尺寸lua/mini/notify.lua窗口能随编辑器尺寸变化完整更新。通知数据模型Notification specificationmini.notify 中每条通知都是一个 Lua table包含以下字段字段类型说明msgstring通知消息多行用\n分隔levelstring通知级别为vim.log.levels的键如ERROR、WARN、INFO等hl_groupstring展示该通知时使用的高亮组datatable附加数据如source等用于携带上下文ts_addnumber通知被添加的时间戳ts_updatenumber最近一次更新的时间戳ts_removenumber|nil通知被移除的时间戳nil表示从未移除即仍处于“活动active”状态两个要点时间戳来自vim.loop.gettimeofday()与strftime()兼容且带小数部分源码 lua/mini/notify.luats_remove是否为nil直接决定了通知是否“active”这也是MiniNotify.get_all()筛选活动通知的依据。通知管理 APIadd / update / remove / clear / refresh所有管理函数都挂在全局MiniNotify表上可直接用:lua MiniNotify.*调用。MiniNotify.add(msg, level, hl_group, data)添加一条通知并立即展示返回通知标识符idlocal id MiniNotify.add(Hello, WARN, Comment) vim.defer_fn(function() MiniNotify.remove(id) end, 1000)参数默认值level INFO、hl_group MiniNotifyNormal、data {}。源码中值得注意的细节是新通知对象被同一个 table 同时写入H.history和H.activelua/mini/notify.lua后续更新均原地修改从而保证历史与活动状态天然同步。MiniNotify.update(id, new)原地更新一条活动通知的内容MiniNotify.update(id, { msg World, level WARN, hl_group String, data { a 11 } })new的键应为通知规范中的非时间戳字段其中data为整体替换不做深合并若要局部修改需先用MiniNotify.get()取出再配合vim.tbl_deep_extend()处理。ts_update会自动刷新为当前时间而ts_add保持不变tests/test_notify.lua。MiniNotify.remove(id) 与 MiniNotify.clear()remove(id)把通知从活动集合中移除设置ts_remove并隐藏对不存在的 id 静默无操作。clear()一次性移除所有活动通知并关闭窗口若已显示。两者都会触发MiniNotify.refresh()重绘。MiniNotify.refresh()刷新窗口的核心流程源码 lua/mini/notify.lua收集活动通知数组应用content.sort输出为空则关闭窗口对每条通知应用content.format并更新消息将内容写入专用缓冲区并展示在浮动窗口中最后执行redraw。refresh()对运行时环境做了完善的兜底处于 fast event 中时用vim.schedule()延迟执行处于 textlock编辑器锁定状态时注册一次性SafeState自动命令等待解锁后再刷新tests/test_notify.lua 用:map-expression场景验证了这一点检测到手动删除缓冲区/窗口时会自动重建。历史记录get / get_all / show_history所有被添加过的通知都会进入历史即使已被移除。相关接口MiniNotify.get(id)按 id 返回单条通知对象的深拷贝。MiniNotify.get_all()返回历史通知的映射表键为 id值为通知对象同样返回深拷贝。示例——筛选活动通知-- Get active notifications vim.tbl_filter( function(notif) return notif.ts_remove nil end, MiniNotify.get_all() )MiniNotify.show_history()在可复用的 scratch 缓冲区中展示全部历史。内容按最近更新时间从旧到新排序消息同样经过content.format处理缓冲区filetype为mininotify-history多次调用复用同一缓冲区而非新建lua/mini/notify.lua测试见 tests/test_notify.lua。深拷贝返回的设计tests/test_notify.lua、tests/test_notify.lua保证外部修改不会污染内部历史。与 vim.notify 集成make_notifysetup()会把vim.notify自动替换为MiniNotify.make_notify()的产物使所有调用vim.notify(msg, level)的插件错误报告、语言服务器消息等统一走浮动窗口。整体思路是用vim.schedule_wrap()包装在安全的时机尽快展示并在可配置的时长后自动移除源码 lua/mini/notify.lua。所有经由此路径的通知都会在data中写入source vim.notify。make_notify(opts)接受以vim.log.levels级别名为键的配置表每个级别可设置duration展示毫秒数0 或负数表示完全不展示和hl_group高亮组默认值如下{ ERROR { duration 5000, hl_group DiagnosticError }, WARN { duration 5000, hl_group DiagnosticWarn }, INFO { duration 5000, hl_group DiagnosticInfo }, DEBUG { duration 0, hl_group DiagnosticHint }, TRACE { duration 0, hl_group DiagnosticOk }, OFF { duration 0, hl_group MiniNotifyNormal }, }可以看到默认只有 ERROR/WARN/INFO 会展示 5 秒DEBUG/TRACE/OFF 默认直接丢弃。定制示例——让错误展示 10 秒require(mini.notify).setup() vim.notify MiniNotify.make_notify({ ERROR { duration 10000 } })如果想保留原始vim.notify例如交给其它插件处理可手动保存恢复local notify_orig vim.notify require(mini.notify).setup() vim.notify notify_orig测试对这套机制验证得很细六个级别按各自 duration 依次消失tests/test_notify.lua、未指定级别时默认 INFO、duration为 0/-100 时完全不展示tests/test_notify.lua、在 fast event 与补全弹窗激活时也能正常工作而不污染消息区。高亮组与外观定制mini.notify 定义了四个默认高亮组源码 lua/mini/notify.lua高亮组默认链接用途MiniNotifyBorderFloatBorder窗口边框MiniNotifyLspProgressMiniNotifyNormalLSP 进度通知MiniNotifyNormalNormalFloat基础前景/背景MiniNotifyTitleFloatTitle窗口标题可通过nvim_set_hl()直接覆盖例如vim.api.nvim_set_hl(0, MiniNotifyBorder, { fg #ff8800 })另外ColorScheme事件会触发高亮组重建lua/mini/notify.lua切换配色方案后样式不会丢失。需要提醒的是make_notify()覆盖的vim.notify按级别使用自己的高亮组即上文默认表中的DiagnosticError等与上面四个组相互独立。禁用与运行时开关想临时/永久关闭通知展示可将vim.g.mininotify_disable全局或vim.b.mininotify_disable缓冲区局部设为true。由于禁用场景高度依赖个人意图比如某些文件类型不弹通知、某些模式下静默官方有意把“何时禁用”的具体规则留给用户自定义可参考 mini.nvim 库文档中的 disabling recipes见 doc/mini-nvim.txt。refresh()中会检查该开关禁用时直接关闭窗口lua/mini/notify.lua相关行为在 tests/test_notify.lua 有截图级验证。快速上手总结最小可用配置只需一行require(mini.notify).setup()即可获得右上角浮动通知窗口、自动接管vim.notify、LSP 进度可视化、历史可回看。进阶路径建议先用content.format/content.sort定制消息外观与排序再用window.config调整位置如右下角与样式最后通过MiniNotify.make_notify()微调各级别的展示时长与高亮。所有接口的完整签名与行为均可查阅 doc/mini-notify.txt源码实现见 lua/mini/notify.lua而 tests/test_notify.lua 中 1200 行的测试覆盖单元与截图集成测试可作为理解边界行为的最佳参考在仓库根目录执行make test_notify参见 Makefile即可运行该模块的测试套件。【免费下载链接】mini.nvimLibrary of 45 independent Lua modules improving Neovim experience with minimal effort项目地址: https://gitcode.com/GitHub_Trending/mi/mini.nvim创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考