diff --git a/src/api/change.rs b/src/api/change.rs new file mode 100644 index 0000000..e911fae --- /dev/null +++ b/src/api/change.rs @@ -0,0 +1,56 @@ +//! # TextChange +//! this represent a range in the previous state of the string and a new content which should be +//! replaced to it, allowing to represent any combination of deletions, insertions or replacements +//! +//! bulk and widespread operations will result in a TextChange effectively sending the whole new +//! buffer, but small changes are efficient and easy to create or apply +//! +//! ### examples +//! to insert 'a' after 4th character we should send a +//! `TextChange { span: 4..4, content: "a".into() }` +//! +//! to delete a the fourth character we should send a +//! `TextChange { span: 3..4, content: "".into() }` +//! + +/// an editor-friendly representation of a text change in a buffer +/// span refers to previous text content +#[derive(Clone, Debug, Default)] +pub struct TextChange { + /// range of text change, as char indexes in buffer previous state + pub span: std::ops::Range, + /// new content of text inside span + pub content: String, +} + +impl TextChange { + /// create a new TextChange from the difference of given strings + pub fn from_diff(before: &str, after: &str) -> TextChange { + let diff = similar::TextDiff::from_chars(before, after); + let mut start = 0; + let mut end = 0; + let mut from_beginning = true; + for op in diff.ops() { + match op { + similar::DiffOp::Equal { .. } => { + if from_beginning { + start += 1 + } else { + end += 1 + } + }, + _ => { + end = 0; + from_beginning = false; + } + } + } + let end_before = before.len() - end; + let end_after = after.len() - end; + + TextChange { + span: start..end_before, + content: after[start..end_after].to_string(), + } + } +} diff --git a/src/api/mod.rs b/src/api/mod.rs index 12a6070..fcb5f3e 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -7,4 +7,8 @@ /// a generic async provider for bidirectional communication pub mod controller; +/// a generic representation of a text change +pub mod change; + pub use controller::Controller; +pub use change::TextChange; diff --git a/src/buffer/controller.rs b/src/buffer/controller.rs index fd44772..52f86e2 100644 --- a/src/buffer/controller.rs +++ b/src/buffer/controller.rs @@ -11,7 +11,7 @@ use tonic::async_trait; use crate::errors::IgnorableError; use crate::{api::Controller, Error}; -use super::TextChange; +use crate::api::TextChange; /// the buffer controller implementation /// diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 6dde13c..1610400 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -12,18 +12,3 @@ pub mod controller; pub(crate) mod worker; pub use controller::BufferController as Controller; - - -/// an editor-friendly representation of a text change in a buffer -/// -/// TODO move in proto -#[derive(Clone, Debug, Default)] -pub struct TextChange { - /// range of text change, as byte indexes in buffer - pub span: std::ops::Range, - /// content of text change, as string - pub content: String, - /// content after this text change - /// note that this field will probably be dropped, don't rely on it - pub after: String -} diff --git a/src/buffer/worker.rs b/src/buffer/worker.rs index a8f7aa1..18e9510 100644 --- a/src/buffer/worker.rs +++ b/src/buffer/worker.rs @@ -12,8 +12,8 @@ use crate::errors::IgnorableError; use crate::proto::{OperationRequest, RawOp}; use crate::proto::buffer_client::BufferClient; use crate::api::controller::ControllerWorker; +use crate::api::TextChange; -use super::TextChange; use super::controller::BufferController; diff --git a/src/prelude.rs b/src/prelude.rs index e49e505..4a147be 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,14 +11,16 @@ pub use crate::{ pub use crate::ot::OperationSeq as CodempOperationSeq; #[cfg(feature = "api")] -pub use crate::api::Controller as CodempController; +pub use crate::api::{ + Controller as CodempController, + TextChange as CodempTextChange, +}; #[cfg(feature = "client")] pub use crate::{ client::Client as CodempClient, cursor::Controller as CodempCursorController, buffer::Controller as CodempBufferController, - buffer::TextChange as CodempTextChange, Instance as CodempInstance, };