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
---@class (exact) DeltaPromise : Promise
local DeltaPromise = {}
---@class (exact) BufferUpdatePromise : Promise
local BufferUpdatePromise = {}
--- block until promise is ready and return value
--- @return Delta
function DeltaPromise:await() end
--- @return BufferUpdate
function BufferUpdatePromise:await() end
--- cancel promise execution
function DeltaPromise:cancel() end
---@param cb fun(x: Delta) callback to invoke
function BufferUpdatePromise:cancel() end
---@param cb fun(x: BufferUpdate) callback to invoke
---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
local MaybeDeltaPromise = {}
---@class (exact) MaybeBufferUpdatePromise : Promise
local MaybeBufferUpdatePromise = {}
--- block until promise is ready and return value
--- @return Delta | nil
function MaybeDeltaPromise:await() end
--- @return BufferUpdate | nil
function MaybeBufferUpdatePromise:await() end
--- cancel promise execution
function MaybeDeltaPromise:cancel() end
---@param cb fun(x: Delta | nil) callback to invoke
function MaybeBufferUpdatePromise:cancel() end
---@param cb fun(x: BufferUpdate | nil) callback to invoke
---invoke callback asynchronously as soon as promise is ready
function MaybeDeltaPromise:and_then(cb) end
function MaybeBufferUpdatePromise:and_then(cb) end
-- [[ END ASYNC STUFF ]]
@ -322,15 +322,13 @@ local BufferController = {}
---@field content string text content of change
---@field start integer start 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 = {}
---@class (exact) Delta
---@class (exact) BufferUpdate
---@field change TextChange text change for this delta
local Delta = {}
---notify controller that this change has been correctly applied
function Delta:ack() end
---@field version [integer] CRDT version after this change
---@field hash integer? optional hash of text buffer after this change, for sync checks
local BufferUpdate = {}
---@param other string text to apply change to
---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)
function BufferController:send(change) end
---@return MaybeDeltaPromise
---@return MaybeBufferUpdatePromise
---@async
---@nodiscard
---try to receive text changes, returning nil if none is available
function BufferController:try_recv() end
---@return DeltaPromise
---@return BufferUpdatePromise
---@async
---@nodiscard
---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
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 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
def ack(self,) -> str: ...
hash: Optional[int]
version: list[int]
class BufferController:
@ -108,6 +108,7 @@ class BufferController:
"""
def path(self) -> str: ...
def content(self) -> Promise[str]: ...
def ack(self, v: list[int]) -> None: ...
def send(self,
start: int,
end: int,
@ -121,14 +122,20 @@ class BufferController:
class Cursor:
class Selection:
"""
An Editor agnostic cursor position representation
"""
start: Tuple[int, int]
end: Tuple[int, int]
buffer: str
user: Optional[str] # can be an empty string
class Cursor:
"""
A remote cursor event
"""
user: str
sel: Selection
class CursorController:

View file

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

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::cursor::Controller, "mp/code/CursorController");
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 {
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> {
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)?;
env.new_object(
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.end.into()),
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::TextChange;
use crate::buffer::controller::{Delta, BufferController};
use crate::api::change::{TextChange, BufferUpdate};
use crate::buffer::controller::BufferController;
use napi::threadsafe_function::{
ErrorStrategy::Fatal, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
};
@ -53,13 +53,13 @@ impl BufferController {
#[napi(js_name = "try_recv")]
pub async fn js_try_recv(
&self,
) -> napi::Result<Option<Delta>> {
) -> napi::Result<Option<BufferUpdate>> {
Ok(self.try_recv().await?)
}
/// Wait for next buffer event and return it
#[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?)
}

View file

@ -6,41 +6,6 @@ use napi::threadsafe_function::{
};
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]
impl CursorController {
/// Register a callback to be called on receive.
@ -74,19 +39,19 @@ impl CursorController {
/// Send a new cursor event to remote
#[napi(js_name = "send")]
pub fn js_send(&self, pos: JsCursor) -> napi::Result<()> {
Ok(self.send(crate::api::Cursor::from(pos))?)
pub fn js_send(&self, sel: crate::api::cursor::Selection) -> napi::Result<()> {
Ok(self.send(sel)?)
}
/// Get next cursor event if available without blocking
#[napi(js_name = "try_recv")]
pub async fn js_try_recv(&self) -> napi::Result<Option<JsCursor>> {
Ok(self.try_recv().await?.map(JsCursor::from))
pub async fn js_try_recv(&self) -> napi::Result<Option<crate::api::Cursor>> {
Ok(self.try_recv().await?.map(crate::api::Cursor::from))
}
/// Block until next
#[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())
}
}

View file

@ -1,4 +1,3 @@
use crate::buffer::controller::Delta;
use crate::prelude::*;
use mlua::prelude::*;
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("poll", |_, this, ()| a_sync! { this => this.poll().await? });
methods.add_method_mut("ack", |_, this, (version,):(Vec<i64>,)| Ok(this.ack(version)));
methods.add_method(
"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("start", |_, this| Ok(this.start));
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
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) {
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()));
}
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::from_lua_serde;
use super::ext::lua_tuple;
impl LuaUserData for CodempCursorController {
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
@ -12,7 +11,7 @@ impl LuaUserData for CodempCursorController {
Ok(format!("{:?}", this))
});
methods.add_method("send", |_, this, (cursor,): (CodempCursor,)| {
methods.add_method("send", |_, this, (cursor,): (CodempSelection,)| {
Ok(this.send(cursor)?)
});
methods.add_method(
@ -33,18 +32,31 @@ impl LuaUserData for CodempCursorController {
from_lua_serde! { 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) {
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
Ok(format!("{:?}", this))
});
}
}
from_lua_serde! { CodempSelection }
impl LuaUserData for CodempSelection {
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("start", |lua, this| lua_tuple(lua, this.start));
fields.add_field_method_get("end", |lua, this| lua_tuple(lua, this.end));
// add a 'finish' accessor too because in Lua 'end' is reserved
fields.add_field_method_get("finish", |lua, this| lua_tuple(lua, this.end));
fields.add_field_method_get("start_row", |_, this| Ok(this.start_row));
fields.add_field_method_get("start_col", |_, this| Ok(this.start_col));
fields.add_field_method_get("end_row", |_, this| Ok(this.end_row));
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::prelude::*;
use mlua::prelude::*;
@ -110,8 +109,10 @@ callback_args! {
MaybeEvent: Option<CodempEvent>,
Cursor: CodempCursor,
MaybeCursor: Option<CodempCursor>,
Selection: CodempSelection,
MaybeSelection: Option<CodempSelection>,
TextChange: CodempTextChange,
MaybeTextChange: Option<CodempTextChange>,
Delta: Delta,
MaybeDelta: Option<Delta>,
BufferUpdate: CodempBufferUpdate,
MaybeBufferUpdate: Option<CodempBufferUpdate>,
}

View file

@ -2,19 +2,9 @@ pub mod a_sync;
pub mod callback;
pub mod log;
use mlua::prelude::*;
use mlua_codemp_patch as mlua;
pub(crate) use a_sync::tokio;
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 {
($($t:ty)*) => {
$(

View file

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