mirror of
https://github.com/hexedtech/codemp.git
synced 2024-12-23 05:14:54 +01:00
feat: wrappers for Cursor and Op
so plugins dont need to interact directly with our underlying proto struct, nor our underlying crdt ops. also for wrapping directly in glue
This commit is contained in:
parent
5e4cbe5fb1
commit
39533ae086
6 changed files with 87 additions and 14 deletions
|
@ -3,7 +3,13 @@
|
|||
//! an editor-friendly representation of a text change in a buffer
|
||||
//! to easily interface with codemp from various editors
|
||||
|
||||
use crate::woot::{WootResult, woot::Woot, crdt::{TextEditor, CRDT, Op}};
|
||||
use crate::woot::{WootResult, woot::Woot, crdt::{TextEditor, CRDT}};
|
||||
|
||||
/// an atomic and orderable operation
|
||||
///
|
||||
/// this under the hood thinly wraps our CRDT operation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Op(pub(crate) woot::crdt::Op);
|
||||
|
||||
/// an editor-friendly representation of a text change in a buffer
|
||||
///
|
||||
|
@ -59,7 +65,7 @@ impl TextChange {
|
|||
}
|
||||
}
|
||||
|
||||
/// consume the [TextChange], transforming it into a Vec of [woot::crdt::Op]
|
||||
/// consume the [TextChange], transforming it into a Vec of [Op]
|
||||
pub fn transform(self, woot: &Woot) -> WootResult<Vec<Op>> {
|
||||
let mut out = Vec::new();
|
||||
if self.is_empty() { return Ok(out); } // no-op
|
||||
|
@ -73,11 +79,11 @@ impl TextChange {
|
|||
similar::ChangeTag::Equal => {},
|
||||
similar::ChangeTag::Delete => match woot.delete_one(self.span.start + i) {
|
||||
Err(e) => tracing::error!("could not create deletion: {}", e),
|
||||
Ok(op) => out.push(op),
|
||||
Ok(op) => out.push(Op(op)),
|
||||
},
|
||||
similar::ChangeTag::Insert => {
|
||||
match woot.insert(self.span.start + i, diff.value()) {
|
||||
Ok(mut op) => out.append(&mut op),
|
||||
Ok(ops) => for op in ops { out.push(Op(op)) },
|
||||
Err(e) => tracing::error!("could not create insertion: {}", e),
|
||||
}
|
||||
},
|
||||
|
|
63
src/api/cursor.rs
Normal file
63
src/api/cursor.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
//! # Cursor
|
||||
//!
|
||||
//! represents the position of an user's cursor, with
|
||||
//! information about their identity
|
||||
|
||||
use uuid::Uuid;
|
||||
use codemp_proto as proto;
|
||||
|
||||
/// user cursor position in a buffer
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Cursor {
|
||||
/// range of text change, as char indexes in buffer previous state
|
||||
pub start: (i32, i32),
|
||||
pub end: (i32, i32),
|
||||
pub buffer: String,
|
||||
pub user: Option<Uuid>,
|
||||
}
|
||||
|
||||
|
||||
impl From<proto::cursor::CursorPosition> for Cursor {
|
||||
fn from(value: proto::cursor::CursorPosition) -> Self {
|
||||
Self {
|
||||
start: (value.start.row, value.start.col),
|
||||
end: (value.end.row, value.end.col),
|
||||
buffer: value.buffer.path,
|
||||
user: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Cursor> for proto::cursor::CursorPosition {
|
||||
fn from(value: Cursor) -> Self {
|
||||
Self {
|
||||
buffer: proto::files::BufferNode { path: value.buffer },
|
||||
start: proto::cursor::RowCol { row: value.start.0, col: value.start.1 },
|
||||
end: proto::cursor::RowCol { row: value.end.0, col: value.end.1 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<proto::cursor::CursorEvent> for Cursor {
|
||||
fn from(value: proto::cursor::CursorEvent) -> Self {
|
||||
Self {
|
||||
start: (value.position.start.row, value.position.start.col),
|
||||
end: (value.position.end.row, value.position.end.col),
|
||||
buffer: value.position.buffer.path,
|
||||
user: Uuid::parse_str(&value.user.id).ok(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Cursor> for proto::cursor::CursorEvent {
|
||||
fn from(value: Cursor) -> Self {
|
||||
Self {
|
||||
user: proto::common::Identity { id: value.user.unwrap_or_default().to_string() },
|
||||
position: proto::cursor::CursorPosition {
|
||||
buffer: proto::files::BufferNode { path: value.buffer },
|
||||
start: proto::cursor::RowCol { row: value.start.0, col: value.start.1 },
|
||||
end: proto::cursor::RowCol { row: value.end.0, col: value.end.1 },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,5 +10,10 @@ pub mod controller;
|
|||
/// a generic representation of a text change
|
||||
pub mod change;
|
||||
|
||||
/// representation for an user's cursor
|
||||
pub mod cursor;
|
||||
|
||||
pub use controller::Controller;
|
||||
pub use change::TextChange;
|
||||
pub use change::Op;
|
||||
pub use cursor::Cursor;
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::hash::{Hash, Hasher};
|
|||
use tokio::sync::{watch, mpsc, oneshot};
|
||||
use tonic::{async_trait, Streaming};
|
||||
use uuid::Uuid;
|
||||
use woot::crdt::{Op, CRDT};
|
||||
use woot::crdt::CRDT;
|
||||
use woot::woot::Woot;
|
||||
|
||||
use crate::errors::IgnorableError;
|
||||
|
@ -99,12 +99,12 @@ impl ControllerWorker<TextChange> for BufferWorker {
|
|||
Err(e) => break tracing::error!("could not apply operation from client: {}", e),
|
||||
Ok(ops) => {
|
||||
for op in ops {
|
||||
self.buffer.merge(op.clone());
|
||||
self.buffer.merge(op.0.clone());
|
||||
let operation = Operation {
|
||||
data: postcard::to_extend(&op, Vec::new()).unwrap(),
|
||||
data: postcard::to_extend(&op.0, Vec::new()).unwrap(),
|
||||
};
|
||||
if let Err(e) = tx.send(operation).await {
|
||||
tracing::error!("server refused to broadcast {}: {}", op, e);
|
||||
tracing::error!("server refused to broadcast {}: {}", op.0, e);
|
||||
}
|
||||
}
|
||||
self.content.send(self.buffer.view())
|
||||
|
@ -117,7 +117,7 @@ impl ControllerWorker<TextChange> for BufferWorker {
|
|||
res = rx.message() => match res {
|
||||
Err(_e) => break,
|
||||
Ok(None) => break,
|
||||
Ok(Some(change)) => match postcard::from_bytes::<Op>(&change.op.data) {
|
||||
Ok(Some(change)) => match postcard::from_bytes::<woot::crdt::Op>(&change.op.data) {
|
||||
Ok(op) => { // TODO here in change we receive info about the author, maybe propagate?
|
||||
self.buffer.merge(op);
|
||||
self.content.send(self.buffer.view()).unwrap_or_warn("could not send buffer update");
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||
use tokio::sync::{mpsc, broadcast::{self}, Mutex, watch};
|
||||
use tonic::{Streaming, async_trait};
|
||||
|
||||
use crate::{api::controller::ControllerWorker, errors::IgnorableError};
|
||||
use crate::{api::{controller::ControllerWorker, Cursor}, errors::IgnorableError};
|
||||
use codemp_proto::cursor::{CursorPosition, CursorEvent};
|
||||
|
||||
use super::controller::CursorController;
|
||||
|
@ -37,7 +37,7 @@ impl Default for CursorWorker {
|
|||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ControllerWorker<CursorEvent> for CursorWorker {
|
||||
impl ControllerWorker<Cursor> for CursorWorker {
|
||||
type Controller = CursorController;
|
||||
type Tx = mpsc::Sender<CursorPosition>;
|
||||
type Rx = Streaming<CursorEvent>;
|
||||
|
|
|
@ -7,15 +7,14 @@ pub use crate::{
|
|||
Result as CodempResult,
|
||||
};
|
||||
|
||||
pub use crate::woot::crdt::Op as CodempOp;
|
||||
|
||||
pub use crate::api::{
|
||||
Controller as CodempController,
|
||||
TextChange as CodempTextChange,
|
||||
Cursor as CodempCursor,
|
||||
Op as CodempOp,
|
||||
};
|
||||
|
||||
pub use crate::{
|
||||
// Instance as CodempInstance,
|
||||
client::Client as CodempClient,
|
||||
workspace::Workspace as CodempWorkspace,
|
||||
workspace::UserInfo as CodempUserInfo,
|
||||
|
|
Loading…
Reference in a new issue