mirror of
https://github.com/hexedtech/codemp.git
synced 2024-11-22 07:14:50 +01:00
fix: cleaned up code, fixed multi-op change issues
This commit is contained in:
parent
99a268185a
commit
c9a36ea8ec
3 changed files with 49 additions and 52 deletions
|
@ -10,7 +10,7 @@ name = "codemp"
|
|||
# core
|
||||
tracing = "0.1"
|
||||
# woot
|
||||
codemp-woot = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/woot.git", features = ["serde"], tag = "v0.1.1", optional = true }
|
||||
codemp-woot = { git = "ssh://git@github.com/codewithotherpeopleandchangenamelater/woot.git", features = ["serde"], tag = "v0.1.2", optional = true }
|
||||
# proto
|
||||
uuid = { version = "1.3.1", features = ["v4"], optional = true }
|
||||
tonic = { version = "0.9", features = ["tls", "tls-roots"], optional = true }
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
//! an editor-friendly representation of a text change in a buffer
|
||||
//! to easily interface with codemp from various editors
|
||||
|
||||
use crate::proto::cursor::RowCol;
|
||||
#[cfg(feature = "woot")]
|
||||
use crate::woot::{WootResult, woot::Woot, crdt::{TextEditor, CRDT, Op}};
|
||||
|
||||
/// an editor-friendly representation of a text change in a buffer
|
||||
///
|
||||
|
@ -59,6 +60,33 @@ impl TextChange {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "woot")]
|
||||
pub fn transform(self, woot: &Woot) -> WootResult<Vec<Op>> {
|
||||
let mut out = Vec::new();
|
||||
if self.is_empty() { return Ok(out); } // no-op
|
||||
let view = woot.view();
|
||||
let Some(span) = view.get(self.span.clone()) else {
|
||||
return Err(crate::woot::WootError::OutOfBounds);
|
||||
};
|
||||
let diff = similar::TextDiff::from_chars(span, &self.content);
|
||||
for (i, diff) in diff.iter_all_changes().enumerate() {
|
||||
match diff.tag() {
|
||||
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),
|
||||
},
|
||||
similar::ChangeTag::Insert => {
|
||||
match woot.insert(self.span.start + i, diff.value()) {
|
||||
Ok(mut op) => out.append(&mut op),
|
||||
Err(e) => tracing::error!("could not create insertion: {}", e),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// returns true if this TextChange deletes existing text
|
||||
pub fn is_deletion(&self) -> bool {
|
||||
!self.span.is_empty()
|
||||
|
@ -84,11 +112,12 @@ impl TextChange {
|
|||
|
||||
/// convert from byte index to row and column
|
||||
/// txt must be the whole content of the buffer, in order to count lines
|
||||
pub fn index_to_rowcol(txt: &str, index: usize) -> RowCol {
|
||||
#[cfg(feature = "transport")]
|
||||
pub fn index_to_rowcol(txt: &str, index: usize) -> crate::proto::cursor::RowCol {
|
||||
// FIXME might panic, use .get()
|
||||
let row = txt[..index].matches('\n').count() as i32;
|
||||
let col = txt[..index].split('\n').last().unwrap_or("").len() as i32;
|
||||
RowCol { row, col }
|
||||
crate::proto::cursor::RowCol { row, col }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use similar::{TextDiff, ChangeTag};
|
||||
use tokio::sync::{watch, mpsc, oneshot};
|
||||
use tonic::{async_trait, Streaming};
|
||||
use uuid::Uuid;
|
||||
use woot::crdt::{Op, CRDT, TextEditor};
|
||||
use woot::crdt::{Op, CRDT};
|
||||
use woot::woot::Woot;
|
||||
|
||||
use crate::errors::IgnorableError;
|
||||
|
@ -95,53 +94,22 @@ impl ControllerWorker<TextChange> for BufferWorker {
|
|||
|
||||
// received a text change from editor
|
||||
res = self.operations.recv() => match res {
|
||||
None => break,
|
||||
Some(change) => {
|
||||
if !change.is_empty() {
|
||||
let view = self.buffer.view();
|
||||
match view.get(change.span.clone()) {
|
||||
None => tracing::error!("received illegal span from client: {:?} but buffer is of len {}", change.span, view.len()),
|
||||
Some(span) => {
|
||||
let diff = TextDiff::from_chars(span, &change.content);
|
||||
|
||||
let mut i = 0;
|
||||
let mut ops = Vec::new();
|
||||
for diff in diff.iter_all_changes() {
|
||||
match diff.tag() {
|
||||
ChangeTag::Equal => i += 1,
|
||||
ChangeTag::Delete => match self.buffer.delete(change.span.start + i) {
|
||||
Ok(op) => ops.push(op),
|
||||
Err(e) => tracing::error!("could not apply deletion: {}", e),
|
||||
},
|
||||
ChangeTag::Insert => {
|
||||
for c in diff.value().chars() {
|
||||
match self.buffer.insert(change.span.start + i, c) {
|
||||
Ok(op) => {
|
||||
ops.push(op);
|
||||
i += 1;
|
||||
},
|
||||
Err(e) => tracing::error!("could not apply insertion: {}", e),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
for op in ops {
|
||||
let operation = Operation {
|
||||
data: postcard::to_extend(&op, Vec::new()).unwrap(),
|
||||
};
|
||||
|
||||
match tx.send(operation).await {
|
||||
Err(e) => tracing::error!("server refused to broadcast {}: {}", op, e),
|
||||
Ok(()) => {
|
||||
self.content.send(self.buffer.view()).unwrap_or_warn("could not send buffer update");
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
None => break tracing::debug!("stopping: editor closed channel"),
|
||||
Some(change) => match change.transform(&self.buffer) {
|
||||
Err(e) => break tracing::error!("could not apply operation from client: {}", e),
|
||||
Ok(ops) => {
|
||||
for op in ops {
|
||||
self.buffer.merge(op.clone());
|
||||
let operation = Operation {
|
||||
data: postcard::to_extend(&op, Vec::new()).unwrap(),
|
||||
};
|
||||
if let Err(e) = tx.send(operation).await {
|
||||
tracing::error!("server refused to broadcast {}: {}", op, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.content.send(self.buffer.view())
|
||||
.unwrap_or_warn("could not send buffer update");
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in a new issue