feat: implemented callback api

it works well!!?! when it doesnt crash... but its also pretty clean
maybe its our fault? could be worth investigating more lua ffi
This commit is contained in:
əlemi 2024-08-16 03:49:24 +02:00
parent 36f997b42e
commit 3e9647ab39
Signed by: alemi
GPG key ID: A4895B84D311642C
4 changed files with 63 additions and 123 deletions

View file

@ -1,54 +0,0 @@
local state = require('codemp.state')
local function register_controller_handler(target, controller, handler, delay)
local async = vim.loop.new_async(function()
while true do
local success, event = pcall(controller.try_recv, controller)
if success then
if event == nil then break end
vim.schedule(function()
local ok, res = pcall(handler, event)
if not ok then
print(" !! error running callback handler: " .. res)
end
end)
else
print("error receiving: " .. tostring(event))
break
end
end
end)
-- TODO controller can't be passed to the uvloop new_thread: when sent to the new
-- Lua runtime it "loses" its methods defined with mlua, making the userdata object
-- completely useless. We can circumvent this by requiring codemp again in the new
-- thread and requesting a new reference to the same controller from che global instance
-- NOTE variables prefixed with underscore live in another Lua runtime
vim.loop.new_thread({}, function(_async, _target, _delay, _ws, _id)
local _codemp = require("codemp.loader").load() -- TODO maybe make a native.load() idk
local _client = _codemp.get_client(_id)
if _client == nil then error("cant find client " .. _id .. " from thread") end
local _workspace = _client:get_workspace(_ws)
if _workspace == nil then error("cant find workspace " .. _ws .. " from thread") end
local _controller = _target ~= nil and _workspace:get_buffer(_target) or _workspace.cursor
if _controller == nil then error("cant find controller " .. _target .. " from thread") end
if _controller.poll == nil then error("controller has nil poll field???") end
while true do
local success, _ = pcall(_controller.poll, _controller)
if success then
_async:send()
if _delay ~= nil then vim.loop.sleep(_delay) end
else
local my_name = "cursor"
if _target ~= nil then
my_name = "buffer(" .. _target .. ")"
end
print(" -- stopping " .. my_name .. " controller poller")
break
end
end
end, async, target, delay, state.workspace, state.client.id)
end
return {
handler = register_controller_handler,
}

View file

@ -1,5 +1,4 @@
local utils = require('codemp.utils') local utils = require('codemp.utils')
local async = require('codemp.async')
local state = require('codemp.state') local state = require('codemp.state')
local id_buffer_map = {} local id_buffer_map = {}
@ -63,21 +62,32 @@ local function attach(name, current, content)
end, end,
}) })
-- hook clientbound callbacks local async = vim.loop.new_async(vim.schedule_wrap(function ()
async.handler(name, controller, function(event) while true do
ticks[buffer] = vim.api.nvim_buf_get_changedtick(buffer) local success, event = pcall(controller.try_recv, controller)
-- print(" ~~ applying change ~~ " .. event.first .. ".." .. event.last .. "::[" .. event.content .. "]") if not success then
utils.buffer.set_content(buffer, event.content, event.first, event.last) print("error in buffer async handler: " .. tostring(event))
if event.hash ~= nil then break
if utils.hash(utils.buffer.get_content(buffer)) ~= event.hash then end
-- OUT OF SYNC! if event == nil then break end
-- TODO this may be destructive! we should probably prompt the user before doing this ticks[buffer] = vim.api.nvim_buf_get_changedtick(buffer)
print(" /!\\ out of sync, resynching...") -- print(" ~~ applying change ~~ " .. event.first .. ".." .. event.last .. "::[" .. event.content .. "]")
utils.buffer.set_content(buffer, controller:content()) utils.buffer.set_content(buffer, event.content, event.first, event.last)
return if event.hash ~= nil then
if utils.hash(utils.buffer.get_content(buffer)) ~= event.hash then
-- OUT OF SYNC!
-- TODO this may be destructive! we should probably prompt the user before doing this
print(" /!\\ out of sync, resynching...")
utils.buffer.set_content(buffer, controller:content())
return
end
end end
end end
end, 20) -- wait 20ms before polling again because it overwhelms libuv? end))
controller:callback(function (_controller) async:send() end)
vim.schedule(function ()
async:send() -- run once to try_recv anything we synched in the meantime
end)
print(" ++ attached to buffer " .. name) print(" ++ attached to buffer " .. name)
return controller return controller

View file

