From bdf800aa5a74b10f816992d38294dedff5f03e8b Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 4 Feb 2024 00:56:20 +0100 Subject: [PATCH] chore: simplify and adapt lib to workspace model very lazy port, if it works i'll make it better later:tm: --- Cargo.toml | 4 +- src/codemp.lua | 54 +++++++++++-------- src/lib.rs | 140 +++++++++++++++++++++---------------------------- 3 files changed, 93 insertions(+), 105 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e839143..b800c3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,11 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -codemp = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/codemp.git", tag = "v0.5.1", features = ["global", "sync"] } mlua = { version = "0.9.0", features = ["module", "luajit"] } +codemp = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/codemp.git", rev = "5cc0bd3a869d83f7decc8ce1185eebb38771621a", features = ["global", "sync"] } thiserror = "1.0.47" derive_more = "0.99.17" tracing-subscriber = "0.3.17" tracing = "0.1.37" +lazy_static = "1.4.0" +tokio = { version = "1.35.1", features = ["rt"] } diff --git a/src/codemp.lua b/src/codemp.lua index 6c6193e..ef85b87 100644 --- a/src/codemp.lua +++ b/src/codemp.lua @@ -1,4 +1,5 @@ local codemp = require("libcodemp_nvim") +local active_workspace = nil local codemp_changed_tick = {} @@ -19,9 +20,10 @@ local function register_controller_handler(target, controller, handler, delay) -- 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) + vim.loop.new_thread({}, function(_async, _workspace, _target, _delay) local _codemp = require("libcodemp_nvim") - local _controller = _target ~= nil and _codemp.get_buffer(_target) or _codemp.get_cursor() + local _ws = _codemp.get_workspace(_workspace) + local _controller = _target ~= nil and _ws:get_buffer(_target) or _ws:get_cursor() while true do local success, _ = pcall(_controller.poll, _controller) if success then @@ -35,7 +37,7 @@ local function register_controller_handler(target, controller, handler, delay) print(" -- stopping " .. my_name .. " controller poller") end end - end, async, target, delay) + end, async, active_workspace, target, delay) end local function split_without_trim(str, sep) @@ -146,19 +148,20 @@ local available_colors = { -- TODO these are definitely not portable! "CmpItemKindInterface", } -vim.api.nvim_create_user_command( - "Connect", - function (args) - codemp.connect(#args.args > 0 and args.args or nil) - print(" ++ connected") - end, - { nargs = "?" } -) +-- vim.api.nvim_create_user_command( +-- "Connect", +-- function (args) +-- codemp.connect(#args.args > 0 and args.args or nil) +-- print(" ++ connected") +-- end, +-- { nargs = "?" } +-- ) vim.api.nvim_create_user_command( "Join", function (args) - local controller = codemp.join(args.args) + local controller = codemp.join_workspace(args.args) + active_workspace = args.args -- hook serverbound callbacks vim.api.nvim_create_autocmd({"CursorMoved", "CursorMovedI", "ModeChanged"}, { @@ -207,7 +210,7 @@ vim.api.nvim_create_user_command( content = buffer_get_content(buf) -- TODO send content! end - codemp.create(args.args, content) + codemp.get_workspace(active_workspace):create_buffer(args.args, content) print(" ++ created buffer " .. args.args) end, @@ -228,7 +231,7 @@ vim.api.nvim_create_user_command( vim.api.nvim_buf_set_name(buffer, "codemp::" .. args.args) vim.api.nvim_set_current_buf(buffer) end - local controller = codemp.attach(args.args) + local controller = codemp.get_workspace(active_workspace):attach_buffer(args.args) -- TODO map name to uuid @@ -242,13 +245,18 @@ vim.api.nvim_create_user_command( on_bytes = function(_, buf, tick, start_row, start_col, start_offset, old_end_row, old_end_col, old_end_byte_len, new_end_row, new_end_col, new_byte_len) if tick <= codemp_changed_tick[buf] then return end if buffer_mappings[buf] == nil then return true end -- unregister callback handler - local content = "" - if old_end_row < new_end_row and new_byte_len == 1 then - content = "\n" - else - content = table.concat(vim.api.nvim_buf_get_text(buf, start_row, start_col, start_row + new_end_row, start_col + new_byte_len, {}), '\n') - end - controller:send(start_offset, start_offset + old_end_byte_len, content) + controller:replace(buffer_get_content(buf)) + -- local content = "" + -- if old_end_row < new_end_row and new_byte_len == 1 then + -- content = "\n" + -- else + -- print(string.format("%s %s %s %s", start_row, start_col, start_row + new_end_row, start_col + new_end_col)) + -- content = table.concat(vim.api.nvim_buf_get_text(buf, start_row - 1, start_col, start_row + new_end_row - 1, start_col + new_end_col, {}), '\n') + -- end + -- if old_end_row < new_end_row then + -- start_offset = start_offset - 1 + -- end + -- controller:send(start_offset, start_offset + old_end_byte_len, content) end, }) @@ -278,7 +286,7 @@ vim.api.nvim_create_user_command( local buffer = vim.api.nvim_get_current_buf() local name = buffer_mappings[buffer] if name ~= nil then - local controller = codemp.get_buffer(name) + local controller = codemp.get_workspace(active_workspace):get_buffer(name) codemp_changed_tick[buffer] = vim.api.nvim_buf_get_changedtick(buffer) buffer_set_content(buffer, controller.content) print(" :: synched buffer " .. name) @@ -297,7 +305,7 @@ vim.api.nvim_create_user_command( local name = buffer_mappings[buffer] buffer_mappings[buffer] = nil buffer_mappings_reverse[name] = nil - codemp.disconnect_buffer(name) + codemp.get_workspace(active_workspace):disconnect_buffer(name) vim.api.nvim_buf_delete(buffer, {}) print(" -- detached from buffer " .. name) end, diff --git a/src/lib.rs b/src/lib.rs index ad514fb..aa89763 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,19 @@ use std::io::Write; -use std::sync::{Arc, Mutex, mpsc}; +use std::sync::{mpsc, Arc, Mutex}; use codemp::prelude::*; use codemp::woot::crdt::Op; use mlua::prelude::*; +use tokio::runtime::Runtime; +use tokio::sync::RwLock; + +lazy_static::lazy_static!{ + // TODO use a runtime::Builder::new_current_thread() runtime to not behave like malware + static ref RT : Runtime = Runtime::new().expect("could not instantiate tokio runtime"); + static ref CODEMP : RwLock = RwLock::new(RT.block_on(CodempClient::new( + &std::env::var("CODEMP_BASE_HOST").unwrap_or("http://codemp.alemi.dev:50051".to_string()) + )).expect("could not connect to codemp servers")); +} #[derive(Debug, thiserror::Error, derive_more::From, derive_more::Display)] @@ -18,53 +28,58 @@ impl From:: for LuaError { // TODO put friendlier constructor directly in lib? fn make_cursor(buffer: String, start_row: i32, start_col: i32, end_row: i32, end_col: i32) -> CodempCursorPosition { CodempCursorPosition { - buffer, - start: Some(CodempRowCol { - row: start_row, col: start_col, - }), - end: Some(CodempRowCol { - row: end_row, col: end_col, - }), + buffer, start: CodempRowCol { row: start_row, col: start_col}, end: CodempRowCol { row: end_row, col: end_col }, } } +fn id(_: &Lua, (): ()) -> LuaResult { + Ok(CODEMP.blocking_read().user_id().to_string()) +} + + +/// join a remote workspace and start processing cursor events +fn join_workspace(_: &Lua, (session,): (String,)) -> LuaResult { + let ws = RT.block_on(async { CODEMP.write().await.join_workspace(&session).await }) + .map_err(LuaCodempError::from)?; + let cursor = ws.blocking_read().cursor().clone(); + Ok(LuaCursorController(cursor)) +} + +fn get_workspace(_: &Lua, (session,): (String,)) -> LuaResult> { + Ok(CODEMP.blocking_read().workspaces.get(&session).cloned().map(LuaWorkspace)) +} + #[derive(Debug, derive_more::From)] struct LuaOp(Op); impl LuaUserData for LuaOp { } -/// connect to remote server -fn connect(_: &Lua, (host,): (Option,)) -> LuaResult<()> { - let addr = host.unwrap_or("http://127.0.0.1:50051".into()); - CODEMP_INSTANCE.connect(&addr) - .map_err(LuaCodempError::from)?; - Ok(()) -} +#[derive(derive_more::From)] +struct LuaWorkspace(Arc>); +impl LuaUserData for LuaWorkspace { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_method("create_buffer", |_, this, (name,):(String,)| { + Ok(RT.block_on(async { this.0.write().await.create(&name).await }).map_err(LuaCodempError::from)?) + }); -fn get_cursor(_: &Lua, _args: ()) -> LuaResult { - Ok( - CODEMP_INSTANCE.get_cursor() - .map_err(LuaCodempError::from)? - .into() - ) -} + methods.add_method("attach_buffer", |_, this, (name,):(String,)| { + Ok(LuaBufferController(RT.block_on(async { this.0.write().await.attach(&name).await }).map_err(LuaCodempError::from)?)) + }); -fn get_buffer(_: &Lua, (path,): (String,)) -> LuaResult { - Ok( - CODEMP_INSTANCE.get_buffer(&path) - .map_err(LuaCodempError::from)? - .into() - ) + // TODO disconnect_buffer + // TODO leave_workspace:w + + methods.add_method("get_buffer", |_, this, (name,):(String,)| Ok(RT.block_on(async { this.0.read().await.buffer_by_name(&name) }).map(|x| LuaBufferController(x)))); + } + + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("cursor", |_, this| Ok(LuaCursorController(RT.block_on(async { this.0.read().await.cursor() })))); + fields.add_field_method_get("filetree", |_, this| Ok(RT.block_on(async { this.0.read().await.filetree() }))); + // methods.add_method("users", |_, this| Ok(this.0.users())); // TODO + } } -/// join a remote workspace and start processing cursor events -fn join(_: &Lua, (session,): (String,)) -> LuaResult { - let controller = CODEMP_INSTANCE.join(&session) - .map_err(LuaCodempError::from)?; - Ok(LuaCursorController(controller)) -} - #[derive(Debug, derive_more::From)] struct LuaCursorController(Arc); impl LuaUserData for LuaCursorController { @@ -80,7 +95,7 @@ impl LuaUserData for LuaCursorController { } }); methods.add_method("poll", |_, this, ()| { - CODEMP_INSTANCE.rt().block_on(this.0.poll()) + RT.block_on(this.0.poll()) .map_err(LuaCodempError::from)?; Ok(()) }); @@ -91,9 +106,9 @@ impl LuaUserData for LuaCursorController { struct LuaCursorEvent(CodempCursorEvent); impl LuaUserData for LuaCursorEvent { fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { - fields.add_field_method_get("user", |_, this| Ok(this.0.user.clone())); + fields.add_field_method_get("user", |_, this| Ok(this.0.user.id.clone())); fields.add_field_method_get("position", |_, this| - Ok(this.0.position.as_ref().map(|x| LuaCursorPosition(x.clone()))) + Ok(LuaCursorPosition(this.0.position.clone())) ); } } @@ -103,29 +118,12 @@ struct LuaCursorPosition(CodempCursorPosition); impl LuaUserData for LuaCursorPosition { fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { fields.add_field_method_get("buffer", |_, this| Ok(this.0.buffer.clone())); - fields.add_field_method_get("start", |_, this| Ok(LuaRowCol(this.0.start()))); - fields.add_field_method_get("finish", |_, this| Ok(LuaRowCol(this.0.end()))); + fields.add_field_method_get("start", |_, this| Ok(LuaRowCol(this.0.start.clone()))); + fields.add_field_method_get("finish", |_, this| Ok(LuaRowCol(this.0.end.clone()))); } } - -/// create a new buffer in current workspace -fn create(_: &Lua, (path, content): (String, Option)) -> LuaResult<()> { - CODEMP_INSTANCE.create(&path, content.as_deref()) - .map_err(LuaCodempError::from)?; - Ok(()) -} - - - -/// attach to remote buffer and start processing buffer events -fn attach(_: &Lua, (path,): (String,)) -> LuaResult { - let controller = CODEMP_INSTANCE.attach(&path) - .map_err(LuaCodempError::from)?; - Ok(LuaBufferController(controller)) -} - #[derive(Debug, derive_more::From)] struct LuaBufferController(Arc); impl LuaUserData for LuaBufferController { @@ -149,7 +147,7 @@ impl LuaUserData for LuaBufferController { } }); methods.add_method("poll", |_, this, ()| { - CODEMP_INSTANCE.rt().block_on(this.0.poll()) + RT.block_on(this.0.poll()) .map_err(LuaCodempError::from)?; Ok(()) }); @@ -221,18 +219,6 @@ impl Write for LuaLoggerProducer { fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } -fn disconnect_buffer(_: &Lua, (path,): (String,)) -> LuaResult<()> { - CODEMP_INSTANCE.disconnect_buffer(&path) - .map_err(LuaCodempError::from)?; - Ok(()) -} - -fn leave_workspace(_: &Lua, (): ()) -> LuaResult<()> { - CODEMP_INSTANCE.leave_workspace() - .map_err(LuaCodempError::from)?; - Ok(()) -} - fn setup_tracing(_: &Lua, (debug,): (Option,)) -> LuaResult { let (tx, rx) = mpsc::channel(); let level = if debug.unwrap_or(false) { tracing::Level::DEBUG } else {tracing::Level::INFO }; @@ -254,25 +240,17 @@ fn setup_tracing(_: &Lua, (debug,): (Option,)) -> LuaResult { Ok(LuaLogger(Arc::new(Mutex::new(rx)))) } - - // define module and exports #[mlua::lua_module] fn libcodemp_nvim(lua: &Lua) -> LuaResult { let exports = lua.create_table()?; // core proto functions - exports.set("connect", lua.create_function(connect)?)?; - exports.set("join", lua.create_function(join)?)?; - exports.set("create", lua.create_function(create)?)?; - exports.set("attach", lua.create_function(attach)?)?; + exports.set("join_workspace", lua.create_function(join_workspace)?)?; // state helpers - exports.set("get_cursor", lua.create_function(get_cursor)?)?; - exports.set("get_buffer", lua.create_function(get_buffer)?)?; - // cleanup - exports.set("disconnect_buffer", lua.create_function(disconnect_buffer)?)?; - exports.set("leave_workspace", lua.create_function(leave_workspace)?)?; + exports.set("get_workspace", lua.create_function(get_workspace)?)?; // debug + exports.set("id", lua.create_function(id)?)?; exports.set("setup_tracing", lua.create_function(setup_tracing)?)?; Ok(exports)