chore: update glue code

Co-authored-by: zaaarf <me@zaaarf.foo>
This commit is contained in:
əlemi 2024-10-10 02:12:24 +02:00 committed by alemi.dev
parent 560a634499
commit 6035c448fa
11 changed files with 90 additions and 126 deletions

View file

@ -135,28 +135,28 @@ function MaybeCursorPromise:cancel() end
function MaybeCursorPromise:and_then(cb) end function MaybeCursorPromise:and_then(cb) end
---@class (exact) DeltaPromise : Promise ---@class (exact) BufferUpdatePromise : Promise
local DeltaPromise = {} local BufferUpdatePromise = {}
--- block until promise is ready and return value --- block until promise is ready and return value
--- @return Delta --- @return BufferUpdate
function DeltaPromise:await() end function BufferUpdatePromise:await() end
--- cancel promise execution --- cancel promise execution
function DeltaPromise:cancel() end function BufferUpdatePromise:cancel() end
---@param cb fun(x: Delta) callback to invoke ---@param cb fun(x: BufferUpdate) callback to invoke
---invoke callback asynchronously as soon as promise is ready ---invoke callback asynchronously as soon as promise is ready
function DeltaPromise:and_then(cb) end function BufferUpdatePromise:and_then(cb) end
---@class (exact) MaybeDeltaPromise : Promise ---@class (exact) MaybeBufferUpdatePromise : Promise
local MaybeDeltaPromise = {} local MaybeBufferUpdatePromise = {}
--- block until promise is ready and return value --- block until promise is ready and return value
--- @return Delta | nil --- @return BufferUpdate | nil
function MaybeDeltaPromise:await() end function MaybeBufferUpdatePromise:await() end
--- cancel promise execution --- cancel promise execution
function MaybeDeltaPromise:cancel() end function MaybeBufferUpdatePromise:cancel() end
---@param cb fun(x: Delta | nil) callback to invoke ---@param cb fun(x: BufferUpdate | nil) callback to invoke
---invoke callback asynchronously as soon as promise is ready ---invoke callback asynchronously as soon as promise is ready
function MaybeDeltaPromise:and_then(cb) end function MaybeBufferUpdatePromise:and_then(cb) end
-- [[ END ASYNC STUFF ]] -- [[ END ASYNC STUFF ]]
@ -322,15 +322,13 @@ local BufferController = {}
---@field content string text content of change ---@field content string text content of change
---@field start integer start index of change ---@field start integer start index of change
---@field finish integer end index of change ---@field finish integer end index of change
---@field hash integer? optional hash of text buffer after this change, for sync checks
local TextChange = {} local TextChange = {}
---@class (exact) Delta ---@class (exact) BufferUpdate
---@field change TextChange text change for this delta ---@field change TextChange text change for this delta
local Delta = {} ---@field version [integer] CRDT version after this change
---@field hash integer? optional hash of text buffer after this change, for sync checks
---notify controller that this change has been correctly applied local BufferUpdate = {}
function Delta:ack() end
---@param other string text to apply change to ---@param other string text to apply change to
---apply this text change to a string, returning the result ---apply this text change to a string, returning the result
@ -343,13 +341,13 @@ function TextChange:apply(other) end
---update buffer with a text change; note that to delete content should be empty but not span, while to insert span should be empty but not content (can insert and delete at the same time) ---update buffer with a text change; note that to delete content should be empty but not span, while to insert span should be empty but not content (can insert and delete at the same time)
function BufferController:send(change) end function BufferController:send(change) end
---@return MaybeDeltaPromise ---@return MaybeBufferUpdatePromise
---@async ---@async
---@nodiscard ---@nodiscard
---try to receive text changes, returning nil if none is available ---try to receive text changes, returning nil if none is available
function BufferController:try_recv() end function BufferController:try_recv() end
---@return DeltaPromise ---@return BufferUpdatePromise
---@async ---@async
---@nodiscard ---@nodiscard
---block until next text change and return it ---block until next text change and return it
@ -374,6 +372,10 @@ function BufferController:callback(cb) end
---get current content of buffer controller, marking all pending changes as seen ---get current content of buffer controller, marking all pending changes as seen
function BufferController:content() end function BufferController:content() end
---@param version [integer] version to ack
---notify controller that this version's change has been correctly applied
function BufferController:ack(version) end

View file

