From 2f413a8e670556571d1604ec2463a8d85b5b636b Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 7 Dec 2021 10:39:01 +0800 Subject: [PATCH] generic lib-ot --- backend/src/services/doc/edit/editor.rs | 12 +- backend/src/services/user/auth.rs | 1 - backend/tests/document/edit.rs | 6 +- backend/tests/document/helper.rs | 10 +- .../src/services/doc/edit/editor.rs | 18 +- .../src/services/doc/edit/queue.rs | 24 +- .../src/services/doc/revision/manager.rs | 8 +- .../src/services/doc/revision/persistence.rs | 10 +- .../tests/editor/attribute_test.rs | 12 +- .../flowy-document/tests/editor/mod.rs | 36 +-- .../flowy-document/tests/editor/op_test.rs | 131 +++++----- .../flowy-document/tests/editor/serde_test.rs | 32 +-- .../flowy-document-infra/src/core/document.rs | 34 +-- .../core/extensions/delete/default_delete.rs | 4 +- .../delete/preserve_line_format_merge.rs | 15 +- .../src/core/extensions/format/helper.rs | 10 +- .../extensions/format/resolve_block_format.rs | 12 +- .../format/resolve_inline_format.rs | 4 +- .../core/extensions/insert/auto_exit_block.rs | 13 +- .../src/core/extensions/insert/auto_format.rs | 16 +- .../core/extensions/insert/default_insert.rs | 18 +- .../src/core/extensions/insert/mod.rs | 10 +- .../insert/preserve_block_format.rs | 16 +- .../insert/preserve_inline_format.rs | 18 +- .../insert/reset_format_on_new_line.rs | 18 +- .../src/core/extensions/mod.rs | 8 +- .../flowy-document-infra/src/core/history.rs | 16 +- .../flowy-document-infra/src/core/view.rs | 18 +- .../src/entities/doc/doc.rs | 8 +- .../src/entities/doc/revision.rs | 4 +- .../flowy-document-infra/src/user_default.rs | 8 +- .../lib-ot/src/core/attributes/attribute.rs | 92 +++---- .../lib-ot/src/core/attributes/attributes.rs | 98 ++++--- .../src/core/attributes/attributes_serde.rs | 92 +++---- .../lib-ot/src/core/attributes/builder.rs | 10 +- .../lib-ot/src/core/attributes/macros.rs | 8 +- shared-lib/lib-ot/src/core/delta/builder.rs | 28 +- shared-lib/lib-ot/src/core/delta/cursor.rs | 38 +-- shared-lib/lib-ot/src/core/delta/delta.rs | 163 +++++++----- .../lib-ot/src/core/delta/delta_serde.rs | 28 +- shared-lib/lib-ot/src/core/delta/iterator.rs | 84 +++--- .../lib-ot/src/core/operation/builder.rs | 29 ++- .../lib-ot/src/core/operation/operation.rs | 149 +++++++---- .../src/core/operation/operation_serde.rs | 240 +++++++++++++++++- 44 files changed, 1023 insertions(+), 586 deletions(-) diff --git a/backend/src/services/doc/edit/editor.rs b/backend/src/services/doc/edit/editor.rs index f148a9d692..e685af5a96 100644 --- a/backend/src/services/doc/edit/editor.rs +++ b/backend/src/services/doc/edit/editor.rs @@ -13,7 +13,7 @@ use flowy_document_infra::{ entities::ws::{WsDataType, WsDocumentData}, protobuf::{Doc, RevId, RevType, Revision, RevisionRange, UpdateDocParams}, }; -use lib_ot::core::{Delta, OperationTransformable}; +use lib_ot::core::{OperationTransformable, RichTextDelta}; use parking_lot::RwLock; use protobuf::Message; use sqlx::PgPool; @@ -35,7 +35,7 @@ pub struct ServerDocEditor { impl ServerDocEditor { pub fn new(doc: Doc) -> Result { - let delta = Delta::from_bytes(&doc.data).map_err(internal_error)?; + let delta = RichTextDelta::from_bytes(&doc.data).map_err(internal_error)?; let document = Arc::new(RwLock::new(Document::from_delta(delta))); let users = DashMap::new(); Ok(Self { @@ -123,7 +123,7 @@ impl ServerDocEditor { pub fn document_json(&self) -> String { self.document.read().to_json() } async fn compose_revision(&self, revision: &Revision, pg_pool: Data) -> Result<(), ServerError> { - let delta = Delta::from_bytes(&revision.delta_data).map_err(internal_error)?; + let delta = RichTextDelta::from_bytes(&revision.delta_data).map_err(internal_error)?; let _ = self.compose_delta(delta)?; let _ = self.rev_id.fetch_update(SeqCst, SeqCst, |_e| Some(revision.rev_id)); let _ = self.save_revision(&revision, pg_pool).await?; @@ -132,7 +132,7 @@ impl ServerDocEditor { #[tracing::instrument(level = "debug", skip(self, revision))] fn transform_revision(&self, revision: &Revision) -> Result { - let cli_delta = Delta::from_bytes(&revision.delta_data).map_err(internal_error)?; + let cli_delta = RichTextDelta::from_bytes(&revision.delta_data).map_err(internal_error)?; let (cli_prime, server_prime) = self .document .read() @@ -145,7 +145,7 @@ impl ServerDocEditor { Ok(cli_revision) } - fn mk_revision(&self, base_rev_id: i64, delta: Delta) -> Revision { + fn mk_revision(&self, base_rev_id: i64, delta: RichTextDelta) -> Revision { let delta_data = delta.to_bytes().to_vec(); let md5 = md5(&delta_data); Revision { @@ -167,7 +167,7 @@ impl ServerDocEditor { result, ) )] - fn compose_delta(&self, delta: Delta) -> Result<(), ServerError> { + fn compose_delta(&self, delta: RichTextDelta) -> Result<(), ServerError> { if delta.is_empty() { log::warn!("Composed delta is empty"); } diff --git a/backend/src/services/user/auth.rs b/backend/src/services/user/auth.rs index 19d0759969..16e8367940 100644 --- a/backend/src/services/user/auth.rs +++ b/backend/src/services/user/auth.rs @@ -16,7 +16,6 @@ use flowy_user_infra::{ use sqlx::{PgPool, Postgres}; use super::AUTHORIZED_USERS; -use crate::services::user::user_default::create_default_workspace; pub async fn sign_in(pool: &PgPool, params: SignInParams) -> Result { let email = UserEmail::parse(params.email).map_err(|e| ServerError::params_invalid().context(e))?; diff --git a/backend/tests/document/edit.rs b/backend/tests/document/edit.rs index b883c1759e..d821b0d9e6 100644 --- a/backend/tests/document/edit.rs +++ b/backend/tests/document/edit.rs @@ -1,6 +1,6 @@ use crate::document::helper::{DocScript, DocumentTest}; use flowy_document_infra::core::{Document, FlowyDoc}; -use lib_ot::core::{Attribute, Interval}; +use lib_ot::core::{Interval, RichTextAttribute}; #[rustfmt::skip] // ┌─────────┐ ┌─────────┐ @@ -51,11 +51,11 @@ async fn delta_sync_while_editing_with_attribute() { DocScript::ClientConnectWs, DocScript::ClientOpenDoc, DocScript::ClientInsertText(0, "abc"), - DocScript::ClientFormatText(Interval::new(0, 3), Attribute::Bold(true)), + DocScript::ClientFormatText(Interval::new(0, 3), RichTextAttribute::Bold(true)), DocScript::AssertClient(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"\n"}]"#), DocScript::AssertServer(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"\n"}]"#, 2), DocScript::ClientInsertText(3, "efg"), - DocScript::ClientFormatText(Interval::new(3, 5), Attribute::Italic(true)), + DocScript::ClientFormatText(Interval::new(3, 5), RichTextAttribute::Italic(true)), DocScript::AssertClient(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"ef","attributes":{"bold":true,"italic":true}},{"insert":"g","attributes":{"bold":true}},{"insert":"\n"}]"#), DocScript::AssertServer(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"ef","attributes":{"bold":true,"italic":true}},{"insert":"g","attributes":{"bold":true}},{"insert":"\n"}]"#, 4), ]) diff --git a/backend/tests/document/helper.rs b/backend/tests/document/helper.rs index 494617ef8d..1e93834b9b 100644 --- a/backend/tests/document/helper.rs +++ b/backend/tests/document/helper.rs @@ -1,7 +1,7 @@ #![allow(clippy::all)] #![cfg_attr(rustfmt, rustfmt::skip)] use actix_web::web::Data; -use backend::service::doc::{crud::update_doc, manager::DocManager}; +use backend::services::doc::{crud::update_doc, manager::DocManager}; use flowy_document::services::doc::ClientDocEditor as ClientEditDocContext; use flowy_test::{workspace::ViewTest, FlowyTest}; use flowy_user::services::user::UserSession; @@ -12,7 +12,7 @@ use tokio::time::{sleep, Duration}; // use crate::helper::*; use crate::util::helper::{spawn_server, TestServer}; use flowy_document_infra::{entities::doc::DocIdentifier, protobuf::UpdateDocParams}; -use lib_ot::core::{Attribute, Delta, Interval}; +use lib_ot::core::{RichTextAttribute, RichTextDelta, Interval}; use parking_lot::RwLock; pub struct DocumentTest { @@ -23,7 +23,7 @@ pub struct DocumentTest { pub enum DocScript { ClientConnectWs, ClientInsertText(usize, &'static str), - ClientFormatText(Interval, Attribute), + ClientFormatText(Interval, RichTextAttribute), ClientOpenDoc, AssertClient(&'static str), AssertServer(&'static str, i64), @@ -150,8 +150,8 @@ async fn run_scripts(context: Arc>, scripts: Vec(&self, index: usize, data: T) -> Result<(), DocError> { - let (ret, rx) = oneshot::channel::>(); + let (ret, rx) = oneshot::channel::>(); let msg = EditCommand::Insert { index, data: data.to_string(), @@ -73,7 +73,7 @@ impl ClientDocEditor { } pub async fn delete(&self, interval: Interval) -> Result<(), DocError> { - let (ret, rx) = oneshot::channel::>(); + let (ret, rx) = oneshot::channel::>(); let msg = EditCommand::Delete { interval, ret }; let _ = self.edit_tx.send(msg); let delta = rx.await.map_err(internal_error)??; @@ -81,8 +81,8 @@ impl ClientDocEditor { Ok(()) } - pub async fn format(&self, interval: Interval, attribute: Attribute) -> Result<(), DocError> { - let (ret, rx) = oneshot::channel::>(); + pub async fn format(&self, interval: Interval, attribute: RichTextAttribute) -> Result<(), DocError> { + let (ret, rx) = oneshot::channel::>(); let msg = EditCommand::Format { interval, attribute, @@ -95,7 +95,7 @@ impl ClientDocEditor { } pub async fn replace(&mut self, interval: Interval, data: T) -> Result<(), DocError> { - let (ret, rx) = oneshot::channel::>(); + let (ret, rx) = oneshot::channel::>(); let msg = EditCommand::Replace { interval, data: data.to_string(), @@ -149,7 +149,7 @@ impl ClientDocEditor { }) } - async fn save_local_delta(&self, delta: Delta) -> Result { + async fn save_local_delta(&self, delta: RichTextDelta) -> Result { let delta_data = delta.to_bytes(); let (base_rev_id, rev_id) = self.rev_manager.next_rev_id(); let delta_data = delta_data.to_vec(); @@ -160,7 +160,7 @@ impl ClientDocEditor { #[tracing::instrument(level = "debug", skip(self, data), err)] pub(crate) async fn composing_local_delta(&self, data: Bytes) -> Result<(), DocError> { - let delta = Delta::from_bytes(&data)?; + let delta = RichTextDelta::from_bytes(&data)?; let (ret, rx) = oneshot::channel::>(); let msg = EditCommand::ComposeDelta { delta: delta.clone(), @@ -314,7 +314,7 @@ fn spawn_rev_receiver(mut receiver: mpsc::UnboundedReceiver, ws: Arc) -> UnboundedSender { +fn spawn_edit_queue(doc_id: &str, delta: RichTextDelta, _pool: Arc) -> UnboundedSender { let (sender, receiver) = mpsc::unbounded_channel::(); let actor = EditCommandQueue::new(doc_id, delta, receiver); tokio::spawn(actor.run()); diff --git a/frontend/rust-lib/flowy-document/src/services/doc/edit/queue.rs b/frontend/rust-lib/flowy-document/src/services/doc/edit/queue.rs index 0584dabc1b..caeb062601 100644 --- a/frontend/rust-lib/flowy-document/src/services/doc/edit/queue.rs +++ b/frontend/rust-lib/flowy-document/src/services/doc/edit/queue.rs @@ -6,7 +6,7 @@ use flowy_document_infra::{ errors::DocumentError, }; use futures::stream::StreamExt; -use lib_ot::core::{Attribute, Delta, Interval, OperationTransformable}; +use lib_ot::core::{Interval, OperationTransformable, RichTextAttribute, RichTextDelta}; use std::{convert::TryFrom, sync::Arc}; use tokio::sync::{mpsc, oneshot, RwLock}; @@ -17,7 +17,7 @@ pub(crate) struct EditCommandQueue { } impl EditCommandQueue { - pub(crate) fn new(doc_id: &str, delta: Delta, receiver: mpsc::UnboundedReceiver) -> Self { + pub(crate) fn new(doc_id: &str, delta: RichTextDelta, receiver: mpsc::UnboundedReceiver) -> Self { let document = Arc::new(RwLock::new(Document::from_delta(delta))); Self { doc_id: doc_id.to_owned(), @@ -54,7 +54,7 @@ impl EditCommandQueue { }, EditCommand::RemoteRevision { bytes, ret } => { let revision = Revision::try_from(bytes)?; - let delta = Delta::from_bytes(&revision.delta_data)?; + let delta = RichTextDelta::from_bytes(&revision.delta_data)?; let rev_id: RevId = revision.rev_id.into(); let (server_prime, client_prime) = self.document.read().await.delta().transform(&delta)?; let transform_delta = TransformDeltas { @@ -107,7 +107,7 @@ impl EditCommandQueue { } #[tracing::instrument(level = "debug", skip(self, delta), fields(compose_result), err)] - async fn composed_delta(&self, delta: Delta) -> Result<(), DocumentError> { + async fn composed_delta(&self, delta: RichTextDelta) -> Result<(), DocumentError> { // tracing::debug!("{:?} thread handle_message", thread::current(),); let mut document = self.document.write().await; tracing::Span::current().record( @@ -125,7 +125,7 @@ impl EditCommandQueue { pub(crate) type Ret = oneshot::Sender>; pub(crate) enum EditCommand { ComposeDelta { - delta: Delta, + delta: RichTextDelta, ret: Ret<()>, }, RemoteRevision { @@ -135,22 +135,22 @@ pub(crate) enum EditCommand { Insert { index: usize, data: String, - ret: Ret, + ret: Ret, }, Delete { interval: Interval, - ret: Ret, + ret: Ret, }, Format { interval: Interval, - attribute: Attribute, - ret: Ret, + attribute: RichTextAttribute, + ret: Ret, }, Replace { interval: Interval, data: String, - ret: Ret, + ret: Ret, }, CanUndo { ret: oneshot::Sender, @@ -170,7 +170,7 @@ pub(crate) enum EditCommand { } pub(crate) struct TransformDeltas { - pub client_prime: Delta, - pub server_prime: Delta, + pub client_prime: RichTextDelta, + pub server_prime: RichTextDelta, pub server_rev_id: RevId, } diff --git a/frontend/rust-lib/flowy-document/src/services/doc/revision/manager.rs b/frontend/rust-lib/flowy-document/src/services/doc/revision/manager.rs index 89d7587fef..063c82d664 100644 --- a/frontend/rust-lib/flowy-document/src/services/doc/revision/manager.rs +++ b/frontend/rust-lib/flowy-document/src/services/doc/revision/manager.rs @@ -8,7 +8,7 @@ use flowy_document_infra::{ util::RevIdCounter, }; use lib_infra::future::ResultFuture; -use lib_ot::core::{Delta, OperationTransformable}; +use lib_ot::core::{OperationTransformable, RichTextDelta}; use std::sync::Arc; use tokio::sync::mpsc; @@ -38,7 +38,7 @@ impl RevisionManager { } } - pub async fn load_document(&mut self) -> DocResult { + pub async fn load_document(&mut self) -> DocResult { let doc = self.rev_store.fetch_document().await?; self.update_rev_id_counter_value(doc.rev_id); Ok(doc.delta()?) @@ -67,9 +67,9 @@ impl RevisionManager { pub async fn mk_revisions(&self, range: RevisionRange) -> Result { debug_assert!(range.doc_id == self.doc_id); let revisions = self.rev_store.revs_in_range(range.clone()).await?; - let mut new_delta = Delta::new(); + let mut new_delta = RichTextDelta::new(); for revision in revisions { - match Delta::from_bytes(revision.delta_data) { + match RichTextDelta::from_bytes(revision.delta_data) { Ok(delta) => { new_delta = new_delta.compose(&delta)?; }, diff --git a/frontend/rust-lib/flowy-document/src/services/doc/revision/persistence.rs b/frontend/rust-lib/flowy-document/src/services/doc/revision/persistence.rs index f425db7ef0..1b63e33e88 100644 --- a/frontend/rust-lib/flowy-document/src/services/doc/revision/persistence.rs +++ b/frontend/rust-lib/flowy-document/src/services/doc/revision/persistence.rs @@ -9,7 +9,7 @@ use flowy_database::{ConnectionPool, SqliteConnection}; use flowy_document_infra::entities::doc::{revision_from_doc, Doc, RevId, RevType, Revision, RevisionRange}; use futures::stream::StreamExt; use lib_infra::future::ResultFuture; -use lib_ot::core::{Delta, Operation, OperationTransformable}; +use lib_ot::core::{Operation, OperationTransformable, RichTextDelta}; use std::{collections::VecDeque, sync::Arc, time::Duration}; use tokio::{ sync::{broadcast, mpsc, RwLock}, @@ -188,9 +188,9 @@ async fn fetch_from_local(doc_id: &str, persistence: Arc) -> DocRes let base_rev_id: RevId = revisions.last().unwrap().base_rev_id.into(); let rev_id: RevId = revisions.last().unwrap().rev_id.into(); - let mut delta = Delta::new(); + let mut delta = RichTextDelta::new(); for (_, revision) in revisions.into_iter().enumerate() { - match Delta::from_bytes(revision.delta_data) { + match RichTextDelta::from_bytes(revision.delta_data) { Ok(local_delta) => { delta = delta.compose(&local_delta)?; }, @@ -225,7 +225,7 @@ async fn fetch_from_local(doc_id: &str, persistence: Arc) -> DocRes } #[cfg(debug_assertions)] -fn validate_delta(doc_id: &str, persistence: Arc, conn: &SqliteConnection, delta: &Delta) { +fn validate_delta(doc_id: &str, persistence: Arc, conn: &SqliteConnection, delta: &RichTextDelta) { if delta.ops.last().is_none() { return; } @@ -236,7 +236,7 @@ fn validate_delta(doc_id: &str, persistence: Arc, conn: &SqliteConn let result = || { let revisions = persistence.rev_sql.read_rev_tables(&doc_id, conn)?; for revision in revisions { - let delta = Delta::from_bytes(revision.delta_data)?; + let delta = RichTextDelta::from_bytes(revision.delta_data)?; log::error!("Invalid revision: {}:{}", revision.rev_id, delta.to_json()); } Ok::<(), DocError>(()) diff --git a/frontend/rust-lib/flowy-document/tests/editor/attribute_test.rs b/frontend/rust-lib/flowy-document/tests/editor/attribute_test.rs index b817e1f646..80b987262f 100644 --- a/frontend/rust-lib/flowy-document/tests/editor/attribute_test.rs +++ b/frontend/rust-lib/flowy-document/tests/editor/attribute_test.rs @@ -1,7 +1,7 @@ #![cfg_attr(rustfmt, rustfmt::skip)] use crate::editor::{TestBuilder, TestOp::*}; use flowy_document_infra::core::{FlowyDoc, PlainDoc}; -use lib_ot::core::{Delta, Interval, OperationTransformable, NEW_LINE, WHITESPACE, FlowyStr}; +use lib_ot::core::{Interval, OperationTransformable, NEW_LINE, WHITESPACE, FlowyStr, RichTextDelta}; use unicode_segmentation::UnicodeSegmentation; #[test] @@ -760,12 +760,12 @@ fn attributes_preserve_list_format_on_merge() { #[test] fn delta_compose() { - let mut delta = Delta::from_json(r#"[{"insert":"\n"}]"#).unwrap(); + let mut delta = RichTextDelta::from_json(r#"[{"insert":"\n"}]"#).unwrap(); let deltas = vec![ - Delta::from_json(r#"[{"retain":1,"attributes":{"list":"unchecked"}}]"#).unwrap(), - Delta::from_json(r#"[{"insert":"a"}]"#).unwrap(), - Delta::from_json(r#"[{"retain":1},{"insert":"\n","attributes":{"list":"unchecked"}}]"#).unwrap(), - Delta::from_json(r#"[{"retain":2},{"retain":1,"attributes":{"list":""}}]"#).unwrap(), + RichTextDelta::from_json(r#"[{"retain":1,"attributes":{"list":"unchecked"}}]"#).unwrap(), + RichTextDelta::from_json(r#"[{"insert":"a"}]"#).unwrap(), + RichTextDelta::from_json(r#"[{"retain":1},{"insert":"\n","attributes":{"list":"unchecked"}}]"#).unwrap(), + RichTextDelta::from_json(r#"[{"retain":2},{"retain":1,"attributes":{"list":""}}]"#).unwrap(), ]; for d in deltas { diff --git a/frontend/rust-lib/flowy-document/tests/editor/mod.rs b/frontend/rust-lib/flowy-document/tests/editor/mod.rs index 99b8c2ef07..1447fda4f3 100644 --- a/frontend/rust-lib/flowy-document/tests/editor/mod.rs +++ b/frontend/rust-lib/flowy-document/tests/editor/mod.rs @@ -80,8 +80,8 @@ pub enum TestOp { pub struct TestBuilder { documents: Vec, - deltas: Vec>, - primes: Vec>, + deltas: Vec>, + primes: Vec>, } impl TestBuilder { @@ -125,11 +125,11 @@ impl TestBuilder { TestOp::InsertBold(delta_i, s, iv) => { let document = &mut self.documents[*delta_i]; document.insert(iv.start, s).unwrap(); - document.format(*iv, Attribute::Bold(true)).unwrap(); + document.format(*iv, RichTextAttribute::Bold(true)).unwrap(); }, TestOp::Bold(delta_i, iv, enable) => { let document = &mut self.documents[*delta_i]; - let attribute = Attribute::Bold(*enable); + let attribute = RichTextAttribute::Bold(*enable); let delta = document.format(*iv, attribute).unwrap(); tracing::trace!("Bold delta: {}", delta.to_json()); self.deltas.insert(*delta_i, Some(delta)); @@ -137,8 +137,8 @@ impl TestBuilder { TestOp::Italic(delta_i, iv, enable) => { let document = &mut self.documents[*delta_i]; let attribute = match *enable { - true => Attribute::Italic(true), - false => Attribute::Italic(false), + true => RichTextAttribute::Italic(true), + false => RichTextAttribute::Italic(false), }; let delta = document.format(*iv, attribute).unwrap(); tracing::trace!("Italic delta: {}", delta.to_json()); @@ -146,21 +146,21 @@ impl TestBuilder { }, TestOp::Header(delta_i, iv, level) => { let document = &mut self.documents[*delta_i]; - let attribute = Attribute::Header(*level); + let attribute = RichTextAttribute::Header(*level); let delta = document.format(*iv, attribute).unwrap(); tracing::trace!("Header delta: {}", delta.to_json()); self.deltas.insert(*delta_i, Some(delta)); }, TestOp::Link(delta_i, iv, link) => { let document = &mut self.documents[*delta_i]; - let attribute = Attribute::Link(link.to_owned()); + let attribute = RichTextAttribute::Link(link.to_owned()); let delta = document.format(*iv, attribute).unwrap(); tracing::trace!("Link delta: {}", delta.to_json()); self.deltas.insert(*delta_i, Some(delta)); }, TestOp::Bullet(delta_i, iv, enable) => { let document = &mut self.documents[*delta_i]; - let attribute = Attribute::Bullet(*enable); + let attribute = RichTextAttribute::Bullet(*enable); let delta = document.format(*iv, attribute).unwrap(); tracing::debug!("Bullet delta: {}", delta.to_json()); @@ -225,8 +225,8 @@ impl TestBuilder { TestOp::AssertDocJson(delta_i, expected) => { let delta_json = self.documents[*delta_i].to_json(); - let expected_delta: Delta = serde_json::from_str(expected).unwrap(); - let target_delta: Delta = serde_json::from_str(&delta_json).unwrap(); + let expected_delta: RichTextDelta = serde_json::from_str(expected).unwrap(); + let target_delta: RichTextDelta = serde_json::from_str(&delta_json).unwrap(); if expected_delta != target_delta { log::error!("✅ expect: {}", expected,); @@ -237,8 +237,8 @@ impl TestBuilder { TestOp::AssertPrimeJson(doc_i, expected) => { let prime_json = self.primes[*doc_i].as_ref().unwrap().to_json(); - let expected_prime: Delta = serde_json::from_str(expected).unwrap(); - let target_prime: Delta = serde_json::from_str(&prime_json).unwrap(); + let expected_prime: RichTextDelta = serde_json::from_str(expected).unwrap(); + let target_prime: RichTextDelta = serde_json::from_str(&prime_json).unwrap(); if expected_prime != target_prime { log::error!("✅ expect prime: {}", expected,); @@ -292,8 +292,8 @@ impl Rng { .collect() } - pub fn gen_delta(&mut self, s: &str) -> Delta { - let mut delta = Delta::default(); + pub fn gen_delta(&mut self, s: &str) -> RichTextDelta { + let mut delta = RichTextDelta::default(); loop { let left = s.chars().count() - delta.base_len; if left == 0 { @@ -306,18 +306,18 @@ impl Rng { }; match self.0.gen_range(0.0, 1.0) { f if f < 0.2 => { - delta.insert(&self.gen_string(i), Attributes::default()); + delta.insert(&self.gen_string(i), RichTextAttributes::default()); }, f if f < 0.4 => { delta.delete(i); }, _ => { - delta.retain(i, Attributes::default()); + delta.retain(i, RichTextAttributes::default()); }, } } if self.0.gen_range(0.0, 1.0) < 0.3 { - delta.insert(&("1".to_owned() + &self.gen_string(10)), Attributes::default()); + delta.insert(&("1".to_owned() + &self.gen_string(10)), RichTextAttributes::default()); } delta } diff --git a/frontend/rust-lib/flowy-document/tests/editor/op_test.rs b/frontend/rust-lib/flowy-document/tests/editor/op_test.rs index f7b69e171e..deff6dbd8d 100644 --- a/frontend/rust-lib/flowy-document/tests/editor/op_test.rs +++ b/frontend/rust-lib/flowy-document/tests/editor/op_test.rs @@ -35,7 +35,7 @@ fn attributes_insert_text_at_middle() { #[test] fn delta_get_ops_in_interval_1() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); let insert_a = OpBuilder::insert("123").build(); let insert_b = OpBuilder::insert("4").build(); @@ -48,7 +48,7 @@ fn delta_get_ops_in_interval_1() { #[test] fn delta_get_ops_in_interval_2() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); let insert_a = OpBuilder::insert("123").build(); let insert_b = OpBuilder::insert("4").build(); let insert_c = OpBuilder::insert("5").build(); @@ -92,7 +92,7 @@ fn delta_get_ops_in_interval_2() { #[test] fn delta_get_ops_in_interval_3() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); let insert_a = OpBuilder::insert("123456").build(); delta.add(insert_a.clone()); assert_eq!( @@ -103,7 +103,7 @@ fn delta_get_ops_in_interval_3() { #[test] fn delta_get_ops_in_interval_4() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); let insert_a = OpBuilder::insert("12").build(); let insert_b = OpBuilder::insert("34").build(); let insert_c = OpBuilder::insert("56").build(); @@ -133,7 +133,7 @@ fn delta_get_ops_in_interval_4() { #[test] fn delta_get_ops_in_interval_5() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); let insert_a = OpBuilder::insert("123456").build(); let insert_b = OpBuilder::insert("789").build(); delta.ops.push(insert_a.clone()); @@ -151,7 +151,7 @@ fn delta_get_ops_in_interval_5() { #[test] fn delta_get_ops_in_interval_6() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); let insert_a = OpBuilder::insert("12345678").build(); delta.add(insert_a.clone()); assert_eq!( @@ -162,7 +162,7 @@ fn delta_get_ops_in_interval_6() { #[test] fn delta_get_ops_in_interval_7() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); let insert_a = OpBuilder::insert("12345").build(); let retain_a = OpBuilder::retain(3).build(); @@ -182,7 +182,7 @@ fn delta_get_ops_in_interval_7() { #[test] fn delta_seek_1() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); let insert_a = OpBuilder::insert("12345").build(); let retain_a = OpBuilder::retain(3).build(); delta.add(insert_a.clone()); @@ -194,7 +194,7 @@ fn delta_seek_1() { #[test] fn delta_seek_2() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); delta.add(OpBuilder::insert("12345").build()); let mut iter = DeltaIter::new(&delta); @@ -203,7 +203,7 @@ fn delta_seek_2() { #[test] fn delta_seek_3() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); delta.add(OpBuilder::insert("12345").build()); let mut iter = DeltaIter::new(&delta); @@ -218,7 +218,7 @@ fn delta_seek_3() { #[test] fn delta_seek_4() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); delta.add(OpBuilder::insert("12345").build()); let mut iter = DeltaIter::new(&delta); @@ -228,10 +228,10 @@ fn delta_seek_4() { #[test] fn delta_seek_5() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); let attributes = AttributeBuilder::new() - .add_attr(Attribute::Bold(true)) - .add_attr(Attribute::Italic(true)) + .add_attr(RichTextAttribute::Bold(true)) + .add_attr(RichTextAttribute::Italic(true)) .build(); delta.add(OpBuilder::insert("1234").attributes(attributes.clone()).build()); @@ -248,7 +248,7 @@ fn delta_seek_5() { #[test] fn delta_next_op_len_test() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); delta.add(OpBuilder::insert("12345").build()); let mut iter = DeltaIter::new(&delta); @@ -261,7 +261,7 @@ fn delta_next_op_len_test() { #[test] fn delta_next_op_len_test2() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); delta.add(OpBuilder::insert("12345").build()); let mut iter = DeltaIter::new(&delta); @@ -272,7 +272,7 @@ fn delta_next_op_len_test2() { #[test] fn delta_next_op_with_len_zero() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); delta.add(OpBuilder::insert("12345").build()); let mut iter = DeltaIter::new(&delta); assert_eq!(iter.next_op_with_len(0), None,); @@ -281,7 +281,7 @@ fn delta_next_op_with_len_zero() { #[test] fn delta_next_op_with_len_cross_op_return_last() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); delta.add(OpBuilder::insert("12345").build()); delta.add(OpBuilder::retain(1).build()); delta.add(OpBuilder::insert("678").build()); @@ -294,16 +294,16 @@ fn delta_next_op_with_len_cross_op_return_last() { #[test] fn lengths() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); assert_eq!(delta.base_len, 0); assert_eq!(delta.target_len, 0); - delta.retain(5, Attributes::default()); + delta.retain(5, RichTextAttributes::default()); assert_eq!(delta.base_len, 5); assert_eq!(delta.target_len, 5); - delta.insert("abc", Attributes::default()); + delta.insert("abc", RichTextAttributes::default()); assert_eq!(delta.base_len, 5); assert_eq!(delta.target_len, 8); - delta.retain(2, Attributes::default()); + delta.retain(2, RichTextAttributes::default()); assert_eq!(delta.base_len, 7); assert_eq!(delta.target_len, 10); delta.delete(2); @@ -312,11 +312,11 @@ fn lengths() { } #[test] fn sequence() { - let mut delta = Delta::default(); - delta.retain(5, Attributes::default()); - delta.retain(0, Attributes::default()); - delta.insert("appflowy", Attributes::default()); - delta.insert("", Attributes::default()); + let mut delta = RichTextDelta::default(); + delta.retain(5, RichTextAttributes::default()); + delta.retain(0, RichTextAttributes::default()); + delta.insert("appflowy", RichTextAttributes::default()); + delta.insert("", RichTextAttributes::default()); delta.delete(3); delta.delete(0); assert_eq!(delta.ops.len(), 3); @@ -335,12 +335,12 @@ fn apply_1000() { #[test] fn apply() { let s = "hello world,".to_owned(); - let mut delta_a = Delta::default(); - delta_a.insert(&s, Attributes::default()); + let mut delta_a = RichTextDelta::default(); + delta_a.insert(&s, RichTextAttributes::default()); - let mut delta_b = Delta::default(); - delta_b.retain(s.len(), Attributes::default()); - delta_b.insert("appflowy", Attributes::default()); + let mut delta_b = RichTextDelta::default(); + 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(); @@ -349,16 +349,16 @@ fn apply() { #[test] fn base_len_test() { - let mut delta_a = Delta::default(); - delta_a.insert("a", Attributes::default()); - delta_a.insert("b", Attributes::default()); - delta_a.insert("c", Attributes::default()); + let mut delta_a = RichTextDelta::default(); + delta_a.insert("a", RichTextAttributes::default()); + delta_a.insert("b", RichTextAttributes::default()); + delta_a.insert("c", RichTextAttributes::default()); let s = "hello world,".to_owned(); delta_a.delete(s.len()); let after_a = delta_a.apply(&s).unwrap(); - delta_a.insert("d", Attributes::default()); + delta_a.insert("d", RichTextAttributes::default()); assert_eq!("abc", &after_a); } @@ -377,43 +377,43 @@ fn invert() { #[test] fn empty_ops() { - let mut delta = Delta::default(); - delta.retain(0, Attributes::default()); - delta.insert("", Attributes::default()); + let mut delta = RichTextDelta::default(); + delta.retain(0, RichTextAttributes::default()); + delta.insert("", RichTextAttributes::default()); delta.delete(0); assert_eq!(delta.ops.len(), 0); } #[test] fn eq() { - let mut delta_a = Delta::default(); + let mut delta_a = RichTextDelta::default(); delta_a.delete(1); - delta_a.insert("lo", Attributes::default()); - delta_a.retain(2, Attributes::default()); - delta_a.retain(3, Attributes::default()); - let mut delta_b = Delta::default(); + delta_a.insert("lo", RichTextAttributes::default()); + delta_a.retain(2, RichTextAttributes::default()); + delta_a.retain(3, RichTextAttributes::default()); + let mut delta_b = RichTextDelta::default(); delta_b.delete(1); - delta_b.insert("l", Attributes::default()); - delta_b.insert("o", Attributes::default()); - delta_b.retain(5, Attributes::default()); + delta_b.insert("l", RichTextAttributes::default()); + delta_b.insert("o", RichTextAttributes::default()); + delta_b.retain(5, RichTextAttributes::default()); assert_eq!(delta_a, delta_b); delta_a.delete(1); - delta_b.retain(1, Attributes::default()); + delta_b.retain(1, RichTextAttributes::default()); assert_ne!(delta_a, delta_b); } #[test] fn ops_merging() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); assert_eq!(delta.ops.len(), 0); - delta.retain(2, Attributes::default()); + delta.retain(2, RichTextAttributes::default()); assert_eq!(delta.ops.len(), 1); assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(2).build())); - delta.retain(3, Attributes::default()); + delta.retain(3, RichTextAttributes::default()); assert_eq!(delta.ops.len(), 1); assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build())); - delta.insert("abc", Attributes::default()); + delta.insert("abc", RichTextAttributes::default()); assert_eq!(delta.ops.len(), 2); assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abc").build())); - delta.insert("xyz", Attributes::default()); + delta.insert("xyz", RichTextAttributes::default()); assert_eq!(delta.ops.len(), 2); assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abcxyz").build())); delta.delete(1); @@ -425,13 +425,13 @@ fn ops_merging() { } #[test] fn is_noop() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); assert!(delta.is_noop()); - delta.retain(5, Attributes::default()); + delta.retain(5, RichTextAttributes::default()); assert!(delta.is_noop()); - delta.retain(3, Attributes::default()); + delta.retain(3, RichTextAttributes::default()); assert!(delta.is_noop()); - delta.insert("lorem", Attributes::default()); + delta.insert("lorem", RichTextAttributes::default()); assert!(!delta.is_noop()); } #[test] @@ -473,15 +473,18 @@ fn transform_random_delta() { #[test] fn transform_with_two_delta_test() { - let mut a = Delta::default(); + let mut a = RichTextDelta::default(); let mut a_s = String::new(); - a.insert("123", AttributeBuilder::new().add_attr(Attribute::Bold(true)).build()); + a.insert( + "123", + AttributeBuilder::new().add_attr(RichTextAttribute::Bold(true)).build(), + ); a_s = a.apply(&a_s).unwrap(); assert_eq!(&a_s, "123"); - let mut b = Delta::default(); + let mut b = RichTextDelta::default(); let mut b_s = String::new(); - b.insert("456", Attributes::default()); + b.insert("456", RichTextAttributes::default()); b_s = b.apply(&b_s).unwrap(); assert_eq!(&b_s, "456"); @@ -569,10 +572,10 @@ fn transform_two_conflict_non_seq_delta() { #[test] fn delta_invert_no_attribute_delta() { - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); delta.add(OpBuilder::insert("123").build()); - let mut change = Delta::default(); + let mut change = RichTextDelta::default(); change.add(OpBuilder::retain(3).build()); change.add(OpBuilder::insert("456").build()); let undo = change.invert(&delta); diff --git a/frontend/rust-lib/flowy-document/tests/editor/serde_test.rs b/frontend/rust-lib/flowy-document/tests/editor/serde_test.rs index 6efa0db6d1..5f634bce5e 100644 --- a/frontend/rust-lib/flowy-document/tests/editor/serde_test.rs +++ b/frontend/rust-lib/flowy-document/tests/editor/serde_test.rs @@ -4,14 +4,14 @@ use lib_ot::core::*; #[test] fn operation_insert_serialize_test() { let attributes = AttributeBuilder::new() - .add_attr(Attribute::Bold(true)) - .add_attr(Attribute::Italic(true)) + .add_attr(RichTextAttribute::Bold(true)) + .add_attr(RichTextAttribute::Italic(true)) .build(); let operation = OpBuilder::insert("123").attributes(attributes).build(); let json = serde_json::to_string(&operation).unwrap(); eprintln!("{}", json); - let insert_op: Operation = serde_json::from_str(&json).unwrap(); + let insert_op: RichTextOperation = serde_json::from_str(&json).unwrap(); assert_eq!(insert_op, operation); } @@ -20,23 +20,23 @@ fn operation_retain_serialize_test() { let operation = Operation::Retain(12.into()); let json = serde_json::to_string(&operation).unwrap(); eprintln!("{}", json); - let insert_op: Operation = serde_json::from_str(&json).unwrap(); + let insert_op: RichTextOperation = serde_json::from_str(&json).unwrap(); assert_eq!(insert_op, operation); } #[test] fn operation_delete_serialize_test() { - let operation = Operation::Delete(2); + let operation = RichTextOperation::Delete(2); let json = serde_json::to_string(&operation).unwrap(); - let insert_op: Operation = serde_json::from_str(&json).unwrap(); + let insert_op: RichTextOperation = serde_json::from_str(&json).unwrap(); assert_eq!(insert_op, operation); } #[test] fn attributes_serialize_test() { let attributes = AttributeBuilder::new() - .add_attr(Attribute::Bold(true)) - .add_attr(Attribute::Italic(true)) + .add_attr(RichTextAttribute::Bold(true)) + .add_attr(RichTextAttribute::Italic(true)) .build(); let retain = OpBuilder::insert("123").attributes(attributes).build(); @@ -49,8 +49,8 @@ fn delta_serialize_multi_attribute_test() { let mut delta = Delta::default(); let attributes = AttributeBuilder::new() - .add_attr(Attribute::Bold(true)) - .add_attr(Attribute::Italic(true)) + .add_attr(RichTextAttribute::Bold(true)) + .add_attr(RichTextAttribute::Italic(true)) .build(); let retain = OpBuilder::insert("123").attributes(attributes).build(); @@ -73,7 +73,7 @@ fn delta_deserialize_test() { {"retain":2,"attributes":{"italic":"true","bold":"true"}}, {"retain":2,"attributes":{"italic":true,"bold":true}} ]"#; - let delta = Delta::from_json(json).unwrap(); + let delta = RichTextDelta::from_json(json).unwrap(); eprintln!("{}", delta); } @@ -82,10 +82,10 @@ fn delta_deserialize_null_test() { let json = r#"[ {"retain":7,"attributes":{"bold":null}} ]"#; - let delta1 = Delta::from_json(json).unwrap(); + let delta1 = RichTextDelta::from_json(json).unwrap(); - let mut attribute = Attribute::Bold(true); - attribute.value = AttributeValue(None); + let mut attribute = RichTextAttribute::Bold(true); + attribute.value = RichTextAttributeValue(None); let delta2 = DeltaBuilder::new().retain_with_attributes(7, attribute.into()).build(); assert_eq!(delta2.to_json(), r#"[{"retain":7,"attributes":{"bold":""}}]"#); @@ -94,8 +94,8 @@ fn delta_deserialize_null_test() { #[test] fn delta_serde_null_test() { - let mut attribute = Attribute::Bold(true); - attribute.value = AttributeValue(None); + let mut attribute = RichTextAttribute::Bold(true); + attribute.value = RichTextAttributeValue(None); assert_eq!(attribute.to_json(), r#"{"bold":""}"#); } diff --git a/shared-lib/flowy-document-infra/src/core/document.rs b/shared-lib/flowy-document-infra/src/core/document.rs index dbd92488e9..31a8f40986 100644 --- a/shared-lib/flowy-document-infra/src/core/document.rs +++ b/shared-lib/flowy-document-infra/src/core/document.rs @@ -10,21 +10,21 @@ use lib_ot::core::*; use tokio::sync::mpsc; pub trait CustomDocument { - fn init_delta() -> Delta; + fn init_delta() -> RichTextDelta; } pub struct PlainDoc(); impl CustomDocument for PlainDoc { - fn init_delta() -> Delta { Delta::new() } + fn init_delta() -> RichTextDelta { RichTextDelta::new() } } pub struct FlowyDoc(); impl CustomDocument for FlowyDoc { - fn init_delta() -> Delta { doc_initial_delta() } + fn init_delta() -> RichTextDelta { doc_initial_delta() } } pub struct Document { - delta: Delta, + delta: RichTextDelta, history: History, view: View, last_edit_time: usize, @@ -34,7 +34,7 @@ pub struct Document { impl Document { pub fn new() -> Self { Self::from_delta(C::init_delta()) } - pub fn from_delta(delta: Delta) -> Self { + pub fn from_delta(delta: RichTextDelta) -> Self { Document { delta, history: History::new(), @@ -45,7 +45,7 @@ impl Document { } pub fn from_json(json: &str) -> Result { - let delta = Delta::from_json(json)?; + let delta = RichTextDelta::from_json(json)?; Ok(Self::from_delta(delta)) } @@ -55,11 +55,11 @@ impl Document { pub fn to_plain_string(&self) -> String { self.delta.apply("").unwrap() } - pub fn delta(&self) -> &Delta { &self.delta } + pub fn delta(&self) -> &RichTextDelta { &self.delta } pub fn set_notify(&mut self, notify: mpsc::UnboundedSender<()>) { self.notify = Some(notify); } - pub fn set_delta(&mut self, data: Delta) { + pub fn set_delta(&mut self, data: RichTextDelta) { self.delta = data; match &self.notify { @@ -70,7 +70,7 @@ impl Document { } } - pub fn compose_delta(&mut self, mut delta: Delta) -> Result<(), DocumentError> { + pub fn compose_delta(&mut self, mut delta: RichTextDelta) -> Result<(), DocumentError> { trim(&mut delta); tracing::trace!("{} compose {}", &self.delta.to_json(), delta.to_json()); let mut composed_delta = self.delta.compose(&delta)?; @@ -100,7 +100,7 @@ impl Document { Ok(()) } - pub fn insert(&mut self, index: usize, data: T) -> Result { + pub fn insert(&mut self, index: usize, data: T) -> Result { let interval = Interval::new(index, index); let _ = validate_interval(&self.delta, &interval)?; @@ -111,7 +111,7 @@ impl Document { Ok(delta) } - pub fn delete(&mut self, interval: Interval) -> Result { + pub fn delete(&mut self, interval: Interval) -> Result { let _ = validate_interval(&self.delta, &interval)?; debug_assert_eq!(interval.is_empty(), false); let delete = self.view.delete(&self.delta, interval)?; @@ -122,7 +122,7 @@ impl Document { Ok(delete) } - pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result { + pub fn format(&mut self, interval: Interval, attribute: RichTextAttribute) -> Result { let _ = validate_interval(&self.delta, &interval)?; tracing::trace!("format with {} at {}", attribute, interval); let format_delta = self.view.format(&self.delta, attribute, interval).unwrap(); @@ -132,9 +132,9 @@ impl Document { Ok(format_delta) } - pub fn replace(&mut self, interval: Interval, data: T) -> Result { + pub fn replace(&mut self, interval: Interval, data: T) -> Result { let _ = validate_interval(&self.delta, &interval)?; - let mut delta = Delta::default(); + let mut delta = RichTextDelta::default(); let text = data.to_string(); if !text.is_empty() { delta = self.view.insert(&self.delta, &text, interval)?; @@ -184,7 +184,7 @@ impl Document { } impl Document { - fn invert(&self, delta: &Delta) -> Result<(Delta, Delta), DocumentError> { + fn invert(&self, delta: &RichTextDelta) -> Result<(RichTextDelta, RichTextDelta), DocumentError> { // c = a.compose(b) // d = b.invert(a) // a = c.compose(d) @@ -195,7 +195,7 @@ impl Document { } } -fn validate_interval(delta: &Delta, interval: &Interval) -> Result<(), DocumentError> { +fn validate_interval(delta: &RichTextDelta, interval: &Interval) -> Result<(), DocumentError> { if delta.target_len < interval.end { log::error!("{:?} out of bounds. should 0..{}", interval, delta.target_len); return Err(DocumentError::out_of_bound()); @@ -204,7 +204,7 @@ fn validate_interval(delta: &Delta, interval: &Interval) -> Result<(), DocumentE } /// Removes trailing retain operation with empty attributes, if present. -pub fn trim(delta: &mut Delta) { +pub fn trim(delta: &mut RichTextDelta) { if let Some(last) = delta.ops.last() { if last.is_retain() && last.is_plain() { delta.ops.pop(); diff --git a/shared-lib/flowy-document-infra/src/core/extensions/delete/default_delete.rs b/shared-lib/flowy-document-infra/src/core/extensions/delete/default_delete.rs index 47674b906d..a96f8c5a34 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/delete/default_delete.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/delete/default_delete.rs @@ -1,11 +1,11 @@ use crate::core::extensions::DeleteExt; -use lib_ot::core::{Delta, DeltaBuilder, Interval}; +use lib_ot::core::{DeltaBuilder, Interval, RichTextDelta}; pub struct DefaultDelete {} impl DeleteExt for DefaultDelete { fn ext_name(&self) -> &str { "DefaultDelete" } - fn apply(&self, _delta: &Delta, interval: Interval) -> Option { + fn apply(&self, _delta: &RichTextDelta, interval: Interval) -> Option { Some( DeltaBuilder::new() .retain(interval.start) diff --git a/shared-lib/flowy-document-infra/src/core/extensions/delete/preserve_line_format_merge.rs b/shared-lib/flowy-document-infra/src/core/extensions/delete/preserve_line_format_merge.rs index cfabbd89e7..c49a40dfa5 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/delete/preserve_line_format_merge.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/delete/preserve_line_format_merge.rs @@ -1,11 +1,20 @@ use crate::{core::extensions::DeleteExt, util::is_newline}; -use lib_ot::core::{plain_attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, NEW_LINE}; +use lib_ot::core::{ + plain_attributes, + Attributes, + CharMetric, + DeltaBuilder, + DeltaIter, + Interval, + RichTextDelta, + NEW_LINE, +}; pub struct PreserveLineFormatOnMerge {} impl DeleteExt for PreserveLineFormatOnMerge { fn ext_name(&self) -> &str { "PreserveLineFormatOnMerge" } - fn apply(&self, delta: &Delta, interval: Interval) -> Option { + fn apply(&self, delta: &RichTextDelta, interval: Interval) -> Option { if interval.is_empty() { return None; } @@ -40,7 +49,7 @@ impl DeleteExt for PreserveLineFormatOnMerge { attributes.mark_all_as_removed_except(None); if newline_op.has_attribute() { - attributes.extend(newline_op.get_attributes()); + attributes.extend_other(newline_op.get_attributes()); } new_delta.retain(line_break, plain_attributes()); diff --git a/shared-lib/flowy-document-infra/src/core/extensions/format/helper.rs b/shared-lib/flowy-document-infra/src/core/extensions/format/helper.rs index c4cac2efaa..1ecb3b3589 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/format/helper.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/format/helper.rs @@ -1,8 +1,12 @@ use crate::util::find_newline; -use lib_ot::core::{plain_attributes, Attribute, AttributeScope, Delta, Operation}; +use lib_ot::core::{plain_attributes, AttributeScope, RichTextAttribute, RichTextDelta, RichTextOperation}; -pub(crate) fn line_break(op: &Operation, attribute: &Attribute, scope: AttributeScope) -> Delta { - let mut new_delta = Delta::new(); +pub(crate) fn line_break( + op: &RichTextOperation, + attribute: &RichTextAttribute, + scope: AttributeScope, +) -> RichTextDelta { + let mut new_delta = RichTextDelta::new(); let mut start = 0; let end = op.len(); let mut s = op.get_data(); diff --git a/shared-lib/flowy-document-infra/src/core/extensions/format/resolve_block_format.rs b/shared-lib/flowy-document-infra/src/core/extensions/format/resolve_block_format.rs index 18c353fd77..0af20a1bac 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/format/resolve_block_format.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/format/resolve_block_format.rs @@ -2,13 +2,21 @@ use crate::{ core::extensions::{format::helper::line_break, FormatExt}, util::find_newline, }; -use lib_ot::core::{plain_attributes, Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval}; +use lib_ot::core::{ + plain_attributes, + AttributeScope, + DeltaBuilder, + DeltaIter, + Interval, + RichTextAttribute, + RichTextDelta, +}; pub struct ResolveBlockFormat {} impl FormatExt for ResolveBlockFormat { fn ext_name(&self) -> &str { std::any::type_name::() } - fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option { + fn apply(&self, delta: &RichTextDelta, interval: Interval, attribute: &RichTextAttribute) -> Option { if attribute.scope != AttributeScope::Block { return None; } diff --git a/shared-lib/flowy-document-infra/src/core/extensions/format/resolve_inline_format.rs b/shared-lib/flowy-document-infra/src/core/extensions/format/resolve_inline_format.rs index 3bb03467cf..e85839104c 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/format/resolve_inline_format.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/format/resolve_inline_format.rs @@ -2,13 +2,13 @@ use crate::{ core::extensions::{format::helper::line_break, FormatExt}, util::find_newline, }; -use lib_ot::core::{Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval}; +use lib_ot::core::{AttributeScope, DeltaBuilder, DeltaIter, Interval, RichTextAttribute, RichTextDelta}; pub struct ResolveInlineFormat {} impl FormatExt for ResolveInlineFormat { fn ext_name(&self) -> &str { std::any::type_name::() } - fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option { + fn apply(&self, delta: &RichTextDelta, interval: Interval, attribute: &RichTextAttribute) -> Option { if attribute.scope != AttributeScope::Inline { return None; } diff --git a/shared-lib/flowy-document-infra/src/core/extensions/insert/auto_exit_block.rs b/shared-lib/flowy-document-infra/src/core/extensions/insert/auto_exit_block.rs index d2d331635c..d440cd8060 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/insert/auto_exit_block.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/insert/auto_exit_block.rs @@ -1,12 +1,19 @@ use crate::{core::extensions::InsertExt, util::is_newline}; -use lib_ot::core::{attributes_except_header, is_empty_line_at_index, AttributeKey, Delta, DeltaBuilder, DeltaIter}; +use lib_ot::core::{ + attributes_except_header, + is_empty_line_at_index, + DeltaBuilder, + DeltaIter, + RichTextAttributeKey, + RichTextDelta, +}; pub struct AutoExitBlock {} impl InsertExt for AutoExitBlock { fn ext_name(&self) -> &str { std::any::type_name::() } - fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { + fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option { // Auto exit block will be triggered by enter two new lines if !is_newline(text) { return None; @@ -39,7 +46,7 @@ impl InsertExt for AutoExitBlock { }, } - attributes.mark_all_as_removed_except(Some(AttributeKey::Header)); + attributes.mark_all_as_removed_except(Some(RichTextAttributeKey::Header)); Some( DeltaBuilder::new() diff --git a/shared-lib/flowy-document-infra/src/core/extensions/insert/auto_format.rs b/shared-lib/flowy-document-infra/src/core/extensions/insert/auto_format.rs index fe814f27ee..5f517a43c5 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/insert/auto_format.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/insert/auto_format.rs @@ -1,5 +1,13 @@ use crate::{core::extensions::InsertExt, util::is_whitespace}; -use lib_ot::core::{count_utf16_code_units, plain_attributes, Attribute, Attributes, Delta, DeltaBuilder, DeltaIter}; +use lib_ot::core::{ + count_utf16_code_units, + plain_attributes, + DeltaBuilder, + DeltaIter, + RichTextAttribute, + RichTextAttributes, + RichTextDelta, +}; use std::cmp::min; use url::Url; @@ -7,7 +15,7 @@ pub struct AutoFormatExt {} impl InsertExt for AutoFormatExt { fn ext_name(&self) -> &str { std::any::type_name::() } - fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { + fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option { // enter whitespace to trigger auto format if !is_whitespace(text) { return None; @@ -55,9 +63,9 @@ pub enum AutoFormatter { } impl AutoFormatter { - pub fn to_attributes(&self) -> Attributes { + pub fn to_attributes(&self) -> RichTextAttributes { match self { - AutoFormatter::Url(url) => Attribute::Link(url.as_str()).into(), + AutoFormatter::Url(url) => RichTextAttribute::Link(url.as_str()).into(), } } diff --git a/shared-lib/flowy-document-infra/src/core/extensions/insert/default_insert.rs b/shared-lib/flowy-document-infra/src/core/extensions/insert/default_insert.rs index 5fd207e383..76f31d84e2 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/insert/default_insert.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/insert/default_insert.rs @@ -1,13 +1,21 @@ use crate::core::extensions::InsertExt; -use lib_ot::core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE}; +use lib_ot::core::{ + Attributes, + DeltaBuilder, + DeltaIter, + RichTextAttributeKey, + RichTextAttributes, + RichTextDelta, + NEW_LINE, +}; pub struct DefaultInsertAttribute {} impl InsertExt for DefaultInsertAttribute { fn ext_name(&self) -> &str { std::any::type_name::() } - fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { + fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option { let iter = DeltaIter::new(delta); - let mut attributes = Attributes::new(); + let mut attributes = RichTextAttributes::new(); // Enable each line split by "\n" remains the block attributes. for example: // insert "\n" to "123456" at index 3 @@ -18,8 +26,8 @@ impl InsertExt for DefaultInsertAttribute { match iter.last() { None => {}, Some(op) => { - if op.get_attributes().contains_key(&AttributeKey::Header) { - attributes.extend(op.get_attributes()); + if op.get_attributes().contains_key(&RichTextAttributeKey::Header) { + attributes.extend_other(op.get_attributes()); } }, } diff --git a/shared-lib/flowy-document-infra/src/core/extensions/insert/mod.rs b/shared-lib/flowy-document-infra/src/core/extensions/insert/mod.rs index b70105e6bf..fe8e167915 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/insert/mod.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/insert/mod.rs @@ -2,7 +2,7 @@ use crate::core::extensions::InsertExt; pub use auto_exit_block::*; pub use auto_format::*; pub use default_insert::*; -use lib_ot::core::Delta; +use lib_ot::core::RichTextDelta; pub use preserve_block_format::*; pub use preserve_inline_format::*; pub use reset_format_on_new_line::*; @@ -18,12 +18,16 @@ pub struct InsertEmbedsExt {} impl InsertExt for InsertEmbedsExt { fn ext_name(&self) -> &str { "InsertEmbedsExt" } - fn apply(&self, _delta: &Delta, _replace_len: usize, _text: &str, _index: usize) -> Option { None } + fn apply(&self, _delta: &RichTextDelta, _replace_len: usize, _text: &str, _index: usize) -> Option { + None + } } pub struct ForceNewlineForInsertsAroundEmbedExt {} impl InsertExt for ForceNewlineForInsertsAroundEmbedExt { fn ext_name(&self) -> &str { "ForceNewlineForInsertsAroundEmbedExt" } - fn apply(&self, _delta: &Delta, _replace_len: usize, _text: &str, _index: usize) -> Option { None } + fn apply(&self, _delta: &RichTextDelta, _replace_len: usize, _text: &str, _index: usize) -> Option { + None + } } diff --git a/shared-lib/flowy-document-infra/src/core/extensions/insert/preserve_block_format.rs b/shared-lib/flowy-document-infra/src/core/extensions/insert/preserve_block_format.rs index 0d29210ae0..671b1bbe74 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/insert/preserve_block_format.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/insert/preserve_block_format.rs @@ -2,12 +2,12 @@ use crate::{core::extensions::InsertExt, util::is_newline}; use lib_ot::core::{ attributes_except_header, plain_attributes, - Attribute, - AttributeKey, - Attributes, - Delta, DeltaBuilder, DeltaIter, + RichTextAttribute, + RichTextAttributeKey, + RichTextAttributes, + RichTextDelta, NEW_LINE, }; @@ -15,7 +15,7 @@ pub struct PreserveBlockFormatOnInsert {} impl InsertExt for PreserveBlockFormatOnInsert { fn ext_name(&self) -> &str { std::any::type_name::() } - fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { + fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option { if !is_newline(text) { return None; } @@ -30,9 +30,9 @@ impl InsertExt for PreserveBlockFormatOnInsert { return None; } - let mut reset_attribute = Attributes::new(); - if newline_attributes.contains_key(&AttributeKey::Header) { - reset_attribute.add(Attribute::Header(1)); + let mut reset_attribute = RichTextAttributes::new(); + if newline_attributes.contains_key(&RichTextAttributeKey::Header) { + reset_attribute.add(RichTextAttribute::Header(1)); } let lines: Vec<_> = text.split(NEW_LINE).collect(); diff --git a/shared-lib/flowy-document-infra/src/core/extensions/insert/preserve_inline_format.rs b/shared-lib/flowy-document-infra/src/core/extensions/insert/preserve_inline_format.rs index aa72fa4302..11b4e188fc 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/insert/preserve_inline_format.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/insert/preserve_inline_format.rs @@ -2,13 +2,21 @@ use crate::{ core::extensions::InsertExt, util::{contain_newline, is_newline}, }; -use lib_ot::core::{plain_attributes, AttributeKey, Delta, DeltaBuilder, DeltaIter, OpNewline, NEW_LINE}; +use lib_ot::core::{ + plain_attributes, + DeltaBuilder, + DeltaIter, + OpNewline, + RichTextAttributeKey, + RichTextDelta, + NEW_LINE, +}; pub struct PreserveInlineFormat {} impl InsertExt for PreserveInlineFormat { fn ext_name(&self) -> &str { std::any::type_name::() } - fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { + fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option { if contain_newline(text) { return None; } @@ -20,7 +28,7 @@ impl InsertExt for PreserveInlineFormat { } let mut attributes = prev.get_attributes(); - if attributes.is_empty() || !attributes.contains_key(&AttributeKey::Link) { + if attributes.is_empty() || !attributes.contains_key(&RichTextAttributeKey::Link) { return Some( DeltaBuilder::new() .retain(index + replace_len) @@ -52,7 +60,7 @@ pub struct PreserveLineFormatOnSplit {} impl InsertExt for PreserveLineFormatOnSplit { fn ext_name(&self) -> &str { std::any::type_name::() } - fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { + fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option { if !is_newline(text) { return None; } @@ -69,7 +77,7 @@ impl InsertExt for PreserveLineFormatOnSplit { return None; } - let mut new_delta = Delta::new(); + let mut new_delta = RichTextDelta::new(); new_delta.retain(index + replace_len, plain_attributes()); if newline_status.is_contain() { diff --git a/shared-lib/flowy-document-infra/src/core/extensions/insert/reset_format_on_new_line.rs b/shared-lib/flowy-document-infra/src/core/extensions/insert/reset_format_on_new_line.rs index 0e1699a814..d1abaf3179 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/insert/reset_format_on_new_line.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/insert/reset_format_on_new_line.rs @@ -1,11 +1,19 @@ use crate::{core::extensions::InsertExt, util::is_newline}; -use lib_ot::core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE}; +use lib_ot::core::{ + CharMetric, + DeltaBuilder, + DeltaIter, + RichTextAttributeKey, + RichTextAttributes, + RichTextDelta, + NEW_LINE, +}; pub struct ResetLineFormatOnNewLine {} impl InsertExt for ResetLineFormatOnNewLine { fn ext_name(&self) -> &str { std::any::type_name::() } - fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { + fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option { if !is_newline(text) { return None; } @@ -17,9 +25,9 @@ impl InsertExt for ResetLineFormatOnNewLine { return None; } - let mut reset_attribute = Attributes::new(); - if next_op.get_attributes().contains_key(&AttributeKey::Header) { - reset_attribute.delete(&AttributeKey::Header); + let mut reset_attribute = RichTextAttributes::new(); + if next_op.get_attributes().contains_key(&RichTextAttributeKey::Header) { + reset_attribute.delete(&RichTextAttributeKey::Header); } let len = index + replace_len; diff --git a/shared-lib/flowy-document-infra/src/core/extensions/mod.rs b/shared-lib/flowy-document-infra/src/core/extensions/mod.rs index 584dee2d68..31f8b1189c 100644 --- a/shared-lib/flowy-document-infra/src/core/extensions/mod.rs +++ b/shared-lib/flowy-document-infra/src/core/extensions/mod.rs @@ -2,7 +2,7 @@ pub use delete::*; pub use format::*; pub use insert::*; -use lib_ot::core::{Attribute, Delta, Interval}; +use lib_ot::core::{Interval, RichTextAttribute, RichTextDelta}; mod delete; mod format; @@ -14,15 +14,15 @@ pub type DeleteExtension = Box; pub trait InsertExt { fn ext_name(&self) -> &str; - fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option; + fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option; } pub trait FormatExt { fn ext_name(&self) -> &str; - fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option; + fn apply(&self, delta: &RichTextDelta, interval: Interval, attribute: &RichTextAttribute) -> Option; } pub trait DeleteExt { fn ext_name(&self) -> &str; - fn apply(&self, delta: &Delta, interval: Interval) -> Option; + fn apply(&self, delta: &RichTextDelta, interval: Interval) -> Option; } diff --git a/shared-lib/flowy-document-infra/src/core/history.rs b/shared-lib/flowy-document-infra/src/core/history.rs index aa76493c81..82c411b109 100644 --- a/shared-lib/flowy-document-infra/src/core/history.rs +++ b/shared-lib/flowy-document-infra/src/core/history.rs @@ -1,4 +1,4 @@ -use lib_ot::core::Delta; +use lib_ot::core::RichTextDelta; const MAX_UNDOS: usize = 20; @@ -21,8 +21,8 @@ impl UndoResult { pub struct History { #[allow(dead_code)] cur_undo: usize, - undos: Vec, - redoes: Vec, + undos: Vec, + redoes: Vec, capacity: usize, } @@ -44,11 +44,11 @@ impl History { pub fn can_redo(&self) -> bool { !self.redoes.is_empty() } - pub fn add_undo(&mut self, delta: Delta) { self.undos.push(delta); } + pub fn add_undo(&mut self, delta: RichTextDelta) { self.undos.push(delta); } - pub fn add_redo(&mut self, delta: Delta) { self.redoes.push(delta); } + pub fn add_redo(&mut self, delta: RichTextDelta) { self.redoes.push(delta); } - pub fn record(&mut self, delta: Delta) { + pub fn record(&mut self, delta: RichTextDelta) { if delta.ops.is_empty() { return; } @@ -61,7 +61,7 @@ impl History { } } - pub fn undo(&mut self) -> Option { + pub fn undo(&mut self) -> Option { if !self.can_undo() { return None; } @@ -69,7 +69,7 @@ impl History { Some(delta) } - pub fn redo(&mut self) -> Option { + pub fn redo(&mut self) -> Option { if !self.can_redo() { return None; } diff --git a/shared-lib/flowy-document-infra/src/core/view.rs b/shared-lib/flowy-document-infra/src/core/view.rs index 984b461169..cd2d61c73e 100644 --- a/shared-lib/flowy-document-infra/src/core/view.rs +++ b/shared-lib/flowy-document-infra/src/core/view.rs @@ -1,6 +1,6 @@ use crate::core::extensions::*; use lib_ot::{ - core::{trim, Attribute, Delta, Interval}, + core::{trim, Interval, RichTextAttribute, RichTextDelta}, errors::{ErrorBuilder, OTError, OTErrorCode}, }; @@ -21,7 +21,12 @@ impl View { } } - pub(crate) fn insert(&self, delta: &Delta, text: &str, interval: Interval) -> Result { + pub(crate) fn insert( + &self, + delta: &RichTextDelta, + text: &str, + interval: Interval, + ) -> Result { let mut new_delta = None; for ext in &self.insert_exts { if let Some(mut delta) = ext.apply(delta, interval.size(), text, interval.start) { @@ -38,7 +43,7 @@ impl View { } } - pub(crate) fn delete(&self, delta: &Delta, interval: Interval) -> Result { + pub(crate) fn delete(&self, delta: &RichTextDelta, interval: Interval) -> Result { let mut new_delta = None; for ext in &self.delete_exts { if let Some(mut delta) = ext.apply(delta, interval) { @@ -55,7 +60,12 @@ impl View { } } - pub(crate) fn format(&self, delta: &Delta, attribute: Attribute, interval: Interval) -> Result { + pub(crate) fn format( + &self, + delta: &RichTextDelta, + attribute: RichTextAttribute, + interval: Interval, + ) -> Result { let mut new_delta = None; for ext in &self.format_exts { if let Some(mut delta) = ext.apply(delta, interval, &attribute) { diff --git a/shared-lib/flowy-document-infra/src/entities/doc/doc.rs b/shared-lib/flowy-document-infra/src/entities/doc/doc.rs index 3502f81768..90b4c73056 100644 --- a/shared-lib/flowy-document-infra/src/entities/doc/doc.rs +++ b/shared-lib/flowy-document-infra/src/entities/doc/doc.rs @@ -1,5 +1,5 @@ use flowy_derive::ProtoBuf; -use lib_ot::{core::Delta, errors::OTError}; +use lib_ot::{core::RichTextDelta, errors::OTError}; #[derive(ProtoBuf, Default, Debug, Clone)] pub struct CreateDocParams { @@ -35,8 +35,8 @@ pub struct Doc { } impl Doc { - pub fn delta(&self) -> Result { - let delta = Delta::from_bytes(&self.data)?; + pub fn delta(&self) -> Result { + let delta = RichTextDelta::from_bytes(&self.data)?; Ok(delta) } } @@ -59,7 +59,7 @@ pub struct DocDelta { pub doc_id: String, #[pb(index = 2)] - pub data: String, // Delta + pub data: String, // RichTextDelta } #[derive(ProtoBuf, Default, Debug, Clone)] diff --git a/shared-lib/flowy-document-infra/src/entities/doc/revision.rs b/shared-lib/flowy-document-infra/src/entities/doc/revision.rs index 0fdc6b871c..ba4a68d86d 100644 --- a/shared-lib/flowy-document-infra/src/entities/doc/revision.rs +++ b/shared-lib/flowy-document-infra/src/entities/doc/revision.rs @@ -1,6 +1,6 @@ use crate::{entities::doc::Doc, util::md5}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; -use lib_ot::core::Delta; +use lib_ot::core::RichTextDelta; use std::{fmt::Formatter, ops::RangeInclusive}; #[derive(Debug, ProtoBuf_Enum, Clone, Eq, PartialEq)] @@ -87,7 +87,7 @@ impl std::fmt::Debug for Revision { let _ = f.write_fmt(format_args!("doc_id {}, ", self.doc_id))?; let _ = f.write_fmt(format_args!("base_rev_id {}, ", self.base_rev_id))?; let _ = f.write_fmt(format_args!("rev_id {}, ", self.rev_id))?; - match Delta::from_bytes(&self.delta_data) { + match RichTextDelta::from_bytes(&self.delta_data) { Ok(delta) => { let _ = f.write_fmt(format_args!("delta {:?}", delta.to_json()))?; }, diff --git a/shared-lib/flowy-document-infra/src/user_default.rs b/shared-lib/flowy-document-infra/src/user_default.rs index a40e302ed4..91cf28321d 100644 --- a/shared-lib/flowy-document-infra/src/user_default.rs +++ b/shared-lib/flowy-document-infra/src/user_default.rs @@ -1,15 +1,15 @@ -use lib_ot::core::{Delta, DeltaBuilder}; +use lib_ot::core::{DeltaBuilder, RichTextDelta}; #[inline] -pub fn doc_initial_delta() -> Delta { DeltaBuilder::new().insert("\n").build() } +pub fn doc_initial_delta() -> RichTextDelta { DeltaBuilder::new().insert("\n").build() } #[inline] pub fn doc_initial_string() -> String { doc_initial_delta().to_json() } #[inline] -pub fn initial_read_me() -> Delta { +pub fn initial_read_me() -> RichTextDelta { let json = include_str!("READ_ME.json"); - Delta::from_json(json).unwrap() + RichTextDelta::from_json(json).unwrap() } #[cfg(test)] diff --git a/shared-lib/lib-ot/src/core/attributes/attribute.rs b/shared-lib/lib-ot/src/core/attributes/attribute.rs index 631af478aa..45fe5fbec9 100644 --- a/shared-lib/lib-ot/src/core/attributes/attribute.rs +++ b/shared-lib/lib-ot/src/core/attributes/attribute.rs @@ -1,19 +1,19 @@ #![allow(non_snake_case)] -use crate::{block_attribute, core::Attributes, ignore_attribute, inline_attribute, list_attribute}; +use crate::{block_attribute, core::RichTextAttributes, ignore_attribute, inline_attribute, list_attribute}; use lazy_static::lazy_static; use std::{collections::HashSet, fmt, fmt::Formatter, iter::FromIterator}; use strum_macros::Display; #[derive(Debug, Clone)] -pub struct Attribute { - pub key: AttributeKey, - pub value: AttributeValue, +pub struct RichTextAttribute { + pub key: RichTextAttributeKey, + pub value: RichTextAttributeValue, pub scope: AttributeScope, } -impl Attribute { +impl RichTextAttribute { // inline inline_attribute!(Bold, bool); inline_attribute!(Italic, bool); @@ -55,16 +55,16 @@ impl Attribute { } } -impl fmt::Display for Attribute { +impl fmt::Display for RichTextAttribute { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let s = format!("{:?}:{:?} {:?}", self.key, self.value.0, self.scope); f.write_str(&s) } } -impl std::convert::From for Attributes { - fn from(attr: Attribute) -> Self { - let mut attributes = Attributes::new(); +impl std::convert::From for RichTextAttributes { + fn from(attr: RichTextAttribute) -> Self { + let mut attributes = RichTextAttributes::new(); attributes.add(attr); attributes } @@ -73,7 +73,7 @@ impl std::convert::From for Attributes { #[derive(Clone, Debug, Display, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)] // serde.rs/variant-attrs.html // #[serde(rename_all = "snake_case")] -pub enum AttributeKey { +pub enum RichTextAttributeKey { #[serde(rename = "bold")] Bold, #[serde(rename = "italic")] @@ -114,80 +114,80 @@ pub enum AttributeKey { // pub trait AttributeValueData<'a>: Serialize + Deserialize<'a> {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct AttributeValue(pub Option); +pub struct RichTextAttributeValue(pub Option); -impl std::convert::From<&usize> for AttributeValue { - fn from(val: &usize) -> Self { AttributeValue::from(*val) } +impl std::convert::From<&usize> for RichTextAttributeValue { + fn from(val: &usize) -> Self { RichTextAttributeValue::from(*val) } } -impl std::convert::From for AttributeValue { +impl std::convert::From for RichTextAttributeValue { fn from(val: usize) -> Self { if val > 0_usize { - AttributeValue(Some(format!("{}", val))) + RichTextAttributeValue(Some(format!("{}", val))) } else { - AttributeValue(None) + RichTextAttributeValue(None) } } } -impl std::convert::From<&str> for AttributeValue { +impl std::convert::From<&str> for RichTextAttributeValue { fn from(val: &str) -> Self { val.to_owned().into() } } -impl std::convert::From for AttributeValue { +impl std::convert::From for RichTextAttributeValue { fn from(val: String) -> Self { if val.is_empty() { - AttributeValue(None) + RichTextAttributeValue(None) } else { - AttributeValue(Some(val)) + RichTextAttributeValue(Some(val)) } } } -impl std::convert::From<&bool> for AttributeValue { - fn from(val: &bool) -> Self { AttributeValue::from(*val) } +impl std::convert::From<&bool> for RichTextAttributeValue { + fn from(val: &bool) -> Self { RichTextAttributeValue::from(*val) } } -impl std::convert::From for AttributeValue { +impl std::convert::From for RichTextAttributeValue { fn from(val: bool) -> Self { let val = match val { true => Some("true".to_owned()), false => None, }; - AttributeValue(val) + RichTextAttributeValue(val) } } -pub fn is_block_except_header(k: &AttributeKey) -> bool { - if k == &AttributeKey::Header { +pub fn is_block_except_header(k: &RichTextAttributeKey) -> bool { + if k == &RichTextAttributeKey::Header { return false; } BLOCK_KEYS.contains(k) } lazy_static! { - static ref BLOCK_KEYS: HashSet = HashSet::from_iter(vec![ - AttributeKey::Header, - AttributeKey::Indent, - AttributeKey::Align, - AttributeKey::CodeBlock, - AttributeKey::List, - AttributeKey::BlockQuote, + static ref BLOCK_KEYS: HashSet = HashSet::from_iter(vec![ + RichTextAttributeKey::Header, + RichTextAttributeKey::Indent, + RichTextAttributeKey::Align, + RichTextAttributeKey::CodeBlock, + RichTextAttributeKey::List, + RichTextAttributeKey::BlockQuote, ]); - static ref INLINE_KEYS: HashSet = HashSet::from_iter(vec![ - AttributeKey::Bold, - AttributeKey::Italic, - AttributeKey::Underline, - AttributeKey::StrikeThrough, - AttributeKey::Link, - AttributeKey::Color, - AttributeKey::Font, - AttributeKey::Size, - AttributeKey::Background, - AttributeKey::InlineCode, + static ref INLINE_KEYS: HashSet = HashSet::from_iter(vec![ + RichTextAttributeKey::Bold, + RichTextAttributeKey::Italic, + RichTextAttributeKey::Underline, + RichTextAttributeKey::StrikeThrough, + RichTextAttributeKey::Link, + RichTextAttributeKey::Color, + RichTextAttributeKey::Font, + RichTextAttributeKey::Size, + RichTextAttributeKey::Background, + RichTextAttributeKey::InlineCode, ]); - static ref INGORE_KEYS: HashSet = - HashSet::from_iter(vec![AttributeKey::Width, AttributeKey::Height,]); + static ref INGORE_KEYS: HashSet = + HashSet::from_iter(vec![RichTextAttributeKey::Width, RichTextAttributeKey::Height,]); } #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/shared-lib/lib-ot/src/core/attributes/attributes.rs b/shared-lib/lib-ot/src/core/attributes/attributes.rs index 2046ed0766..c52c4a7898 100644 --- a/shared-lib/lib-ot/src/core/attributes/attributes.rs +++ b/shared-lib/lib-ot/src/core/attributes/attributes.rs @@ -1,15 +1,22 @@ use crate::{ - core::{Attribute, AttributeKey, AttributeValue, Operation, OperationTransformable}, + core::{ + Attributes, + OperationTransformable, + RichTextAttribute, + RichTextAttributeKey, + RichTextAttributeValue, + RichTextOperation, + }, errors::OTError, }; use std::{collections::HashMap, fmt}; #[derive(Debug, Clone, Eq, PartialEq)] -pub struct Attributes { - pub(crate) inner: HashMap, +pub struct RichTextAttributes { + pub(crate) inner: HashMap, } -impl std::default::Default for Attributes { +impl std::default::Default for RichTextAttributes { fn default() -> Self { Self { inner: HashMap::with_capacity(0), @@ -17,27 +24,31 @@ impl std::default::Default for Attributes { } } -impl fmt::Display for Attributes { +impl fmt::Display for RichTextAttributes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("{:?}", self.inner)) } } -pub fn plain_attributes() -> Attributes { Attributes::default() } +pub fn plain_attributes() -> RichTextAttributes { RichTextAttributes::default() } -impl Attributes { - pub fn new() -> Self { Attributes { inner: HashMap::new() } } +impl RichTextAttributes { + pub fn new() -> Self { RichTextAttributes { inner: HashMap::new() } } pub fn is_empty(&self) -> bool { self.inner.is_empty() } - pub fn add(&mut self, attribute: Attribute) { - let Attribute { key, value, scope: _ } = attribute; + pub fn add(&mut self, attribute: RichTextAttribute) { + let RichTextAttribute { key, value, scope: _ } = attribute; self.inner.insert(key, value); } - pub fn add_kv(&mut self, key: AttributeKey, value: AttributeValue) { self.inner.insert(key, value); } + pub fn add_kv(&mut self, key: RichTextAttributeKey, value: RichTextAttributeValue) { + self.inner.insert(key, value); + } - pub fn delete(&mut self, key: &AttributeKey) { self.inner.insert(key.clone(), AttributeValue(None)); } + pub fn delete(&mut self, key: &RichTextAttributeKey) { + self.inner.insert(key.clone(), RichTextAttributeValue(None)); + } - pub fn mark_all_as_removed_except(&mut self, attribute: Option) { + pub fn mark_all_as_removed_except(&mut self, attribute: Option) { match attribute { None => { self.inner.iter_mut().for_each(|(_k, v)| v.0 = None); @@ -52,7 +63,7 @@ impl Attributes { } } - pub fn remove(&mut self, key: AttributeKey) { self.inner.retain(|k, _| k != &key); } + pub fn remove(&mut self, key: RichTextAttributeKey) { self.inner.retain(|k, _| k != &key); } // pub fn block_attributes_except_header(attributes: &Attributes) -> Attributes // { let mut new_attributes = Attributes::new(); @@ -65,14 +76,9 @@ impl Attributes { // new_attributes // } - // Remove the empty attribute which value is None. - pub fn remove_empty(&mut self) { self.inner.retain(|_, v| v.0.is_some()); } - - pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); } - // Update inner by constructing new attributes from the other if it's // not None and replace the key/value with self key/value. - pub fn merge(&mut self, other: Option) { + pub fn merge(&mut self, other: Option) { if other.is_none() { return; } @@ -85,13 +91,21 @@ impl Attributes { } } -impl OperationTransformable for Attributes { +impl Attributes for RichTextAttributes { + fn is_empty(&self) -> bool { self.inner.is_empty() } + + fn remove_empty(&mut self) { self.inner.retain(|_, v| v.0.is_some()); } + + fn extend_other(&mut self, other: Self) { self.inner.extend(other.inner); } +} + +impl OperationTransformable for RichTextAttributes { fn compose(&self, other: &Self) -> Result where Self: Sized, { let mut attributes = self.clone(); - attributes.extend(other.clone()); + attributes.extend_other(other.clone()); Ok(attributes) } @@ -99,25 +113,29 @@ impl OperationTransformable for Attributes { where Self: Sized, { - let a = self.iter().fold(Attributes::new(), |mut new_attributes, (k, v)| { - if !other.contains_key(k) { - new_attributes.insert(k.clone(), v.clone()); - } - new_attributes - }); + let a = self + .iter() + .fold(RichTextAttributes::new(), |mut new_attributes, (k, v)| { + if !other.contains_key(k) { + new_attributes.insert(k.clone(), v.clone()); + } + new_attributes + }); - let b = other.iter().fold(Attributes::new(), |mut new_attributes, (k, v)| { - if !self.contains_key(k) { - new_attributes.insert(k.clone(), v.clone()); - } - new_attributes - }); + let b = other + .iter() + .fold(RichTextAttributes::new(), |mut new_attributes, (k, v)| { + if !self.contains_key(k) { + new_attributes.insert(k.clone(), v.clone()); + } + new_attributes + }); Ok((a, b)) } fn invert(&self, other: &Self) -> Self { - let base_inverted = other.iter().fold(Attributes::new(), |mut attributes, (k, v)| { + let base_inverted = other.iter().fold(RichTextAttributes::new(), |mut attributes, (k, v)| { if other.get(k) != self.get(k) && self.contains_key(k) { attributes.insert(k.clone(), v.clone()); } @@ -135,18 +153,18 @@ impl OperationTransformable for Attributes { } } -impl std::ops::Deref for Attributes { - type Target = HashMap; +impl std::ops::Deref for RichTextAttributes { + type Target = HashMap; fn deref(&self) -> &Self::Target { &self.inner } } -impl std::ops::DerefMut for Attributes { +impl std::ops::DerefMut for RichTextAttributes { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } -pub fn attributes_except_header(op: &Operation) -> Attributes { +pub fn attributes_except_header(op: &RichTextOperation) -> RichTextAttributes { let mut attributes = op.get_attributes(); - attributes.remove(AttributeKey::Header); + attributes.remove(RichTextAttributeKey::Header); attributes } diff --git a/shared-lib/lib-ot/src/core/attributes/attributes_serde.rs b/shared-lib/lib-ot/src/core/attributes/attributes_serde.rs index c3e911bbc1..5552297b72 100644 --- a/shared-lib/lib-ot/src/core/attributes/attributes_serde.rs +++ b/shared-lib/lib-ot/src/core/attributes/attributes_serde.rs @@ -1,6 +1,6 @@ #[rustfmt::skip] -use crate::core::AttributeValue; -use crate::core::{Attribute, AttributeKey, Attributes}; +use crate::core::RichTextAttributeValue; +use crate::core::{RichTextAttribute, RichTextAttributeKey, RichTextAttributes}; use serde::{ de, de::{MapAccess, Visitor}, @@ -12,7 +12,7 @@ use serde::{ }; use std::fmt; -impl Serialize for Attribute { +impl Serialize for RichTextAttribute { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: Serializer, @@ -23,7 +23,7 @@ impl Serialize for Attribute { } } -impl Serialize for Attributes { +impl Serialize for RichTextAttributes { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -40,39 +40,43 @@ impl Serialize for Attributes { } } -fn serial_attribute(map_serializer: &mut S, key: &AttributeKey, value: &AttributeValue) -> Result<(), E> +fn serial_attribute( + map_serializer: &mut S, + key: &RichTextAttributeKey, + value: &RichTextAttributeValue, +) -> Result<(), E> where S: SerializeMap, E: From<::Error>, { if let Some(v) = &value.0 { match key { - AttributeKey::Bold - | AttributeKey::Italic - | AttributeKey::Underline - | AttributeKey::StrikeThrough - | AttributeKey::CodeBlock - | AttributeKey::InlineCode - | AttributeKey::BlockQuote => match &v.parse::() { + RichTextAttributeKey::Bold + | RichTextAttributeKey::Italic + | RichTextAttributeKey::Underline + | RichTextAttributeKey::StrikeThrough + | RichTextAttributeKey::CodeBlock + | RichTextAttributeKey::InlineCode + | RichTextAttributeKey::BlockQuote => match &v.parse::() { Ok(value) => map_serializer.serialize_entry(&key, value)?, Err(e) => log::error!("Serial {:?} failed. {:?}", &key, e), }, - AttributeKey::Font - | AttributeKey::Size - | AttributeKey::Header - | AttributeKey::Indent - | AttributeKey::Width - | AttributeKey::Height => match &v.parse::() { + RichTextAttributeKey::Font + | RichTextAttributeKey::Size + | RichTextAttributeKey::Header + | RichTextAttributeKey::Indent + | RichTextAttributeKey::Width + | RichTextAttributeKey::Height => match &v.parse::() { Ok(value) => map_serializer.serialize_entry(&key, value)?, Err(e) => log::error!("Serial {:?} failed. {:?}", &key, e), }, - AttributeKey::Link - | AttributeKey::Color - | AttributeKey::Background - | AttributeKey::Align - | AttributeKey::List => { + RichTextAttributeKey::Link + | RichTextAttributeKey::Color + | RichTextAttributeKey::Background + | RichTextAttributeKey::Align + | RichTextAttributeKey::List => { map_serializer.serialize_entry(&key, v)?; }, } @@ -82,23 +86,23 @@ where Ok(()) } -impl<'de> Deserialize<'de> for Attributes { - fn deserialize(deserializer: D) -> Result +impl<'de> Deserialize<'de> for RichTextAttributes { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct AttributesVisitor; impl<'de> Visitor<'de> for AttributesVisitor { - type Value = Attributes; + type Value = RichTextAttributes; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("Expect map") } fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, { - let mut attributes = Attributes::new(); - while let Some(key) = map.next_key::()? { - let value = map.next_value::()?; + let mut attributes = RichTextAttributes::new(); + while let Some(key) = map.next_key::()? { + let value = map.next_value::()?; attributes.add_kv(key, value); } @@ -109,7 +113,7 @@ impl<'de> Deserialize<'de> for Attributes { } } -impl Serialize for AttributeValue { +impl Serialize for RichTextAttributeValue { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -121,14 +125,14 @@ impl Serialize for AttributeValue { } } -impl<'de> Deserialize<'de> for AttributeValue { - fn deserialize(deserializer: D) -> Result +impl<'de> Deserialize<'de> for RichTextAttributeValue { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct AttributeValueVisitor; impl<'de> Visitor<'de> for AttributeValueVisitor { - type Value = AttributeValue; + type Value = RichTextAttributeValue; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("bool, usize or string") } @@ -143,56 +147,56 @@ impl<'de> Deserialize<'de> for AttributeValue { where E: de::Error, { - Ok(AttributeValue(Some(format!("{}", value)))) + Ok(RichTextAttributeValue(Some(format!("{}", value)))) } fn visit_i16(self, value: i16) -> Result where E: de::Error, { - Ok(AttributeValue(Some(format!("{}", value)))) + Ok(RichTextAttributeValue(Some(format!("{}", value)))) } fn visit_i32(self, value: i32) -> Result where E: de::Error, { - Ok(AttributeValue(Some(format!("{}", value)))) + Ok(RichTextAttributeValue(Some(format!("{}", value)))) } fn visit_i64(self, value: i64) -> Result where E: de::Error, { - Ok(AttributeValue(Some(format!("{}", value)))) + Ok(RichTextAttributeValue(Some(format!("{}", value)))) } fn visit_u8(self, value: u8) -> Result where E: de::Error, { - Ok(AttributeValue(Some(format!("{}", value)))) + Ok(RichTextAttributeValue(Some(format!("{}", value)))) } fn visit_u16(self, value: u16) -> Result where E: de::Error, { - Ok(AttributeValue(Some(format!("{}", value)))) + Ok(RichTextAttributeValue(Some(format!("{}", value)))) } fn visit_u32(self, value: u32) -> Result where E: de::Error, { - Ok(AttributeValue(Some(format!("{}", value)))) + Ok(RichTextAttributeValue(Some(format!("{}", value)))) } fn visit_u64(self, value: u64) -> Result where E: de::Error, { - Ok(AttributeValue(Some(format!("{}", value)))) + Ok(RichTextAttributeValue(Some(format!("{}", value)))) } fn visit_str(self, s: &str) -> Result @@ -206,7 +210,7 @@ impl<'de> Deserialize<'de> for AttributeValue { where E: de::Error, { - Ok(AttributeValue(None)) + Ok(RichTextAttributeValue(None)) } fn visit_unit(self) -> Result @@ -214,7 +218,7 @@ impl<'de> Deserialize<'de> for AttributeValue { E: de::Error, { // the value that contains null will be processed here. - Ok(AttributeValue(None)) + Ok(RichTextAttributeValue(None)) } fn visit_map(self, map: A) -> Result @@ -223,7 +227,7 @@ impl<'de> Deserialize<'de> for AttributeValue { { // https://github.com/serde-rs/json/issues/505 let mut map = map; - let value = map.next_value::()?; + let value = map.next_value::()?; Ok(value) } } diff --git a/shared-lib/lib-ot/src/core/attributes/builder.rs b/shared-lib/lib-ot/src/core/attributes/builder.rs index 599149cc8c..98c0721371 100644 --- a/shared-lib/lib-ot/src/core/attributes/builder.rs +++ b/shared-lib/lib-ot/src/core/attributes/builder.rs @@ -1,13 +1,13 @@ #![allow(non_snake_case)] -use crate::core::{Attribute, Attributes}; +use crate::core::{RichTextAttribute, RichTextAttributes}; pub struct AttributeBuilder { - inner: Attributes, + inner: RichTextAttributes, } impl std::default::Default for AttributeBuilder { fn default() -> Self { Self { - inner: Attributes::default(), + inner: RichTextAttributes::default(), } } } @@ -15,10 +15,10 @@ impl std::default::Default for AttributeBuilder { impl AttributeBuilder { pub fn new() -> Self { AttributeBuilder::default() } - pub fn add_attr(mut self, attribute: Attribute) -> Self { + pub fn add_attr(mut self, attribute: RichTextAttribute) -> Self { self.inner.add(attribute); self } - pub fn build(self) -> Attributes { self.inner } + pub fn build(self) -> RichTextAttributes { self.inner } } diff --git a/shared-lib/lib-ot/src/core/attributes/macros.rs b/shared-lib/lib-ot/src/core/attributes/macros.rs index dcdaf50f42..28fe903183 100644 --- a/shared-lib/lib-ot/src/core/attributes/macros.rs +++ b/shared-lib/lib-ot/src/core/attributes/macros.rs @@ -6,7 +6,7 @@ macro_rules! inline_attribute { ) => { pub fn $key(value: $value) -> Self { Self { - key: AttributeKey::$key, + key: RichTextAttributeKey::$key, value: value.into(), scope: AttributeScope::Inline, } @@ -22,7 +22,7 @@ macro_rules! block_attribute { ) => { pub fn $key(value: $value) -> Self { Self { - key: AttributeKey::$key, + key: RichTextAttributeKey::$key, value: value.into(), scope: AttributeScope::Block, } @@ -41,7 +41,7 @@ macro_rules! list_attribute { true => $value, false => "", }; - Attribute::List(value) + RichTextAttribute::List(value) } }; } @@ -54,7 +54,7 @@ macro_rules! ignore_attribute { ) => { pub fn $key(value: $value) -> Self { Self { - key: AttributeKey::$key, + key: RichTextAttributeKey::$key, value: value.into(), scope: AttributeScope::Ignore, } diff --git a/shared-lib/lib-ot/src/core/delta/builder.rs b/shared-lib/lib-ot/src/core/delta/builder.rs index 351872ac10..369aa33366 100644 --- a/shared-lib/lib-ot/src/core/delta/builder.rs +++ b/shared-lib/lib-ot/src/core/delta/builder.rs @@ -1,23 +1,29 @@ -use crate::core::{plain_attributes, Attributes, Delta, Operation}; +use crate::core::{Attributes, Delta, Operation}; -pub struct DeltaBuilder { - delta: Delta, +pub struct DeltaBuilder { + delta: Delta, } -impl std::default::Default for DeltaBuilder { +impl std::default::Default for DeltaBuilder +where + T: Attributes, +{ fn default() -> Self { Self { delta: Delta::new() } } } -impl DeltaBuilder { +impl DeltaBuilder +where + T: Attributes, +{ pub fn new() -> Self { DeltaBuilder::default() } - pub fn retain_with_attributes(mut self, n: usize, attrs: Attributes) -> Self { + pub fn retain_with_attributes(mut self, n: usize, attrs: T) -> Self { self.delta.retain(n, attrs); self } pub fn retain(mut self, n: usize) -> Self { - self.delta.retain(n, plain_attributes()); + self.delta.retain(n, T::default()); self } @@ -26,13 +32,13 @@ impl DeltaBuilder { self } - pub fn insert_with_attributes(mut self, s: &str, attrs: Attributes) -> Self { + pub fn insert_with_attributes(mut self, s: &str, attrs: T) -> Self { self.delta.insert(s, attrs); self } pub fn insert(mut self, s: &str) -> Self { - self.delta.insert(s, plain_attributes()); + self.delta.insert(s, T::default()); self } @@ -41,10 +47,10 @@ impl DeltaBuilder { self } - pub fn build(self) -> Delta { self.delta } + pub fn build(self) -> Delta { self.delta } } -pub fn trim(delta: &mut Delta) { +pub fn trim(delta: &mut Delta) { let remove_last = match delta.ops.last() { None => false, Some(op) => match op { diff --git a/shared-lib/lib-ot/src/core/delta/cursor.rs b/shared-lib/lib-ot/src/core/delta/cursor.rs index 4b9fc77e1b..7524bded43 100644 --- a/shared-lib/lib-ot/src/core/delta/cursor.rs +++ b/shared-lib/lib-ot/src/core/delta/cursor.rs @@ -1,22 +1,25 @@ use crate::{ - core::{Delta, Interval, Operation}, + core::{Attributes, Delta, Interval, Operation}, errors::{ErrorBuilder, OTError, OTErrorCode}, }; use std::{cmp::min, iter::Enumerate, slice::Iter}; #[derive(Debug)] -pub struct OpCursor<'a> { - pub(crate) delta: &'a Delta, +pub struct OpCursor<'a, T: Attributes> { + pub(crate) delta: &'a Delta, pub(crate) origin_iv: Interval, pub(crate) consume_iv: Interval, pub(crate) consume_count: usize, pub(crate) op_index: usize, - iter: Enumerate>, - next_op: Option, + iter: Enumerate>>, + next_op: Option>, } -impl<'a> OpCursor<'a> { - pub fn new(delta: &'a Delta, interval: Interval) -> OpCursor<'a> { +impl<'a, T> OpCursor<'a, T> +where + T: Attributes, +{ + pub fn new(delta: &'a Delta, interval: Interval) -> OpCursor<'a, T> { // debug_assert!(interval.start <= delta.target_len); let mut cursor = Self { delta, @@ -34,11 +37,11 @@ impl<'a> OpCursor<'a> { // get the next operation interval pub fn next_iv(&self) -> Interval { self.next_iv_before(None).unwrap_or_else(|| Interval::new(0, 0)) } - pub fn next_op(&mut self) -> Option { self.next_with_len(None) } + pub fn next_op(&mut self) -> Option> { self.next_with_len(None) } // get the last operation before the end. // checkout the delta_next_op_with_len_cross_op_return_last test for more detail - pub fn next_with_len(&mut self, force_end: Option) -> Option { + pub fn next_with_len(&mut self, force_end: Option) -> Option> { let mut find_op = None; let holder = self.next_op.clone(); let mut next_op = holder.as_ref(); @@ -121,7 +124,7 @@ impl<'a> OpCursor<'a> { Some(interval) } - pub fn next_iter_op(&self) -> Option<&Operation> { + pub fn next_iter_op(&self) -> Option<&Operation> { let mut next_op = self.next_op.as_ref(); if next_op.is_none() { let mut offset = 0; @@ -137,7 +140,10 @@ impl<'a> OpCursor<'a> { } } -fn find_next<'a>(cursor: &mut OpCursor<'a>) -> Option<&'a Operation> { +fn find_next<'a, T>(cursor: &mut OpCursor<'a, T>) -> Option<&'a Operation> +where + T: Attributes, +{ match cursor.iter.next() { None => None, Some((o_index, op)) => { @@ -149,13 +155,13 @@ fn find_next<'a>(cursor: &mut OpCursor<'a>) -> Option<&'a Operation> { type SeekResult = Result<(), OTError>; pub trait Metric { - fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult; + fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult; } -pub struct OpMetric {} +pub struct OpMetric(); impl Metric for OpMetric { - fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult { + fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult { let _ = check_bound(cursor.op_index, index)?; let mut seek_cursor = OpCursor::new(cursor.delta, cursor.origin_iv); let mut offset = 0; @@ -170,10 +176,10 @@ impl Metric for OpMetric { } } -pub struct CharMetric {} +pub struct CharMetric(); impl Metric for CharMetric { - fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult { + fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult { if index > 0 { let _ = check_bound(cursor.consume_count, index)?; let _ = cursor.next_with_len(Some(index)); diff --git a/shared-lib/lib-ot/src/core/delta/delta.rs b/shared-lib/lib-ot/src/core/delta/delta.rs index 17b2a221ed..fc78cd3ae3 100644 --- a/shared-lib/lib-ot/src/core/delta/delta.rs +++ b/shared-lib/lib-ot/src/core/delta/delta.rs @@ -3,6 +3,7 @@ use crate::{ errors::{ErrorBuilder, OTError, OTErrorCode}, }; use bytes::Bytes; +use serde::de::DeserializeOwned; use std::{ cmp::{min, Ordering}, fmt, @@ -13,13 +14,16 @@ use std::{ // Opti: optimize the memory usage with Arc_mut or Cow #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Delta { - pub ops: Vec, +pub struct Delta { + pub ops: Vec>, pub base_len: usize, pub target_len: usize, } -impl Default for Delta { +impl Default for Delta +where + T: Attributes, +{ fn default() -> Self { Self { ops: Vec::new(), @@ -29,28 +33,10 @@ impl Default for Delta { } } -impl FromStr for Delta { - type Err = (); - - fn from_str(s: &str) -> Result { - let mut delta = Delta::with_capacity(1); - delta.add(Operation::Insert(s.into())); - Ok(delta) - } -} - -impl std::convert::TryFrom> for Delta { - type Error = OTError; - fn try_from(bytes: Vec) -> Result { Delta::from_bytes(bytes) } -} - -impl std::convert::TryFrom for Delta { - type Error = OTError; - - fn try_from(bytes: Bytes) -> Result { Delta::from_bytes(&bytes) } -} - -impl fmt::Display for Delta { +impl fmt::Display for Delta +where + T: Attributes, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // f.write_str(&serde_json::to_string(self).unwrap_or("".to_owned()))?; f.write_str("[ ")?; @@ -62,8 +48,11 @@ impl fmt::Display for Delta { } } -impl FromIterator for Delta { - fn from_iter>(ops: T) -> Self { +impl FromIterator> for Delta +where + T: Attributes, +{ + fn from_iter>>(ops: I) -> Self { let mut operations = Delta::default(); for op in ops { operations.add(op); @@ -72,30 +61,12 @@ impl FromIterator for Delta { } } -impl Delta { +impl Delta +where + T: Attributes, +{ pub fn new() -> Self { Self::default() } - pub fn from_json(json: &str) -> Result { - let delta: Delta = serde_json::from_str(json).map_err(|e| { - tracing::trace!("Deserialize failed: {:?}", e); - tracing::trace!("{:?}", json); - e - })?; - Ok(delta) - } - - pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap_or_else(|_| "".to_owned()) } - - pub fn from_bytes>(bytes: T) -> Result { - let json = str::from_utf8(bytes.as_ref())?; - Self::from_json(json) - } - - pub fn to_bytes(&self) -> Bytes { - let json = self.to_json(); - Bytes::from(json.into_bytes()) - } - #[inline] pub fn with_capacity(capacity: usize) -> Self { Self { @@ -105,7 +76,7 @@ impl Delta { } } - pub fn add(&mut self, op: Operation) { + pub fn add(&mut self, op: Operation) { match op { Operation::Delete(i) => self.delete(i), Operation::Insert(i) => self.insert(&i.s, i.attributes), @@ -125,7 +96,7 @@ impl Delta { } } - pub fn insert(&mut self, s: &str, attributes: Attributes) { + pub fn insert(&mut self, s: &str, attributes: T) { let s: FlowyStr = s.into(); if s.is_empty() { return; @@ -133,20 +104,20 @@ impl Delta { self.target_len += s.count_utf16_code_units(); let new_last = match self.ops.as_mut_slice() { - [.., Operation::Insert(insert)] => { + [.., Operation::::Insert(insert)] => { // insert.merge_or_new_op(&s, attributes) }, - [.., Operation::Insert(pre_insert), Operation::Delete(_)] => { + [.., Operation::::Insert(pre_insert), Operation::Delete(_)] => { // pre_insert.merge_or_new_op(&s, attributes) }, - [.., op_last @ Operation::Delete(_)] => { + [.., op_last @ Operation::::Delete(_)] => { let new_last = op_last.clone(); - *op_last = OpBuilder::insert(&s).attributes(attributes).build(); + *op_last = OpBuilder::::insert(&s).attributes(attributes).build(); Some(new_last) }, - _ => Some(OpBuilder::insert(&s).attributes(attributes).build()), + _ => Some(OpBuilder::::insert(&s).attributes(attributes).build()), }; match new_last { @@ -155,19 +126,19 @@ impl Delta { } } - pub fn retain(&mut self, n: usize, attributes: Attributes) { + pub fn retain(&mut self, n: usize, attributes: T) { if n == 0 { return; } self.base_len += n as usize; self.target_len += n as usize; - if let Some(Operation::Retain(retain)) = self.ops.last_mut() { + if let Some(Operation::::Retain(retain)) = self.ops.last_mut() { if let Some(new_op) = retain.merge_or_new(n, attributes) { self.ops.push(new_op); } } else { - self.ops.push(OpBuilder::retain(n).attributes(attributes).build()); + self.ops.push(OpBuilder::::retain(n).attributes(attributes).build()); } } @@ -207,7 +178,7 @@ impl Delta { for op in &self.ops { match &op { Operation::Retain(retain) => { - inverted.retain(retain.n, Attributes::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 { @@ -234,7 +205,10 @@ impl Delta { pub fn extend(&mut self, other: Self) { other.ops.into_iter().for_each(|op| self.add(op)); } } -impl OperationTransformable for Delta { +impl OperationTransformable for Delta +where + T: Attributes, +{ fn compose(&self, other: &Self) -> Result where Self: Sized, @@ -453,7 +427,13 @@ impl OperationTransformable for Delta { } } -fn invert_from_other(base: &mut Delta, other: &Delta, operation: &Operation, start: usize, end: usize) { +fn invert_from_other( + base: &mut Delta, + other: &Delta, + operation: &Operation, + start: usize, + end: usize, +) { tracing::trace!("invert op: {} [{}:{}]", operation, start, end); let other_ops = DeltaIter::from_interval(other, Interval::new(start, end)).ops(); other_ops.into_iter().for_each(|other_op| match operation { @@ -476,10 +456,10 @@ fn invert_from_other(base: &mut Delta, other: &Delta, operation: &Operation, sta }); } -fn transform_op_attribute(left: &Option, right: &Option) -> Attributes { +fn transform_op_attribute(left: &Option>, right: &Option>) -> T { if left.is_none() { if right.is_none() { - return Attributes::default(); + return T::default(); } return right.as_ref().unwrap().get_attributes(); } @@ -488,3 +468,58 @@ fn transform_op_attribute(left: &Option, right: &Option) - // TODO: It's ok to unwrap? left.transform(&right).unwrap().0 } + +pub type RichTextDelta = Delta; + +impl Delta +where + T: Attributes + DeserializeOwned, +{ + pub fn from_json(json: &str) -> Result { + let delta = serde_json::from_str(json).map_err(|e| { + tracing::trace!("Deserialize failed: {:?}", e); + tracing::trace!("{:?}", json); + e + })?; + Ok(delta) + } + + pub fn from_bytes>(bytes: B) -> Result { + let json = str::from_utf8(bytes.as_ref())?.to_owned(); + let val = Self::from_json(&json)?; + Ok(val) + } +} + +impl Delta +where + T: Attributes + serde::Serialize, +{ + pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap_or_else(|_| "".to_owned()) } + + pub fn to_bytes(&self) -> Bytes { + let json = self.to_json(); + Bytes::from(json.into_bytes()) + } +} + +impl FromStr for RichTextDelta { + type Err = (); + + fn from_str(s: &str) -> Result { + let mut delta = Delta::with_capacity(1); + delta.add(Operation::Insert(s.into())); + Ok(delta) + } +} + +impl std::convert::TryFrom> for RichTextDelta { + type Error = OTError; + fn try_from(bytes: Vec) -> Result { Delta::from_bytes(bytes) } +} + +impl std::convert::TryFrom for RichTextDelta { + type Error = OTError; + + fn try_from(bytes: Bytes) -> Result { Delta::from_bytes(&bytes) } +} diff --git a/shared-lib/lib-ot/src/core/delta/delta_serde.rs b/shared-lib/lib-ot/src/core/delta/delta_serde.rs index b6be5891ab..51045fc764 100644 --- a/shared-lib/lib-ot/src/core/delta/delta_serde.rs +++ b/shared-lib/lib-ot/src/core/delta/delta_serde.rs @@ -1,4 +1,4 @@ -use crate::core::Delta; +use crate::core::{Attributes, Delta}; use serde::{ de::{SeqAccess, Visitor}, ser::SerializeSeq, @@ -7,9 +7,12 @@ use serde::{ Serialize, Serializer, }; -use std::fmt; +use std::{fmt, marker::PhantomData}; -impl Serialize for Delta { +impl Serialize for Delta +where + T: Attributes + Serialize, +{ fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -22,18 +25,25 @@ impl Serialize for Delta { } } -impl<'de> Deserialize<'de> for Delta { - fn deserialize(deserializer: D) -> Result +impl<'de, T> Deserialize<'de> for Delta +where + T: Attributes + Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { - struct OperationSeqVisitor; + struct OperationSeqVisitor(PhantomData T>); - impl<'de> Visitor<'de> for OperationSeqVisitor { - type Value = Delta; + impl<'de, T> Visitor<'de> for OperationSeqVisitor + where + T: Attributes + Deserialize<'de>, + { + type Value = Delta; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a sequence") } + #[inline] fn visit_seq(self, mut seq: A) -> Result where A: SeqAccess<'de>, @@ -46,6 +56,6 @@ impl<'de> Deserialize<'de> for Delta { } } - deserializer.deserialize_seq(OperationSeqVisitor) + deserializer.deserialize_seq(OperationSeqVisitor(PhantomData)) } } diff --git a/shared-lib/lib-ot/src/core/delta/iterator.rs b/shared-lib/lib-ot/src/core/delta/iterator.rs index 4ecb6087ba..478428877c 100644 --- a/shared-lib/lib-ot/src/core/delta/iterator.rs +++ b/shared-lib/lib-ot/src/core/delta/iterator.rs @@ -1,32 +1,35 @@ use super::cursor::*; -use crate::core::{Attributes, Delta, Interval, Operation, NEW_LINE}; +use crate::core::{Attributes, Delta, Interval, Operation, RichTextAttributes, NEW_LINE}; use std::ops::{Deref, DerefMut}; pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize; -pub struct DeltaIter<'a> { - cursor: OpCursor<'a>, +pub struct DeltaIter<'a, T: Attributes> { + cursor: OpCursor<'a, T>, } -impl<'a> DeltaIter<'a> { - pub fn new(delta: &'a Delta) -> Self { +impl<'a, T> DeltaIter<'a, T> +where + T: Attributes, +{ + pub fn new(delta: &'a Delta) -> Self { let interval = Interval::new(0, MAX_IV_LEN); Self::from_interval(delta, interval) } - pub fn from_offset(delta: &'a Delta, offset: usize) -> Self { + pub fn from_offset(delta: &'a Delta, offset: usize) -> Self { let interval = Interval::new(0, MAX_IV_LEN); let mut iter = Self::from_interval(delta, interval); iter.seek::(offset); iter } - pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self { + pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self { let cursor = OpCursor::new(delta, interval); Self { cursor } } - pub fn ops(&mut self) -> Vec { self.collect::>() } + pub fn ops(&mut self) -> Vec> { self.collect::>() } pub fn next_op_len(&self) -> Option { let interval = self.cursor.next_iv(); @@ -37,12 +40,12 @@ impl<'a> DeltaIter<'a> { } } - pub fn next_op(&mut self) -> Option { self.cursor.next_op() } + pub fn next_op(&mut self) -> Option> { self.cursor.next_op() } - pub fn next_op_with_len(&mut self, len: usize) -> Option { self.cursor.next_with_len(Some(len)) } + pub fn next_op_with_len(&mut self, len: usize) -> Option> { self.cursor.next_with_len(Some(len)) } // find next op contains NEW_LINE - pub fn next_op_with_newline(&mut self) -> Option<(Operation, usize)> { + pub fn next_op_with_newline(&mut self) -> Option<(Operation, usize)> { let mut offset = 0; while self.has_next() { if let Some(op) = self.next_op() { @@ -87,12 +90,15 @@ impl<'a> DeltaIter<'a> { } } -impl<'a> Iterator for DeltaIter<'a> { - type Item = Operation; +impl<'a, T> Iterator for DeltaIter<'a, T> +where + T: Attributes, +{ + type Item = Operation; fn next(&mut self) -> Option { self.next_op() } } -pub fn is_empty_line_at_index(delta: &Delta, index: usize) -> bool { +pub fn is_empty_line_at_index(delta: &Delta, index: usize) -> bool { let mut iter = DeltaIter::new(delta); let (prev, next) = (iter.next_op_with_len(index), iter.next_op()); if prev.is_none() { @@ -108,58 +114,70 @@ pub fn is_empty_line_at_index(delta: &Delta, index: usize) -> bool { OpNewline::parse(&prev).is_end() && OpNewline::parse(&next).is_start() } -pub struct AttributesIter<'a> { - delta_iter: DeltaIter<'a>, +pub struct AttributesIter<'a, T: Attributes> { + delta_iter: DeltaIter<'a, T>, } -impl<'a> AttributesIter<'a> { - pub fn new(delta: &'a Delta) -> Self { +impl<'a, T> AttributesIter<'a, T> +where + T: Attributes, +{ + pub fn new(delta: &'a Delta) -> Self { let interval = Interval::new(0, usize::MAX); Self::from_interval(delta, interval) } - pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self { + pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self { let delta_iter = DeltaIter::from_interval(delta, interval); Self { delta_iter } } - pub fn next_or_empty(&mut self) -> Attributes { + pub fn next_or_empty(&mut self) -> T { match self.next() { - None => Attributes::default(), + None => T::default(), Some((_, attributes)) => attributes, } } } -impl<'a> Deref for AttributesIter<'a> { - type Target = DeltaIter<'a>; +impl<'a, T> Deref for AttributesIter<'a, T> +where + T: Attributes, +{ + type Target = DeltaIter<'a, T>; fn deref(&self) -> &Self::Target { &self.delta_iter } } -impl<'a> DerefMut for AttributesIter<'a> { +impl<'a, T> DerefMut for AttributesIter<'a, T> +where + T: Attributes, +{ fn deref_mut(&mut self) -> &mut Self::Target { &mut self.delta_iter } } -impl<'a> Iterator for AttributesIter<'a> { - type Item = (usize, Attributes); +impl<'a, T> Iterator for AttributesIter<'a, T> +where + T: Attributes, +{ + type Item = (usize, T); fn next(&mut self) -> Option { let next_op = self.delta_iter.next_op(); next_op.as_ref()?; let mut length: usize = 0; - let mut attributes = Attributes::new(); + let mut attributes = T::default(); match next_op.unwrap() { - Operation::Delete(_n) => {}, - Operation::Retain(retain) => { + Operation::::Delete(_n) => {}, + Operation::::Retain(retain) => { tracing::trace!("extend retain attributes with {} ", &retain.attributes); - attributes.extend(retain.attributes.clone()); + attributes.extend_other(retain.attributes.clone()); length = retain.n; }, - Operation::Insert(insert) => { + Operation::::Insert(insert) => { tracing::trace!("extend insert attributes with {} ", &insert.attributes); - attributes.extend(insert.attributes.clone()); + attributes.extend_other(insert.attributes.clone()); length = insert.count_of_code_units(); }, } @@ -178,7 +196,7 @@ pub enum OpNewline { } impl OpNewline { - pub fn parse(op: &Operation) -> OpNewline { + pub fn parse(op: &Operation) -> OpNewline { let s = op.get_data(); if s == NEW_LINE { diff --git a/shared-lib/lib-ot/src/core/operation/builder.rs b/shared-lib/lib-ot/src/core/operation/builder.rs index a640267fdc..d7484a3613 100644 --- a/shared-lib/lib-ot/src/core/operation/builder.rs +++ b/shared-lib/lib-ot/src/core/operation/builder.rs @@ -1,30 +1,35 @@ -use crate::core::{Attributes, Operation}; +use crate::core::{Attributes, Operation, RichTextAttributes}; -pub struct OpBuilder { - ty: Operation, - attrs: Attributes, +pub type RichTextOpBuilder = OpBuilder; + +pub struct OpBuilder { + ty: Operation, + attrs: T, } -impl OpBuilder { - pub fn new(ty: Operation) -> OpBuilder { +impl OpBuilder +where + T: Attributes, +{ + pub fn new(ty: Operation) -> OpBuilder { OpBuilder { ty, - attrs: Attributes::default(), + attrs: T::default(), } } - pub fn retain(n: usize) -> OpBuilder { OpBuilder::new(Operation::Retain(n.into())) } + pub fn retain(n: usize) -> OpBuilder { OpBuilder::new(Operation::Retain(n.into())) } - pub fn delete(n: usize) -> OpBuilder { OpBuilder::new(Operation::Delete(n)) } + pub fn delete(n: usize) -> OpBuilder { OpBuilder::new(Operation::Delete(n)) } - pub fn insert(s: &str) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) } + pub fn insert(s: &str) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) } - pub fn attributes(mut self, attrs: Attributes) -> OpBuilder { + pub fn attributes(mut self, attrs: T) -> OpBuilder { self.attrs = attrs; self } - pub fn build(self) -> Operation { + pub fn build(self) -> Operation { let mut operation = self.ty; match &mut operation { Operation::Delete(_) => {}, diff --git a/shared-lib/lib-ot/src/core/operation/operation.rs b/shared-lib/lib-ot/src/core/operation/operation.rs index b7378cbc40..c8f74370e2 100644 --- a/shared-lib/lib-ot/src/core/operation/operation.rs +++ b/shared-lib/lib-ot/src/core/operation/operation.rs @@ -1,19 +1,39 @@ -use crate::core::{Attribute, Attributes, FlowyStr, Interval, OpBuilder}; +use crate::core::{FlowyStr, Interval, OpBuilder, OperationTransformable, RichTextAttribute, RichTextAttributes}; use serde::__private::Formatter; use std::{ cmp::min, fmt, + fmt::Debug, ops::{Deref, DerefMut}, }; -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum Operation { - Delete(usize), - Retain(Retain), - Insert(Insert), +pub trait Attributes: fmt::Display + Eq + PartialEq + Default + Clone + Debug + OperationTransformable { + fn is_empty(&self) -> bool; + + // Remove the empty attribute which value is None. + fn remove_empty(&mut self); + + fn extend_other(&mut self, other: Self); } -impl Operation { +pub type RichTextOperation = Operation; +impl RichTextOperation { + pub fn contain_attribute(&self, attribute: &RichTextAttribute) -> bool { + self.get_attributes().contains_key(&attribute.key) + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Operation { + Delete(usize), + Retain(Retain), + Insert(Insert), +} + +impl Operation +where + T: Attributes, +{ pub fn get_data(&self) -> &str { match self { Operation::Delete(_) => "", @@ -22,15 +42,15 @@ impl Operation { } } - pub fn get_attributes(&self) -> Attributes { + pub fn get_attributes(&self) -> T { match self { - Operation::Delete(_) => Attributes::default(), + Operation::Delete(_) => T::default(), Operation::Retain(retain) => retain.attributes.clone(), Operation::Insert(insert) => insert.attributes.clone(), } } - pub fn set_attributes(&mut self, attributes: Attributes) { + pub fn set_attributes(&mut self, attributes: T) { match self { Operation::Delete(_) => log::error!("Delete should not contains attributes"), Operation::Retain(retain) => retain.attributes = attributes, @@ -40,10 +60,6 @@ impl Operation { pub fn has_attribute(&self) -> bool { !self.get_attributes().is_empty() } - pub fn contain_attribute(&self, attribute: &Attribute) -> bool { - self.get_attributes().contains_key(&attribute.key) - } - pub fn len(&self) -> usize { match self { Operation::Delete(n) => *n, @@ -55,28 +71,28 @@ impl Operation { pub fn is_empty(&self) -> bool { self.len() == 0 } #[allow(dead_code)] - pub fn split(&self, index: usize) -> (Option, Option) { + pub fn split(&self, index: usize) -> (Option>, Option>) { debug_assert!(index < self.len()); let left; let right; match self { Operation::Delete(n) => { - left = Some(OpBuilder::delete(index).build()); - right = Some(OpBuilder::delete(*n - index).build()); + left = Some(OpBuilder::::delete(index).build()); + right = Some(OpBuilder::::delete(*n - index).build()); }, Operation::Retain(retain) => { - left = Some(OpBuilder::delete(index).build()); - right = Some(OpBuilder::delete(retain.n - index).build()); + left = Some(OpBuilder::::delete(index).build()); + right = Some(OpBuilder::::delete(retain.n - index).build()); }, Operation::Insert(insert) => { let attributes = self.get_attributes(); left = Some( - OpBuilder::insert(&insert.s[0..index]) + OpBuilder::::insert(&insert.s[0..index]) .attributes(attributes.clone()) .build(), ); right = Some( - OpBuilder::insert(&insert.s[index..insert.count_of_code_units()]) + OpBuilder::::insert(&insert.s[index..insert.count_of_code_units()]) .attributes(attributes) .build(), ); @@ -86,7 +102,7 @@ impl Operation { (left, right) } - pub fn shrink(&self, interval: Interval) -> Option { + pub fn shrink(&self, interval: Interval) -> Option> { let op = match self { Operation::Delete(n) => OpBuilder::delete(min(*n, interval.size())).build(), Operation::Retain(retain) => OpBuilder::retain(min(retain.n, interval.size())) @@ -145,7 +161,10 @@ impl Operation { } } -impl fmt::Display for Operation { +impl fmt::Display for Operation +where + T: Attributes, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("{")?; match self { @@ -164,15 +183,18 @@ impl fmt::Display for Operation { } } -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -pub struct Retain { - #[serde(rename(serialize = "retain", deserialize = "retain"))] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Retain { + // #[serde(rename(serialize = "retain", deserialize = "retain"))] pub n: usize, - #[serde(skip_serializing_if = "is_empty")] - pub attributes: Attributes, + // #[serde(skip_serializing_if = "is_empty")] + pub attributes: T, } -impl fmt::Display for Retain { +impl fmt::Display for Retain +where + T: Attributes, +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if self.attributes.is_empty() { f.write_fmt(format_args!("retain: {}", self.n)) @@ -182,8 +204,11 @@ impl fmt::Display for Retain { } } -impl Retain { - pub fn merge_or_new(&mut self, n: usize, attributes: Attributes) -> Option { +impl Retain +where + T: Attributes, +{ + pub fn merge_or_new(&mut self, n: usize, attributes: T) -> Option> { tracing::trace!( "merge_retain_or_new_op: len: {:?}, l: {} - r: {}", n, @@ -201,35 +226,47 @@ impl Retain { pub fn is_plain(&self) -> bool { self.attributes.is_empty() } } -impl std::convert::From for Retain { +impl std::convert::From for Retain +where + T: Attributes, +{ fn from(n: usize) -> Self { Retain { n, - attributes: Attributes::default(), + attributes: T::default(), } } } -impl Deref for Retain { +impl Deref for Retain +where + T: Attributes, +{ type Target = usize; fn deref(&self) -> &Self::Target { &self.n } } -impl DerefMut for Retain { +impl DerefMut for Retain +where + T: Attributes, +{ fn deref_mut(&mut self) -> &mut Self::Target { &mut self.n } } -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -pub struct Insert { - #[serde(rename(serialize = "insert", deserialize = "insert"))] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Insert { + // #[serde(rename(serialize = "insert", deserialize = "insert"))] pub s: FlowyStr, - #[serde(skip_serializing_if = "is_empty")] - pub attributes: Attributes, + // #[serde(skip_serializing_if = "is_empty")] + pub attributes: T, } -impl fmt::Display for Insert { +impl fmt::Display for Insert +where + T: Attributes, +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut s = self.s.clone(); if s.ends_with('\n') { @@ -247,41 +284,51 @@ impl fmt::Display for Insert { } } -impl Insert { +impl Insert +where + T: Attributes, +{ pub fn count_of_code_units(&self) -> usize { self.s.count_utf16_code_units() } - pub fn merge_or_new_op(&mut self, s: &str, attributes: Attributes) -> Option { + pub fn merge_or_new_op(&mut self, s: &str, attributes: T) -> Option> { if self.attributes == attributes { self.s += s; None } else { - Some(OpBuilder::insert(s).attributes(attributes).build()) + Some(OpBuilder::::insert(s).attributes(attributes).build()) } } pub fn is_plain(&self) -> bool { self.attributes.is_empty() } } -impl std::convert::From for Insert { +impl std::convert::From for Insert +where + T: Attributes, +{ fn from(s: String) -> Self { Insert { s: s.into(), - attributes: Attributes::default(), + attributes: T::default(), } } } -impl std::convert::From<&str> for Insert { +impl std::convert::From<&str> for Insert +where + T: Attributes, +{ fn from(s: &str) -> Self { Insert::from(s.to_owned()) } } -impl std::convert::From for Insert { +impl std::convert::From for Insert +where + T: Attributes, +{ fn from(s: FlowyStr) -> Self { Insert { s, - attributes: Attributes::default(), + attributes: T::default(), } } } - -fn is_empty(attributes: &Attributes) -> bool { attributes.is_empty() } diff --git a/shared-lib/lib-ot/src/core/operation/operation_serde.rs b/shared-lib/lib-ot/src/core/operation/operation_serde.rs index 868eefdb55..7f8391281e 100644 --- a/shared-lib/lib-ot/src/core/operation/operation_serde.rs +++ b/shared-lib/lib-ot/src/core/operation/operation_serde.rs @@ -1,16 +1,19 @@ -use crate::core::{Attributes, Operation}; +use crate::core::{Attributes, FlowyStr, Insert, Operation, Retain}; use serde::{ de, - de::{MapAccess, Visitor}, + de::{MapAccess, SeqAccess, Visitor}, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer, }; -use std::fmt; +use std::{fmt, marker::PhantomData}; -impl Serialize for Operation { +impl Serialize for Operation +where + T: Attributes + Serialize, +{ fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -27,20 +30,27 @@ impl Serialize for Operation { } } -impl<'de> Deserialize<'de> for Operation { - fn deserialize(deserializer: D) -> Result +impl<'de, T> Deserialize<'de> for Operation +where + T: Attributes + Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { - struct OperationVisitor; + struct OperationVisitor(PhantomData T>); - impl<'de> Visitor<'de> for OperationVisitor { - type Value = Operation; + impl<'de, T> Visitor<'de> for OperationVisitor + where + T: Attributes + Deserialize<'de>, + { + type Value = Operation; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("an integer between -2^64 and 2^63 or a string") } + #[inline] fn visit_map(self, mut map: V) -> Result where V: MapAccess<'de>, @@ -53,27 +63,27 @@ impl<'de> Deserialize<'de> for Operation { if operation.is_some() { return Err(de::Error::duplicate_field("operation")); } - operation = Some(Operation::Delete(map.next_value()?)); + operation = Some(Operation::::Delete(map.next_value()?)); }, "retain" => { if operation.is_some() { return Err(de::Error::duplicate_field("operation")); } let i: usize = map.next_value()?; - operation = Some(Operation::Retain(i.into())); + operation = Some(Operation::::Retain(i.into())); }, "insert" => { if operation.is_some() { return Err(de::Error::duplicate_field("operation")); } let i: String = map.next_value()?; - operation = Some(Operation::Insert(i.into())); + operation = Some(Operation::::Insert(i.into())); }, "attributes" => { if attributes.is_some() { return Err(de::Error::duplicate_field("attributes")); } - let map: Attributes = map.next_value()?; + let map: T = map.next_value()?; attributes = Some(map); }, _ => panic!(), @@ -91,6 +101,208 @@ impl<'de> Deserialize<'de> for Operation { } } - deserializer.deserialize_any(OperationVisitor) + deserializer.deserialize_any(OperationVisitor(PhantomData)) + } +} + +impl Serialize for Retain +where + T: Attributes + Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let len = false as usize + 1 + if self.attributes.is_empty() { 0 } else { 1 }; + let mut serde_state = serializer.serialize_struct("Retain", len)?; + let _ = serde::ser::SerializeStruct::serialize_field(&mut serde_state, "retain", &self.n)?; + if !self.attributes.is_empty() { + let _ = serde::ser::SerializeStruct::serialize_field(&mut serde_state, "attributes", &self.attributes)?; + } + serde::ser::SerializeStruct::end(serde_state) + } +} + +impl<'de, T> Deserialize<'de> for Retain +where + T: Attributes + Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result>::Error> + where + D: Deserializer<'de>, + { + struct RetainVisitor(PhantomData T>); + + impl<'de, T> Visitor<'de> for RetainVisitor + where + T: Attributes + Deserialize<'de>, + { + type Value = Retain; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("struct Retain") } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let len = match serde::de::SeqAccess::next_element::(&mut seq)? { + Some(val) => val, + None => { + return Err(de::Error::invalid_length(0, &"struct Retain with 2 elements")); + }, + }; + + let attributes = match serde::de::SeqAccess::next_element::(&mut seq)? { + Some(val) => val, + None => { + return Err(de::Error::invalid_length(1, &"struct Retain with 2 elements")); + }, + }; + + Ok(Retain:: { n: len, attributes }) + } + + #[inline] + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut len: Option = None; + let mut attributes: Option = None; + while let Some(key) = map.next_key()? { + match key { + "retain" => { + if len.is_some() { + return Err(de::Error::duplicate_field("retain")); + } + len = Some(map.next_value()?); + }, + "attributes" => { + if attributes.is_some() { + return Err(de::Error::duplicate_field("attributes")); + } + attributes = Some(map.next_value()?); + }, + _ => panic!(), + } + } + + if len.is_none() { + return Err(de::Error::missing_field("len")); + } + + if attributes.is_none() { + return Err(de::Error::missing_field("attributes")); + } + Ok(Retain:: { + n: len.unwrap(), + attributes: attributes.unwrap(), + }) + } + } + const FIELDS: &[&str] = &["retain", "attributes"]; + serde::Deserializer::deserialize_struct(deserializer, "Retain", FIELDS, RetainVisitor(PhantomData)) + } +} + +impl Serialize for Insert +where + T: Attributes + Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let len = false as usize + 1 + if self.attributes.is_empty() { 0 } else { 1 }; + let mut serde_state = serializer.serialize_struct("Insert", len)?; + let _ = serde::ser::SerializeStruct::serialize_field(&mut serde_state, "insert", &self.s)?; + if !self.attributes.is_empty() { + let _ = serde::ser::SerializeStruct::serialize_field(&mut serde_state, "attributes", &self.attributes)?; + } + serde::ser::SerializeStruct::end(serde_state) + } +} + +impl<'de, T> Deserialize<'de> for Insert +where + T: Attributes + Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result>::Error> + where + D: Deserializer<'de>, + { + struct InsertVisitor(PhantomData T>); + + impl<'de, T> Visitor<'de> for InsertVisitor + where + T: Attributes + Deserialize<'de>, + { + type Value = Insert; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("struct Insert") } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let s = match serde::de::SeqAccess::next_element::(&mut seq)? { + Some(val) => val, + None => { + return Err(de::Error::invalid_length(0, &"struct Insert with 2 elements")); + }, + }; + + let attributes = match serde::de::SeqAccess::next_element::(&mut seq)? { + Some(val) => val, + None => { + return Err(de::Error::invalid_length(1, &"struct Retain with 2 elements")); + }, + }; + + Ok(Insert:: { s, attributes }) + } + + #[inline] + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut s: Option = None; + let mut attributes: Option = None; + while let Some(key) = map.next_key()? { + match key { + "insert" => { + if s.is_some() { + return Err(de::Error::duplicate_field("insert")); + } + s = Some(map.next_value()?); + }, + "attributes" => { + if attributes.is_some() { + return Err(de::Error::duplicate_field("attributes")); + } + attributes = Some(map.next_value()?); + }, + _ => panic!(), + } + } + + if s.is_none() { + return Err(de::Error::missing_field("s")); + } + + if attributes.is_none() { + return Err(de::Error::missing_field("attributes")); + } + Ok(Insert:: { + s: s.unwrap(), + attributes: attributes.unwrap(), + }) + } + } + const FIELDS: &[&str] = &["insert", "attributes"]; + serde::Deserializer::deserialize_struct(deserializer, "Insert", FIELDS, InsertVisitor(PhantomData)) } }