diff --git a/src/async.lua b/src/async.lua index f8d3ee9..97151d6 100644 --- a/src/async.lua +++ b/src/async.lua @@ -1,4 +1,6 @@ -local function register_controller_handler(workspace, target, controller, handler, delay) +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) @@ -20,10 +22,15 @@ local function register_controller_handler(workspace, target, controller, handle -- 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, _workspace, _target, _delay) + vim.loop.new_thread({}, function(_async, _target, _delay, _ws, _id) local _codemp = require("codemp.loader").load() -- TODO maybe make a native.load() idk - local _ws = _codemp.get_workspace(_workspace) - local _controller = _target ~= nil and _ws:get_buffer(_target) or _ws.cursor + 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 @@ -38,7 +45,7 @@ local function register_controller_handler(workspace, target, controller, handle break end end - end, async, workspace, target, delay) + end, async, target, delay, state.workspace, state.client.id) end return { diff --git a/src/buffers.lua b/src/buffers.lua index 5e0eca7..3543563 100644 --- a/src/buffers.lua +++ b/src/buffers.lua @@ -1,18 +1,17 @@ -local native = require('codemp.loader').load() - local utils = require('codemp.utils') local async = require('codemp.async') +local state = require('codemp.state') local id_buffer_map = {} local buffer_id_map = {} local ticks = {} -local function create(workspace, name, content) - native.get_workspace(workspace):create_buffer(name, content) - print(" ++ created buffer '" .. name .. "' on " .. workspace) +local function create(name, content) + state.client:get_workspace(state.workspace):create_buffer(name, content) + print(" ++ created buffer '" .. name .. "' on " .. state.workspace) end -local function attach(workspace, name, force) +local function attach(name, force) local buffer = nil if force then buffer = vim.api.nvim_get_current_buf() @@ -24,7 +23,7 @@ local function attach(workspace, name, force) vim.api.nvim_buf_set_name(buffer, "codemp::" .. name) vim.api.nvim_set_current_buf(buffer) end - local controller = native.get_workspace(workspace):attach_buffer(name) + local controller = state.client:get_workspace(state.workspace):attach_buffer(name) -- TODO map name to uuid @@ -55,7 +54,7 @@ local function attach(workspace, name, force) -- vim.loop.sleep(200) -- moved inside poller thread to at least not block ui -- hook clientbound callbacks - async.handler(workspace, name, controller, function(event) + async.handler(name, controller, function(event) ticks[buffer] = vim.api.nvim_buf_get_changedtick(buffer) local before = utils.buffer.get_content(buffer) local after = event:apply(before) @@ -67,21 +66,21 @@ local function attach(workspace, name, force) print(" ++ attached to buffer " .. name) end -local function detach(workspace, name) +local function detach(name) local buffer = buffer_id_map[name] id_buffer_map[buffer] = nil buffer_id_map[name] = nil - native.get_workspace(workspace):disconnect_buffer(name) + state.client:get_workspace(state.workspace):disconnect_buffer(name) vim.api.nvim_buf_delete(buffer, {}) print(" -- detached from buffer " .. name) end -local function sync(workspace) +local function sync() local buffer = vim.api.nvim_get_current_buf() local name = id_buffer_map[buffer] if name ~= nil then - local controller = native.get_workspace(workspace):get_buffer(name) + local controller = state.client:get_workspace(state.workspace):get_buffer(name) ticks[buffer] = vim.api.nvim_buf_get_changedtick(buffer) utils.buffer.set_content(buffer, controller.content) print(" :: synched buffer " .. name) diff --git a/src/client.lua b/src/client.lua deleted file mode 100644 index 7f716db..0000000 --- a/src/client.lua +++ /dev/null @@ -1,14 +0,0 @@ -local native = require('codemp.loader').load() - -local workspace = nil - - -local function login(username, password, ws) - native.login(username, password, ws) - print(" ++ logged in as '" .. username .. "' on " .. ws) -end - -return { - login = login, - workspace = workspace, -} diff --git a/src/command.lua b/src/command.lua index 9df8d12..4ef736e 100644 --- a/src/command.lua +++ b/src/command.lua @@ -1,7 +1,6 @@ -local client = require('codemp.client') +local state = require('codemp.state') local buffers = require('codemp.buffers') local workspace = require('codemp.workspace') -local utils = require('codemp.utils') local native = require('codemp.loader').load() @@ -15,48 +14,75 @@ local function filter(needle, haystack) return hints end +-- always available +local base_actions = { + connect = function(host) + if host == nil then host = 'http://codemp.alemi.dev:50053' end + local user = vim.fn.input("username > ", "user-" .. vim.fn.rand() % 1024) + local password = vim.fn.input("password > ", "lmaodefaultpassword") + state.client = native.connect(host, user, password) + print(" ++ connected to " .. host .. " as " .. user) + end, +} + +-- only available if state.client is not nil +local connected_actions = { + id = function() + print(" ::codemp#" .. state.client.id) + end, + + join = function(ws) + if ws == nil then error("missing workspace name") end + state.workspace = ws + workspace.join(ws) + print(" >< joined workspace " .. ws) + end, +} + +-- only available if state.workspace is not nil +local joined_actions = { + create = function(path) + if path == nil then error("missing buffer name") end + buffers.create(path) + end, + + buffers = function() + workspace.open_buffer_tree() + end, + + sync = function() + buffers.sync() + end, + + attach = function(path, bang) + if path == nil then error("missing buffer name") end + buffers.attach(path, bang) + end, + +} + vim.api.nvim_create_user_command( "MP", function (args) - if args.fargs[1] == "login" then - if #args.fargs < 2 then error("missing workspace name") end - local user = vim.fn.input("username > ", "user-" .. vim.fn.rand() % 1024) - local password = vim.fn.input("password > ", "lmaodefaultpassword") - client.login(user, password, args.fargs[2]) - elseif args.fargs[1] == "create" then - if #args.fargs < 2 then error("missing buffer name") end - if client.workspace == nil then error("connect to a workspace first") end - buffers.create(client.workspace, args.fargs[2]) - elseif args.fargs[1] == "join" then - if #args.fargs < 2 then error("missing workspace name") end - client.workspace = args.fargs[2] - workspace.join(client.workspace) - elseif args.fargs[1] == "attach" then - if #args.fargs < 2 then error("missing buffer name") end - if client.workspace == nil then error("connect to a workspace first") end - buffers.attach(client.workspace, args.fargs[2], args.bang) - elseif args.fargs[1] == "sync" then - if client.workspace == nil then error("connect to a workspace first") end - buffers.sync(client.workspace) - elseif args.fargs[1] == "buffers" then - if client.workspace == nil then error("connect to a workspace first") end - workspace.open_buffer_tree(client.workspace) - elseif args.fargs[1] == "id" then - print(" ::codemp#" .. native.id()) - -- elseif args.fargs[1] == "users" then - -- if client.workspace == nil then error("connect to a workspace first") end - -- workspace.users(client.workspace) - -- elseif args.fargs[1] == "detach" then - -- if #args.fargs < 2 then error("missing buffer name") end - -- if client.workspace == nil then error("connect to a workspace first") end - -- buffers.detach(client.workspace, args.fargs[2]) - -- elseif args.fargs[1] == "leave" then - -- if client.workspace == nil then error("connect to a workspace first") end - -- workspace.leave() - -- client.workspace = nil + local action = args.fargs[1] + local fn = nil + + if base_actions[action] ~= nil then + fn = base_actions[action] end - if args.bang then - print("pls stop shouting :'c") + + if state.client ~= nil and connected_actions[action] ~= nil then + fn = connected_actions[action] + end + + if state.workspace ~= nil and joined_actions[action] ~= nil then + fn = joined_actions[action] + end + + if fn ~= nil then + fn(args.fargs[2], args.bang) + else + print(" ?? invalid command") end end, { @@ -69,11 +95,29 @@ vim.api.nvim_create_user_command( if stage == 1 then return { "MP" } elseif stage == 2 then - return filter(lead, {'login', 'create', 'join', 'attach', 'sync', 'buffers', 'users', 'detach', 'leave'}) + local suggestions = {} + local n = 0 + for sugg, _ in pairs(base_actions) do + n = n + 1 + suggestions[n] = sugg + end + if state.client ~= nil then + for sugg, _ in pairs(connected_actions) do + n = n + 1 + suggestions[n] = sugg + end + end + if state.workspace ~= nil then + for sugg, _ in pairs(joined_actions) do + n = n + 1 + suggestions[n] = sugg + end + end + return filter(lead, suggestions) elseif stage == 3 then if args[#args-1] == 'attach' or args[#args-1] == 'detach' then - if client.workspace ~= nil then - local ws = native.get_workspace(client.workspace) + if state.client ~= nil and state.workspace ~= nil then + local ws = state.client:get_workspace(state.workspace) if ws ~= nil then return filter(lead, ws.filetree) end diff --git a/src/init.lua b/src/init.lua index 324c555..5e601d0 100644 --- a/src/init.lua +++ b/src/init.lua @@ -24,6 +24,21 @@ end -- end 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') +native.runtime_drive_forever() -- spawn thread to drive tokio runtime + +vim.api.nvim_create_autocmd( + {"ExitPre"}, + { + callback = function (ev) + if state.client ~= nil then + print(" xx disconnecting codemp client") + native.close_client(state.client.id) + state.client = nil + end + end + } +) -- TODO nvim docs say that we should stop all threads before exiting nvim -- but we like to live dangerously (: @@ -41,7 +56,7 @@ require('codemp.command') return { native = native, - client = require('codemp.client'), + state = require('codemp.state'), buffers = require('codemp.buffers'), workspace = require('codemp.workspace'), utils = require('codemp.utils'), diff --git a/src/state.lua b/src/state.lua new file mode 100644 index 0000000..2283a5f --- /dev/null +++ b/src/state.lua @@ -0,0 +1,7 @@ +local workspace = nil +local client = nil + +return { + workspace = workspace, + client = client, +} diff --git a/src/workspace.lua b/src/workspace.lua index a3a0771..2c32f2a 100644 --- a/src/workspace.lua +++ b/src/workspace.lua @@ -3,6 +3,7 @@ local native = require('codemp.loader').load() local utils = require('codemp.utils') local buffers = require('codemp.buffers') local async = require('codemp.async') +local state = require('codemp.state') local user_hl = {} local user_buffer = {} @@ -17,12 +18,12 @@ local available_colors = { -- TODO these are definitely not portable! "CmpItemKindInterface", } -local function register_cursor_callback(controller, workspace, buffer) +local function register_cursor_callback(controller) vim.api.nvim_create_autocmd({"CursorMoved", "CursorMovedI", "ModeChanged"}, { - group = vim.api.nvim_create_augroup("codemp-workspace-" .. workspace, { clear = true }), + group = vim.api.nvim_create_augroup("codemp-workspace-" .. state.workspace, { clear = true }), callback = function (_) local cur = utils.cursor.position() - local buf = buffer or vim.api.nvim_get_current_buf() + local buf = vim.api.nvim_get_current_buf() if buffers.map[buf] ~= nil then controller:send(buffers.map[buf], cur[1][1], cur[1][2], cur[2][1], cur[2][2]) end @@ -30,8 +31,8 @@ local function register_cursor_callback(controller, workspace, buffer) }) end -local function register_cursor_handler(controller, workspace) - async.handler(workspace, nil, controller, function(event) +local function register_cursor_handler(controller) + async.handler(nil, controller, function(event) if user_hl[event.user] == nil then user_hl[event.user] = { ns = vim.api.nvim_create_namespace("codemp-cursor-" .. event.user), @@ -54,10 +55,9 @@ local function register_cursor_handler(controller, workspace) end local function join(workspace) - local controller = native.join_workspace(workspace) - register_cursor_callback(controller, workspace) - register_cursor_handler(controller, workspace) - print(" ++ joined workspace " .. workspace) + local controller = state.client:join_workspace(workspace) + register_cursor_callback(controller) + register_cursor_handler(controller) end local function leave() @@ -65,15 +65,15 @@ local function leave() print(" -- left workspace") end -local function open_buffer_tree(workspace) - local tree = native.get_workspace(workspace).filetree +local function open_buffer_tree() + local tree = state.client:get_workspace(state.workspace).filetree if tree_buf == nil then tree_buf = vim.api.nvim_create_buf(false, true) - vim.api.nvim_buf_set_name(tree_buf, "codemp::" .. workspace) + vim.api.nvim_buf_set_name(tree_buf, "codemp::" .. state.workspace) vim.api.nvim_set_option_value('buftype', 'nofile', { buf = tree_buf }) end vim.api.nvim_set_option_value('modifiable', true, { buf = tree_buf }) - utils.buffer.set_content(tree_buf, "codemp::" .. workspace .. "\n\n- " .. vim.fn.join(tree, "\n- ")) + utils.buffer.set_content(tree_buf, "codemp::" .. state.workspace .. "\n\n- " .. vim.fn.join(tree, "\n- ")) vim.api.nvim_set_option_value('modifiable', false, { buf = tree_buf }) vim.api.nvim_open_win(tree_buf, true, { win = 0,