@ -92,13 +92,13 @@ class TextChange:
def is_empty(self) -> bool: ... def is_empty(self) -> bool: ...
def apply(self, txt: str) -> str: ... def apply(self, txt: str) -> str: ...
class Delta: class BufferUpdate:
""" """
A single editor delta event, wrapping a TextChange and the corresponding ACK channel A single editor delta event, wrapping a TextChange and the new version
""" """
change: TextChange change: TextChange
hash: Optional[int]
def ack(self,) -> str: ... version: list[int]
class BufferController: class BufferController:
@ -108,6 +108,7 @@ class BufferController:
""" """
def path(self) -> str: ... def path(self) -> str: ...
def content(self) -> Promise[str]: ... def content(self) -> Promise[str]: ...
def ack(self, v: list[int]) -> None: ...
def send(self, def send(self,
start: int, start: int,
end: int, end: int,
@ -121,14 +122,20 @@ class BufferController:
class Cursor: class Selection:
""" """
An Editor agnostic cursor position representation An Editor agnostic cursor position representation
""" """
start: Tuple[int, int] start: Tuple[int, int]
end: Tuple[int, int] end: Tuple[int, int]
buffer: str buffer: str
user: Optional[str] # can be an empty string
class Cursor:
"""
A remote cursor event
"""
user: str
sel: Selection
class CursorController: class CursorController:

View file

@ -3,10 +3,8 @@ use jni_toolbox::jni;
use crate::{ use crate::{
api::{ api::{
controller::{AsyncReceiver, AsyncSender}, change::BufferUpdate, controller::{AsyncReceiver, AsyncSender}, TextChange
TextChange,
}, },
buffer::controller::Delta,
errors::ControllerError, errors::ControllerError,
}; };
@ -28,13 +26,13 @@ fn get_content(controller: &mut crate::buffer::Controller) -> Result<String, Con
#[jni(package = "mp.code", class = "BufferController")] #[jni(package = "mp.code", class = "BufferController")]
fn try_recv( fn try_recv(
controller: &mut crate::buffer::Controller, controller: &mut crate::buffer::Controller,
) -> Result<Option<Delta>, ControllerError> { ) -> Result<Option<BufferUpdate>, ControllerError> {
super::tokio().block_on(controller.try_recv()) super::tokio().block_on(controller.try_recv())
} }
/// Block until it receives a [TextChange]. /// Block until it receives a [TextChange].
#[jni(package = "mp.code", class = "BufferController")] #[jni(package = "mp.code", class = "BufferController")]
fn recv(controller: &mut crate::buffer::Controller) -> Result<Delta, ControllerError> { fn recv(controller: &mut crate::buffer::Controller) -> Result<BufferUpdate, ControllerError> {
super::tokio().block_on(controller.recv()) super::tokio().block_on(controller.recv())
} }

View file

@ -141,7 +141,6 @@ into_java_ptr_class!(crate::Client, "mp/code/Client");
into_java_ptr_class!(crate::Workspace, "mp/code/Workspace"); into_java_ptr_class!(crate::Workspace, "mp/code/Workspace");
into_java_ptr_class!(crate::cursor::Controller, "mp/code/CursorController"); into_java_ptr_class!(crate::cursor::Controller, "mp/code/CursorController");
into_java_ptr_class!(crate::buffer::Controller, "mp/code/BufferController"); into_java_ptr_class!(crate::buffer::Controller, "mp/code/BufferController");
into_java_ptr_class!(crate::buffer::controller::Delta, "mp/code/data/Delta");
impl<'j> jni_toolbox::IntoJavaObject<'j> for crate::api::User { impl<'j> jni_toolbox::IntoJavaObject<'j> for crate::api::User {
const CLASS: &'static str = "mp/code/data/User"; const CLASS: &'static str = "mp/code/data/User";
@ -202,19 +201,6 @@ impl<'j> jni_toolbox::IntoJavaObject<'j> for crate::api::TextChange {
) -> Result<jni::objects::JObject<'j>, jni::errors::Error> { ) -> Result<jni::objects::JObject<'j>, jni::errors::Error> {
let content = env.new_string(self.content)?; let content = env.new_string(self.content)?;
let hash_class = env.find_class("java/util/OptionalLong")?;
let hash = if let Some(h) = self.hash {
env.call_static_method(
hash_class,
"of",
"(J)Ljava/util/OptionalLong;",
&[jni::objects::JValueGen::Long(h)],
)
} else {
env.call_static_method(hash_class, "empty", "()Ljava/util/OptionalLong;", &[])
}?
.l()?;
let class = env.find_class(Self::CLASS)?; let class = env.find_class(Self::CLASS)?;
env.new_object( env.new_object(
class, class,
@ -223,7 +209,6 @@ impl<'j> jni_toolbox::IntoJavaObject<'j> for crate::api::TextChange {
jni::objects::JValueGen::Long(self.start.into()), jni::objects::JValueGen::Long(self.start.into()),
jni::objects::JValueGen::Long(self.end.into()), jni::objects::JValueGen::Long(self.end.into()),
jni::objects::JValueGen::Object(&content), jni::objects::JValueGen::Object(&content),
jni::objects::JValueGen::Object(&hash),
], ],
) )
} }

View file

@ -1,6 +1,6 @@
use crate::api::controller::{AsyncReceiver, AsyncSender}; use crate::api::controller::{AsyncReceiver, AsyncSender};
use crate::api::TextChange; use crate::api::change::{TextChange, BufferUpdate};
use crate::buffer::controller::{Delta, BufferController}; use crate::buffer::controller::BufferController;
use napi::threadsafe_function::{ use napi::threadsafe_function::{
ErrorStrategy::Fatal, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode, ErrorStrategy::Fatal, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
}; };
@ -53,13 +53,13 @@ impl BufferController {
#[napi(js_name = "try_recv")] #[napi(js_name = "try_recv")]
pub async fn js_try_recv( pub async fn js_try_recv(
&self, &self,
) -> napi::Result<Option<Delta>> { ) -> napi::Result<Option<BufferUpdate>> {
Ok(self.try_recv().await?) Ok(self.try_recv().await?)
} }
/// Wait for next buffer event and return it /// Wait for next buffer event and return it
#[napi(js_name = "recv")] #[napi(js_name = "recv")]
pub async fn js_recv(&self) -> napi::Result<Delta> { pub async fn js_recv(&self) -> napi::Result<BufferUpdate> {
Ok(self.recv().await?) Ok(self.recv().await?)
} }

View file

@ -6,41 +6,6 @@ use napi::threadsafe_function::{
}; };
use napi_derive::napi; use napi_derive::napi;
#[napi(object, js_name = "Cursor")]
pub struct JsCursor {
/// range of text change, as char indexes in buffer previous state
pub start_row: i32,
pub start_col: i32,
pub end_row: i32,
pub end_col: i32,
pub buffer: String,
pub user: Option<String>,
}
impl From<JsCursor> for crate::api::Cursor {
fn from(value: JsCursor) -> Self {
crate::api::Cursor {
start: (value.start_row, value.start_col),
end: (value.end_row, value.end_col),
buffer: value.buffer,
user: value.user,
}
}
}
impl From<crate::api::Cursor> for JsCursor {
fn from(value: crate::api::Cursor) -> Self {
JsCursor {
start_row: value.start.0,
start_col: value.start.1,
end_row: value.end.0,
end_col: value.end.1,
buffer: value.buffer,
user: value.user.map(|x| x.to_string()),
}
}
}
#[napi] #[napi]
impl CursorController { impl CursorController {
/// Register a callback to be called on receive. /// Register a callback to be called on receive.
@ -74,19 +39,19 @@ impl CursorController {
/// Send a new cursor event to remote /// Send a new cursor event to remote
#[napi(js_name = "send")] #[napi(js_name = "send")]
pub fn js_send(&self, pos: JsCursor) -> napi::Result<()> { pub fn js_send(&self, sel: crate::api::cursor::Selection) -> napi::Result<()> {
Ok(self.send(crate::api::Cursor::from(pos))?) Ok(self.send(sel)?)
} }
/// Get next cursor event if available without blocking /// Get next cursor event if available without blocking
#[napi(js_name = "try_recv")] #[napi(js_name = "try_recv")]
pub async fn js_try_recv(&self) -> napi::Result<Option<JsCursor>> { pub async fn js_try_recv(&self) -> napi::Result<Option<crate::api::Cursor>> {
Ok(self.try_recv().await?.map(JsCursor::from)) Ok(self.try_recv().await?.map(crate::api::Cursor::from))
} }
/// Block until next /// Block until next
#[napi(js_name = "recv")] #[napi(js_name = "recv")]
pub async fn js_recv(&self) -> napi::Result<JsCursor> { pub async fn js_recv(&self) -> napi::Result<crate::api::Cursor> {
Ok(self.recv().await?.into()) Ok(self.recv().await?.into())
} }
} }

View file

@ -1,4 +1,3 @@
use crate::buffer::controller::Delta;
use crate::prelude::*; use crate::prelude::*;
use mlua::prelude::*; use mlua::prelude::*;
use mlua_codemp_patch as mlua; use mlua_codemp_patch as mlua;
@ -22,6 +21,7 @@ impl LuaUserData for CodempBufferController {
); );
methods.add_method("recv", |_, this, ()| a_sync! { this => this.recv().await? }); methods.add_method("recv", |_, this, ()| a_sync! { this => this.recv().await? });
methods.add_method("poll", |_, this, ()| a_sync! { this => this.poll().await? }); methods.add_method("poll", |_, this, ()| a_sync! { this => this.poll().await? });
methods.add_method_mut("ack", |_, this, (version,):(Vec<i64>,)| Ok(this.ack(version)));
methods.add_method( methods.add_method(
"content", "content",
@ -43,7 +43,6 @@ impl LuaUserData for CodempTextChange {
fields.add_field_method_get("content", |_, this| Ok(this.content.clone())); fields.add_field_method_get("content", |_, this| Ok(this.content.clone()));
fields.add_field_method_get("start", |_, this| Ok(this.start)); fields.add_field_method_get("start", |_, this| Ok(this.start));
fields.add_field_method_get("end", |_, this| Ok(this.end)); fields.add_field_method_get("end", |_, this| Ok(this.end));
fields.add_field_method_get("hash", |_, this| Ok(this.hash));
// add a 'finish' accessor too because in Lua 'end' is reserved // add a 'finish' accessor too because in Lua 'end' is reserved
fields.add_field_method_get("finish", |_, this| Ok(this.end)); fields.add_field_method_get("finish", |_, this| Ok(this.end));
} }
@ -56,12 +55,17 @@ impl LuaUserData for CodempTextChange {
} }
} }
impl LuaUserData for Delta { from_lua_serde! { CodempBufferUpdate }
impl LuaUserData for CodempBufferUpdate {
fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) { fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("hash", |_, this| Ok(this.hash));
fields.add_field_method_get("version", |_, this| Ok(this.version.clone()));
fields.add_field_method_get("change", |_, this| Ok(this.change.clone())); fields.add_field_method_get("change", |_, this| Ok(this.change.clone()));
} }
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) { fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
methods.add_method_mut("ack", |_, this, ()| Ok(this.ack())); methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
Ok(format!("{:?}", this))
});
} }
} }

