Merge pull request #702 from AppFlowy-IO/feat/operation_transform_documentation

chore: add documentation for lib-ot crate
This commit is contained in:
Nathan.fooo 2022-07-26 11:59:33 +08:00 committed by GitHub
commit 7728206e42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 624 additions and 437 deletions

View File

@ -215,7 +215,7 @@ impl DefaultFolderBuilder {
for app in workspace_rev.apps.iter() { for app in workspace_rev.apps.iter() {
for (index, view) in app.belongings.iter().enumerate() { for (index, view) in app.belongings.iter().enumerate() {
let view_data = if index == 0 { let view_data = if index == 0 {
initial_read_me().to_delta_str() initial_read_me().to_json_str()
} else { } else {
initial_quill_delta_string() initial_quill_delta_string()
}; };

View File

@ -10,7 +10,7 @@ use flowy_sync::{
entities::{revision::Revision, ws_data::ServerRevisionWSData}, entities::{revision::Revision, ws_data::ServerRevisionWSData},
}; };
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
use lib_ot::core::PlainTextAttributes; use lib_ot::core::PhantomAttributes;
use parking_lot::RwLock; use parking_lot::RwLock;
use std::sync::Arc; use std::sync::Arc;
@ -80,7 +80,7 @@ impl FolderEditor {
pub(crate) fn apply_change(&self, change: FolderChange) -> FlowyResult<()> { pub(crate) fn apply_change(&self, change: FolderChange) -> FlowyResult<()> {
let FolderChange { delta, md5 } = change; let FolderChange { delta, md5 } = change;
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair(); let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let delta_data = delta.to_delta_bytes(); let delta_data = delta.to_json_bytes();
let revision = Revision::new( let revision = Revision::new(
&self.rev_manager.object_id, &self.rev_manager.object_id,
base_rev_id, base_rev_id,
@ -132,7 +132,7 @@ impl FolderEditor {
pub struct FolderRevisionCompactor(); pub struct FolderRevisionCompactor();
impl RevisionCompactor for FolderRevisionCompactor { impl RevisionCompactor for FolderRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> { fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?; let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
Ok(delta.to_delta_bytes()) Ok(delta.to_json_bytes())
} }
} }

View File

@ -110,7 +110,7 @@ impl FolderPersistence {
pub async fn save_folder(&self, user_id: &str, folder_id: &FolderId, folder: FolderPad) -> FlowyResult<()> { pub async fn save_folder(&self, user_id: &str, folder_id: &FolderId, folder: FolderPad) -> FlowyResult<()> {
let pool = self.database.db_pool()?; let pool = self.database.db_pool()?;
let json = folder.to_json()?; let json = folder.to_json()?;
let delta_data = PlainTextDeltaBuilder::new().insert(&json).build().to_delta_bytes(); let delta_data = PlainTextDeltaBuilder::new().insert(&json).build().to_json_bytes();
let revision = Revision::initial_revision(user_id, folder_id.as_ref(), delta_data); let revision = Revision::initial_revision(user_id, folder_id.as_ref(), delta_data);
let record = RevisionRecord { let record = RevisionRecord {
revision, revision,

View File

@ -10,7 +10,7 @@ use flowy_sync::{
}, },
}; };
use lib_infra::future::{BoxResultFuture, FutureResult}; use lib_infra::future::{BoxResultFuture, FutureResult};
use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta}; use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta};
use parking_lot::RwLock; use parking_lot::RwLock;
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
@ -24,7 +24,7 @@ pub(crate) async fn make_folder_ws_manager(
) -> Arc<RevisionWebSocketManager> { ) -> Arc<RevisionWebSocketManager> {
let ws_data_provider = Arc::new(WSDataProvider::new(folder_id, Arc::new(rev_manager.clone()))); let ws_data_provider = Arc::new(WSDataProvider::new(folder_id, Arc::new(rev_manager.clone())));
let resolver = Arc::new(FolderConflictResolver { folder_pad }); let resolver = Arc::new(FolderConflictResolver { folder_pad });
let conflict_controller = ConflictController::<PlainTextAttributes>::new( let conflict_controller = ConflictController::<PhantomAttributes>::new(
user_id, user_id,
resolver, resolver,
Arc::new(ws_data_provider.clone()), Arc::new(ws_data_provider.clone()),
@ -55,7 +55,7 @@ struct FolderConflictResolver {
folder_pad: Arc<RwLock<FolderPad>>, folder_pad: Arc<RwLock<FolderPad>>,
} }
impl ConflictResolver<PlainTextAttributes> for FolderConflictResolver { impl ConflictResolver<PhantomAttributes> for FolderConflictResolver {
fn compose_delta(&self, delta: PlainTextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> { fn compose_delta(&self, delta: PlainTextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> {
let folder_pad = self.folder_pad.clone(); let folder_pad = self.folder_pad.clone();
Box::pin(async move { Box::pin(async move {
@ -67,7 +67,7 @@ impl ConflictResolver<PlainTextAttributes> for FolderConflictResolver {
fn transform_delta( fn transform_delta(
&self, &self,
delta: PlainTextDelta, delta: PlainTextDelta,
) -> BoxResultFuture<TransformDeltas<PlainTextAttributes>, FlowyError> { ) -> BoxResultFuture<TransformDeltas<PhantomAttributes>, FlowyError> {
let folder_pad = self.folder_pad.clone(); let folder_pad = self.folder_pad.clone();
Box::pin(async move { Box::pin(async move {
let read_guard = folder_pad.read(); let read_guard = folder_pad.read();

View File

@ -192,7 +192,7 @@ pub async fn make_grid_view_data(
// Create grid's block // Create grid's block
let grid_block_delta = make_grid_block_delta(block_meta_data); let grid_block_delta = make_grid_block_delta(block_meta_data);
let block_delta_data = grid_block_delta.to_delta_bytes(); let block_delta_data = grid_block_delta.to_json_bytes();
let repeated_revision: RepeatedRevision = let repeated_revision: RepeatedRevision =
Revision::initial_revision(user_id, block_id, block_delta_data).into(); Revision::initial_revision(user_id, block_id, block_delta_data).into();
let _ = grid_manager.create_grid_block(&block_id, repeated_revision).await?; let _ = grid_manager.create_grid_block(&block_id, repeated_revision).await?;
@ -202,7 +202,7 @@ pub async fn make_grid_view_data(
// Create grid // Create grid
let grid_meta_delta = make_grid_delta(&grid_rev); let grid_meta_delta = make_grid_delta(&grid_rev);
let grid_delta_data = grid_meta_delta.to_delta_bytes(); let grid_delta_data = grid_meta_delta.to_json_bytes();
let repeated_revision: RepeatedRevision = let repeated_revision: RepeatedRevision =
Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into(); Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into();
let _ = grid_manager.create_grid(view_id, repeated_revision).await?; let _ = grid_manager.create_grid(view_id, repeated_revision).await?;

View File

@ -7,7 +7,7 @@ use flowy_sync::client_grid::{GridBlockMetaChange, GridBlockRevisionPad};
use flowy_sync::entities::revision::Revision; use flowy_sync::entities::revision::Revision;
use flowy_sync::util::make_delta_from_revisions; use flowy_sync::util::make_delta_from_revisions;
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
use lib_ot::core::PlainTextAttributes; use lib_ot::core::PhantomAttributes;
use std::borrow::Cow; use std::borrow::Cow;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -161,7 +161,7 @@ impl GridBlockRevisionEditor {
let GridBlockMetaChange { delta, md5 } = change; let GridBlockMetaChange { delta, md5 } = change;
let user_id = self.user_id.clone(); let user_id = self.user_id.clone();
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair(); let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let delta_data = delta.to_delta_bytes(); let delta_data = delta.to_json_bytes();
let revision = Revision::new( let revision = Revision::new(
&self.rev_manager.object_id, &self.rev_manager.object_id,
base_rev_id, base_rev_id,
@ -200,7 +200,7 @@ impl RevisionObjectBuilder for GridBlockMetaPadBuilder {
pub struct GridBlockRevisionCompactor(); pub struct GridBlockRevisionCompactor();
impl RevisionCompactor for GridBlockRevisionCompactor { impl RevisionCompactor for GridBlockRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> { fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?; let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
Ok(delta.to_delta_bytes()) Ok(delta.to_json_bytes())
} }
} }

View File

@ -1,7 +1,7 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::entities::FieldType; use crate::entities::FieldType;
use crate::services::cell::{CellDataOperation}; use crate::services::cell::CellDataOperation;
use crate::services::field::type_options::checkbox_type_option::*; use crate::services::field::type_options::checkbox_type_option::*;
use crate::services::field::FieldBuilder; use crate::services::field::FieldBuilder;
use flowy_grid_data_model::revision::FieldRevision; use flowy_grid_data_model::revision::FieldRevision;

View File

@ -1,7 +1,7 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::entities::FieldType; use crate::entities::FieldType;
use crate::services::cell::{CellDataOperation}; use crate::services::cell::CellDataOperation;
use crate::services::field::*; use crate::services::field::*;
// use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat}; // use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat};
use flowy_grid_data_model::revision::FieldRevision; use flowy_grid_data_model::revision::FieldRevision;

View File

@ -21,7 +21,7 @@ use flowy_sync::entities::revision::Revision;
use flowy_sync::errors::CollaborateResult; use flowy_sync::errors::CollaborateResult;
use flowy_sync::util::make_delta_from_revisions; use flowy_sync::util::make_delta_from_revisions;
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
use lib_ot::core::PlainTextAttributes; use lib_ot::core::PhantomAttributes;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -573,7 +573,7 @@ impl GridRevisionEditor {
let GridChangeset { delta, md5 } = change; let GridChangeset { delta, md5 } = change;
let user_id = self.user.user_id()?; let user_id = self.user.user_id()?;
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair(); let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let delta_data = delta.to_delta_bytes(); let delta_data = delta.to_json_bytes();
let revision = Revision::new( let revision = Revision::new(
&self.rev_manager.object_id, &self.rev_manager.object_id,
base_rev_id, base_rev_id,
@ -664,8 +664,8 @@ impl RevisionCloudService for GridRevisionCloudService {
pub struct GridRevisionCompactor(); pub struct GridRevisionCompactor();
impl RevisionCompactor for GridRevisionCompactor { impl RevisionCompactor for GridRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> { fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?; let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
Ok(delta.to_delta_bytes()) Ok(delta.to_json_bytes())
} }
} }

View File

@ -48,7 +48,7 @@ impl GridMigration {
let pool = self.database.db_pool()?; let pool = self.database.db_pool()?;
let grid_rev_pad = self.get_grid_revision_pad(grid_id).await?; let grid_rev_pad = self.get_grid_revision_pad(grid_id).await?;
let json = grid_rev_pad.json_str()?; let json = grid_rev_pad.json_str()?;
let delta_data = PlainTextDeltaBuilder::new().insert(&json).build().to_delta_bytes(); let delta_data = PlainTextDeltaBuilder::new().insert(&json).build().to_json_bytes();
let revision = Revision::initial_revision(&user_id, grid_id, delta_data); let revision = Revision::initial_revision(&user_id, grid_id, delta_data);
let record = RevisionRecord::new(revision); let record = RevisionRecord::new(revision);
// //

View File

@ -9,7 +9,7 @@ use flowy_sync::{
util::make_delta_from_revisions, util::make_delta_from_revisions,
}; };
use lib_infra::future::BoxResultFuture; use lib_infra::future::BoxResultFuture;
use lib_ot::core::{Attributes, Delta, PlainTextAttributes}; use lib_ot::core::{Attributes, Delta, PhantomAttributes};
use lib_ot::rich_text::RichTextAttributes; use lib_ot::rich_text::RichTextAttributes;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use std::{convert::TryFrom, sync::Arc}; use std::{convert::TryFrom, sync::Arc};
@ -31,7 +31,7 @@ pub trait ConflictRevisionSink: Send + Sync + 'static {
} }
pub type RichTextConflictController = ConflictController<RichTextAttributes>; pub type RichTextConflictController = ConflictController<RichTextAttributes>;
pub type PlainTextConflictController = ConflictController<PlainTextAttributes>; pub type PlainTextConflictController = ConflictController<PhantomAttributes>;
pub struct ConflictController<T> pub struct ConflictController<T>
where where
@ -154,7 +154,7 @@ where
&rev_manager.object_id, &rev_manager.object_id,
base_rev_id, base_rev_id,
rev_id, rev_id,
client_delta.to_delta_bytes(), client_delta.to_json_bytes(),
user_id, user_id,
md5.clone(), md5.clone(),
); );
@ -166,7 +166,7 @@ where
&rev_manager.object_id, &rev_manager.object_id,
base_rev_id, base_rev_id,
rev_id, rev_id,
server_delta.to_delta_bytes(), server_delta.to_json_bytes(),
user_id, user_id,
md5, md5,
); );

View File

@ -238,7 +238,7 @@ impl RevisionObjectBuilder for TextBlockInfoBuilder {
Result::<DocumentPB, FlowyError>::Ok(DocumentPB { Result::<DocumentPB, FlowyError>::Ok(DocumentPB {
block_id: object_id.to_owned(), block_id: object_id.to_owned(),
text: delta.to_delta_str(), text: delta.to_json_str(),
rev_id, rev_id,
base_rev_id, base_rev_id,
}) })

View File

@ -175,7 +175,7 @@ impl EditBlockQueue {
} }
async fn save_local_delta(&self, delta: RichTextDelta, md5: String) -> Result<RevId, FlowyError> { async fn save_local_delta(&self, delta: RichTextDelta, md5: String) -> Result<RevId, FlowyError> {
let delta_data = delta.to_delta_bytes(); let delta_data = delta.to_json_bytes();
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair(); let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
let user_id = self.user.user_id()?; let user_id = self.user.user_id()?;
let revision = Revision::new( let revision = Revision::new(
@ -195,7 +195,7 @@ pub(crate) struct TextBlockRevisionCompactor();
impl RevisionCompactor for TextBlockRevisionCompactor { impl RevisionCompactor for TextBlockRevisionCompactor {
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> { fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
let delta = make_delta_from_revisions::<RichTextAttributes>(revisions)?; let delta = make_delta_from_revisions::<RichTextAttributes>(revisions)?;
Ok(delta.to_delta_bytes()) Ok(delta.to_json_bytes())
} }
} }

View File

@ -75,7 +75,7 @@ impl TextBlockEditorTest {
let delta = self.editor.text_block_delta().await.unwrap(); let delta = self.editor.text_block_delta().await.unwrap();
if expected_delta != delta { if expected_delta != delta {
eprintln!("✅ expect: {}", expected,); eprintln!("✅ expect: {}", expected,);
eprintln!("❌ receive: {}", delta.to_delta_str()); eprintln!("❌ receive: {}", delta.to_json_str());
} }
assert_eq!(expected_delta, delta); assert_eq!(expected_delta, delta);
} }

View File

@ -762,19 +762,19 @@ fn attributes_preserve_list_format_on_merge() {
#[test] #[test]
fn delta_compose() { fn delta_compose() {
let mut delta = RichTextDelta::from_delta_str(r#"[{"insert":"\n"}]"#).unwrap(); let mut delta = RichTextDelta::from_json_str(r#"[{"insert":"\n"}]"#).unwrap();
let deltas = vec![ let deltas = vec![
RichTextDelta::from_delta_str(r#"[{"retain":1,"attributes":{"list":"unchecked"}}]"#).unwrap(), RichTextDelta::from_json_str(r#"[{"retain":1,"attributes":{"list":"unchecked"}}]"#).unwrap(),
RichTextDelta::from_delta_str(r#"[{"insert":"a"}]"#).unwrap(), RichTextDelta::from_json_str(r#"[{"insert":"a"}]"#).unwrap(),
RichTextDelta::from_delta_str(r#"[{"retain":1},{"insert":"\n","attributes":{"list":"unchecked"}}]"#).unwrap(), RichTextDelta::from_json_str(r#"[{"retain":1},{"insert":"\n","attributes":{"list":"unchecked"}}]"#).unwrap(),
RichTextDelta::from_delta_str(r#"[{"retain":2},{"retain":1,"attributes":{"list":""}}]"#).unwrap(), RichTextDelta::from_json_str(r#"[{"retain":2},{"retain":1,"attributes":{"list":""}}]"#).unwrap(),
]; ];
for d in deltas { for d in deltas {
delta = delta.compose(&d).unwrap(); delta = delta.compose(&d).unwrap();
} }
assert_eq!( assert_eq!(
delta.to_delta_str(), delta.to_json_str(),
r#"[{"insert":"a"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"\n"}]"# r#"[{"insert":"a"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"\n"}]"#
); );

View File

@ -108,20 +108,20 @@ impl TestBuilder {
TestOp::Insert(delta_i, s, index) => { TestOp::Insert(delta_i, s, index) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let delta = document.insert(*index, s).unwrap(); let delta = document.insert(*index, s).unwrap();
tracing::debug!("Insert delta: {}", delta.to_delta_str()); tracing::debug!("Insert delta: {}", delta.to_json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Delete(delta_i, iv) => { TestOp::Delete(delta_i, iv) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let delta = document.replace(*iv, "").unwrap(); let delta = document.replace(*iv, "").unwrap();
tracing::trace!("Delete delta: {}", delta.to_delta_str()); tracing::trace!("Delete delta: {}", delta.to_json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Replace(delta_i, iv, s) => { TestOp::Replace(delta_i, iv, s) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let delta = document.replace(*iv, s).unwrap(); let delta = document.replace(*iv, s).unwrap();
tracing::trace!("Replace delta: {}", delta.to_delta_str()); tracing::trace!("Replace delta: {}", delta.to_json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::InsertBold(delta_i, s, iv) => { TestOp::InsertBold(delta_i, s, iv) => {
@ -133,7 +133,7 @@ impl TestBuilder {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Bold(*enable); let attribute = RichTextAttribute::Bold(*enable);
let delta = document.format(*iv, attribute).unwrap(); let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Bold delta: {}", delta.to_delta_str()); tracing::trace!("Bold delta: {}", delta.to_json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Italic(delta_i, iv, enable) => { TestOp::Italic(delta_i, iv, enable) => {
@ -143,28 +143,28 @@ impl TestBuilder {
false => RichTextAttribute::Italic(false), false => RichTextAttribute::Italic(false),
}; };
let delta = document.format(*iv, attribute).unwrap(); let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Italic delta: {}", delta.to_delta_str()); tracing::trace!("Italic delta: {}", delta.to_json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Header(delta_i, iv, level) => { TestOp::Header(delta_i, iv, level) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Header(*level); let attribute = RichTextAttribute::Header(*level);
let delta = document.format(*iv, attribute).unwrap(); let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Header delta: {}", delta.to_delta_str()); tracing::trace!("Header delta: {}", delta.to_json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Link(delta_i, iv, link) => { TestOp::Link(delta_i, iv, link) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Link(link.to_owned()); let attribute = RichTextAttribute::Link(link.to_owned());
let delta = document.format(*iv, attribute).unwrap(); let delta = document.format(*iv, attribute).unwrap();
tracing::trace!("Link delta: {}", delta.to_delta_str()); tracing::trace!("Link delta: {}", delta.to_json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
TestOp::Bullet(delta_i, iv, enable) => { TestOp::Bullet(delta_i, iv, enable) => {
let document = &mut self.documents[*delta_i]; let document = &mut self.documents[*delta_i];
let attribute = RichTextAttribute::Bullet(*enable); let attribute = RichTextAttribute::Bullet(*enable);
let delta = document.format(*iv, attribute).unwrap(); let delta = document.format(*iv, attribute).unwrap();
tracing::debug!("Bullet delta: {}", delta.to_delta_str()); tracing::debug!("Bullet delta: {}", delta.to_json_str());
self.deltas.insert(*delta_i, Some(delta)); self.deltas.insert(*delta_i, Some(delta));
} }
@ -194,15 +194,15 @@ impl TestBuilder {
let delta_a = &self.documents[*delta_a_i].delta(); let delta_a = &self.documents[*delta_a_i].delta();
let delta_b = &self.documents[*delta_b_i].delta(); let delta_b = &self.documents[*delta_b_i].delta();
tracing::debug!("Invert: "); tracing::debug!("Invert: ");
tracing::debug!("a: {}", delta_a.to_delta_str()); tracing::debug!("a: {}", delta_a.to_json_str());
tracing::debug!("b: {}", delta_b.to_delta_str()); tracing::debug!("b: {}", delta_b.to_json_str());
let (_, b_prime) = delta_a.transform(delta_b).unwrap(); let (_, b_prime) = delta_a.transform(delta_b).unwrap();
let undo = b_prime.invert(delta_a); let undo = b_prime.invert(delta_a);
let new_delta = delta_a.compose(&b_prime).unwrap(); let new_delta = delta_a.compose(&b_prime).unwrap();
tracing::debug!("new delta: {}", new_delta.to_delta_str()); tracing::debug!("new delta: {}", new_delta.to_json_str());
tracing::debug!("undo delta: {}", undo.to_delta_str()); tracing::debug!("undo delta: {}", undo.to_json_str());
let new_delta_after_undo = new_delta.compose(&undo).unwrap(); let new_delta_after_undo = new_delta.compose(&undo).unwrap();
@ -238,7 +238,7 @@ impl TestBuilder {
} }
TestOp::AssertPrimeJson(doc_i, expected) => { TestOp::AssertPrimeJson(doc_i, expected) => {
let prime_json = self.primes[*doc_i].as_ref().unwrap().to_delta_str(); let prime_json = self.primes[*doc_i].as_ref().unwrap().to_json_str();
let expected_prime: RichTextDelta = serde_json::from_str(expected).unwrap(); let expected_prime: RichTextDelta = serde_json::from_str(expected).unwrap();
let target_prime: RichTextDelta = serde_json::from_str(&prime_json).unwrap(); let target_prime: RichTextDelta = serde_json::from_str(&prime_json).unwrap();

View File

@ -2,6 +2,7 @@
use crate::editor::{Rng, TestBuilder, TestOp::*}; use crate::editor::{Rng, TestBuilder, TestOp::*};
use flowy_sync::client_document::{NewlineDoc, PlainDoc}; use flowy_sync::client_document::{NewlineDoc, PlainDoc};
use lib_ot::{ use lib_ot::{
core::Interval,
core::*, core::*,
rich_text::{AttributeBuilder, RichTextAttribute, RichTextAttributes, RichTextDelta}, rich_text::{AttributeBuilder, RichTextAttribute, RichTextAttributes, RichTextDelta},
}; };
@ -39,23 +40,23 @@ fn attributes_insert_text_at_middle() {
#[test] #[test]
fn delta_get_ops_in_interval_1() { fn delta_get_ops_in_interval_1() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
let insert_a = OpBuilder::insert("123").build(); let insert_a = OperationBuilder::insert("123").build();
let insert_b = OpBuilder::insert("4").build(); let insert_b = OperationBuilder::insert("4").build();
delta.add(insert_a.clone()); delta.add(insert_a.clone());
delta.add(insert_b.clone()); delta.add(insert_b.clone());
let mut iterator = DeltaIter::from_interval(&delta, Interval::new(0, 4)); let mut iterator = DeltaIterator::from_interval(&delta, Interval::new(0, 4));
assert_eq!(iterator.ops(), delta.ops); assert_eq!(iterator.ops(), delta.ops);
} }
#[test] #[test]
fn delta_get_ops_in_interval_2() { fn delta_get_ops_in_interval_2() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
let insert_a = OpBuilder::insert("123").build(); let insert_a = OperationBuilder::insert("123").build();
let insert_b = OpBuilder::insert("4").build(); let insert_b = OperationBuilder::insert("4").build();
let insert_c = OpBuilder::insert("5").build(); let insert_c = OperationBuilder::insert("5").build();
let retain_a = OpBuilder::retain(3).build(); let retain_a = OperationBuilder::retain(3).build();
delta.add(insert_a.clone()); delta.add(insert_a.clone());
delta.add(retain_a.clone()); delta.add(retain_a.clone());
@ -63,32 +64,32 @@ fn delta_get_ops_in_interval_2() {
delta.add(insert_c.clone()); delta.add(insert_c.clone());
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(0, 2)).ops(), DeltaIterator::from_interval(&delta, Interval::new(0, 2)).ops(),
vec![OpBuilder::insert("12").build()] vec![OperationBuilder::insert("12").build()]
); );
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(1, 3)).ops(), DeltaIterator::from_interval(&delta, Interval::new(1, 3)).ops(),
vec![OpBuilder::insert("23").build()] vec![OperationBuilder::insert("23").build()]
); );
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(0, 3)).ops(), DeltaIterator::from_interval(&delta, Interval::new(0, 3)).ops(),
vec![insert_a.clone()] vec![insert_a.clone()]
); );
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(0, 4)).ops(), DeltaIterator::from_interval(&delta, Interval::new(0, 4)).ops(),
vec![insert_a.clone(), OpBuilder::retain(1).build()] vec![insert_a.clone(), OperationBuilder::retain(1).build()]
); );
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(0, 6)).ops(), DeltaIterator::from_interval(&delta, Interval::new(0, 6)).ops(),
vec![insert_a.clone(), retain_a.clone()] vec![insert_a.clone(), retain_a.clone()]
); );
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(0, 7)).ops(), DeltaIterator::from_interval(&delta, Interval::new(0, 7)).ops(),
vec![insert_a.clone(), retain_a.clone(), insert_b.clone()] vec![insert_a.clone(), retain_a.clone(), insert_b.clone()]
); );
} }
@ -96,54 +97,60 @@ fn delta_get_ops_in_interval_2() {
#[test] #[test]
fn delta_get_ops_in_interval_3() { fn delta_get_ops_in_interval_3() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
let insert_a = OpBuilder::insert("123456").build(); let insert_a = OperationBuilder::insert("123456").build();
delta.add(insert_a.clone()); delta.add(insert_a.clone());
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(3, 5)).ops(), DeltaIterator::from_interval(&delta, Interval::new(3, 5)).ops(),
vec![OpBuilder::insert("45").build()] vec![OperationBuilder::insert("45").build()]
); );
} }
#[test] #[test]
fn delta_get_ops_in_interval_4() { fn delta_get_ops_in_interval_4() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
let insert_a = OpBuilder::insert("12").build(); let insert_a = OperationBuilder::insert("12").build();
let insert_b = OpBuilder::insert("34").build(); let insert_b = OperationBuilder::insert("34").build();
let insert_c = OpBuilder::insert("56").build(); let insert_c = OperationBuilder::insert("56").build();
delta.ops.push(insert_a.clone()); delta.ops.push(insert_a.clone());
delta.ops.push(insert_b.clone()); delta.ops.push(insert_b.clone());
delta.ops.push(insert_c.clone()); delta.ops.push(insert_c.clone());
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(0, 2)).ops(), DeltaIterator::from_interval(&delta, Interval::new(0, 2)).ops(),
vec![insert_a] vec![insert_a]
); );
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(2, 4)).ops(), DeltaIterator::from_interval(&delta, Interval::new(2, 4)).ops(),
vec![insert_b] vec![insert_b]
); );
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(4, 6)).ops(), DeltaIterator::from_interval(&delta, Interval::new(4, 6)).ops(),
vec![insert_c] vec![insert_c]
); );
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(2, 5)).ops(), DeltaIterator::from_interval(&delta, Interval::new(2, 5)).ops(),
vec![OpBuilder::insert("34").build(), OpBuilder::insert("5").build()] vec![
OperationBuilder::insert("34").build(),
OperationBuilder::insert("5").build()
]
); );
} }
#[test] #[test]
fn delta_get_ops_in_interval_5() { fn delta_get_ops_in_interval_5() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
let insert_a = OpBuilder::insert("123456").build(); let insert_a = OperationBuilder::insert("123456").build();
let insert_b = OpBuilder::insert("789").build(); let insert_b = OperationBuilder::insert("789").build();
delta.ops.push(insert_a.clone()); delta.ops.push(insert_a.clone());
delta.ops.push(insert_b.clone()); delta.ops.push(insert_b.clone());
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(4, 8)).ops(), DeltaIterator::from_interval(&delta, Interval::new(4, 8)).ops(),
vec![OpBuilder::insert("56").build(), OpBuilder::insert("78").build()] vec![
OperationBuilder::insert("56").build(),
OperationBuilder::insert("78").build()
]
); );
// assert_eq!( // assert_eq!(
@ -155,54 +162,60 @@ fn delta_get_ops_in_interval_5() {
#[test] #[test]
fn delta_get_ops_in_interval_6() { fn delta_get_ops_in_interval_6() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
let insert_a = OpBuilder::insert("12345678").build(); let insert_a = OperationBuilder::insert("12345678").build();
delta.add(insert_a.clone()); delta.add(insert_a.clone());
assert_eq!( assert_eq!(
DeltaIter::from_interval(&delta, Interval::new(4, 6)).ops(), DeltaIterator::from_interval(&delta, Interval::new(4, 6)).ops(),
vec![OpBuilder::insert("56").build()] vec![OperationBuilder::insert("56").build()]
); );
} }
#[test] #[test]
fn delta_get_ops_in_interval_7() { fn delta_get_ops_in_interval_7() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
let insert_a = OpBuilder::insert("12345").build(); let insert_a = OperationBuilder::insert("12345").build();
let retain_a = OpBuilder::retain(3).build(); let retain_a = OperationBuilder::retain(3).build();
delta.add(insert_a.clone()); delta.add(insert_a.clone());
delta.add(retain_a.clone()); delta.add(retain_a.clone());
let mut iter_1 = DeltaIter::from_offset(&delta, 2); let mut iter_1 = DeltaIterator::from_offset(&delta, 2);
assert_eq!(iter_1.next_op().unwrap(), OpBuilder::insert("345").build()); assert_eq!(iter_1.next_op().unwrap(), OperationBuilder::insert("345").build());
assert_eq!(iter_1.next_op().unwrap(), OpBuilder::retain(3).build()); assert_eq!(iter_1.next_op().unwrap(), OperationBuilder::retain(3).build());
let mut iter_2 = DeltaIter::new(&delta); let mut iter_2 = DeltaIterator::new(&delta);
assert_eq!(iter_2.next_op_with_len(2).unwrap(), OpBuilder::insert("12").build()); assert_eq!(
assert_eq!(iter_2.next_op().unwrap(), OpBuilder::insert("345").build()); iter_2.next_op_with_len(2).unwrap(),
OperationBuilder::insert("12").build()
);
assert_eq!(iter_2.next_op().unwrap(), OperationBuilder::insert("345").build());
assert_eq!(iter_2.next_op().unwrap(), OpBuilder::retain(3).build()); assert_eq!(iter_2.next_op().unwrap(), OperationBuilder::retain(3).build());
} }
#[test] #[test]
fn delta_op_seek() { fn delta_op_seek() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
let insert_a = OpBuilder::insert("12345").build(); let insert_a = OperationBuilder::insert("12345").build();
let retain_a = OpBuilder::retain(3).build(); let retain_a = OperationBuilder::retain(3).build();
delta.add(insert_a.clone()); delta.add(insert_a.clone());
delta.add(retain_a.clone()); delta.add(retain_a.clone());
let mut iter = DeltaIter::new(&delta); let mut iter = DeltaIterator::new(&delta);
iter.seek::<OpMetric>(1); iter.seek::<OpMetric>(1);
assert_eq!(iter.next_op().unwrap(), OpBuilder::retain(3).build()); assert_eq!(iter.next_op().unwrap(), retain_a);
} }
#[test] #[test]
fn delta_utf16_code_unit_seek() { fn delta_utf16_code_unit_seek() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
delta.add(OpBuilder::insert("12345").build()); delta.add(OperationBuilder::insert("12345").build());
let mut iter = DeltaIter::new(&delta); let mut iter = DeltaIterator::new(&delta);
iter.seek::<Utf16CodeUnitMetric>(3); iter.seek::<Utf16CodeUnitMetric>(3);
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("45").build()); assert_eq!(
iter.next_op_with_len(2).unwrap(),
OperationBuilder::insert("45").build()
);
} }
#[test] #[test]
@ -213,77 +226,92 @@ fn delta_utf16_code_unit_seek_with_attributes() {
.add_attr(RichTextAttribute::Italic(true)) .add_attr(RichTextAttribute::Italic(true))
.build(); .build();
delta.add(OpBuilder::insert("1234").attributes(attributes.clone()).build()); delta.add(OperationBuilder::insert("1234").attributes(attributes.clone()).build());
delta.add(OpBuilder::insert("\n").build()); delta.add(OperationBuilder::insert("\n").build());
let mut iter = DeltaIter::new(&delta); let mut iter = DeltaIterator::new(&delta);
iter.seek::<Utf16CodeUnitMetric>(0); iter.seek::<Utf16CodeUnitMetric>(0);
assert_eq!( assert_eq!(
iter.next_op_with_len(4).unwrap(), iter.next_op_with_len(4).unwrap(),
OpBuilder::insert("1234").attributes(attributes).build(), OperationBuilder::insert("1234").attributes(attributes).build(),
); );
} }
#[test] #[test]
fn delta_next_op_len() { fn delta_next_op_len() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
delta.add(OpBuilder::insert("12345").build()); delta.add(OperationBuilder::insert("12345").build());
let mut iter = DeltaIter::new(&delta); let mut iter = DeltaIterator::new(&delta);
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("12").build()); assert_eq!(
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("34").build()); iter.next_op_with_len(2).unwrap(),
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("5").build()); OperationBuilder::insert("12").build()
);
assert_eq!(
iter.next_op_with_len(2).unwrap(),
OperationBuilder::insert("34").build()
);
assert_eq!(iter.next_op_with_len(2).unwrap(), OperationBuilder::insert("5").build());
assert_eq!(iter.next_op_with_len(1), None); assert_eq!(iter.next_op_with_len(1), None);
} }
#[test] #[test]
fn delta_next_op_len_with_chinese() { fn delta_next_op_len_with_chinese() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
delta.add(OpBuilder::insert("你好").build()); delta.add(OperationBuilder::insert("你好").build());
let mut iter = DeltaIter::new(&delta); let mut iter = DeltaIterator::new(&delta);
assert_eq!(iter.next_op_len().unwrap(), 2); assert_eq!(iter.next_op_len().unwrap(), 2);
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("你好").build()); assert_eq!(
iter.next_op_with_len(2).unwrap(),
OperationBuilder::insert("你好").build()
);
} }
#[test] #[test]
fn delta_next_op_len_with_english() { fn delta_next_op_len_with_english() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
delta.add(OpBuilder::insert("ab").build()); delta.add(OperationBuilder::insert("ab").build());
let mut iter = DeltaIter::new(&delta); let mut iter = DeltaIterator::new(&delta);
assert_eq!(iter.next_op_len().unwrap(), 2); assert_eq!(iter.next_op_len().unwrap(), 2);
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("ab").build()); assert_eq!(
iter.next_op_with_len(2).unwrap(),
OperationBuilder::insert("ab").build()
);
} }
#[test] #[test]
fn delta_next_op_len_after_seek() { fn delta_next_op_len_after_seek() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
delta.add(OpBuilder::insert("12345").build()); delta.add(OperationBuilder::insert("12345").build());
let mut iter = DeltaIter::new(&delta); let mut iter = DeltaIterator::new(&delta);
assert_eq!(iter.next_op_len().unwrap(), 5); assert_eq!(iter.next_op_len().unwrap(), 5);
iter.seek::<Utf16CodeUnitMetric>(3); iter.seek::<Utf16CodeUnitMetric>(3);
assert_eq!(iter.next_op_len().unwrap(), 2); assert_eq!(iter.next_op_len().unwrap(), 2);
assert_eq!(iter.next_op_with_len(1).unwrap(), OpBuilder::insert("4").build()); assert_eq!(iter.next_op_with_len(1).unwrap(), OperationBuilder::insert("4").build());
assert_eq!(iter.next_op_len().unwrap(), 1); assert_eq!(iter.next_op_len().unwrap(), 1);
assert_eq!(iter.next_op().unwrap(), OpBuilder::insert("5").build()); assert_eq!(iter.next_op().unwrap(), OperationBuilder::insert("5").build());
} }
#[test] #[test]
fn delta_next_op_len_none() { fn delta_next_op_len_none() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
delta.add(OpBuilder::insert("12345").build()); delta.add(OperationBuilder::insert("12345").build());
let mut iter = DeltaIter::new(&delta); let mut iter = DeltaIterator::new(&delta);
assert_eq!(iter.next_op_len().unwrap(), 5); assert_eq!(iter.next_op_len().unwrap(), 5);
assert_eq!(iter.next_op_with_len(5).unwrap(), OpBuilder::insert("12345").build()); assert_eq!(
iter.next_op_with_len(5).unwrap(),
OperationBuilder::insert("12345").build()
);
assert_eq!(iter.next_op_len(), None); assert_eq!(iter.next_op_len(), None);
} }
#[test] #[test]
fn delta_next_op_with_len_zero() { fn delta_next_op_with_len_zero() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
delta.add(OpBuilder::insert("12345").build()); delta.add(OperationBuilder::insert("12345").build());
let mut iter = DeltaIter::new(&delta); let mut iter = DeltaIterator::new(&delta);
assert_eq!(iter.next_op_with_len(0), None,); assert_eq!(iter.next_op_with_len(0), None,);
assert_eq!(iter.next_op_len().unwrap(), 5); assert_eq!(iter.next_op_len().unwrap(), 5);
} }
@ -291,14 +319,14 @@ fn delta_next_op_with_len_zero() {
#[test] #[test]
fn delta_next_op_with_len_cross_op_return_last() { fn delta_next_op_with_len_cross_op_return_last() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
delta.add(OpBuilder::insert("12345").build()); delta.add(OperationBuilder::insert("12345").build());
delta.add(OpBuilder::retain(1).build()); delta.add(OperationBuilder::retain(1).build());
delta.add(OpBuilder::insert("678").build()); delta.add(OperationBuilder::insert("678").build());
let mut iter = DeltaIter::new(&delta); let mut iter = DeltaIterator::new(&delta);
iter.seek::<Utf16CodeUnitMetric>(4); iter.seek::<Utf16CodeUnitMetric>(4);
assert_eq!(iter.next_op_len().unwrap(), 1); assert_eq!(iter.next_op_len().unwrap(), 1);
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::retain(1).build()); assert_eq!(iter.next_op_with_len(2).unwrap(), OperationBuilder::retain(1).build());
} }
#[test] #[test]
@ -342,18 +370,17 @@ fn apply_1000() {
} }
#[test] #[test]
fn apply() { fn apply_test() {
let s = "hello world,".to_owned(); let s = "hello";
let mut delta_a = RichTextDelta::default(); let delta_a = PlainTextDeltaBuilder::new().insert(s).build();
delta_a.insert(&s, RichTextAttributes::default()); let delta_b = PlainTextDeltaBuilder::new()
.retain(s.len())
.insert(", AppFlowy")
.build();
let mut delta_b = RichTextDelta::default(); let after_a = delta_a.content_str().unwrap();
delta_b.retain(s.len(), RichTextAttributes::default());
delta_b.insert("appflowy", RichTextAttributes::default());
let after_a = delta_a.apply("").unwrap();
let after_b = delta_b.apply(&after_a).unwrap(); let after_b = delta_b.apply(&after_a).unwrap();
assert_eq!("hello world,appflowy", &after_b); assert_eq!("hello, AppFlowy", &after_b);
} }
#[test] #[test]
@ -384,6 +411,17 @@ fn invert() {
} }
} }
#[test]
fn invert_test() {
let s = "hello world";
let delta = PlainTextDeltaBuilder::new().insert(s).build();
let invert_delta = delta.invert_str("");
assert_eq!(delta.utf16_base_len, invert_delta.utf16_target_len);
assert_eq!(delta.utf16_target_len, invert_delta.utf16_base_len);
assert_eq!(invert_delta.apply(s).unwrap(), "")
}
#[test] #[test]
fn empty_ops() { fn empty_ops() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
@ -415,23 +453,24 @@ fn ops_merging() {
assert_eq!(delta.ops.len(), 0); assert_eq!(delta.ops.len(), 0);
delta.retain(2, RichTextAttributes::default()); delta.retain(2, RichTextAttributes::default());
assert_eq!(delta.ops.len(), 1); assert_eq!(delta.ops.len(), 1);
assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(2).build())); assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(2).build()));
delta.retain(3, RichTextAttributes::default()); delta.retain(3, RichTextAttributes::default());
assert_eq!(delta.ops.len(), 1); assert_eq!(delta.ops.len(), 1);
assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build())); assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(5).build()));
delta.insert("abc", RichTextAttributes::default()); delta.insert("abc", RichTextAttributes::default());
assert_eq!(delta.ops.len(), 2); assert_eq!(delta.ops.len(), 2);
assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abc").build())); assert_eq!(delta.ops.last(), Some(&OperationBuilder::insert("abc").build()));
delta.insert("xyz", RichTextAttributes::default()); delta.insert("xyz", RichTextAttributes::default());
assert_eq!(delta.ops.len(), 2); assert_eq!(delta.ops.len(), 2);
assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abcxyz").build())); assert_eq!(delta.ops.last(), Some(&OperationBuilder::insert("abcxyz").build()));
delta.delete(1); delta.delete(1);
assert_eq!(delta.ops.len(), 3); assert_eq!(delta.ops.len(), 3);
assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(1).build())); assert_eq!(delta.ops.last(), Some(&OperationBuilder::delete(1).build()));
delta.delete(1); delta.delete(1);
assert_eq!(delta.ops.len(), 3); assert_eq!(delta.ops.len(), 3);
assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(2).build())); assert_eq!(delta.ops.last(), Some(&OperationBuilder::delete(2).build()));
} }
#[test] #[test]
fn is_noop() { fn is_noop() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
@ -582,11 +621,11 @@ fn transform_two_conflict_non_seq_delta() {
#[test] #[test]
fn delta_invert_no_attribute_delta() { fn delta_invert_no_attribute_delta() {
let mut delta = RichTextDelta::default(); let mut delta = RichTextDelta::default();
delta.add(OpBuilder::insert("123").build()); delta.add(OperationBuilder::insert("123").build());
let mut change = RichTextDelta::default(); let mut change = RichTextDelta::default();
change.add(OpBuilder::retain(3).build()); change.add(OperationBuilder::retain(3).build());
change.add(OpBuilder::insert("456").build()); change.add(OperationBuilder::insert("456").build());
let undo = change.invert(&delta); let undo = change.invert(&delta);
let new_delta = delta.compose(&change).unwrap(); let new_delta = delta.compose(&change).unwrap();

View File

@ -11,7 +11,7 @@ fn operation_insert_serialize_test() {
.add_attr(RichTextAttribute::Bold(true)) .add_attr(RichTextAttribute::Bold(true))
.add_attr(RichTextAttribute::Italic(true)) .add_attr(RichTextAttribute::Italic(true))
.build(); .build();
let operation = OpBuilder::insert("123").attributes(attributes).build(); let operation = OperationBuilder::insert("123").attributes(attributes).build();
let json = serde_json::to_string(&operation).unwrap(); let json = serde_json::to_string(&operation).unwrap();
eprintln!("{}", json); eprintln!("{}", json);
@ -42,7 +42,7 @@ fn attributes_serialize_test() {
.add_attr(RichTextAttribute::Bold(true)) .add_attr(RichTextAttribute::Bold(true))
.add_attr(RichTextAttribute::Italic(true)) .add_attr(RichTextAttribute::Italic(true))
.build(); .build();
let retain = OpBuilder::insert("123").attributes(attributes).build(); let retain = OperationBuilder::insert("123").attributes(attributes).build();
let json = serde_json::to_string(&retain).unwrap(); let json = serde_json::to_string(&retain).unwrap();
eprintln!("{}", json); eprintln!("{}", json);
@ -56,7 +56,7 @@ fn delta_serialize_multi_attribute_test() {
.add_attr(RichTextAttribute::Bold(true)) .add_attr(RichTextAttribute::Bold(true))
.add_attr(RichTextAttribute::Italic(true)) .add_attr(RichTextAttribute::Italic(true))
.build(); .build();
let retain = OpBuilder::insert("123").attributes(attributes).build(); let retain = OperationBuilder::insert("123").attributes(attributes).build();
delta.add(retain); delta.add(retain);
delta.add(Operation::Retain(5.into())); delta.add(Operation::Retain(5.into()));
@ -65,7 +65,7 @@ fn delta_serialize_multi_attribute_test() {
let json = serde_json::to_string(&delta).unwrap(); let json = serde_json::to_string(&delta).unwrap();
eprintln!("{}", json); eprintln!("{}", json);
let delta_from_json = Delta::from_delta_str(&json).unwrap(); let delta_from_json = Delta::from_json_str(&json).unwrap();
assert_eq!(delta_from_json, delta); assert_eq!(delta_from_json, delta);
} }
@ -77,7 +77,7 @@ fn delta_deserialize_test() {
{"retain":2,"attributes":{"italic":"true","bold":"true"}}, {"retain":2,"attributes":{"italic":"true","bold":"true"}},
{"retain":2,"attributes":{"italic":true,"bold":true}} {"retain":2,"attributes":{"italic":true,"bold":true}}
]"#; ]"#;
let delta = RichTextDelta::from_delta_str(json).unwrap(); let delta = RichTextDelta::from_json_str(json).unwrap();
eprintln!("{}", delta); eprintln!("{}", delta);
} }
@ -86,13 +86,13 @@ fn delta_deserialize_null_test() {
let json = r#"[ let json = r#"[
{"retain":7,"attributes":{"bold":null}} {"retain":7,"attributes":{"bold":null}}
]"#; ]"#;
let delta1 = RichTextDelta::from_delta_str(json).unwrap(); let delta1 = RichTextDelta::from_json_str(json).unwrap();
let mut attribute = RichTextAttribute::Bold(true); let mut attribute = RichTextAttribute::Bold(true);
attribute.value = RichTextAttributeValue(None); attribute.value = RichTextAttributeValue(None);
let delta2 = DeltaBuilder::new().retain_with_attributes(7, attribute.into()).build(); let delta2 = DeltaBuilder::new().retain_with_attributes(7, attribute.into()).build();
assert_eq!(delta2.to_delta_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#); assert_eq!(delta2.to_json_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#);
assert_eq!(delta1, delta2); assert_eq!(delta1, delta2);
} }

View File

@ -108,6 +108,7 @@ fn history_bold_redo_with_lagging() {
fn history_delete_undo() { fn history_delete_undo() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Wait(RECORD_THRESHOLD),
AssertDocJson(0, r#"[{"insert":"123"}]"#), AssertDocJson(0, r#"[{"insert":"123"}]"#),
Delete(0, Interval::new(0, 3)), Delete(0, Interval::new(0, 3)),
AssertDocJson(0, r#"[]"#), AssertDocJson(0, r#"[]"#),

View File

@ -7,13 +7,13 @@ pub fn initial_quill_delta() -> RichTextDelta {
#[inline] #[inline]
pub fn initial_quill_delta_string() -> String { pub fn initial_quill_delta_string() -> String {
initial_quill_delta().to_delta_str() initial_quill_delta().to_json_str()
} }
#[inline] #[inline]
pub fn initial_read_me() -> RichTextDelta { pub fn initial_read_me() -> RichTextDelta {
let json = include_str!("READ_ME.json"); let json = include_str!("READ_ME.json");
RichTextDelta::from_delta_str(json).unwrap() RichTextDelta::from_json_str(json).unwrap()
} }
#[cfg(test)] #[cfg(test)]
@ -22,6 +22,6 @@ mod tests {
#[test] #[test]
fn load_read_me() { fn load_read_me() {
println!("{}", initial_read_me().to_delta_str()); println!("{}", initial_read_me().to_json_str());
} }
} }

View File

@ -55,16 +55,16 @@ impl ClientDocument {
} }
pub fn from_json(json: &str) -> Result<Self, CollaborateError> { pub fn from_json(json: &str) -> Result<Self, CollaborateError> {
let delta = RichTextDelta::from_delta_str(json)?; let delta = RichTextDelta::from_json_str(json)?;
Ok(Self::from_delta(delta)) Ok(Self::from_delta(delta))
} }
pub fn delta_str(&self) -> String { pub fn delta_str(&self) -> String {
self.delta.to_delta_str() self.delta.to_json_str()
} }
pub fn to_bytes(&self) -> Bytes { pub fn to_bytes(&self) -> Bytes {
self.delta.to_delta_bytes() self.delta.to_json_bytes()
} }
pub fn to_plain_string(&self) -> String { pub fn to_plain_string(&self) -> String {
@ -85,7 +85,7 @@ impl ClientDocument {
} }
pub fn set_delta(&mut self, data: RichTextDelta) { pub fn set_delta(&mut self, data: RichTextDelta) {
tracing::trace!("document: {}", data.to_delta_str()); tracing::trace!("document: {}", data.to_json_str());
self.delta = data; self.delta = data;
match &self.notify { match &self.notify {
@ -97,7 +97,7 @@ impl ClientDocument {
} }
pub fn compose_delta(&mut self, delta: RichTextDelta) -> Result<(), CollaborateError> { pub fn compose_delta(&mut self, delta: RichTextDelta) -> Result<(), CollaborateError> {
tracing::trace!("{} compose {}", &self.delta.to_delta_str(), delta.to_delta_str()); tracing::trace!("{} compose {}", &self.delta.to_json_str(), delta.to_json_str());
let composed_delta = self.delta.compose(&delta)?; let composed_delta = self.delta.compose(&delta)?;
let mut undo_delta = delta.invert(&self.delta); let mut undo_delta = delta.invert(&self.delta);

View File

@ -1,6 +1,6 @@
use crate::{client_document::DeleteExt, util::is_newline}; use crate::{client_document::DeleteExt, util::is_newline};
use lib_ot::{ use lib_ot::{
core::{Attributes, DeltaBuilder, DeltaIter, Interval, Utf16CodeUnitMetric, NEW_LINE}, core::{Attributes, DeltaBuilder, DeltaIterator, Interval, Utf16CodeUnitMetric, NEW_LINE},
rich_text::{plain_attributes, RichTextDelta}, rich_text::{plain_attributes, RichTextDelta},
}; };
@ -16,7 +16,7 @@ impl DeleteExt for PreserveLineFormatOnMerge {
} }
// seek to the interval start pos. e.g. You backspace enter pos // seek to the interval start pos. e.g. You backspace enter pos
let mut iter = DeltaIter::from_offset(delta, interval.start); let mut iter = DeltaIterator::from_offset(delta, interval.start);
// op will be the "\n" // op will be the "\n"
let newline_op = iter.next_op_with_len(1)?; let newline_op = iter.next_op_with_len(1)?;

View File

@ -1,5 +1,5 @@
use lib_ot::{ use lib_ot::{
core::{DeltaBuilder, DeltaIter, Interval}, core::{DeltaBuilder, DeltaIterator, Interval},
rich_text::{plain_attributes, AttributeScope, RichTextAttribute, RichTextDelta}, rich_text::{plain_attributes, AttributeScope, RichTextAttribute, RichTextDelta},
}; };
@ -20,7 +20,7 @@ impl FormatExt for ResolveBlockFormat {
} }
let mut new_delta = DeltaBuilder::new().retain(interval.start).build(); let mut new_delta = DeltaBuilder::new().retain(interval.start).build();
let mut iter = DeltaIter::from_offset(delta, interval.start); let mut iter = DeltaIterator::from_offset(delta, interval.start);
let mut start = 0; let mut start = 0;
let end = interval.size(); let end = interval.size();
while start < end && iter.has_next() { while start < end && iter.has_next() {

View File

@ -1,5 +1,5 @@
use lib_ot::{ use lib_ot::{
core::{DeltaBuilder, DeltaIter, Interval}, core::{DeltaBuilder, DeltaIterator, Interval},
rich_text::{AttributeScope, RichTextAttribute, RichTextDelta}, rich_text::{AttributeScope, RichTextAttribute, RichTextDelta},
}; };
@ -19,7 +19,7 @@ impl FormatExt for ResolveInlineFormat {
return None; return None;
} }
let mut new_delta = DeltaBuilder::new().retain(interval.start).build(); let mut new_delta = DeltaBuilder::new().retain(interval.start).build();
let mut iter = DeltaIter::from_offset(delta, interval.start); let mut iter = DeltaIterator::from_offset(delta, interval.start);
let mut start = 0; let mut start = 0;
let end = interval.size(); let end = interval.size();

View File

@ -1,8 +1,6 @@
use crate::{client_document::InsertExt, util::is_newline}; use crate::{client_document::InsertExt, util::is_newline};
use lib_ot::{ use lib_ot::core::{is_empty_line_at_index, DeltaBuilder, DeltaIterator};
core::{is_empty_line_at_index, DeltaBuilder, DeltaIter}, use lib_ot::rich_text::{attributes_except_header, RichTextAttributeKey, RichTextDelta};
rich_text::{attributes_except_header, RichTextAttributeKey, RichTextDelta},
};
pub struct AutoExitBlock {} pub struct AutoExitBlock {}
@ -21,7 +19,7 @@ impl InsertExt for AutoExitBlock {
return None; return None;
} }
let mut iter = DeltaIter::from_offset(delta, index); let mut iter = DeltaIterator::from_offset(delta, index);
let next = iter.next_op()?; let next = iter.next_op()?;
let mut attributes = next.get_attributes(); let mut attributes = next.get_attributes();

View File

@ -1,6 +1,6 @@
use crate::{client_document::InsertExt, util::is_whitespace}; use crate::{client_document::InsertExt, util::is_whitespace};
use lib_ot::{ use lib_ot::{
core::{count_utf16_code_units, DeltaBuilder, DeltaIter}, core::{count_utf16_code_units, DeltaBuilder, DeltaIterator},
rich_text::{plain_attributes, RichTextAttribute, RichTextAttributes, RichTextDelta}, rich_text::{plain_attributes, RichTextAttribute, RichTextAttributes, RichTextDelta},
}; };
use std::cmp::min; use std::cmp::min;
@ -17,7 +17,7 @@ impl InsertExt for AutoFormatExt {
if !is_whitespace(text) { if !is_whitespace(text) {
return None; return None;
} }
let mut iter = DeltaIter::new(delta); let mut iter = DeltaIterator::new(delta);
if let Some(prev) = iter.next_op_with_len(index) { if let Some(prev) = iter.next_op_with_len(index) {
match AutoFormat::parse(prev.get_data()) { match AutoFormat::parse(prev.get_data()) {
None => {} None => {}

View File

@ -1,6 +1,6 @@
use crate::client_document::InsertExt; use crate::client_document::InsertExt;
use lib_ot::{ use lib_ot::{
core::{Attributes, DeltaBuilder, DeltaIter, NEW_LINE}, core::{Attributes, DeltaBuilder, DeltaIterator, NEW_LINE},
rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta}, rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta},
}; };
@ -11,7 +11,7 @@ impl InsertExt for DefaultInsertAttribute {
} }
fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option<RichTextDelta> { fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option<RichTextDelta> {
let iter = DeltaIter::new(delta); let iter = DeltaIterator::new(delta);
let mut attributes = RichTextAttributes::new(); let mut attributes = RichTextAttributes::new();
// Enable each line split by "\n" remains the block attributes. for example: // Enable each line split by "\n" remains the block attributes. for example:

View File

@ -1,6 +1,6 @@
use crate::{client_document::InsertExt, util::is_newline}; use crate::{client_document::InsertExt, util::is_newline};
use lib_ot::{ use lib_ot::{
core::{DeltaBuilder, DeltaIter, NEW_LINE}, core::{DeltaBuilder, DeltaIterator, NEW_LINE},
rich_text::{ rich_text::{
attributes_except_header, plain_attributes, RichTextAttribute, RichTextAttributeKey, RichTextAttributes, attributes_except_header, plain_attributes, RichTextAttribute, RichTextAttributeKey, RichTextAttributes,
RichTextDelta, RichTextDelta,
@ -18,7 +18,7 @@ impl InsertExt for PreserveBlockFormatOnInsert {
return None; return None;
} }
let mut iter = DeltaIter::from_offset(delta, index); let mut iter = DeltaIterator::from_offset(delta, index);
match iter.next_op_with_newline() { match iter.next_op_with_newline() {
None => {} None => {}
Some((newline_op, offset)) => { Some((newline_op, offset)) => {

View File

@ -3,7 +3,7 @@ use crate::{
util::{contain_newline, is_newline}, util::{contain_newline, is_newline},
}; };
use lib_ot::{ use lib_ot::{
core::{DeltaBuilder, DeltaIter, OpNewline, NEW_LINE}, core::{DeltaBuilder, DeltaIterator, OpNewline, NEW_LINE},
rich_text::{plain_attributes, RichTextAttributeKey, RichTextDelta}, rich_text::{plain_attributes, RichTextAttributeKey, RichTextDelta},
}; };
@ -18,7 +18,7 @@ impl InsertExt for PreserveInlineFormat {
return None; return None;
} }
let mut iter = DeltaIter::new(delta); let mut iter = DeltaIterator::new(delta);
let prev = iter.next_op_with_len(index)?; let prev = iter.next_op_with_len(index)?;
if OpNewline::parse(&prev).is_contain() { if OpNewline::parse(&prev).is_contain() {
return None; return None;
@ -64,7 +64,7 @@ impl InsertExt for PreserveLineFormatOnSplit {
return None; return None;
} }
let mut iter = DeltaIter::new(delta); let mut iter = DeltaIterator::new(delta);
let prev = iter.next_op_with_len(index)?; let prev = iter.next_op_with_len(index)?;
if OpNewline::parse(&prev).is_end() { if OpNewline::parse(&prev).is_end() {
return None; return None;

View File

@ -1,6 +1,6 @@
use crate::{client_document::InsertExt, util::is_newline}; use crate::{client_document::InsertExt, util::is_newline};
use lib_ot::{ use lib_ot::{
core::{DeltaBuilder, DeltaIter, Utf16CodeUnitMetric, NEW_LINE}, core::{DeltaBuilder, DeltaIterator, Utf16CodeUnitMetric, NEW_LINE},
rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta}, rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta},
}; };
@ -15,7 +15,7 @@ impl InsertExt for ResetLineFormatOnNewLine {
return None; return None;
} }
let mut iter = DeltaIter::new(delta); let mut iter = DeltaIterator::new(delta);
iter.seek::<Utf16CodeUnitMetric>(index); iter.seek::<Utf16CodeUnitMetric>(index);
let next_op = iter.next_op()?; let next_op = iter.next_op()?;
if !next_op.get_data().starts_with(NEW_LINE) { if !next_op.get_data().starts_with(NEW_LINE) {

View File

@ -7,7 +7,7 @@ use crate::{
}; };
use flowy_folder_data_model::revision::{TrashRevision, WorkspaceRevision}; use flowy_folder_data_model::revision::{TrashRevision, WorkspaceRevision};
use lib_ot::core::{PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder}; use lib_ot::core::{PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
@ -41,9 +41,9 @@ impl FolderPadBuilder {
} }
// TODO: Reconvert from history if delta.to_str() failed. // TODO: Reconvert from history if delta.to_str() failed.
let folder_json = delta.to_str()?; let content = delta.content_str()?;
let mut folder: FolderPad = serde_json::from_str(&folder_json).map_err(|e| { let mut folder: FolderPad = serde_json::from_str(&content).map_err(|e| {
tracing::error!("Deserialize folder from json failed: {}", folder_json); tracing::error!("Deserialize folder from {} failed", content);
return CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e)); return CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e));
})?; })?;
folder.delta = delta; folder.delta = delta;
@ -51,7 +51,7 @@ impl FolderPadBuilder {
} }
pub(crate) fn build_with_revisions(self, revisions: Vec<Revision>) -> CollaborateResult<FolderPad> { pub(crate) fn build_with_revisions(self, revisions: Vec<Revision>) -> CollaborateResult<FolderPad> {
let folder_delta: FolderDelta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?; let folder_delta: FolderDelta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
self.build_with_delta(folder_delta) self.build_with_delta(folder_delta)
} }

View File

@ -295,7 +295,7 @@ impl FolderPad {
} }
pub fn md5(&self) -> String { pub fn md5(&self) -> String {
md5(&self.delta.to_delta_bytes()) md5(&self.delta.to_json_bytes())
} }
pub fn to_json(&self) -> CollaborateResult<String> { pub fn to_json(&self) -> CollaborateResult<String> {
@ -315,7 +315,7 @@ impl FolderPad {
Some(_) => { Some(_) => {
let old = cloned_self.to_json()?; let old = cloned_self.to_json()?;
let new = self.to_json()?; let new = self.to_json()?;
match cal_diff::<PlainTextAttributes>(old, new) { match cal_diff::<PhantomAttributes>(old, new) {
None => Ok(None), None => Ok(None),
Some(delta) => { Some(delta) => {
self.delta = self.delta.compose(&delta)?; self.delta = self.delta.compose(&delta)?;
@ -350,7 +350,7 @@ impl FolderPad {
Some(_) => { Some(_) => {
let old = cloned_self.to_json()?; let old = cloned_self.to_json()?;
let new = self.to_json()?; let new = self.to_json()?;
match cal_diff::<PlainTextAttributes>(old, new) { match cal_diff::<PhantomAttributes>(old, new) {
None => Ok(None), None => Ok(None),
Some(delta) => { Some(delta) => {
self.delta = self.delta.compose(&delta)?; self.delta = self.delta.compose(&delta)?;

View File

@ -4,7 +4,7 @@ use crate::util::{cal_diff, make_delta_from_revisions};
use flowy_grid_data_model::revision::{ use flowy_grid_data_model::revision::{
gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision, gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision,
}; };
use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder}; use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder};
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
@ -46,7 +46,7 @@ impl GridBlockRevisionPad {
} }
pub fn from_delta(delta: GridBlockRevisionDelta) -> CollaborateResult<Self> { pub fn from_delta(delta: GridBlockRevisionDelta) -> CollaborateResult<Self> {
let s = delta.to_str()?; let s = delta.content_str()?;
let block_revision: GridBlockRevision = serde_json::from_str(&s).map_err(|e| { let block_revision: GridBlockRevision = serde_json::from_str(&s).map_err(|e| {
let msg = format!("Deserialize delta to block meta failed: {}", e); let msg = format!("Deserialize delta to block meta failed: {}", e);
tracing::error!("{}", s); tracing::error!("{}", s);
@ -56,7 +56,7 @@ impl GridBlockRevisionPad {
} }
pub fn from_revisions(_grid_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> { pub fn from_revisions(_grid_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
let block_delta: GridBlockRevisionDelta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?; let block_delta: GridBlockRevisionDelta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
Self::from_delta(block_delta) Self::from_delta(block_delta)
} }
@ -195,10 +195,10 @@ impl GridBlockRevisionPad {
Some(_) => { Some(_) => {
let old = cloned_self.to_json()?; let old = cloned_self.to_json()?;
let new = self.to_json()?; let new = self.to_json()?;
match cal_diff::<PlainTextAttributes>(old, new) { match cal_diff::<PhantomAttributes>(old, new) {
None => Ok(None), None => Ok(None),
Some(delta) => { Some(delta) => {
tracing::trace!("[GridBlockMeta] Composing delta {}", delta.to_delta_str()); tracing::trace!("[GridBlockMeta] Composing delta {}", delta.to_json_str());
// tracing::debug!( // tracing::debug!(
// "[GridBlockMeta] current delta: {}", // "[GridBlockMeta] current delta: {}",
// self.delta.to_str().unwrap_or_else(|_| "".to_string()) // self.delta.to_str().unwrap_or_else(|_| "".to_string())
@ -231,11 +231,11 @@ impl GridBlockRevisionPad {
} }
pub fn md5(&self) -> String { pub fn md5(&self) -> String {
md5(&self.delta.to_delta_bytes()) md5(&self.delta.to_json_bytes())
} }
pub fn delta_str(&self) -> String { pub fn delta_str(&self) -> String {
self.delta.to_delta_str() self.delta.to_json_str()
} }
} }
@ -252,7 +252,7 @@ pub fn make_grid_block_delta(block_rev: &GridBlockRevision) -> GridBlockRevision
pub fn make_grid_block_revisions(user_id: &str, grid_block_meta_data: &GridBlockRevision) -> RepeatedRevision { pub fn make_grid_block_revisions(user_id: &str, grid_block_meta_data: &GridBlockRevision) -> RepeatedRevision {
let delta = make_grid_block_delta(grid_block_meta_data); let delta = make_grid_block_delta(grid_block_meta_data);
let bytes = delta.to_delta_bytes(); let bytes = delta.to_json_bytes();
let revision = Revision::initial_revision(user_id, &grid_block_meta_data.block_id, bytes); let revision = Revision::initial_revision(user_id, &grid_block_meta_data.block_id, bytes);
revision.into() revision.into()
} }
@ -289,7 +289,7 @@ mod tests {
let change = pad.add_row_rev(row.clone(), None).unwrap().unwrap(); let change = pad.add_row_rev(row.clone(), None).unwrap().unwrap();
assert_eq!(pad.rows.first().unwrap().as_ref(), &row); assert_eq!(pad.rows.first().unwrap().as_ref(), &row);
assert_eq!( assert_eq!(
change.delta.to_delta_str(), change.delta.to_json_str(),
r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"# r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
); );
} }
@ -303,19 +303,19 @@ mod tests {
let change = pad.add_row_rev(row_1.clone(), None).unwrap().unwrap(); let change = pad.add_row_rev(row_1.clone(), None).unwrap().unwrap();
assert_eq!( assert_eq!(
change.delta.to_delta_str(), change.delta.to_json_str(),
r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"# r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
); );
let change = pad.add_row_rev(row_2.clone(), None).unwrap().unwrap(); let change = pad.add_row_rev(row_2.clone(), None).unwrap().unwrap();
assert_eq!( assert_eq!(
change.delta.to_delta_str(), change.delta.to_json_str(),
r#"[{"retain":90},{"insert":",{\"id\":\"2\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"# r#"[{"retain":90},{"insert":",{\"id\":\"2\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
); );
let change = pad.add_row_rev(row_3.clone(), Some("2".to_string())).unwrap().unwrap(); let change = pad.add_row_rev(row_3.clone(), Some("2".to_string())).unwrap().unwrap();
assert_eq!( assert_eq!(
change.delta.to_delta_str(), change.delta.to_json_str(),
r#"[{"retain":157},{"insert":",{\"id\":\"3\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"# r#"[{"retain":157},{"insert":",{\"id\":\"3\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
); );
@ -381,7 +381,7 @@ mod tests {
let _ = pad.add_row_rev(row.clone(), None).unwrap().unwrap(); let _ = pad.add_row_rev(row.clone(), None).unwrap().unwrap();
let change = pad.delete_rows(vec![Cow::Borrowed(&row.id)]).unwrap().unwrap(); let change = pad.delete_rows(vec![Cow::Borrowed(&row.id)]).unwrap().unwrap();
assert_eq!( assert_eq!(
change.delta.to_delta_str(), change.delta.to_json_str(),
r#"[{"retain":24},{"delete":66},{"retain":2}]"# r#"[{"retain":24},{"delete":66},{"retain":2}]"#
); );
@ -410,7 +410,7 @@ mod tests {
let change = pad.update_row(changeset).unwrap().unwrap(); let change = pad.update_row(changeset).unwrap().unwrap();
assert_eq!( assert_eq!(
change.delta.to_delta_str(), change.delta.to_json_str(),
r#"[{"retain":69},{"insert":"10"},{"retain":15},{"insert":"tru"},{"delete":4},{"retain":4}]"# r#"[{"retain":69},{"insert":"10"},{"retain":15},{"insert":"tru"},{"delete":4},{"retain":4}]"#
); );
@ -422,7 +422,7 @@ mod tests {
fn test_pad() -> GridBlockRevisionPad { fn test_pad() -> GridBlockRevisionPad {
let delta = let delta =
GridBlockRevisionDelta::from_delta_str(r#"[{"insert":"{\"block_id\":\"1\",\"rows\":[]}"}]"#).unwrap(); GridBlockRevisionDelta::from_json_str(r#"[{"insert":"{\"block_id\":\"1\",\"rows\":[]}"}]"#).unwrap();
GridBlockRevisionPad::from_delta(delta).unwrap() GridBlockRevisionPad::from_delta(delta).unwrap()
} }
} }

View File

@ -9,7 +9,7 @@ use flowy_grid_data_model::revision::{
GridLayoutRevision, GridRevision, GridSettingRevision, GridSortRevision, GridLayoutRevision, GridRevision, GridSettingRevision, GridSortRevision,
}; };
use lib_infra::util::move_vec_element; use lib_infra::util::move_vec_element;
use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder}; use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
@ -52,8 +52,8 @@ impl GridRevisionPad {
} }
pub fn from_delta(delta: GridRevisionDelta) -> CollaborateResult<Self> { pub fn from_delta(delta: GridRevisionDelta) -> CollaborateResult<Self> {
let s = delta.to_str()?; let content = delta.content_str()?;
let grid: GridRevision = serde_json::from_str(&s) let grid: GridRevision = serde_json::from_str(&content)
.map_err(|e| CollaborateError::internal().context(format!("Deserialize delta to grid failed: {}", e)))?; .map_err(|e| CollaborateError::internal().context(format!("Deserialize delta to grid failed: {}", e)))?;
Ok(Self { Ok(Self {
@ -63,7 +63,7 @@ impl GridRevisionPad {
} }
pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> { pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
let grid_delta: GridRevisionDelta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?; let grid_delta: GridRevisionDelta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
Self::from_delta(grid_delta) Self::from_delta(grid_delta)
} }
@ -457,15 +457,15 @@ impl GridRevisionPad {
} }
pub fn md5(&self) -> String { pub fn md5(&self) -> String {
md5(&self.delta.to_delta_bytes()) md5(&self.delta.to_json_bytes())
} }
pub fn delta_str(&self) -> String { pub fn delta_str(&self) -> String {
self.delta.to_delta_str() self.delta.to_json_str()
} }
pub fn delta_bytes(&self) -> Bytes { pub fn delta_bytes(&self) -> Bytes {
self.delta.to_delta_bytes() self.delta.to_json_bytes()
} }
pub fn fields(&self) -> &[Arc<FieldRevision>] { pub fn fields(&self) -> &[Arc<FieldRevision>] {
@ -482,7 +482,7 @@ impl GridRevisionPad {
Some(_) => { Some(_) => {
let old = make_grid_rev_json_str(&cloned_grid)?; let old = make_grid_rev_json_str(&cloned_grid)?;
let new = self.json_str()?; let new = self.json_str()?;
match cal_diff::<PlainTextAttributes>(old, new) { match cal_diff::<PhantomAttributes>(old, new) {
None => Ok(None), None => Ok(None),
Some(delta) => { Some(delta) => {
self.delta = self.delta.compose(&delta)?; self.delta = self.delta.compose(&delta)?;
@ -553,7 +553,7 @@ pub fn make_grid_delta(grid_rev: &GridRevision) -> GridRevisionDelta {
pub fn make_grid_revisions(user_id: &str, grid_rev: &GridRevision) -> RepeatedRevision { pub fn make_grid_revisions(user_id: &str, grid_rev: &GridRevision) -> RepeatedRevision {
let delta = make_grid_delta(grid_rev); let delta = make_grid_delta(grid_rev);
let bytes = delta.to_delta_bytes(); let bytes = delta.to_json_bytes();
let revision = Revision::initial_revision(user_id, &grid_rev.grid_id, bytes); let revision = Revision::initial_revision(user_id, &grid_rev.grid_id, bytes);
revision.into() revision.into()
} }

View File

@ -89,7 +89,7 @@ impl std::fmt::Debug for Revision {
let _ = f.write_fmt(format_args!("rev_id {}, ", self.rev_id))?; let _ = f.write_fmt(format_args!("rev_id {}, ", self.rev_id))?;
match RichTextDelta::from_bytes(&self.delta_data) { match RichTextDelta::from_bytes(&self.delta_data) {
Ok(delta) => { Ok(delta) => {
let _ = f.write_fmt(format_args!("delta {:?}", delta.to_delta_str()))?; let _ = f.write_fmt(format_args!("delta {:?}", delta.to_json_str()))?;
} }
Err(e) => { Err(e) => {
let _ = f.write_fmt(format_args!("delta {:?}", e))?; let _ = f.write_fmt(format_args!("delta {:?}", e))?;

View File

@ -46,7 +46,7 @@ impl std::convert::TryFrom<Revision> for DocumentPB {
} }
let delta = RichTextDelta::from_bytes(&revision.delta_data)?; let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
let doc_json = delta.to_delta_str(); let doc_json = delta.to_json_str();
Ok(DocumentPB { Ok(DocumentPB {
block_id: revision.object_id, block_id: revision.object_id,

View File

@ -39,7 +39,7 @@ impl RevisionSyncObject<RichTextAttributes> for ServerDocument {
} }
fn to_json(&self) -> String { fn to_json(&self) -> String {
self.delta.to_delta_str() self.delta.to_json_str()
} }
fn set_delta(&mut self, new_delta: Delta<RichTextAttributes>) { fn set_delta(&mut self, new_delta: Delta<RichTextAttributes>) {

View File

@ -13,7 +13,7 @@ use crate::{
use async_stream::stream; use async_stream::stream;
use futures::stream::StreamExt; use futures::stream::StreamExt;
use lib_infra::future::BoxResultFuture; use lib_infra::future::BoxResultFuture;
use lib_ot::core::PlainTextAttributes; use lib_ot::core::PhantomAttributes;
use std::{collections::HashMap, fmt::Debug, sync::Arc}; use std::{collections::HashMap, fmt::Debug, sync::Arc};
use tokio::{ use tokio::{
sync::{mpsc, oneshot, RwLock}, sync::{mpsc, oneshot, RwLock},
@ -188,7 +188,7 @@ impl ServerFolderManager {
} }
} }
type FolderRevisionSynchronizer = RevisionSynchronizer<PlainTextAttributes>; type FolderRevisionSynchronizer = RevisionSynchronizer<PhantomAttributes>;
struct OpenFolderHandler { struct OpenFolderHandler {
folder_id: String, folder_id: String,

View File

@ -1,5 +1,5 @@
use crate::{entities::folder::FolderDelta, errors::CollaborateError, synchronizer::RevisionSyncObject}; use crate::{entities::folder::FolderDelta, errors::CollaborateError, synchronizer::RevisionSyncObject};
use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta}; use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta};
pub struct ServerFolder { pub struct ServerFolder {
folder_id: String, folder_id: String,
@ -15,7 +15,7 @@ impl ServerFolder {
} }
} }
impl RevisionSyncObject<PlainTextAttributes> for ServerFolder { impl RevisionSyncObject<PhantomAttributes> for ServerFolder {
fn id(&self) -> &str { fn id(&self) -> &str {
&self.folder_id &self.folder_id
} }
@ -32,7 +32,7 @@ impl RevisionSyncObject<PlainTextAttributes> for ServerFolder {
} }
fn to_json(&self) -> String { fn to_json(&self) -> String {
self.delta.to_delta_str() self.delta.to_json_str()
} }
fn set_delta(&mut self, new_delta: PlainTextDelta) { fn set_delta(&mut self, new_delta: PlainTextDelta) {

View File

@ -149,7 +149,7 @@ pub fn make_folder_from_revisions_pb(
folder_delta = folder_delta.compose(&delta)?; folder_delta = folder_delta.compose(&delta)?;
} }
let text = folder_delta.to_delta_str(); let text = folder_delta.to_json_str();
Ok(Some(FolderInfo { Ok(Some(FolderInfo {
folder_id: folder_id.to_string(), folder_id: folder_id.to_string(),
text, text,
@ -183,7 +183,7 @@ pub fn make_document_from_revision_pbs(
delta = delta.compose(&new_delta)?; delta = delta.compose(&new_delta)?;
} }
let text = delta.to_delta_str(); let text = delta.to_json_str();
Ok(Some(DocumentPB { Ok(Some(DocumentPB {
block_id: doc_id.to_owned(), block_id: doc_id.to_owned(),

View File

@ -1,7 +1,12 @@
use crate::core::{trim, Attributes, Delta, PlainTextAttributes}; use crate::core::delta::{trim, Delta};
use crate::core::operation::{Attributes, PhantomAttributes};
pub type PlainTextDeltaBuilder = DeltaBuilder<PlainTextAttributes>; pub type PlainTextDeltaBuilder = DeltaBuilder<PhantomAttributes>;
/// A builder for creating new [Delta] objects.
///
/// Note that all edit operations must be sorted; the start point of each
/// interval must be no less than the end point of the previous one.
pub struct DeltaBuilder<T: Attributes> { pub struct DeltaBuilder<T: Attributes> {
delta: Delta<T>, delta: Delta<T>,
} }
@ -23,6 +28,8 @@ where
DeltaBuilder::default() DeltaBuilder::default()
} }
/// Retain the 'n' characters with the attributes. Use 'retain' instead if you don't
/// need any attributes.
pub fn retain_with_attributes(mut self, n: usize, attrs: T) -> Self { pub fn retain_with_attributes(mut self, n: usize, attrs: T) -> Self {
self.delta.retain(n, attrs); self.delta.retain(n, attrs);
self self
@ -33,11 +40,14 @@ where
self self
} }
/// Deletes the given interval. Panics if interval is not properly sorted.
pub fn delete(mut self, n: usize) -> Self { pub fn delete(mut self, n: usize) -> Self {
self.delta.delete(n); self.delta.delete(n);
self self
} }
/// Inserts the string with attributes. Use 'insert' instead if you don't
/// need any attributes.
pub fn insert_with_attributes(mut self, s: &str, attrs: T) -> Self { pub fn insert_with_attributes(mut self, s: &str, attrs: T) -> Self {
self.delta.insert(s, attrs); self.delta.insert(s, attrs);
self self
@ -53,6 +63,7 @@ where
self self
} }
/// Builds the `Delta`
pub fn build(self) -> Delta<T> { pub fn build(self) -> Delta<T> {
self.delta self.delta
} }

View File

@ -1,33 +1,51 @@
#![allow(clippy::while_let_on_iterator)] #![allow(clippy::while_let_on_iterator)]
use crate::{ use crate::core::delta::Delta;
core::{Attributes, Delta, Interval, Operation}, use crate::core::interval::Interval;
errors::{ErrorBuilder, OTError, OTErrorCode}, use crate::core::operation::{Attributes, Operation};
}; use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
use std::{cmp::min, iter::Enumerate, slice::Iter}; use std::{cmp::min, iter::Enumerate, slice::Iter};
/// A [DeltaCursor] is used to iterate the delta and return the corresponding delta.
#[derive(Debug)] #[derive(Debug)]
pub struct OpCursor<'a, T: Attributes> { pub struct DeltaCursor<'a, T: Attributes> {
pub(crate) delta: &'a Delta<T>, pub(crate) delta: &'a Delta<T>,
pub(crate) origin_iv: Interval, pub(crate) origin_iv: Interval,
pub(crate) consume_iv: Interval, pub(crate) consume_iv: Interval,
pub(crate) consume_count: usize, pub(crate) consume_count: usize,
pub(crate) op_index: usize, pub(crate) op_offset: usize,
iter: Enumerate<Iter<'a, Operation<T>>>, iter: Enumerate<Iter<'a, Operation<T>>>,
next_op: Option<Operation<T>>, next_op: Option<Operation<T>>,
} }
impl<'a, T> OpCursor<'a, T> impl<'a, T> DeltaCursor<'a, T>
where where
T: Attributes, T: Attributes,
{ {
pub fn new(delta: &'a Delta<T>, interval: Interval) -> OpCursor<'a, T> { /// # Arguments
///
/// * `delta`: The delta you want to iterate over.
/// * `interval`: The range for the cursor movement.
///
/// # Examples
///
/// ```
/// use lib_ot::core::{DeltaIterator, Interval, OperationBuilder};
/// use lib_ot::rich_text::RichTextDelta;
/// let mut delta = RichTextDelta::default();
/// let op_1 = OperationBuilder::insert("123").build();
/// let op_2 = OperationBuilder::insert("4").build();
/// delta.add(op_1.clone());
/// delta.add(op_2.clone());
/// assert_eq!(DeltaIterator::from_interval(&delta, Interval::new(0, 3)).ops(), vec![op_1.clone()]);
/// ```
pub fn new(delta: &'a Delta<T>, interval: Interval) -> DeltaCursor<'a, T> {
// debug_assert!(interval.start <= delta.target_len); // debug_assert!(interval.start <= delta.target_len);
let mut cursor = Self { let mut cursor = Self {
delta, delta,
origin_iv: interval, origin_iv: interval,
consume_iv: interval, consume_iv: interval,
consume_count: 0, consume_count: 0,
op_index: 0, op_offset: 0,
iter: delta.ops.iter().enumerate(), iter: delta.ops.iter().enumerate(),
next_op: None, next_op: None,
}; };
@ -35,17 +53,37 @@ where
cursor cursor
} }
// get the next operation interval /// Returns the next operation interval
pub fn next_iv(&self) -> Interval { pub fn next_iv(&self) -> Interval {
self.next_iv_with_len(None).unwrap_or_else(|| Interval::new(0, 0)) self.next_iv_with_len(None).unwrap_or_else(|| Interval::new(0, 0))
} }
pub fn next_op(&mut self) -> Option<Operation<T>> { /// Returns the next operation
pub fn get_next_op(&mut self) -> Option<Operation<T>> {
self.next_with_len(None) self.next_with_len(None)
} }
// get the last operation before the end. /// Returns the reference of the next operation
// checkout the delta_next_op_with_len_cross_op_return_last test for more detail pub fn next_op(&self) -> Option<&Operation<T>> {
let mut next_op = self.next_op.as_ref();
if next_op.is_none() {
let mut offset = 0;
for op in &self.delta.ops {
offset += op.len();
if offset > self.consume_count {
next_op = Some(op);
break;
}
}
}
next_op
}
/// # Arguments
///
/// * `expected_len`: Return the next operation with the specified length.
///
///
pub fn next_with_len(&mut self, expected_len: Option<usize>) -> Option<Operation<T>> { pub fn next_with_len(&mut self, expected_len: Option<usize>) -> Option<Operation<T>> {
let mut find_op = None; let mut find_op = None;
let holder = self.next_op.clone(); let holder = self.next_op.clone();
@ -97,17 +135,24 @@ where
} }
pub fn has_next(&self) -> bool { pub fn has_next(&self) -> bool {
self.next_iter_op().is_some() self.next_op().is_some()
} }
fn descend(&mut self, index: usize) { /// Finds the op within the current offset.
self.consume_iv.start += index; /// This function sets the start of the consume_iv to the offset, updates the consume_count
/// and the next_op reference.
///
/// # Arguments
///
/// * `offset`: Represents the offset of the delta string, in Utf16CodeUnit unit.
fn descend(&mut self, offset: usize) {
self.consume_iv.start += offset;
if self.consume_count >= self.consume_iv.start { if self.consume_count >= self.consume_iv.start {
return; return;
} }
while let Some((o_index, op)) = self.iter.next() { while let Some((o_index, op)) = self.iter.next() {
self.op_index = o_index; self.op_offset = o_index;
let start = self.consume_count; let start = self.consume_count;
let end = start + op.len(); let end = start + op.len();
let intersect = Interval::new(start, end).intersect(self.consume_iv); let intersect = Interval::new(start, end).intersect(self.consume_iv);
@ -121,7 +166,7 @@ where
} }
fn next_iv_with_len(&self, expected_len: Option<usize>) -> Option<Interval> { fn next_iv_with_len(&self, expected_len: Option<usize>) -> Option<Interval> {
let op = self.next_iter_op()?; let op = self.next_op()?;
let start = self.consume_count; let start = self.consume_count;
let end = match expected_len { let end = match expected_len {
None => self.consume_count + op.len(), None => self.consume_count + op.len(),
@ -132,31 +177,16 @@ where
let interval = intersect.translate_neg(start); let interval = intersect.translate_neg(start);
Some(interval) Some(interval)
} }
pub fn next_iter_op(&self) -> Option<&Operation<T>> {
let mut next_op = self.next_op.as_ref();
if next_op.is_none() {
let mut offset = 0;
for op in &self.delta.ops {
offset += op.len();
if offset > self.consume_count {
next_op = Some(op);
break;
}
}
}
next_op
}
} }
fn find_next<'a, T>(cursor: &mut OpCursor<'a, T>) -> Option<&'a Operation<T>> fn find_next<'a, T>(cursor: &mut DeltaCursor<'a, T>) -> Option<&'a Operation<T>>
where where
T: Attributes, T: Attributes,
{ {
match cursor.iter.next() { match cursor.iter.next() {
None => None, None => None,
Some((o_index, op)) => { Some((o_index, op)) => {
cursor.op_index = o_index; cursor.op_offset = o_index;
Some(op) Some(op)
} }
} }
@ -164,31 +194,34 @@ where
type SeekResult = Result<(), OTError>; type SeekResult = Result<(), OTError>;
pub trait Metric { pub trait Metric {
fn seek<T: Attributes>(cursor: &mut OpCursor<T>, offset: usize) -> SeekResult; fn seek<T: Attributes>(cursor: &mut DeltaCursor<T>, offset: usize) -> SeekResult;
} }
/// [OpMetric] is used by [DeltaIterator] for seeking operations
/// The unit of the movement is Operation
pub struct OpMetric(); pub struct OpMetric();
impl Metric for OpMetric { impl Metric for OpMetric {
fn seek<T: Attributes>(cursor: &mut OpCursor<T>, offset: usize) -> SeekResult { fn seek<T: Attributes>(cursor: &mut DeltaCursor<T>, op_offset: usize) -> SeekResult {
let _ = check_bound(cursor.op_index, offset)?; let _ = check_bound(cursor.op_offset, op_offset)?;
let mut seek_cursor = OpCursor::new(cursor.delta, cursor.origin_iv); let mut seek_cursor = DeltaCursor::new(cursor.delta, cursor.origin_iv);
let mut cur_offset = 0;
while let Some((_, op)) = seek_cursor.iter.next() { while let Some((_, op)) = seek_cursor.iter.next() {
cur_offset += op.len(); cursor.descend(op.len());
if cur_offset > offset { if cursor.op_offset >= op_offset {
break; break;
} }
} }
cursor.descend(cur_offset);
Ok(()) Ok(())
} }
} }
/// [Utf16CodeUnitMetric] is used by [DeltaIterator] for seeking operations.
/// The unit of the movement is Utf16CodeUnit
pub struct Utf16CodeUnitMetric(); pub struct Utf16CodeUnitMetric();
impl Metric for Utf16CodeUnitMetric { impl Metric for Utf16CodeUnitMetric {
fn seek<T: Attributes>(cursor: &mut OpCursor<T>, offset: usize) -> SeekResult { fn seek<T: Attributes>(cursor: &mut DeltaCursor<T>, offset: usize) -> SeekResult {
if offset > 0 { if offset > 0 {
let _ = check_bound(cursor.consume_count, offset)?; let _ = check_bound(cursor.consume_count, offset)?;
let _ = cursor.next_with_len(Some(offset)); let _ = cursor.next_with_len(Some(offset));

View File

@ -1,8 +1,9 @@
use crate::{ use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
core::{operation::*, DeltaIter, FlowyStr, Interval, OperationTransformable, MAX_IV_LEN},
errors::{ErrorBuilder, OTError, OTErrorCode},
};
use crate::core::delta::{DeltaIterator, MAX_IV_LEN};
use crate::core::flowy_str::FlowyStr;
use crate::core::interval::Interval;
use crate::core::operation::{Attributes, Operation, OperationBuilder, OperationTransformable, PhantomAttributes};
use bytes::Bytes; use bytes::Bytes;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use std::{ use std::{
@ -13,13 +14,25 @@ use std::{
str::FromStr, str::FromStr,
}; };
pub type PlainTextDelta = Delta<PlainTextAttributes>; pub type PlainTextDelta = Delta<PhantomAttributes>;
// TODO: optimize the memory usage with Arc::make_mut or Cow /// A [Delta] contains list of operations that consists of 'Retain', 'Delete' and 'Insert' operation.
/// Check out the [Operation] for more details. It describes the document as a sequence of
/// operations.
///
/// If the [T] supports 'serde', that will enable delta to serialize to JSON or deserialize from
/// a JSON string.
///
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct Delta<T: Attributes> { pub struct Delta<T: Attributes> {
pub ops: Vec<Operation<T>>, pub ops: Vec<Operation<T>>,
/// 'Delete' and 'Retain' operation will update the [utf16_base_len]
/// Transforming the other delta, it requires the utf16_base_len must be equal.
pub utf16_base_len: usize, pub utf16_base_len: usize,
/// Represents the current len of the delta.
/// 'Insert' and 'Retain' operation will update the [utf16_target_len]
pub utf16_target_len: usize, pub utf16_target_len: usize,
} }
@ -81,6 +94,7 @@ where
} }
} }
/// Adding an operation. It will be added in sequence.
pub fn add(&mut self, op: Operation<T>) { pub fn add(&mut self, op: Operation<T>) {
match op { match op {
Operation::Delete(i) => self.delete(i), Operation::Delete(i) => self.delete(i),
@ -89,6 +103,7 @@ where
} }
} }
/// Creating a [Delete] operation with len [n]
pub fn delete(&mut self, n: usize) { pub fn delete(&mut self, n: usize) {
if n == 0 { if n == 0 {
return; return;
@ -97,10 +112,11 @@ where
if let Some(Operation::Delete(n_last)) = self.ops.last_mut() { if let Some(Operation::Delete(n_last)) = self.ops.last_mut() {
*n_last += n; *n_last += n;
} else { } else {
self.ops.push(OpBuilder::delete(n).build()); self.ops.push(OperationBuilder::delete(n).build());
} }
} }
/// Creating a [Insert] operation with string, [s].
pub fn insert(&mut self, s: &str, attributes: T) { pub fn insert(&mut self, s: &str, attributes: T) {
let s: FlowyStr = s.into(); let s: FlowyStr = s.into();
if s.is_empty() { if s.is_empty() {
@ -119,10 +135,10 @@ where
} }
[.., op_last @ Operation::<T>::Delete(_)] => { [.., op_last @ Operation::<T>::Delete(_)] => {
let new_last = op_last.clone(); let new_last = op_last.clone();
*op_last = OpBuilder::<T>::insert(&s).attributes(attributes).build(); *op_last = OperationBuilder::<T>::insert(&s).attributes(attributes).build();
Some(new_last) Some(new_last)
} }
_ => Some(OpBuilder::<T>::insert(&s).attributes(attributes).build()), _ => Some(OperationBuilder::<T>::insert(&s).attributes(attributes).build()),
}; };
match new_last { match new_last {
@ -131,6 +147,7 @@ where
} }
} }
/// Creating a [Retain] operation with len, [n].
pub fn retain(&mut self, n: usize, attributes: T) { pub fn retain(&mut self, n: usize, attributes: T) {
if n == 0 { if n == 0 {
return; return;
@ -143,24 +160,48 @@ where
self.ops.push(new_op); self.ops.push(new_op);
} }
} else { } else {
self.ops.push(OpBuilder::<T>::retain(n).attributes(attributes).build()); self.ops
.push(OperationBuilder::<T>::retain(n).attributes(attributes).build());
} }
} }
/// Applies an operation to a string, returning a new string. /// Return the a new string described by this delta. The new string will contains the input string.
pub fn apply(&self, s: &str) -> Result<String, OTError> { /// The length of the [applied_str] must be equal to the the [utf16_base_len].
let s: FlowyStr = s.into(); ///
if s.utf16_size() != self.utf16_base_len { /// # Arguments
///
/// * `applied_str`: A string represents the utf16_base_len content. it will be consumed by the [retain]
/// or [delete] operations.
///
///
/// # Examples
///
/// ```
/// use lib_ot::core::PlainTextDeltaBuilder;
/// let s = "hello";
/// let delta_a = PlainTextDeltaBuilder::new().insert(s).build();
/// let delta_b = PlainTextDeltaBuilder::new()
/// .retain(s.len())
/// .insert(", AppFlowy")
/// .build();
///
/// let after_a = delta_a.content_str().unwrap();
/// let after_b = delta_b.apply(&after_a).unwrap();
/// assert_eq!("hello, AppFlowy", &after_b);
/// ```
pub fn apply(&self, applied_str: &str) -> Result<String, OTError> {
let applied_str: FlowyStr = applied_str.into();
if applied_str.utf16_size() != self.utf16_base_len {
return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength) return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength)
.msg(format!( .msg(format!(
"Expected: {}, received: {}", "Expected: {}, but received: {}",
self.utf16_base_len, self.utf16_base_len,
s.utf16_size() applied_str.utf16_size()
)) ))
.build()); .build());
} }
let mut new_s = String::new(); let mut new_s = String::new();
let code_point_iter = &mut s.utf16_code_unit_iter(); let code_point_iter = &mut applied_str.utf16_code_unit_iter();
for op in &self.ops { for op in &self.ops {
match &op { match &op {
Operation::Retain(retain) => { Operation::Retain(retain) => {
@ -181,34 +222,60 @@ where
Ok(new_s) Ok(new_s)
} }
/// Computes the inverse of an operation. The inverse of an operation is the /// Computes the inverse [Delta]. The inverse of an operation is the
/// operation that reverts the effects of the operation /// operation that reverts the effects of the operation
pub fn invert_str(&self, s: &str) -> Self { /// # Arguments
///
/// * `inverted_s`: A string represents the utf16_base_len content. The len of [inverted_s]
/// must equal to the [utf16_base_len], it will be consumed by the [retain] or [delete] operations.
///
/// If the delta's operations just contain a insert operation. The inverted_s must be empty string.
///
/// # Examples
///
/// ```
/// use lib_ot::core::PlainTextDeltaBuilder;
/// let s = "hello world";
/// let delta = PlainTextDeltaBuilder::new().insert(s).build();
/// let invert_delta = delta.invert_str(s);
/// assert_eq!(delta.utf16_base_len, invert_delta.utf16_target_len);
/// assert_eq!(delta.utf16_target_len, invert_delta.utf16_base_len);
///
/// assert_eq!(invert_delta.apply(s).unwrap(), "")
///
/// ```
///
pub fn invert_str(&self, inverted_s: &str) -> Self {
let mut inverted = Delta::default(); let mut inverted = Delta::default();
let chars = &mut s.chars(); let inverted_s: FlowyStr = inverted_s.into();
let code_point_iter = &mut inverted_s.utf16_code_unit_iter();
for op in &self.ops { for op in &self.ops {
match &op { match &op {
Operation::Retain(retain) => { Operation::Retain(retain) => {
inverted.retain(retain.n, T::default()); inverted.retain(retain.n, T::default());
// TODO: use advance_by instead, but it's unstable now
// chars.advance_by(retain.num)
for _ in 0..retain.n { for _ in 0..retain.n {
chars.next(); code_point_iter.next();
} }
} }
Operation::Insert(insert) => { Operation::Insert(insert) => {
inverted.delete(insert.utf16_size()); inverted.delete(insert.utf16_size());
} }
Operation::Delete(delete) => { Operation::Delete(delete) => {
inverted.insert(&chars.take(*delete as usize).collect::<String>(), op.get_attributes()); let bytes = code_point_iter
.take(*delete as usize)
.into_iter()
.flat_map(|a| str::from_utf8(a.0).ok())
.collect::<String>();
inverted.insert(&bytes, op.get_attributes());
} }
} }
} }
inverted inverted
} }
/// Checks if this operation has no effect. /// Return true if the delta doesn't contain any [Insert] or [Delete] operations.
#[inline]
pub fn is_noop(&self) -> bool { pub fn is_noop(&self) -> bool {
matches!(self.ops.as_slice(), [] | [Operation::Retain(_)]) matches!(self.ops.as_slice(), [] | [Operation::Retain(_)])
} }
@ -231,8 +298,8 @@ where
Self: Sized, Self: Sized,
{ {
let mut new_delta = Delta::default(); let mut new_delta = Delta::default();
let mut iter = DeltaIter::new(self); let mut iter = DeltaIterator::new(self);
let mut other_iter = DeltaIter::new(other); let mut other_iter = DeltaIterator::new(other);
while iter.has_next() || other_iter.has_next() { while iter.has_next() || other_iter.has_next() {
if other_iter.is_next_insert() { if other_iter.is_next_insert() {
@ -252,10 +319,10 @@ where
let op = iter let op = iter
.next_op_with_len(length) .next_op_with_len(length)
.unwrap_or_else(|| OpBuilder::retain(length).build()); .unwrap_or_else(|| OperationBuilder::retain(length).build());
let other_op = other_iter let other_op = other_iter
.next_op_with_len(length) .next_op_with_len(length)
.unwrap_or_else(|| OpBuilder::retain(length).build()); .unwrap_or_else(|| OperationBuilder::retain(length).build());
// debug_assert_eq!(op.len(), other_op.len(), "Composing delta failed,"); // debug_assert_eq!(op.len(), other_op.len(), "Composing delta failed,");
@ -263,12 +330,16 @@ where
(Operation::Retain(retain), Operation::Retain(other_retain)) => { (Operation::Retain(retain), Operation::Retain(other_retain)) => {
let composed_attrs = retain.attributes.compose(&other_retain.attributes)?; let composed_attrs = retain.attributes.compose(&other_retain.attributes)?;
new_delta.add(OpBuilder::retain(retain.n).attributes(composed_attrs).build()) new_delta.add(OperationBuilder::retain(retain.n).attributes(composed_attrs).build())
} }
(Operation::Insert(insert), Operation::Retain(other_retain)) => { (Operation::Insert(insert), Operation::Retain(other_retain)) => {
let mut composed_attrs = insert.attributes.compose(&other_retain.attributes)?; let mut composed_attrs = insert.attributes.compose(&other_retain.attributes)?;
composed_attrs.remove_empty(); composed_attrs.remove_empty();
new_delta.add(OpBuilder::insert(op.get_data()).attributes(composed_attrs).build()) new_delta.add(
OperationBuilder::insert(op.get_data())
.attributes(composed_attrs)
.build(),
)
} }
(Operation::Retain(_), Operation::Delete(_)) => { (Operation::Retain(_), Operation::Delete(_)) => {
new_delta.add(other_op); new_delta.add(other_op);
@ -331,7 +402,7 @@ where
Ordering::Less => { Ordering::Less => {
a_prime.retain(retain.n, composed_attrs.clone()); a_prime.retain(retain.n, composed_attrs.clone());
b_prime.retain(retain.n, composed_attrs.clone()); b_prime.retain(retain.n, composed_attrs.clone());
next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build()); next_op2 = Some(OperationBuilder::retain(o_retain.n - retain.n).build());
next_op1 = ops1.next(); next_op1 = ops1.next();
} }
Ordering::Equal => { Ordering::Equal => {
@ -343,14 +414,14 @@ where
Ordering::Greater => { Ordering::Greater => {
a_prime.retain(o_retain.n, composed_attrs.clone()); a_prime.retain(o_retain.n, composed_attrs.clone());
b_prime.retain(o_retain.n, composed_attrs.clone()); b_prime.retain(o_retain.n, composed_attrs.clone());
next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build()); next_op1 = Some(OperationBuilder::retain(retain.n - o_retain.n).build());
next_op2 = ops2.next(); next_op2 = ops2.next();
} }
}; };
} }
(Some(Operation::Delete(i)), Some(Operation::Delete(j))) => match i.cmp(j) { (Some(Operation::Delete(i)), Some(Operation::Delete(j))) => match i.cmp(j) {
Ordering::Less => { Ordering::Less => {
next_op2 = Some(OpBuilder::delete(*j - *i).build()); next_op2 = Some(OperationBuilder::delete(*j - *i).build());
next_op1 = ops1.next(); next_op1 = ops1.next();
} }
Ordering::Equal => { Ordering::Equal => {
@ -358,7 +429,7 @@ where
next_op2 = ops2.next(); next_op2 = ops2.next();
} }
Ordering::Greater => { Ordering::Greater => {
next_op1 = Some(OpBuilder::delete(*i - *j).build()); next_op1 = Some(OperationBuilder::delete(*i - *j).build());
next_op2 = ops2.next(); next_op2 = ops2.next();
} }
}, },
@ -366,7 +437,7 @@ where
match i.cmp(o_retain) { match i.cmp(o_retain) {
Ordering::Less => { Ordering::Less => {
a_prime.delete(*i); a_prime.delete(*i);
next_op2 = Some(OpBuilder::retain(o_retain.n - *i).build()); next_op2 = Some(OperationBuilder::retain(o_retain.n - *i).build());
next_op1 = ops1.next(); next_op1 = ops1.next();
} }
Ordering::Equal => { Ordering::Equal => {
@ -376,7 +447,7 @@ where
} }
Ordering::Greater => { Ordering::Greater => {
a_prime.delete(o_retain.n); a_prime.delete(o_retain.n);
next_op1 = Some(OpBuilder::delete(*i - o_retain.n).build()); next_op1 = Some(OperationBuilder::delete(*i - o_retain.n).build());
next_op2 = ops2.next(); next_op2 = ops2.next();
} }
}; };
@ -385,7 +456,7 @@ where
match retain.cmp(j) { match retain.cmp(j) {
Ordering::Less => { Ordering::Less => {
b_prime.delete(retain.n); b_prime.delete(retain.n);
next_op2 = Some(OpBuilder::delete(*j - retain.n).build()); next_op2 = Some(OperationBuilder::delete(*j - retain.n).build());
next_op1 = ops1.next(); next_op1 = ops1.next();
} }
Ordering::Equal => { Ordering::Equal => {
@ -395,7 +466,7 @@ where
} }
Ordering::Greater => { Ordering::Greater => {
b_prime.delete(*j); b_prime.delete(*j);
next_op1 = Some(OpBuilder::retain(retain.n - *j).build()); next_op1 = Some(OperationBuilder::retain(retain.n - *j).build());
next_op2 = ops2.next(); next_op2 = ops2.next();
} }
}; };
@ -407,21 +478,17 @@ where
fn invert(&self, other: &Self) -> Self { fn invert(&self, other: &Self) -> Self {
let mut inverted = Delta::default(); let mut inverted = Delta::default();
if other.is_empty() {
return inverted;
}
let mut index = 0; let mut index = 0;
for op in &self.ops { for op in &self.ops {
let len: usize = op.len() as usize; let len: usize = op.len() as usize;
match op { match op {
Operation::Delete(n) => { Operation::Delete(n) => {
invert_from_other(&mut inverted, other, op, index, index + *n); invert_other(&mut inverted, other, op, index, index + *n);
index += len; index += len;
} }
Operation::Retain(_) => { Operation::Retain(_) => {
match op.has_attribute() { match op.has_attribute() {
true => invert_from_other(&mut inverted, other, op, index, index + len), true => invert_other(&mut inverted, other, op, index, index + len),
false => { false => {
// tracing::trace!("invert retain: {} by retain {} {}", op, len, // tracing::trace!("invert retain: {} by retain {} {}", op, len,
// op.get_attributes()); // op.get_attributes());
@ -452,7 +519,7 @@ where
} }
} }
fn invert_from_other<T: Attributes>( fn invert_other<T: Attributes>(
base: &mut Delta<T>, base: &mut Delta<T>,
other: &Delta<T>, other: &Delta<T>,
operation: &Operation<T>, operation: &Operation<T>,
@ -460,7 +527,7 @@ fn invert_from_other<T: Attributes>(
end: usize, end: usize,
) { ) {
tracing::trace!("invert op: {} [{}:{}]", operation, start, end); tracing::trace!("invert op: {} [{}:{}]", operation, start, end);
let other_ops = DeltaIter::from_interval(other, Interval::new(start, end)).ops(); let other_ops = DeltaIterator::from_interval(other, Interval::new(start, end)).ops();
other_ops.into_iter().for_each(|other_op| match operation { other_ops.into_iter().for_each(|other_op| match operation {
Operation::Delete(_n) => { Operation::Delete(_n) => {
// tracing::trace!("invert delete: {} by add {}", n, other_op); // tracing::trace!("invert delete: {} by add {}", n, other_op);
@ -501,7 +568,7 @@ impl<T> Delta<T>
where where
T: Attributes + DeserializeOwned, T: Attributes + DeserializeOwned,
{ {
pub fn from_delta_str(json: &str) -> Result<Self, OTError> { pub fn from_json_str(json: &str) -> Result<Self, OTError> {
let delta = serde_json::from_str(json).map_err(|e| { let delta = serde_json::from_str(json).map_err(|e| {
tracing::trace!("Deserialize failed: {:?}", e); tracing::trace!("Deserialize failed: {:?}", e);
tracing::trace!("{:?}", json); tracing::trace!("{:?}", json);
@ -512,7 +579,7 @@ where
pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Result<Self, OTError> { pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Result<Self, OTError> {
let json = str::from_utf8(bytes.as_ref())?.to_owned(); let json = str::from_utf8(bytes.as_ref())?.to_owned();
let val = Self::from_delta_str(&json)?; let val = Self::from_json_str(&json)?;
Ok(val) Ok(val)
} }
} }
@ -521,16 +588,16 @@ impl<T> Delta<T>
where where
T: Attributes + serde::Serialize, T: Attributes + serde::Serialize,
{ {
pub fn to_delta_str(&self) -> String { pub fn to_json_str(&self) -> String {
serde_json::to_string(self).unwrap_or_else(|_| "".to_owned()) serde_json::to_string(self).unwrap_or_else(|_| "".to_owned())
} }
pub fn to_str(&self) -> Result<String, OTError> { pub fn content_str(&self) -> Result<String, OTError> {
self.apply("") self.apply("")
} }
pub fn to_delta_bytes(&self) -> Bytes { pub fn to_json_bytes(&self) -> Bytes {
let json = self.to_delta_str(); let json = self.to_json_str();
Bytes::from(json.into_bytes()) Bytes::from(json.into_bytes())
} }
} }

View File

@ -1,4 +1,5 @@
use crate::core::{Attributes, Delta}; use crate::core::delta::Delta;
use crate::core::operation::Attributes;
use serde::{ use serde::{
de::{SeqAccess, Visitor}, de::{SeqAccess, Visitor},
ser::SerializeSeq, ser::SerializeSeq,

View File

@ -1,17 +1,17 @@
use super::cursor::*; use super::cursor::*;
use crate::{ use crate::core::delta::{Delta, NEW_LINE};
core::{Attributes, Delta, Interval, Operation, NEW_LINE}, use crate::core::interval::Interval;
rich_text::RichTextAttributes, use crate::core::operation::{Attributes, Operation};
}; use crate::rich_text::RichTextAttributes;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize; pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize;
pub struct DeltaIter<'a, T: Attributes> { pub struct DeltaIterator<'a, T: Attributes> {
cursor: OpCursor<'a, T>, cursor: DeltaCursor<'a, T>,
} }
impl<'a, T> DeltaIter<'a, T> impl<'a, T> DeltaIterator<'a, T>
where where
T: Attributes, T: Attributes,
{ {
@ -28,7 +28,7 @@ where
} }
pub fn from_interval(delta: &'a Delta<T>, interval: Interval) -> Self { pub fn from_interval(delta: &'a Delta<T>, interval: Interval) -> Self {
let cursor = OpCursor::new(delta, interval); let cursor = DeltaCursor::new(delta, interval);
Self { cursor } Self { cursor }
} }
@ -46,7 +46,7 @@ where
} }
pub fn next_op(&mut self) -> Option<Operation<T>> { pub fn next_op(&mut self) -> Option<Operation<T>> {
self.cursor.next_op() self.cursor.get_next_op()
} }
pub fn next_op_with_len(&mut self, len: usize) -> Option<Operation<T>> { pub fn next_op_with_len(&mut self, len: usize) -> Option<Operation<T>> {
@ -80,28 +80,28 @@ where
} }
pub fn is_next_insert(&self) -> bool { pub fn is_next_insert(&self) -> bool {
match self.cursor.next_iter_op() { match self.cursor.next_op() {
None => false, None => false,
Some(op) => op.is_insert(), Some(op) => op.is_insert(),
} }
} }
pub fn is_next_retain(&self) -> bool { pub fn is_next_retain(&self) -> bool {
match self.cursor.next_iter_op() { match self.cursor.next_op() {
None => false, None => false,
Some(op) => op.is_retain(), Some(op) => op.is_retain(),
} }
} }
pub fn is_next_delete(&self) -> bool { pub fn is_next_delete(&self) -> bool {
match self.cursor.next_iter_op() { match self.cursor.next_op() {
None => false, None => false,
Some(op) => op.is_delete(), Some(op) => op.is_delete(),
} }
} }
} }
impl<'a, T> Iterator for DeltaIter<'a, T> impl<'a, T> Iterator for DeltaIterator<'a, T>
where where
T: Attributes, T: Attributes,
{ {
@ -112,7 +112,7 @@ where
} }
pub fn is_empty_line_at_index(delta: &Delta<RichTextAttributes>, index: usize) -> bool { pub fn is_empty_line_at_index(delta: &Delta<RichTextAttributes>, index: usize) -> bool {
let mut iter = DeltaIter::new(delta); let mut iter = DeltaIterator::new(delta);
let (prev, next) = (iter.next_op_with_len(index), iter.next_op()); let (prev, next) = (iter.next_op_with_len(index), iter.next_op());
if prev.is_none() { if prev.is_none() {
return true; return true;
@ -128,7 +128,7 @@ pub fn is_empty_line_at_index(delta: &Delta<RichTextAttributes>, index: usize) -
} }
pub struct AttributesIter<'a, T: Attributes> { pub struct AttributesIter<'a, T: Attributes> {
delta_iter: DeltaIter<'a, T>, delta_iter: DeltaIterator<'a, T>,
} }
impl<'a, T> AttributesIter<'a, T> impl<'a, T> AttributesIter<'a, T>
@ -141,7 +141,7 @@ where
} }
pub fn from_interval(delta: &'a Delta<T>, interval: Interval) -> Self { pub fn from_interval(delta: &'a Delta<T>, interval: Interval) -> Self {
let delta_iter = DeltaIter::from_interval(delta, interval); let delta_iter = DeltaIterator::from_interval(delta, interval);
Self { delta_iter } Self { delta_iter }
} }
@ -157,7 +157,7 @@ impl<'a, T> Deref for AttributesIter<'a, T>
where where
T: Attributes, T: Attributes,
{ {
type Target = DeltaIter<'a, T>; type Target = DeltaIterator<'a, T>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.delta_iter &self.delta_iter

View File

@ -177,7 +177,7 @@ impl<'a> FlowyUtf16CodePointIterator<'a> {
} }
} }
use crate::core::Interval; use crate::core::interval::Interval;
use std::str; use std::str;
impl<'a> Iterator for FlowyUtf16CodePointIterator<'a> { impl<'a> Iterator for FlowyUtf16CodePointIterator<'a> {
@ -226,7 +226,8 @@ pub fn len_utf8_from_first_byte(b: u8) -> usize {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::core::{FlowyStr, Interval}; use crate::core::flowy_str::FlowyStr;
use crate::core::interval::Interval;
#[test] #[test]
fn flowy_str_code_unit() { fn flowy_str_code_unit() {

View File

@ -157,7 +157,7 @@ impl From<RangeToInclusive<usize>> for Interval {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::core::Interval; use crate::core::interval::Interval;
#[test] #[test]
fn contains() { fn contains() {

View File

@ -3,28 +3,7 @@ mod flowy_str;
mod interval; mod interval;
mod operation; mod operation;
use crate::errors::OTError;
pub use delta::*; pub use delta::*;
pub use flowy_str::*; pub use flowy_str::*;
pub use interval::*; pub use interval::*;
pub use operation::*; pub use operation::*;
pub trait OperationTransformable {
/// Merges the operation with `other` into one operation while preserving
/// the changes of both.
fn compose(&self, other: &Self) -> Result<Self, OTError>
where
Self: Sized;
/// Transforms two operations a and b that happened concurrently and
/// produces two operations a' and b'.
/// (a', b') = a.transform(b)
/// a.compose(b') = b.compose(a')
fn transform(&self, other: &Self) -> Result<(Self, Self), OTError>
where
Self: Sized;
/// Inverts the operation with `other` to produces undo operation.
/// undo = a.invert(b)
/// new_b = b.compose(a)
/// b = new_b.compose(undo)
fn invert(&self, other: &Self) -> Self;
}

View File

@ -1,40 +1,38 @@
use crate::{ use crate::core::operation::{Attributes, Operation, PhantomAttributes};
core::{Attributes, Operation, PlainTextAttributes}, use crate::rich_text::RichTextAttributes;
rich_text::RichTextAttributes,
};
pub type RichTextOpBuilder = OpBuilder<RichTextAttributes>; pub type RichTextOpBuilder = OperationBuilder<RichTextAttributes>;
pub type PlainTextOpBuilder = OpBuilder<PlainTextAttributes>; pub type PlainTextOpBuilder = OperationBuilder<PhantomAttributes>;
pub struct OpBuilder<T: Attributes> { pub struct OperationBuilder<T: Attributes> {
ty: Operation<T>, ty: Operation<T>,
attrs: T, attrs: T,
} }
impl<T> OpBuilder<T> impl<T> OperationBuilder<T>
where where
T: Attributes, T: Attributes,
{ {
pub fn new(ty: Operation<T>) -> OpBuilder<T> { pub fn new(ty: Operation<T>) -> OperationBuilder<T> {
OpBuilder { OperationBuilder {
ty, ty,
attrs: T::default(), attrs: T::default(),
} }
} }
pub fn retain(n: usize) -> OpBuilder<T> { pub fn retain(n: usize) -> OperationBuilder<T> {
OpBuilder::new(Operation::Retain(n.into())) OperationBuilder::new(Operation::Retain(n.into()))
} }
pub fn delete(n: usize) -> OpBuilder<T> { pub fn delete(n: usize) -> OperationBuilder<T> {
OpBuilder::new(Operation::Delete(n)) OperationBuilder::new(Operation::Delete(n))
} }
pub fn insert(s: &str) -> OpBuilder<T> { pub fn insert(s: &str) -> OperationBuilder<T> {
OpBuilder::new(Operation::Insert(s.into())) OperationBuilder::new(Operation::Insert(s.into()))
} }
pub fn attributes(mut self, attrs: T) -> OpBuilder<T> { pub fn attributes(mut self, attrs: T) -> OperationBuilder<T> {
self.attrs = attrs; self.attrs = attrs;
self self
} }

View File

@ -1,8 +1,9 @@
use crate::{ use crate::core::flowy_str::FlowyStr;
core::{FlowyStr, Interval, OpBuilder, OperationTransformable}, use crate::core::interval::Interval;
errors::OTError, use crate::core::operation::OperationBuilder;
}; use crate::errors::OTError;
use serde::{Deserialize, Serialize, __private::Formatter}; use serde::{Deserialize, Serialize, __private::Formatter};
use std::fmt::Display;
use std::{ use std::{
cmp::min, cmp::min,
fmt, fmt,
@ -10,15 +11,89 @@ use std::{
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
pub trait Attributes: fmt::Display + Eq + PartialEq + Default + Clone + Debug + OperationTransformable { pub trait OperationTransformable {
fn is_empty(&self) -> bool; /// Merges the operation with `other` into one operation while preserving
/// the changes of both.
///
/// # Arguments
///
/// * `other`: The delta gonna to merge.
///
/// # Examples
///
/// ```
/// use lib_ot::core::{OperationTransformable, PlainTextDeltaBuilder};
/// let document = PlainTextDeltaBuilder::new().build();
/// let delta = PlainTextDeltaBuilder::new().insert("abc").build();
/// let new_document = document.compose(&delta).unwrap();
/// assert_eq!(new_document.content_str().unwrap(), "abc".to_owned());
/// ```
fn compose(&self, other: &Self) -> Result<Self, OTError>
where
Self: Sized;
// Remove the empty attribute which value is None. /// Transforms two operations a and b that happened concurrently and
fn remove_empty(&mut self); /// produces two operations a' and b'.
/// (a', b') = a.transform(b)
/// a.compose(b') = b.compose(a')
///
fn transform(&self, other: &Self) -> Result<(Self, Self), OTError>
where
Self: Sized;
fn extend_other(&mut self, other: Self); /// Returns the invert delta from the other. It can be used to do the undo operation.
///
/// # Arguments
///
/// * `other`: Generate the undo delta for [Other]. [Other] can compose the undo delta to return
/// to the previous state.
///
/// # Examples
///
/// ```
/// use lib_ot::core::{OperationTransformable, PlainTextDeltaBuilder};
/// let original_document = PlainTextDeltaBuilder::new().build();
/// let delta = PlainTextDeltaBuilder::new().insert("abc").build();
///
/// let undo_delta = delta.invert(&original_document);
/// let new_document = original_document.compose(&delta).unwrap();
/// let document = new_document.compose(&undo_delta).unwrap();
///
/// assert_eq!(original_document, document);
///
/// ```
fn invert(&self, other: &Self) -> Self;
} }
/// Each operation can carry attributes. For example, the [RichTextAttributes] has a list of key/value attributes.
/// Such as { bold: true, italic: true }.
///
/// Because [Operation] is generic over the T, so you must specify the T. For example, the [PlainTextDelta]. It use
/// use [PhantomAttributes] as the T. [PhantomAttributes] does nothing, just a phantom.
///
pub trait Attributes: Default + Display + Eq + PartialEq + Clone + Debug + OperationTransformable {
fn is_empty(&self) -> bool {
true
}
/// Remove the empty attribute which value is None.
fn remove_empty(&mut self) {
// Do nothing
}
fn extend_other(&mut self, _other: Self) {
// Do nothing
}
}
/// [Operation] consists of three types.
/// * Delete
/// * Retain
/// * Insert
///
/// The [T] should support serde if you want to serialize/deserialize the operation
/// to json string. You could check out the operation_serde.rs for more information.
///
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub enum Operation<T: Attributes> { pub enum Operation<T: Attributes> {
Delete(usize), Delete(usize),
@ -77,22 +152,22 @@ where
let right; let right;
match self { match self {
Operation::Delete(n) => { Operation::Delete(n) => {
left = Some(OpBuilder::<T>::delete(index).build()); left = Some(OperationBuilder::<T>::delete(index).build());
right = Some(OpBuilder::<T>::delete(*n - index).build()); right = Some(OperationBuilder::<T>::delete(*n - index).build());
} }
Operation::Retain(retain) => { Operation::Retain(retain) => {
left = Some(OpBuilder::<T>::delete(index).build()); left = Some(OperationBuilder::<T>::delete(index).build());
right = Some(OpBuilder::<T>::delete(retain.n - index).build()); right = Some(OperationBuilder::<T>::delete(retain.n - index).build());
} }
Operation::Insert(insert) => { Operation::Insert(insert) => {
let attributes = self.get_attributes(); let attributes = self.get_attributes();
left = Some( left = Some(
OpBuilder::<T>::insert(&insert.s[0..index]) OperationBuilder::<T>::insert(&insert.s[0..index])
.attributes(attributes.clone()) .attributes(attributes.clone())
.build(), .build(),
); );
right = Some( right = Some(
OpBuilder::<T>::insert(&insert.s[index..insert.utf16_size()]) OperationBuilder::<T>::insert(&insert.s[index..insert.utf16_size()])
.attributes(attributes) .attributes(attributes)
.build(), .build(),
); );
@ -104,16 +179,18 @@ where
pub fn shrink(&self, interval: Interval) -> Option<Operation<T>> { pub fn shrink(&self, interval: Interval) -> Option<Operation<T>> {
let op = match self { let op = match self {
Operation::Delete(n) => OpBuilder::delete(min(*n, interval.size())).build(), Operation::Delete(n) => OperationBuilder::delete(min(*n, interval.size())).build(),
Operation::Retain(retain) => OpBuilder::retain(min(retain.n, interval.size())) Operation::Retain(retain) => OperationBuilder::retain(min(retain.n, interval.size()))
.attributes(retain.attributes.clone()) .attributes(retain.attributes.clone())
.build(), .build(),
Operation::Insert(insert) => { Operation::Insert(insert) => {
if interval.start > insert.utf16_size() { if interval.start > insert.utf16_size() {
OpBuilder::insert("").build() OperationBuilder::insert("").build()
} else { } else {
let s = insert.s.sub_str(interval).unwrap_or_else(|| "".to_owned()); let s = insert.s.sub_str(interval).unwrap_or_else(|| "".to_owned());
OpBuilder::insert(&s).attributes(insert.attributes.clone()).build() OperationBuilder::insert(&s)
.attributes(insert.attributes.clone())
.build()
} }
} }
}; };
@ -178,9 +255,7 @@ where
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct Retain<T: Attributes> { pub struct Retain<T: Attributes> {
// #[serde(rename(serialize = "retain", deserialize = "retain"))]
pub n: usize, pub n: usize,
// #[serde(skip_serializing_if = "is_empty")]
pub attributes: T, pub attributes: T,
} }
@ -212,7 +287,7 @@ where
self.n += n; self.n += n;
None None
} else { } else {
Some(OpBuilder::retain(n).attributes(attributes).build()) Some(OperationBuilder::retain(n).attributes(attributes).build())
} }
} }
@ -255,10 +330,7 @@ where
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct Insert<T: Attributes> { pub struct Insert<T: Attributes> {
// #[serde(rename(serialize = "insert", deserialize = "insert"))]
pub s: FlowyStr, pub s: FlowyStr,
// #[serde(skip_serializing_if = "is_empty")]
pub attributes: T, pub attributes: T,
} }
@ -296,7 +368,7 @@ where
self.s += s; self.s += s;
None None
} else { } else {
Some(OpBuilder::<T>::insert(s).attributes(attributes).build()) Some(OperationBuilder::<T>::insert(s).attributes(attributes).build())
} }
} }
@ -339,24 +411,16 @@ where
} }
#[derive(Debug, Clone, Eq, PartialEq, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, Default, Serialize, Deserialize)]
pub struct PlainTextAttributes(); pub struct PhantomAttributes();
impl fmt::Display for PlainTextAttributes { impl fmt::Display for PhantomAttributes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("PlainAttributes") f.write_str("PhantomAttributes")
} }
} }
impl Attributes for PlainTextAttributes { impl Attributes for PhantomAttributes {}
fn is_empty(&self) -> bool {
true
}
fn remove_empty(&mut self) {} impl OperationTransformable for PhantomAttributes {
fn extend_other(&mut self, _other: Self) {}
}
impl OperationTransformable for PlainTextAttributes {
fn compose(&self, _other: &Self) -> Result<Self, OTError> { fn compose(&self, _other: &Self) -> Result<Self, OTError> {
Ok(self.clone()) Ok(self.clone())
} }

View File

@ -1,4 +1,5 @@
use crate::core::{Attributes, FlowyStr, Insert, Operation, Retain}; use crate::core::flowy_str::FlowyStr;
use crate::core::operation::{Attributes, Insert, Operation, Retain};
use serde::{ use serde::{
de, de,
de::{MapAccess, SeqAccess, Visitor}, de::{MapAccess, SeqAccess, Visitor},

View File

@ -1,10 +1,6 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::{ use crate::core::{Attributes, Operation, OperationTransformable};
block_attribute, use crate::{block_attribute, errors::OTError, ignore_attribute, inline_attribute, list_attribute};
core::{Attributes, Operation, OperationTransformable},
errors::OTError,
ignore_attribute, inline_attribute, list_attribute,
};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},

View File

@ -1,7 +1,5 @@
use crate::{ use crate::core::{Delta, DeltaBuilder};
core::{Delta, DeltaBuilder}, use crate::rich_text::RichTextAttributes;
rich_text::RichTextAttributes,
};
pub type RichTextDelta = Delta<RichTextAttributes>; pub type RichTextDelta = Delta<RichTextAttributes>;
pub type RichTextDeltaBuilder = DeltaBuilder<RichTextAttributes>; pub type RichTextDeltaBuilder = DeltaBuilder<RichTextAttributes>;