feat: improved async poller for events

now, when :abort() is available, it properly cancels promises so that we
dont get dangling references
This commit is contained in:
əlemi 2024-09-26 05:40:58 +02:00
parent e8005fbeab
commit 5ae49d3c49
Signed by: alemi
GPG key ID: A4895B84D311642C
2 changed files with 59 additions and 13 deletions

View file

@ -29,18 +29,44 @@ local function color(name)
} }
end end
---@class AsyncPoller
---@field promise WorkspaceEventPromise | nil
---@field timer luv.Timer
---@field generator fun(): WorkspaceEventPromise
---@field callback fun(e: WorkspaceEvent)
---@field stop fun(self: AsyncPoller)
---@return AsyncPoller
local function async_poller(generator, callback) local function async_poller(generator, callback)
local promise = nil ---@type AsyncPoller
local timer = vim.loop.new_timer() local poller = {
timer:start(500, 500, function() promise = nil,
if promise == nil then promise = generator() end generator = generator,
if promise.ready then callback = callback,
local res = promise:await() timer = vim.uv.new_timer(),
vim.schedule(function() callback(res) end) stop = function (this)
promise = nil print("stopping async poller")
if this.promise ~= nil then
-- TODO the :abort() change still hasnt been merged, so check for its presence!
if this.promise.abort ~= nil then
this.promise:abort()
end
end
this.timer:stop()
this.timer:close()
end
}
poller.timer:start(500, 500, function()
print("ticking poller")
if poller.promise == nil then poller.promise = poller.generator() end
if poller.promise.ready then
print("spawning callback")
local res = poller.promise:await()
vim.schedule(function() poller.callback(res) end)
poller.promise = nil
end end
end) end)
return poller
end end
---@param first integer ---@param first integer

View file

@ -150,6 +150,8 @@ local function register_cursor_handler(controller)
controller:callback(function (_controller) async:send() end) controller:callback(function (_controller) async:send() end)
end end
local events_poller = nil
---@param workspace string workspace name to join ---@param workspace string workspace name to join
---join a workspace and register event handlers ---join a workspace and register event handlers
local function join(workspace) local function join(workspace)
@ -169,8 +171,14 @@ local function join(workspace)
} }
end end
require('codemp.window').update() require('codemp.window').update()
utils.poller( local ws_name = ws.name
function() return ws:event() end, events_poller = utils.poller(
function()
if CODEMP.client == nil then return nil end
local wspace = CODEMP.client:get_workspace(ws_name)
if wspace == nil then return nil end
return wspace:event()
end,
function(event) function(event)
if event.type == "leave" then if event.type == "leave" then
if buffers.users[event.value] ~= nil then if buffers.users[event.value] ~= nil then
@ -198,9 +206,21 @@ local function join(workspace)
end end
local function leave() local function leave()
CODEMP.client:leave_workspace(CODEMP.workspace.name) local name = CODEMP.workspace.name
print(" -- left workspace")
CODEMP.workspace = nil CODEMP.workspace = nil
if events_poller ~= nil then
events_poller:stop()
events_poller = nil
end
if not CODEMP.client:leave_workspace(name) then
collectgarbage("collect")
-- TODO codemp disconnects when all references to its objects are dropped. since it
-- hands out Arc<> of things, all references still not garbage collected in Lua will
-- prevent it from disconnecting. while running a full cycle may be a bit slow, this
-- only happens when manually requested, and it's not like the extra garbage collection
-- is an effort for nothing... still it would be more elegant to not need this!!
end
print(" -- left workspace " .. name)
require('codemp.window').update() require('codemp.window').update()
end end