View file

@ -4,7 +4,6 @@ use mlua_codemp_patch as mlua;
use super::ext::a_sync::a_sync; use super::ext::a_sync::a_sync;
use super::ext::from_lua_serde; use super::ext::from_lua_serde;
use super::ext::lua_tuple;
impl LuaUserData for CodempCursorController { impl LuaUserData for CodempCursorController {
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) { fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
@ -12,7 +11,7 @@ impl LuaUserData for CodempCursorController {
Ok(format!("{:?}", this)) Ok(format!("{:?}", this))
}); });
methods.add_method("send", |_, this, (cursor,): (CodempCursor,)| { methods.add_method("send", |_, this, (cursor,): (CodempSelection,)| {
Ok(this.send(cursor)?) Ok(this.send(cursor)?)
}); });
methods.add_method( methods.add_method(
@ -33,18 +32,31 @@ impl LuaUserData for CodempCursorController {
from_lua_serde! { CodempCursor } from_lua_serde! { CodempCursor }
impl LuaUserData for CodempCursor { impl LuaUserData for CodempCursor {
fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("user", |_, this| Ok(this.user.clone()));
fields.add_field_method_get("sel", |_, this| Ok(this.sel.clone()));
}
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) { fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| { methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
Ok(format!("{:?}", this)) Ok(format!("{:?}", this))
}); });
} }
}
from_lua_serde! { CodempSelection }
impl LuaUserData for CodempSelection {
fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) { fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("user", |_, this| Ok(this.user.clone()));
fields.add_field_method_get("buffer", |_, this| Ok(this.buffer.clone())); fields.add_field_method_get("buffer", |_, this| Ok(this.buffer.clone()));
fields.add_field_method_get("start", |lua, this| lua_tuple(lua, this.start)); fields.add_field_method_get("start_row", |_, this| Ok(this.start_row));
fields.add_field_method_get("end", |lua, this| lua_tuple(lua, this.end)); fields.add_field_method_get("start_col", |_, this| Ok(this.start_col));
// add a 'finish' accessor too because in Lua 'end' is reserved fields.add_field_method_get("end_row", |_, this| Ok(this.end_row));
fields.add_field_method_get("finish", |lua, this| lua_tuple(lua, this.end)); fields.add_field_method_get("end_col", |_, this| Ok(this.end_col));
}
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
Ok(format!("{:?}", this))
});
} }
} }

