mirror of
https://github.com/hexedtech/codemp.git
synced 2024-12-25 06:14:54 +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
|
||||
|
||||
|
||||
---@class (exact) TextChangePromise : Promise
|
||||
local TextChangePromise = {}
|
||||
---@class (exact) DeltaPromise : Promise
|
||||
local DeltaPromise = {}
|
||||
--- block until promise is ready and return value
|
||||
--- @return TextChange
|
||||
function TextChangePromise:await() end
|
||||
--- @return Delta
|
||||
function DeltaPromise:await() end
|
||||
--- cancel promise execution
|
||||
function TextChangePromise:cancel() end
|
||||
---@param cb fun(x: TextChange) callback to invoke
|
||||
function DeltaPromise:cancel() end
|
||||
---@param cb fun(x: Delta) callback to invoke
|
||||
---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
|
||||
local MaybeTextChangePromise = {}
|
||||
---@class (exact) MaybeDeltaPromise : Promise
|
||||
local MaybeDeltaPromise = {}
|
||||
--- block until promise is ready and return value
|
||||
--- @return TextChange | nil
|
||||
function MaybeTextChangePromise:await() end
|
||||
--- @return Delta | nil
|
||||
function MaybeDeltaPromise:await() end
|
||||
--- cancel promise execution
|
||||
function MaybeTextChangePromise:cancel() end
|
||||
---@param cb fun(x: TextChange | nil) callback to invoke
|
||||
function MaybeDeltaPromise:cancel() end
|
||||
---@param cb fun(x: Delta | nil) callback to invoke
|
||||
---invoke callback asynchronously as soon as promise is ready
|
||||
function MaybeTextChangePromise:and_then(cb) end
|
||||
function MaybeDeltaPromise:and_then(cb) end
|
||||
|
||||
-- [[ END ASYNC STUFF ]]
|
||||
|
||||
|
@ -325,6 +325,13 @@ local BufferController = {}
|
|||
---@field hash integer? optional hash of text buffer after this change, for sync checks
|
||||
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
|
||||
---apply this text change to a string, returning the result
|
||||
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)
|
||||
function BufferController:send(change) end
|
||||
|
||||
---@return MaybeTextChangePromise
|
||||
---@return MaybeDeltaPromise
|
||||
---@async
|
||||
---@nodiscard
|
||||
---try to receive text changes, returning nil if none is available
|
||||
function BufferController:try_recv() end
|
||||
|
||||
---@return TextChangePromise
|
||||
---@return DeltaPromise
|
||||
---@async
|
||||
---@nodiscard
|
||||
---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 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:
|
||||
"""
|
||||
|
|
|
@ -45,22 +45,6 @@ pub struct TextChange {
|
|||
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 {
|
||||
/// Returns the [`std::ops::Range`] representing this change's span.
|
||||
pub fn span(&self) -> std::ops::Range<usize> {
|
||||
|
|
|
@ -6,7 +6,6 @@ use std::sync::Arc;
|
|||
use diamond_types::LocalVersion;
|
||||
use tokio::sync::{mpsc, oneshot, watch};
|
||||
|
||||
use crate::api::change::{Acknowledgeable, Delta};
|
||||
use crate::api::controller::{AsyncReceiver, AsyncSender, Controller, ControllerCallback};
|
||||
use crate::api::TextChange;
|
||||
use crate::errors::ControllerResult;
|
||||
|
@ -14,16 +13,30 @@ use crate::ext::IgnorableError;
|
|||
|
||||
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)]
|
||||
pub(crate) struct BufferAck {
|
||||
pub(crate) tx: mpsc::UnboundedSender<LocalVersion>,
|
||||
pub(crate) version: LocalVersion,
|
||||
}
|
||||
|
||||
impl Acknowledgeable for BufferAck {
|
||||
fn send(&mut self) {
|
||||
self.tx
|
||||
.send(self.version.clone())
|
||||
#[cfg_attr(any(feature = "py", feature = "py-noabi"), pyo3::pymethods)]
|
||||
#[cfg_attr(feature = "js", napi_derive::napi)]
|
||||
impl Delta {
|
||||
pub fn ack(&mut self) {
|
||||
self.ack.tx
|
||||
.send(self.ack.version.clone())
|
||||
.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)]
|
||||
impl Controller<TextChange, Delta<BufferAck>> for BufferController {}
|
||||
impl Controller<TextChange, Delta> for BufferController {}
|
||||
|
||||
impl AsyncSender<TextChange> for BufferController {
|
||||
fn send(&self, op: TextChange) -> ControllerResult<()> {
|
||||
|
@ -75,7 +88,7 @@ impl AsyncSender<TextChange> for BufferController {
|
|||
}
|
||||
|
||||
#[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<()> {
|
||||
if *self.0.local_version.borrow() != *self.0.latest_version.borrow() {
|
||||
return Ok(());
|
||||
|
@ -87,7 +100,7 @@ impl AsyncReceiver<Delta<BufferAck>> for BufferController {
|
|||
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 latest_version = self.0.latest_version.borrow().clone();
|
||||
|
||||
|
|
|
@ -6,16 +6,15 @@ use tokio::sync::{mpsc, oneshot, watch};
|
|||
use tonic::Streaming;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::api::change::Delta;
|
||||
use crate::api::controller::ControllerCallback;
|
||||
use crate::api::TextChange;
|
||||
use crate::ext::IgnorableError;
|
||||
|
||||
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>);
|
||||
|
||||
struct BufferWorker {
|
||||
|
|
|
@ -3,11 +3,10 @@ use jni_toolbox::jni;
|
|||
|
||||
use crate::{
|
||||
api::{
|
||||
change::Delta,
|
||||
controller::{AsyncReceiver, AsyncSender},
|
||||
TextChange,
|
||||
},
|
||||
buffer::controller::BufferAck,
|
||||
buffer::controller::Delta,
|
||||
errors::ControllerError,
|
||||
};
|
||||
|
||||
|
@ -29,13 +28,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<BufferAck>>, ControllerError> {
|
||||
) -> Result<Option<Delta>, 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<BufferAck>, ControllerError> {
|
||||
fn recv(controller: &mut crate::buffer::Controller) -> Result<Delta, ControllerError> {
|
||||
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::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";
|
||||
|
@ -275,6 +276,7 @@ from_java_ptr!(crate::Client);
|
|||
from_java_ptr!(crate::Workspace);
|
||||
from_java_ptr!(crate::cursor::Controller);
|
||||
from_java_ptr!(crate::buffer::Controller);
|
||||
from_java_ptr!(crate::buffer::controller::Delta);
|
||||
|
||||
impl<'j> jni_toolbox::FromJava<'j> for crate::api::Config {
|
||||
type From = jni::objects::JObject<'j>;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::api::change::Delta;
|
||||
use crate::api::controller::{AsyncReceiver, AsyncSender};
|
||||
use crate::api::TextChange;
|
||||
use crate::buffer::controller::BufferController;
|
||||
use crate::buffer::controller::{Delta, BufferController};
|
||||
use napi::threadsafe_function::{
|
||||
ErrorStrategy::Fatal, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
|
||||
};
|
||||
|
@ -54,13 +53,13 @@ impl BufferController {
|
|||
#[napi(js_name = "try_recv")]
|
||||
pub async fn js_try_recv(
|
||||
&self,
|
||||
) -> napi::Result<Option<Delta<crate::buffer::controller::BufferAck>>> {
|
||||
) -> napi::Result<Option<Delta>> {
|
||||
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<crate::buffer::controller::BufferAck>> {
|
||||
pub async fn js_recv(&self) -> napi::Result<Delta> {
|
||||
Ok(self.recv().await?)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::api::change::{Acknowledgeable, Delta};
|
||||
use crate::buffer::controller::BufferAck;
|
||||
use crate::buffer::controller::Delta;
|
||||
use crate::prelude::*;
|
||||
use mlua::prelude::*;
|
||||
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) {
|
||||
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) {
|
||||
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::BufferAck;
|
||||
use crate::buffer::controller::Delta;
|
||||
use crate::ext::IgnorableError;
|
||||
use crate::prelude::*;
|
||||
use mlua::prelude::*;
|
||||
|
@ -113,6 +112,6 @@ callback_args! {
|
|||
MaybeCursor: Option<CodempCursor>,
|
||||
TextChange: CodempTextChange,
|
||||
MaybeTextChange: Option<CodempTextChange>,
|
||||
Delta: Delta<BufferAck>,
|
||||
MaybeDelta: Option<Delta<BufferAck>>,
|
||||
Delta: Delta,
|
||||
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.
|
||||
//!
|
||||
|
||||
#![allow(clippy::unit_arg)]
|
||||
|
||||
/// java bindings, built with [jni]
|
||||
#[cfg(feature = "java")]
|
||||
pub mod java;
|
||||
|
|
Loading…
Reference in a new issue