@ -25,7 +25,10 @@ end
local native = require('codemp.loader').load() -- make sure we can load the native library correctly, otherwise no point going forward local native = require('codemp.loader').load() -- make sure we can load the native library correctly, otherwise no point going forward
local state = require('codemp.state') local state = require('codemp.state')
native.runtime_drive_forever() -- spawn thread to drive tokio runtime local rt = native.runtime_drive_forever() -- spawn thread to drive tokio runtime
-- native.logger(function (msg)
-- vim.schedule(function () print(msg) end)
-- end, true)
vim.api.nvim_create_autocmd( vim.api.nvim_create_autocmd(
{"ExitPre"}, {"ExitPre"},
@ -40,18 +43,6 @@ vim.api.nvim_create_autocmd(
} }
) )
-- TODO nvim docs say that we should stop all threads before exiting nvim
-- but we like to live dangerously (:
vim.loop.new_thread({}, function()
vim.loop.sleep(1000) -- allow user to setup their own logger options
local _codemp = require('codemp.loader').load()
_codemp.setup_logger()
local logger = _codemp.get_logger()
while true do
print(logger:recv())
end
end)
require('codemp.command') require('codemp.command')
return { return {
@ -61,5 +52,5 @@ return {
workspace = require('codemp.workspace'), workspace = require('codemp.workspace'),
window = require('codemp.window'), window = require('codemp.window'),
utils = require('codemp.utils'), utils = require('codemp.utils'),
async = require('codemp.async'), rt = rt,
} }

View file

@ -2,7 +2,6 @@ local native = require('codemp.loader').load()
local utils = require('codemp.utils') local utils = require('codemp.utils')
local buffers = require('codemp.buffers') local buffers = require('codemp.buffers')
local async = require('codemp.async')
local state = require('codemp.state') local state = require('codemp.state')
local window = require('codemp.window') local window = require('codemp.window')
@ -33,51 +32,45 @@ local function register_cursor_callback(controller)
end end
local function register_cursor_handler(controller) local function register_cursor_handler(controller)
async.handler(nil, controller, function(event) local async = vim.loop.new_async(vim.schedule_wrap(function ()
if user_hl[event.user] == nil then while true do
user_hl[event.user] = { local success, event = pcall(controller.try_recv, controller)
ns = vim.api.nvim_create_namespace("codemp-cursor-" .. event.user), if not success then
hi = available_colors[ math.random( #available_colors ) ], print("error in cursor callback: " .. tostring(event))
} break
end
if event == nil then break end
if user_hl[event.user] == nil then
user_hl[event.user] = {
ns = vim.api.nvim_create_namespace("codemp-cursor-" .. event.user),
hi = available_colors[ math.random( #available_colors ) ],
}
end
user_buffer[event.user] = event.buffer
local buffer = buffers.map_rev[event.buffer]
if buffer ~= nil then
vim.api.nvim_buf_clear_namespace(buffer, user_hl[event.user].ns, 0, -1)
utils.multiline_highlight(
buffer,
user_hl[event.user].ns,
user_hl[event.user].hi,
event.start,
event.finish
)
end
end end
user_buffer[event.user] = event.buffer end))
local buffer = buffers.map_rev[event.buffer] controller:callback(function (_controller) async:send() end)
if buffer ~= nil then
vim.api.nvim_buf_clear_namespace(buffer, user_hl[event.user].ns, 0, -1)
utils.multiline_highlight(
buffer,
user_hl[event.user].ns,
user_hl[event.user].hi,
event.start,
event.finish
)
end
end, 20)
end end
local function join(workspace) local function join(workspace)
local controller = state.client:join_workspace(workspace) local ws = state.client:join_workspace(workspace)
register_cursor_callback(controller) register_cursor_callback(ws.cursor)
register_cursor_handler(controller) register_cursor_handler(ws.cursor)
-- TODO nvim docs say that we should stop all threads before exiting nvim
-- but we like to live dangerously (:
local refresher = vim.loop.new_async(function () vim.schedule(function() window.update() end) end)
vim.loop.new_thread({}, function(_id, _ws, _refresher)
local _codemp = require('codemp.loader').load()
local _workspace = _codemp.get_client(_id):get_workspace(_ws)
while true do
local success, res = pcall(_workspace.event, _workspace)
if success then
print("workspace event!")
_refresher:send()
else
print("error waiting for workspace event: " .. res)
break
end
end
end, state.client.id, state.workspace, refresher)
-- ws:callback(function (_ev)
-- vim.schedule(function() window.update() end)
-- end)
window.update() window.update()
end end