View file

@ -1,4 +1,3 @@
use crate::buffer::controller::Delta;
use crate::ext::IgnorableError; use crate::ext::IgnorableError;
use crate::prelude::*; use crate::prelude::*;
use mlua::prelude::*; use mlua::prelude::*;
@ -110,8 +109,10 @@ callback_args! {
MaybeEvent: Option<CodempEvent>, MaybeEvent: Option<CodempEvent>,
Cursor: CodempCursor, Cursor: CodempCursor,
MaybeCursor: Option<CodempCursor>, MaybeCursor: Option<CodempCursor>,
Selection: CodempSelection,
MaybeSelection: Option<CodempSelection>,
TextChange: CodempTextChange, TextChange: CodempTextChange,
MaybeTextChange: Option<CodempTextChange>, MaybeTextChange: Option<CodempTextChange>,
Delta: Delta, BufferUpdate: CodempBufferUpdate,
MaybeDelta: Option<Delta>, MaybeBufferUpdate: Option<CodempBufferUpdate>,
} }

View file

@ -2,19 +2,9 @@ pub mod a_sync;
pub mod callback; pub mod callback;
pub mod log; pub mod log;
use mlua::prelude::*;
use mlua_codemp_patch as mlua;
pub(crate) use a_sync::tokio; pub(crate) use a_sync::tokio;
pub(crate) use callback::callback; pub(crate) use callback::callback;
pub(crate) fn lua_tuple<T: IntoLua>(lua: &Lua, (a, b): (T, T)) -> LuaResult<LuaTable> {
let table = lua.create_table()?;
table.set(1, a)?;
table.set(2, b)?;
Ok(table)
}
macro_rules! from_lua_serde { macro_rules! from_lua_serde {
($($t:ty)*) => { ($($t:ty)*) => {
$( $(

View file

@ -1,5 +1,5 @@
use crate::api::controller::{AsyncReceiver, AsyncSender}; use crate::api::controller::{AsyncReceiver, AsyncSender};
use crate::api::Cursor; use crate::api::cursor::{Cursor, Selection};
use crate::api::TextChange; use crate::api::TextChange;
use crate::buffer::Controller as BufferController; use crate::buffer::Controller as BufferController;
use crate::cursor::Controller as CursorController; use crate::cursor::Controller as CursorController;
@ -20,11 +20,12 @@ impl CursorController {
start: (i32, i32), start: (i32, i32),
end: (i32, i32), end: (i32, i32),
) -> PyResult<()> { ) -> PyResult<()> {
let pos = Cursor { let pos = Selection {
start, start_row: start.0,
end, start_col: start.1,
end_row: end.0,
end_col: end.1,
buffer: path, buffer: path,
user: None,
}; };
let this = self.clone(); let this = self.clone();
this.send(pos)?; this.send(pos)?;
@ -90,7 +91,6 @@ impl BufferController {
start, start,
end, end,
content: txt, content: txt,
hash: None,
}; };
let this = self.clone(); let this = self.clone();
this.send(op)?; this.send(op)?;
@ -143,21 +143,21 @@ impl BufferController {
impl Cursor { impl Cursor {
#[getter(start)] #[getter(start)]
fn pystart(&self) -> (i32, i32) { fn pystart(&self) -> (i32, i32) {
self.start (self.sel.start_row, self.sel.start_col)
} }
#[getter(end)] #[getter(end)]
fn pyend(&self) -> (i32, i32) { fn pyend(&self) -> (i32, i32) {
self.end (self.sel.end_row, self.sel.end_col)
} }
#[getter(buffer)] #[getter(buffer)]
fn pybuffer(&self) -> String { fn pybuffer(&self) -> String {
self.buffer.clone() self.sel.buffer.clone()
} }
#[getter(user)] #[getter(user)]
fn pyuser(&self) -> Option<String> { fn pyuser(&self) -> Option<String> {
self.user.clone() Some(self.user.clone())
} }
} }