From 12945a4cdf9278d9c26fd35e328f135142b0ab89 Mon Sep 17 00:00:00 2001 From: Jonathan Apodaca Date: Thu, 2 Oct 2025 19:53:04 -0600 Subject: [PATCH] (examples/notify.lua) eliminate dependency on non-existent class --- examples/notify.lua | 155 +++++++++++++++++++++++++++++--------------- 1 file changed, 101 insertions(+), 54 deletions(-) diff --git a/examples/notify.lua b/examples/notify.lua index 869404f..c74461b 100644 --- a/examples/notify.lua +++ b/examples/notify.lua @@ -1,8 +1,7 @@ -local Buffer = require 'u.buffer' +local Renderer = require('u.renderer').Renderer local TreeBuilder = require('u.renderer').TreeBuilder local tracker = require 'u.tracker' local utils = require 'u.utils' -local Window = require 'my.window' local TIMEOUT = 4000 local ICONS = { @@ -14,15 +13,25 @@ local ICONS = { } local DEFAULT_ICON = { text = '', group = 'DiagnosticSignOk' } ---- @alias Notification { +local S_EDITOR_DIMENSIONS = + tracker.create_signal(utils.get_editor_dimensions(), 's:editor_dimensions') +vim.api.nvim_create_autocmd('VimResized', { + callback = function() + local new_dim = utils.get_editor_dimensions() + S_EDITOR_DIMENSIONS:set(new_dim) + end, +}) + +--- @alias u.example.Notification { --- kind: number; --- id: number; --- text: string; +--- timer: uv.uv_timer_t; --- } local M = {} ---- @type Window | nil +--- @type { win: integer, buf: integer, renderer: u.Renderer } | nil local notifs_w local s_notifications_raw = tracker.create_signal {} @@ -30,44 +39,49 @@ local s_notifications = s_notifications_raw:debounce(50) -- Render effect: tracker.create_effect(function() - --- @type Notification[] + --- @type u.example.Notification[] local notifs = s_notifications:get() + --- @type { width: integer, height: integer } + local editor_size = S_EDITOR_DIMENSIONS:get() if #notifs == 0 then if notifs_w then - notifs_w:close(true) + if vim.api.nvim_win_is_valid(notifs_w.win) then vim.api.nvim_win_close(notifs_w.win, true) end notifs_w = nil end return end + local avail_width = editor_size.width + local float_width = 40 + local float_height = math.min(#notifs, editor_size.height - 3) + local win_config = { + relative = 'editor', + anchor = 'NE', + row = 0, + col = avail_width, + width = float_width, + height = float_height, + border = 'single', + focusable = false, + zindex = 900, + } vim.schedule(function() - local editor_size = utils.get_editor_dimensions() - local avail_width = editor_size.width - local float_width = 40 - local win_config = { - relative = 'editor', - anchor = 'NE', - row = 0, - col = avail_width, - width = float_width, - height = math.min(#notifs, editor_size.height - 3), - border = 'single', - focusable = false, - } if not notifs_w or not vim.api.nvim_win_is_valid(notifs_w.win) then - notifs_w = Window.new(Buffer.create(false, true), win_config) - vim.wo[notifs_w.win].cursorline = false - vim.wo[notifs_w.win].list = false - vim.wo[notifs_w.win].listchars = '' - vim.wo[notifs_w.win].number = false - vim.wo[notifs_w.win].relativenumber = false - vim.wo[notifs_w.win].wrap = false + local b = vim.api.nvim_create_buf(false, true) + local w = vim.api.nvim_open_win(b, false, win_config) + vim.wo[w].cursorline = false + vim.wo[w].list = false + vim.wo[w].listchars = '' + vim.wo[w].number = false + vim.wo[w].relativenumber = false + vim.wo[w].wrap = false + notifs_w = { win = w, buf = b, renderer = Renderer.new(b) } else - notifs_w:set_config(win_config) + vim.api.nvim_win_set_config(notifs_w.win, win_config) end - notifs_w:render(TreeBuilder.new() + notifs_w.renderer:render(TreeBuilder.new() :nest(function(tb) for idx, notif in ipairs(notifs) do if idx > 1 then tb:put '\n' end @@ -79,48 +93,81 @@ tracker.create_effect(function() end) :tree()) vim.api.nvim_win_call(notifs_w.win, function() - -- scroll to bottom: - vim.cmd.normal 'G' - -- scroll all the way to the left: - vim.cmd.normal '9999zh' + vim.fn.winrestview { + -- scroll all the way left: + leftcol = 0, + -- set the bottom line to be at the bottom of the window: + topline = vim.api.nvim_buf_line_count(notifs_w.buf) - win_config.height + 1, + } end) end) end) +--- @param id number +local function _delete_notif(id) + --- @param notifs u.example.Notification[] + s_notifications_raw:schedule_update(function(notifs) + for i, notif in ipairs(notifs) do + if notif.id == id then + notif.timer:stop() + notif.timer:close() + table.remove(notifs, i) + break + end + end + return notifs + end) +end + local _orig_notify --- @param msg string --- @param level integer|nil ---- @param opts table|nil -local function my_notify(msg, level, opts) - vim.schedule(function() _orig_notify(msg, level, opts) end) +--- @param opts? { id: number } +function M.notify(msg, level, opts) if level == nil then level = vim.log.levels.INFO end - if level < vim.log.levels.INFO then return end - local id = math.random(math.huge) + opts = opts or {} + local id = opts.id or math.random(999999999) - --- @param notifs Notification[] - s_notifications_raw:schedule_update(function(notifs) - table.insert(notifs, { kind = level, id = id, text = msg }) - return notifs - end) + --- @type u.example.Notification? + local notif = vim.iter(s_notifications_raw:get()):find(function(n) return n.id == id end) + if not notif then + -- Create a new notification (maybe): + if vim.trim(msg) == '' then return id end + if level < vim.log.levels.INFO then return id end - vim.defer_fn(function() - --- @param notifs Notification[] + local timer = assert((vim.uv or vim.loop).new_timer(), 'could not create timer') + timer:start(TIMEOUT, 0, function() _delete_notif(id) end) + notif = { + id = id, + kind = level, + text = msg, + timer = timer, + } + --- @param notifs u.example.Notification[] s_notifications_raw:schedule_update(function(notifs) - for i, notif in ipairs(notifs) do - if notif.id == id then - table.remove(notifs, i) - break - end - end + table.insert(notifs, notif) return notifs end) - end, TIMEOUT) + else + -- Update an existing notification: + s_notifications_raw:schedule_update(function(notifs) + -- We already have a copy-by-reference of the notif we want to modify: + notif.timer:stop() + notif.text = msg + notif.kind = level + notif.timer:start(TIMEOUT, 0, function() _delete_notif(id) end) + + return notifs + end) + end + + return id end local _once_msgs = {} -local function my_notify_once(msg, level, opts) +function M.notify_once(msg, level, opts) if vim.tbl_contains(_once_msgs, msg) then return false end table.insert(_once_msgs, msg) vim.notify(msg, level, opts) @@ -130,8 +177,8 @@ end function M.setup() if _orig_notify == nil then _orig_notify = vim.notify end - vim.notify = my_notify - vim.notify_once = my_notify_once + vim.notify = M.notify + vim.notify_once = M.notify_once end return M