mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-24 16:14:48 +01:00
feat: added Delta object to ffis
This commit is contained in:
parent
a318e3bc28
commit
45864e19f6
12 changed files with 107 additions and 62 deletions
37
dist/java/src/mp/code/data/Delta.java
vendored
Normal file
37
dist/java/src/mp/code/data/Delta.java
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package mp.code.data;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import mp.code.data.Config;
|
||||||
|
import mp.code.data.User;
|
||||||
|
import mp.code.exceptions.ConnectionException;
|
||||||
|
import mp.code.exceptions.ConnectionRemoteException;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public final class Delta {
|
||||||
|
private final long ptr;
|
||||||
|
|
||||||
|
Delta(long ptr) {
|
||||||
|
this.ptr = ptr;
|
||||||
|
Extensions.CLEANER.register(this, () -> free(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native TextChange get_text_change(long self);
|
||||||
|
|
||||||
|
public mp.code.data.TextChange getTextChange() {
|
||||||
|
return get_text_change(this.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native void ack_native(long self, boolean success) throws ConnectionException;
|
||||||
|
|
||||||
|
public void ack(boolean success) throws ConnectionException {
|
||||||
|
return ack_native(this.ptr, success);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native void free(long self);
|
||||||
|
|
||||||
|
static {
|
||||||
|
NativeUtils.loadLibraryIfNeeded();
|
||||||
|
}
|
||||||
|
}
|
39
dist/lua/annotations.lua
vendored
39
dist/lua/annotations.lua
vendored
|
@ -135,28 +135,28 @@ function MaybeCursorPromise:cancel() end
|
||||||
function MaybeCursorPromise:and_then(cb) end
|
function MaybeCursorPromise:and_then(cb) end
|
||||||
|
|
||||||
|
|
||||||
---@class (exact) TextChangePromise : Promise
|
---@class (exact) DeltaPromise : Promise
|
||||||
local TextChangePromise = {}
|
local DeltaPromise = {}
|
||||||
--- block until promise is ready and return value
|
--- block until promise is ready and return value
|
||||||
--- @return TextChange
|
--- @return Delta
|
||||||
function TextChangePromise:await() end
|
function DeltaPromise:await() end
|
||||||
--- cancel promise execution
|
--- cancel promise execution
|
||||||
function TextChangePromise:cancel() end
|
function DeltaPromise:cancel() end
|
||||||
---@param cb fun(x: TextChange) callback to invoke
|
---@param cb fun(x: Delta) callback to invoke
|
||||||
---invoke callback asynchronously as soon as promise is ready
|
---invoke callback asynchronously as soon as promise is ready
|
||||||
function TextChangePromise:and_then(cb) end
|
function DeltaPromise:and_then(cb) end
|
||||||
|
|
||||||
|
|
||||||
---@class (exact) MaybeTextChangePromise : Promise
|
---@class (exact) MaybeDeltaPromise : Promise
|
||||||
local MaybeTextChangePromise = {}
|
local MaybeDeltaPromise = {}
|
||||||
--- block until promise is ready and return value
|
--- block until promise is ready and return value
|
||||||
--- @return TextChange | nil
|
--- @return Delta | nil
|
||||||
function MaybeTextChangePromise:await() end
|
function MaybeDeltaPromise:await() end
|
||||||
--- cancel promise execution
|
--- cancel promise execution
|
||||||
function MaybeTextChangePromise:cancel() end
|
function MaybeDeltaPromise:cancel() end
|
||||||
---@param cb fun(x: TextChange | nil) callback to invoke
|
---@param cb fun(x: Delta | nil) callback to invoke
|
||||||
---invoke callback asynchronously as soon as promise is ready
|
---invoke callback asynchronously as soon as promise is ready
|
||||||
function MaybeTextChangePromise:and_then(cb) end
|
function MaybeDeltaPromise:and_then(cb) end
|
||||||
|
|
||||||
-- [[ END ASYNC STUFF ]]
|
-- [[ END ASYNC STUFF ]]
|
||||||
|
|
||||||
|
@ -325,6 +325,13 @@ local BufferController = {}
|
||||||
---@field hash integer? optional hash of text buffer after this change, for sync checks
|
---@field hash integer? optional hash of text buffer after this change, for sync checks
|
||||||
local TextChange = {}
|
local TextChange = {}
|
||||||
|
|
||||||
|
---@class (exact) Delta
|
||||||
|
---@field change TextChange text change for this delta
|
||||||
|
local Delta = {}
|
||||||
|
|
||||||
|
---notify controller that this change has been correctly applied
|
||||||
|
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
|
||||||
function TextChange:apply(other) end
|
function TextChange:apply(other) end
|
||||||
|
@ -336,13 +343,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 MaybeTextChangePromise
|
---@return MaybeDeltaPromise
|
||||||
---@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 TextChangePromise
|
---@return DeltaPromise
|
||||||
---@async
|
---@async
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---block until next text change and return it
|
---block until next text change and return it
|
||||||
|
|
8
dist/py/src/codemp/codemp.pyi
vendored
8
dist/py/src/codemp/codemp.pyi
vendored
|
@ -92,6 +92,14 @@ 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:
|
||||||
|
"""
|
||||||
|
A single editor delta event, wrapping a TextChange and the corresponding ACK channel
|
||||||
|
"""
|
||||||
|
change: TextChange
|
||||||
|
|
||||||
|
def ack(self,) -> str: ...
|
||||||
|
|
||||||
|
|
||||||
class BufferController:
|
class BufferController:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -45,22 +45,6 @@ pub struct TextChange {
|
||||||
pub hash: Option<i64>,
|
pub hash: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This wrapper around a [`TextChange`] contains a handle to Acknowledge correct change
|
|
||||||
/// application
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Delta<T: Acknowledgeable> {
|
|
||||||
/// The change received
|
|
||||||
pub change: TextChange,
|
|
||||||
/// The ack handle, must be called after correctly applying this change
|
|
||||||
pub ack: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A token which can be used to acknowledge changes
|
|
||||||
pub trait Acknowledgeable {
|
|
||||||
/// Send Acknowledgement. This action is idempotent
|
|
||||||
fn send(&mut self);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextChange {
|
impl TextChange {
|
||||||
/// Returns the [`std::ops::Range`] representing this change's span.
|
/// Returns the [`std::ops::Range`] representing this change's span.
|
||||||
pub fn span(&self) -> std::ops::Range<usize> {
|
pub fn span(&self) -> std::ops::Range<usize> {
|
||||||
|
|
|
@ -6,7 +6,6 @@ use std::sync::Arc;
|
||||||
use diamond_types::LocalVersion;
|
use diamond_types::LocalVersion;
|
||||||
use tokio::sync::{mpsc, oneshot, watch};
|
use tokio::sync::{mpsc, oneshot, watch};
|
||||||
|
|
||||||
use crate::api::change::{Acknowledgeable, Delta};
|
|
||||||
use crate::api::controller::{AsyncReceiver, AsyncSender, Controller, ControllerCallback};
|
use crate::api::controller::{AsyncReceiver, AsyncSender, Controller, ControllerCallback};
|
||||||
use crate::api::TextChange;
|
use crate::api::TextChange;
|
||||||
use crate::errors::ControllerResult;
|
use crate::errors::ControllerResult;
|
||||||
|
@ -14,16 +13,30 @@ use crate::ext::IgnorableError;
|
||||||
|
|
||||||
use super::worker::DeltaRequest;
|
use super::worker::DeltaRequest;
|
||||||
|
|
||||||
|
/// This wrapper around a [`TextChange`] contains a handle to Acknowledge correct change
|
||||||
|
/// application
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(any(feature = "py", feature = "py-noabi"), pyo3::pyclass)]
|
||||||
|
#[cfg_attr(feature = "js", napi_derive::napi)]
|
||||||
|
pub struct Delta {
|
||||||
|
/// The change received
|
||||||
|
pub change: TextChange,
|
||||||
|
/// The ack handle, must be called after correctly applying this change
|
||||||
|
pub(crate) ack: BufferAck,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct BufferAck {
|
pub(crate) struct BufferAck {
|
||||||
pub(crate) tx: mpsc::UnboundedSender<LocalVersion>,
|
pub(crate) tx: mpsc::UnboundedSender<LocalVersion>,
|
||||||
pub(crate) version: LocalVersion,
|
pub(crate) version: LocalVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Acknowledgeable for BufferAck {
|
#[cfg_attr(any(feature = "py", feature = "py-noabi"), pyo3::pymethods)]
|
||||||
fn send(&mut self) {
|
#[cfg_attr(feature = "js", napi_derive::napi)]
|
||||||
self.tx
|
impl Delta {
|
||||||
.send(self.version.clone())
|
pub fn ack(&mut self) {
|
||||||
|
self.ack.tx
|
||||||
|
.send(self.ack.version.clone())
|
||||||
.unwrap_or_warn("no worker to receive sent ack");
|
.unwrap_or_warn("no worker to receive sent ack");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +78,7 @@ pub(crate) struct BufferControllerInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "async-trait", async_trait::async_trait)]
|
#[cfg_attr(feature = "async-trait", async_trait::async_trait)]
|
||||||
impl Controller<TextChange, Delta<BufferAck>> for BufferController {}
|
impl Controller<TextChange, Delta> for BufferController {}
|
||||||
|
|
||||||
impl AsyncSender<TextChange> for BufferController {
|
impl AsyncSender<TextChange> for BufferController {
|
||||||
fn send(&self, op: TextChange) -> ControllerResult<()> {
|
fn send(&self, op: TextChange) -> ControllerResult<()> {
|
||||||
|
@ -75,7 +88,7 @@ impl AsyncSender<TextChange> for BufferController {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "async-trait", async_trait::async_trait)]
|
#[cfg_attr(feature = "async-trait", async_trait::async_trait)]
|
||||||
impl AsyncReceiver<Delta<BufferAck>> for BufferController {
|
impl AsyncReceiver<Delta> for BufferController {
|
||||||
async fn poll(&self) -> ControllerResult<()> {
|
async fn poll(&self) -> ControllerResult<()> {
|
||||||
if *self.0.local_version.borrow() != *self.0.latest_version.borrow() {
|
if *self.0.local_version.borrow() != *self.0.latest_version.borrow() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -87,7 +100,7 @@ impl AsyncReceiver<Delta<BufferAck>> for BufferController {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_recv(&self) -> ControllerResult<Option<Delta<BufferAck>>> {
|
async fn try_recv(&self) -> ControllerResult<Option<Delta>> {
|
||||||
let last_update = self.0.local_version.borrow().clone();
|
let last_update = self.0.local_version.borrow().clone();
|
||||||
let latest_version = self.0.latest_version.borrow().clone();
|
let latest_version = self.0.latest_version.borrow().clone();
|
||||||
|
|
||||||
|
|
|
@ -6,16 +6,15 @@ use tokio::sync::{mpsc, oneshot, watch};
|
||||||
use tonic::Streaming;
|
use tonic::Streaming;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::api::change::Delta;
|
|
||||||
use crate::api::controller::ControllerCallback;
|
use crate::api::controller::ControllerCallback;
|
||||||
use crate::api::TextChange;
|
use crate::api::TextChange;
|
||||||
use crate::ext::IgnorableError;
|
use crate::ext::IgnorableError;
|
||||||
|
|
||||||
use codemp_proto::buffer::{BufferEvent, Operation};
|
use codemp_proto::buffer::{BufferEvent, Operation};
|
||||||
|
|
||||||
use super::controller::{BufferAck, BufferController, BufferControllerInner};
|
use super::controller::{BufferAck, BufferController, BufferControllerInner, Delta};
|
||||||
|
|
||||||
pub(crate) type DeltaOp = Option<Delta<BufferAck>>;
|
pub(crate) type DeltaOp = Option<Delta>;
|
||||||
pub(crate) type DeltaRequest = (LocalVersion, oneshot::Sender<DeltaOp>);
|
pub(crate) type DeltaRequest = (LocalVersion, oneshot::Sender<DeltaOp>);
|
||||||
|
|
||||||
struct BufferWorker {
|
struct BufferWorker {
|
||||||
|
|
|
@ -3,11 +3,10 @@ use jni_toolbox::jni;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{
|
api::{
|
||||||
change::Delta,
|
|
||||||
controller::{AsyncReceiver, AsyncSender},
|
controller::{AsyncReceiver, AsyncSender},
|
||||||
TextChange,
|
TextChange,
|
||||||
},
|
},
|
||||||
buffer::controller::BufferAck,
|
buffer::controller::Delta,
|
||||||
errors::ControllerError,
|
errors::ControllerError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,13 +28,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<BufferAck>>, ControllerError> {
|
) -> Result<Option<Delta>, 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<BufferAck>, ControllerError> {
|
fn recv(controller: &mut crate::buffer::Controller) -> Result<Delta, ControllerError> {
|
||||||
super::tokio().block_on(controller.recv())
|
super::tokio().block_on(controller.recv())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,7 @@ 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";
|
||||||
|
@ -275,6 +276,7 @@ from_java_ptr!(crate::Client);
|
||||||
from_java_ptr!(crate::Workspace);
|
from_java_ptr!(crate::Workspace);
|
||||||
from_java_ptr!(crate::cursor::Controller);
|
from_java_ptr!(crate::cursor::Controller);
|
||||||
from_java_ptr!(crate::buffer::Controller);
|
from_java_ptr!(crate::buffer::Controller);
|
||||||
|
from_java_ptr!(crate::buffer::controller::Delta);
|
||||||
|
|
||||||
impl<'j> jni_toolbox::FromJava<'j> for crate::api::Config {
|
impl<'j> jni_toolbox::FromJava<'j> for crate::api::Config {
|
||||||
type From = jni::objects::JObject<'j>;
|
type From = jni::objects::JObject<'j>;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::api::change::Delta;
|
|
||||||
use crate::api::controller::{AsyncReceiver, AsyncSender};
|
use crate::api::controller::{AsyncReceiver, AsyncSender};
|
||||||
use crate::api::TextChange;
|
use crate::api::TextChange;
|
||||||
use crate::buffer::controller::BufferController;
|
use crate::buffer::controller::{Delta, BufferController};
|
||||||
use napi::threadsafe_function::{
|
use napi::threadsafe_function::{
|
||||||
ErrorStrategy::Fatal, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
|
ErrorStrategy::Fatal, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
|
||||||
};
|
};
|
||||||
|
@ -54,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<crate::buffer::controller::BufferAck>>> {
|
) -> napi::Result<Option<Delta>> {
|
||||||
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<crate::buffer::controller::BufferAck>> {
|
pub async fn js_recv(&self) -> napi::Result<Delta> {
|
||||||
Ok(self.recv().await?)
|
Ok(self.recv().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::api::change::{Acknowledgeable, Delta};
|
use crate::buffer::controller::Delta;
|
||||||
use crate::buffer::controller::BufferAck;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
use mlua_codemp_patch as mlua;
|
use mlua_codemp_patch as mlua;
|
||||||
|
@ -62,15 +61,12 @@ impl LuaUserData for CodempTextChange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LuaUserData for Delta<BufferAck> {
|
impl LuaUserData for Delta {
|
||||||
fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) {
|
fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) {
|
||||||
fields.add_field_method_get("change", |_, this| Ok(this.change.clone()));
|
fields.add_field_method_get("change", |_, this| Ok(this.change.clone()));
|
||||||
fields.add_field_method_get("ack", |_, this| Ok(this.ack.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaUserData for BufferAck {
|
|
||||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
||||||
methods.add_method_mut("send", |_, this, ()| Ok(this.send()));
|
methods.add_method_mut("ack", |_, this, ()| Ok(this.ack()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::api::change::Delta;
|
use crate::buffer::controller::Delta;
|
||||||
use crate::buffer::controller::BufferAck;
|
|
||||||
use crate::ext::IgnorableError;
|
use crate::ext::IgnorableError;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
|
@ -113,6 +112,6 @@ callback_args! {
|
||||||
MaybeCursor: Option<CodempCursor>,
|
MaybeCursor: Option<CodempCursor>,
|
||||||
TextChange: CodempTextChange,
|
TextChange: CodempTextChange,
|
||||||
MaybeTextChange: Option<CodempTextChange>,
|
MaybeTextChange: Option<CodempTextChange>,
|
||||||
Delta: Delta<BufferAck>,
|
Delta: Delta,
|
||||||
MaybeDelta: Option<Delta<BufferAck>>,
|
MaybeDelta: Option<Delta>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,8 @@
|
||||||
//! `JNIException`s are however unchecked: there is nothing you can do to recover from them, as they usually represent a severe error in the glue code. If they arise, it's probably a bug.
|
//! `JNIException`s are however unchecked: there is nothing you can do to recover from them, as they usually represent a severe error in the glue code. If they arise, it's probably a bug.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
#![allow(clippy::unit_arg)]
|
||||||
|
|
||||||
/// java bindings, built with [jni]
|
/// java bindings, built with [jni]
|
||||||
#[cfg(feature = "java")]
|
#[cfg(feature = "java")]
|
||||||
pub mod java;
|
pub mod java;
|
||||||
|
|
Loading…
Reference in a new issue