From c9914732592449ed36343bc87e358861b4c381d1 Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 13 Sep 2022 11:38:19 +0800 Subject: [PATCH] refactor: refactor delta attributes --- frontend/rust-lib/Cargo.lock | 1 + .../flowy-revision/src/conflict_resolve.rs | 8 +- frontend/rust-lib/flowy-text-block/Cargo.toml | 2 + .../rust-lib/flowy-text-block/src/editor.rs | 5 +- .../rust-lib/flowy-text-block/src/queue.rs | 7 +- .../flowy-text-block/src/web_socket.rs | 5 +- .../tests/editor/attribute_test.rs | 176 ++++---- .../flowy-text-block/tests/editor/mod.rs | 27 +- .../flowy-text-block/tests/editor/op_test.rs | 145 ++++--- .../tests/editor/serde_test.rs | 50 +-- .../tests/editor/undo_redo_test.rs | 22 +- .../src/client_document/document_pad.rs | 13 +- .../delete/preserve_line_format_merge.rs | 8 +- .../extensions/format/resolve_block_format.rs | 14 +- .../format/resolve_inline_format.rs | 8 +- .../src/client_document/extensions/helper.rs | 11 +- .../extensions/insert/auto_exit_block.rs | 4 +- .../extensions/insert/auto_format.rs | 9 +- .../extensions/insert/default_insert.rs | 10 +- .../insert/preserve_block_format.rs | 17 +- .../insert/preserve_inline_format.rs | 12 +- .../insert/reset_format_on_new_line.rs | 12 +- .../src/client_document/extensions/mod.rs | 8 +- .../flowy-sync/src/client_document/view.rs | 11 +- .../src/server_document/document_manager.rs | 5 +- .../src/server_document/document_pad.rs | 9 +- .../src/codec/markdown/markdown_encoder.rs | 71 ++-- shared-lib/lib-ot/src/codec/markdown/mod.rs | 2 +- .../lib-ot/src/core/attributes/attribute.rs | 150 ++++++- .../src/core/attributes/attribute_serde.rs | 59 +-- shared-lib/lib-ot/src/core/delta/builder.rs | 8 +- shared-lib/lib-ot/src/core/delta/iterator.rs | 5 +- .../src/core/delta/operation/builder.rs | 3 +- shared-lib/lib-ot/src/core/delta/ops.rs | 2 +- .../src/text_delta/attribute_builder.rs | 30 -- .../lib-ot/src/text_delta/attributes.rs | 375 ++++-------------- .../lib-ot/src/text_delta/attributes_serde.rs | 231 ----------- shared-lib/lib-ot/src/text_delta/delta.rs | 9 +- shared-lib/lib-ot/src/text_delta/macros.rs | 45 +-- shared-lib/lib-ot/src/text_delta/mod.rs | 3 - shared-lib/lib-ot/tests/node/editor_test.rs | 13 +- .../lib-ot/tests/node/operation_test.rs | 2 +- 42 files changed, 591 insertions(+), 1016 deletions(-) delete mode 100644 shared-lib/lib-ot/src/text_delta/attribute_builder.rs delete mode 100644 shared-lib/lib-ot/src/text_delta/attributes_serde.rs diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 7ace722d28..66d576a455 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -1172,6 +1172,7 @@ dependencies = [ "strum_macros", "tokio", "tracing", + "tracing-subscriber", "unicode-segmentation", "url", ] diff --git a/frontend/rust-lib/flowy-revision/src/conflict_resolve.rs b/frontend/rust-lib/flowy-revision/src/conflict_resolve.rs index 2443bf4488..51e37a363c 100644 --- a/frontend/rust-lib/flowy-revision/src/conflict_resolve.rs +++ b/frontend/rust-lib/flowy-revision/src/conflict_resolve.rs @@ -9,8 +9,8 @@ use flowy_sync::{ util::make_delta_from_revisions, }; use lib_infra::future::BoxResultFuture; -use lib_ot::core::{EmptyAttributes, OperationAttributes, Operations}; -use lib_ot::text_delta::TextAttributes; +use lib_ot::core::{Attributes, EmptyAttributes, OperationAttributes, Operations}; + use serde::de::DeserializeOwned; use std::{convert::TryFrom, sync::Arc}; @@ -30,7 +30,7 @@ pub trait ConflictRevisionSink: Send + Sync + 'static { fn ack(&self, rev_id: String, ty: ServerRevisionWSDataType) -> BoxResultFuture<(), FlowyError>; } -pub type RichTextConflictController = ConflictController; +pub type RichTextConflictController = ConflictController; pub type PlainTextConflictController = ConflictController; pub struct ConflictController @@ -175,7 +175,7 @@ where } } -pub type RichTextTransformDeltas = TransformDeltas; +pub type RichTextTransformDeltas = TransformDeltas; pub struct TransformDeltas where diff --git a/frontend/rust-lib/flowy-text-block/Cargo.toml b/frontend/rust-lib/flowy-text-block/Cargo.toml index 576fa5cbcd..74c7f1de2a 100644 --- a/frontend/rust-lib/flowy-text-block/Cargo.toml +++ b/frontend/rust-lib/flowy-text-block/Cargo.toml @@ -26,6 +26,7 @@ unicode-segmentation = "1.8" log = "0.4.14" tokio = {version = "1", features = ["sync"]} tracing = { version = "0.1", features = ["log"] } + bytes = { version = "1.1" } strum = "0.21" strum_macros = "0.21" @@ -42,6 +43,7 @@ futures = "0.3.15" flowy-test = { path = "../flowy-test" } flowy-text-block = { path = "../flowy-text-block", features = ["flowy_unit_test"]} derive_more = {version = "0.99", features = ["display"]} +tracing-subscriber = "0.2.0" color-eyre = { version = "0.5", default-features = false } criterion = "0.3" diff --git a/frontend/rust-lib/flowy-text-block/src/editor.rs b/frontend/rust-lib/flowy-text-block/src/editor.rs index a8b5a6a8ae..8f75a67d88 100644 --- a/frontend/rust-lib/flowy-text-block/src/editor.rs +++ b/frontend/rust-lib/flowy-text-block/src/editor.rs @@ -13,9 +13,10 @@ use flowy_sync::{ errors::CollaborateResult, util::make_delta_from_revisions, }; +use lib_ot::core::AttributeEntry; use lib_ot::{ core::{Interval, Operation}, - text_delta::{TextAttribute, TextDelta}, + text_delta::TextDelta, }; use lib_ws::WSConnectState; use std::sync::Arc; @@ -85,7 +86,7 @@ impl TextBlockEditor { Ok(()) } - pub async fn format(&self, interval: Interval, attribute: TextAttribute) -> Result<(), FlowyError> { + pub async fn format(&self, interval: Interval, attribute: AttributeEntry) -> Result<(), FlowyError> { let (ret, rx) = oneshot::channel::>(); let msg = EditorCommand::Format { interval, diff --git a/frontend/rust-lib/flowy-text-block/src/queue.rs b/frontend/rust-lib/flowy-text-block/src/queue.rs index 3f6a185e66..d81461d25e 100644 --- a/frontend/rust-lib/flowy-text-block/src/queue.rs +++ b/frontend/rust-lib/flowy-text-block/src/queue.rs @@ -11,9 +11,10 @@ use flowy_sync::{ errors::CollaborateError, }; use futures::stream::StreamExt; +use lib_ot::core::{AttributeEntry, Attributes}; use lib_ot::{ core::{Interval, OperationTransform}, - text_delta::{TextAttribute, TextAttributes, TextDelta}, + text_delta::TextDelta, }; use std::sync::Arc; use tokio::sync::{oneshot, RwLock}; @@ -194,7 +195,7 @@ impl EditBlockQueue { pub(crate) struct TextBlockRevisionCompactor(); impl RevisionCompactor for TextBlockRevisionCompactor { fn bytes_from_revisions(&self, revisions: Vec) -> FlowyResult { - let delta = make_delta_from_revisions::(revisions)?; + let delta = make_delta_from_revisions::(revisions)?; Ok(delta.json_bytes()) } } @@ -229,7 +230,7 @@ pub(crate) enum EditorCommand { }, Format { interval: Interval, - attribute: TextAttribute, + attribute: AttributeEntry, ret: Ret<()>, }, Replace { diff --git a/frontend/rust-lib/flowy-text-block/src/web_socket.rs b/frontend/rust-lib/flowy-text-block/src/web_socket.rs index 81d5e7f2a7..06b2791664 100644 --- a/frontend/rust-lib/flowy-text-block/src/web_socket.rs +++ b/frontend/rust-lib/flowy-text-block/src/web_socket.rs @@ -10,7 +10,8 @@ use flowy_sync::{ errors::CollaborateResult, }; use lib_infra::future::{BoxResultFuture, FutureResult}; -use lib_ot::text_delta::TextAttributes; +use lib_ot::core::Attributes; + use lib_ot::text_delta::TextDelta; use lib_ws::WSConnectState; use std::{sync::Arc, time::Duration}; @@ -111,7 +112,7 @@ struct TextBlockConflictResolver { edit_cmd_tx: EditorCommandSender, } -impl ConflictResolver for TextBlockConflictResolver { +impl ConflictResolver for TextBlockConflictResolver { fn compose_delta(&self, delta: TextDelta) -> BoxResultFuture { let tx = self.edit_cmd_tx.clone(); Box::pin(async move { diff --git a/frontend/rust-lib/flowy-text-block/tests/editor/attribute_test.rs b/frontend/rust-lib/flowy-text-block/tests/editor/attribute_test.rs index 4dc2e7a47a..16dc3a69a9 100644 --- a/frontend/rust-lib/flowy-text-block/tests/editor/attribute_test.rs +++ b/frontend/rust-lib/flowy-text-block/tests/editor/attribute_test.rs @@ -1,6 +1,6 @@ #![cfg_attr(rustfmt, rustfmt::skip)] use crate::editor::{TestBuilder, TestOp::*}; -use flowy_sync::client_document::{NewlineDoc, PlainDoc}; +use flowy_sync::client_document::{NewlineDoc, EmptyDoc}; use lib_ot::core::{Interval, OperationTransform, NEW_LINE, WHITESPACE, OTString}; use unicode_segmentation::UnicodeSegmentation; use lib_ot::text_delta::TextDelta; @@ -14,12 +14,12 @@ fn attributes_bold_added() { 0, r#"[ {"insert":"123"}, - {"insert":"45","attributes":{"bold":"true"}}, + {"insert":"45","attributes":{"bold":true}}, {"insert":"6"} ]"#, ), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -27,11 +27,11 @@ fn attributes_bold_added_and_invert_all() { let ops = vec![ Insert(0, "123", 0), Bold(0, Interval::new(0, 3), true), - AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":true}}]"#), Bold(0, Interval::new(0, 3), false), AssertDocJson(0, r#"[{"insert":"123"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -39,11 +39,11 @@ fn attributes_bold_added_and_invert_partial_suffix() { let ops = vec![ Insert(0, "1234", 0), Bold(0, Interval::new(0, 4), true), - AssertDocJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"1234","attributes":{"bold":true}}]"#), Bold(0, Interval::new(2, 4), false), - AssertDocJson(0, r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#), + AssertDocJson(0, r#"[{"insert":"12","attributes":{"bold":true}},{"insert":"34"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -51,13 +51,13 @@ fn attributes_bold_added_and_invert_partial_suffix2() { let ops = vec![ Insert(0, "1234", 0), Bold(0, Interval::new(0, 4), true), - AssertDocJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"1234","attributes":{"bold":true}}]"#), Bold(0, Interval::new(2, 4), false), - AssertDocJson(0, r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#), + AssertDocJson(0, r#"[{"insert":"12","attributes":{"bold":true}},{"insert":"34"}]"#), Bold(0, Interval::new(2, 4), true), - AssertDocJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"1234","attributes":{"bold":true}}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -67,22 +67,22 @@ fn attributes_bold_added_with_new_line() { Bold(0, Interval::new(0, 6), true), AssertDocJson( 0, - r#"[{"insert":"123456","attributes":{"bold":"true"}},{"insert":"\n"}]"#, + r#"[{"insert":"123456","attributes":{"bold":true}},{"insert":"\n"}]"#, ), Insert(0, "\n", 3), AssertDocJson( 0, - r#"[{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"},{"insert":"456","attributes":{"bold":"true"}},{"insert":"\n"}]"#, + r#"[{"insert":"123","attributes":{"bold":true}},{"insert":"\n"},{"insert":"456","attributes":{"bold":true}},{"insert":"\n"}]"#, ), Insert(0, "\n", 4), AssertDocJson( 0, - r#"[{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n\n"},{"insert":"456","attributes":{"bold":"true"}},{"insert":"\n"}]"#, + r#"[{"insert":"123","attributes":{"bold":true}},{"insert":"\n\n"},{"insert":"456","attributes":{"bold":true}},{"insert":"\n"}]"#, ), Insert(0, "a", 4), AssertDocJson( 0, - r#"[{"insert":"123","attributes":{"bold":"true"}},{"insert":"\na\n"},{"insert":"456","attributes":{"bold":"true"}},{"insert":"\n"}]"#, + r#"[{"insert":"123","attributes":{"bold":true}},{"insert":"\na\n"},{"insert":"456","attributes":{"bold":true}},{"insert":"\n"}]"#, ), ]; TestBuilder::new().run_scripts::(ops); @@ -93,11 +93,11 @@ fn attributes_bold_added_and_invert_partial_prefix() { let ops = vec![ Insert(0, "1234", 0), Bold(0, Interval::new(0, 4), true), - AssertDocJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"1234","attributes":{"bold":true}}]"#), Bold(0, Interval::new(0, 2), false), - AssertDocJson(0, r#"[{"insert":"12"},{"insert":"34","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"12"},{"insert":"34","attributes":{"bold":true}}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -105,11 +105,11 @@ fn attributes_bold_added_consecutive() { let ops = vec![ Insert(0, "1234", 0), Bold(0, Interval::new(0, 1), true), - AssertDocJson(0, r#"[{"insert":"1","attributes":{"bold":"true"}},{"insert":"234"}]"#), + AssertDocJson(0, r#"[{"insert":"1","attributes":{"bold":true}},{"insert":"234"}]"#), Bold(0, Interval::new(1, 2), true), - AssertDocJson(0, r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#), + AssertDocJson(0, r#"[{"insert":"12","attributes":{"bold":true}},{"insert":"34"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -120,12 +120,12 @@ fn attributes_bold_added_italic() { Italic(0, Interval::new(0, 4), true), AssertDocJson( 0, - r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}},{"insert":"\n"}]"#, + r#"[{"insert":"1234","attributes":{"italic":true,"bold":true}},{"insert":"\n"}]"#, ), Insert(0, "5678", 4), AssertDocJson( 0, - r#"[{"insert":"12345678","attributes":{"bold":"true","italic":"true"}},{"insert":"\n"}]"#, + r#"[{"insert":"12345678","attributes":{"bold":true,"italic":true}},{"insert":"\n"}]"#, ), ]; TestBuilder::new().run_scripts::(ops); @@ -136,27 +136,27 @@ fn attributes_bold_added_italic2() { let ops = vec![ Insert(0, "123456", 0), Bold(0, Interval::new(0, 6), true), - AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":true}}]"#), Italic(0, Interval::new(0, 2), true), AssertDocJson( 0, r#"[ - {"insert":"12","attributes":{"italic":"true","bold":"true"}}, - {"insert":"3456","attributes":{"bold":"true"}}] + {"insert":"12","attributes":{"italic":true,"bold":true}}, + {"insert":"3456","attributes":{"bold":true}}] "#, ), Italic(0, Interval::new(4, 6), true), AssertDocJson( 0, r#"[ - {"insert":"12","attributes":{"italic":"true","bold":"true"}}, - {"insert":"34","attributes":{"bold":"true"}}, - {"insert":"56","attributes":{"italic":"true","bold":"true"}}] + {"insert":"12","attributes":{"italic":true,"bold":true}}, + {"insert":"34","attributes":{"bold":true}}, + {"insert":"56","attributes":{"italic":true,"bold":true}}] "#, ), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -168,16 +168,16 @@ fn attributes_bold_added_italic3() { AssertDocJson( 0, r#"[ - {"insert":"12","attributes":{"bold":"true","italic":"true"}}, - {"insert":"345","attributes":{"bold":"true"}},{"insert":"6789"}] + {"insert":"12","attributes":{"bold":true,"italic":true}}, + {"insert":"345","attributes":{"bold":true}},{"insert":"6789"}] "#, ), Italic(0, Interval::new(2, 4), true), AssertDocJson( 0, r#"[ - {"insert":"1234","attributes":{"bold":"true","italic":"true"}}, - {"insert":"5","attributes":{"bold":"true"}}, + {"insert":"1234","attributes":{"bold":true,"italic":true}}, + {"insert":"5","attributes":{"bold":true}}, {"insert":"6789"}] "#, ), @@ -185,15 +185,15 @@ fn attributes_bold_added_italic3() { AssertDocJson( 0, r#"[ - {"insert":"1234","attributes":{"bold":"true","italic":"true"}}, - {"insert":"5","attributes":{"bold":"true"}}, + {"insert":"1234","attributes":{"bold":true,"italic":true}}, + {"insert":"5","attributes":{"bold":true}}, {"insert":"67"}, - {"insert":"89","attributes":{"bold":"true"}}] + {"insert":"89","attributes":{"bold":true}}] "#, ), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -205,57 +205,57 @@ fn attributes_bold_added_italic_delete() { AssertDocJson( 0, r#"[ - {"insert":"12","attributes":{"italic":"true","bold":"true"}}, - {"insert":"345","attributes":{"bold":"true"}},{"insert":"6789"}] + {"insert":"12","attributes":{"italic":true,"bold":true}}, + {"insert":"345","attributes":{"bold":true}},{"insert":"6789"}] "#, ), Italic(0, Interval::new(2, 4), true), AssertDocJson( 0, r#"[ - {"insert":"1234","attributes":{"bold":"true","italic":"true"}} - ,{"insert":"5","attributes":{"bold":"true"}},{"insert":"6789"}]"#, + {"insert":"1234","attributes":{"bold":true,"italic":true}} + ,{"insert":"5","attributes":{"bold":true}},{"insert":"6789"}]"#, ), Bold(0, Interval::new(7, 9), true), AssertDocJson( 0, r#"[ - {"insert":"1234","attributes":{"bold":"true","italic":"true"}}, - {"insert":"5","attributes":{"bold":"true"}},{"insert":"67"}, - {"insert":"89","attributes":{"bold":"true"}}] + {"insert":"1234","attributes":{"bold":true,"italic":true}}, + {"insert":"5","attributes":{"bold":true}},{"insert":"67"}, + {"insert":"89","attributes":{"bold":true}}] "#, ), Delete(0, Interval::new(0, 5)), - AssertDocJson(0, r#"[{"insert":"67"},{"insert":"89","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"67"},{"insert":"89","attributes":{"bold":true}}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] fn attributes_merge_inserted_text_with_same_attribute() { let ops = vec![ InsertBold(0, "123", Interval::new(0, 3)), - AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":true}}]"#), InsertBold(0, "456", Interval::new(3, 6)), - AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":true}}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] fn attributes_compose_attr_attributes_with_attr_attributes_test() { let ops = vec![ InsertBold(0, "123456", Interval::new(0, 6)), - AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":true}}]"#), InsertBold(1, "7", Interval::new(0, 1)), - AssertDocJson(1, r#"[{"insert":"7","attributes":{"bold":"true"}}]"#), + AssertDocJson(1, r#"[{"insert":"7","attributes":{"bold":true}}]"#), Transform(0, 1), - AssertDocJson(0, r#"[{"insert":"1234567","attributes":{"bold":"true"}}]"#), - AssertDocJson(1, r#"[{"insert":"1234567","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"1234567","attributes":{"bold":true}}]"#), + AssertDocJson(1, r#"[{"insert":"1234567","attributes":{"bold":true}}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -268,113 +268,113 @@ fn attributes_compose_attr_attributes_with_attr_attributes_test2() { AssertDocJson( 0, r#"[ - {"insert":"12","attributes":{"bold":"true","italic":"true"}}, - {"insert":"34","attributes":{"bold":"true"}}, - {"insert":"56","attributes":{"italic":"true","bold":"true"}}] + {"insert":"12","attributes":{"bold":true,"italic":true}}, + {"insert":"34","attributes":{"bold":true}}, + {"insert":"56","attributes":{"italic":true,"bold":true}}] "#, ), InsertBold(1, "7", Interval::new(0, 1)), - AssertDocJson(1, r#"[{"insert":"7","attributes":{"bold":"true"}}]"#), + AssertDocJson(1, r#"[{"insert":"7","attributes":{"bold":true}}]"#), Transform(0, 1), AssertDocJson( 0, r#"[ - {"insert":"12","attributes":{"italic":"true","bold":"true"}}, - {"insert":"34","attributes":{"bold":"true"}}, - {"insert":"56","attributes":{"italic":"true","bold":"true"}}, - {"insert":"7","attributes":{"bold":"true"}}] + {"insert":"12","attributes":{"italic":true,"bold":true}}, + {"insert":"34","attributes":{"bold":true}}, + {"insert":"56","attributes":{"italic":true,"bold":true}}, + {"insert":"7","attributes":{"bold":true}}] "#, ), AssertDocJson( 1, r#"[ - {"insert":"12","attributes":{"italic":"true","bold":"true"}}, - {"insert":"34","attributes":{"bold":"true"}}, - {"insert":"56","attributes":{"italic":"true","bold":"true"}}, - {"insert":"7","attributes":{"bold":"true"}}] + {"insert":"12","attributes":{"italic":true,"bold":true}}, + {"insert":"34","attributes":{"bold":true}}, + {"insert":"56","attributes":{"italic":true,"bold":true}}, + {"insert":"7","attributes":{"bold":true}}] "#, ), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] fn attributes_compose_attr_attributes_with_no_attr_attributes_test() { - let expected = r#"[{"insert":"123456","attributes":{"bold":"true"}},{"insert":"7"}]"#; + let expected = r#"[{"insert":"123456","attributes":{"bold":true}},{"insert":"7"}]"#; let ops = vec![ InsertBold(0, "123456", Interval::new(0, 6)), - AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":true}}]"#), Insert(1, "7", 0), AssertDocJson(1, r#"[{"insert":"7"}]"#), Transform(0, 1), AssertDocJson(0, expected), AssertDocJson(1, expected), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] fn attributes_replace_heading() { let ops = vec![ InsertBold(0, "123456", Interval::new(0, 6)), - AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":true}}]"#), Delete(0, Interval::new(0, 2)), - AssertDocJson(0, r#"[{"insert":"3456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"3456","attributes":{"bold":true}}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] fn attributes_replace_trailing() { let ops = vec![ InsertBold(0, "123456", Interval::new(0, 6)), - AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":true}}]"#), Delete(0, Interval::new(5, 6)), - AssertDocJson(0, r#"[{"insert":"12345","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"12345","attributes":{"bold":true}}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] fn attributes_replace_middle() { let ops = vec![ InsertBold(0, "123456", Interval::new(0, 6)), - AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":true}}]"#), Delete(0, Interval::new(0, 2)), - AssertDocJson(0, r#"[{"insert":"3456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"3456","attributes":{"bold":true}}]"#), Delete(0, Interval::new(2, 4)), - AssertDocJson(0, r#"[{"insert":"34","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"34","attributes":{"bold":true}}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] fn attributes_replace_all() { let ops = vec![ InsertBold(0, "123456", Interval::new(0, 6)), - AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":true}}]"#), Delete(0, Interval::new(0, 6)), AssertDocJson(0, r#"[]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] fn attributes_replace_with_text() { let ops = vec![ InsertBold(0, "123456", Interval::new(0, 6)), - AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":true}}]"#), Replace(0, Interval::new(0, 3), "ab"), - AssertDocJson(0, r#"[{"insert":"ab"},{"insert":"456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"ab"},{"insert":"456","attributes":{"bold":true}}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -472,7 +472,7 @@ fn attributes_link_format_with_bold() { AssertDocJson( 0, r#"[ - {"insert":"123","attributes":{"bold":"true","link":"https://appflowy.io"}}, + {"insert":"123","attributes":{"bold":true,"link":"https://appflowy.io"}}, {"insert":"456","attributes":{"link":"https://appflowy.io"}}, {"insert":"\n"}] "#, diff --git a/frontend/rust-lib/flowy-text-block/tests/editor/mod.rs b/frontend/rust-lib/flowy-text-block/tests/editor/mod.rs index 392315b292..c2eb30e3f4 100644 --- a/frontend/rust-lib/flowy-text-block/tests/editor/mod.rs +++ b/frontend/rust-lib/flowy-text-block/tests/editor/mod.rs @@ -8,13 +8,11 @@ use derive_more::Display; use flowy_sync::client_document::{ClientDocument, InitialDocumentText}; use lib_ot::{ core::*, - text_delta::{TextAttribute, TextAttributes, TextDelta}, + text_delta::{BuildInTextAttribute, TextDelta}, }; use rand::{prelude::*, Rng as WrappedRng}; use std::{sync::Once, time::Duration}; -const LEVEL: &str = "info"; - #[derive(Clone, Debug, Display)] pub enum TestOp { #[display(fmt = "Insert")] @@ -92,7 +90,8 @@ impl TestBuilder { static INIT: Once = Once::new(); INIT.call_once(|| { let _ = color_eyre::install(); - std::env::set_var("RUST_LOG", LEVEL); + // let subscriber = FmtSubscriber::builder().with_max_level(Level::INFO).finish(); + // tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); }); Self { @@ -127,11 +126,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, TextAttribute::Bold(true)).unwrap(); + document.format(*iv, BuildInTextAttribute::Bold(true)).unwrap(); } TestOp::Bold(delta_i, iv, enable) => { let document = &mut self.documents[*delta_i]; - let attribute = TextAttribute::Bold(*enable); + let attribute = BuildInTextAttribute::Bold(*enable); let delta = document.format(*iv, attribute).unwrap(); tracing::trace!("Bold delta: {}", delta.json_str()); self.deltas.insert(*delta_i, Some(delta)); @@ -139,8 +138,8 @@ impl TestBuilder { TestOp::Italic(delta_i, iv, enable) => { let document = &mut self.documents[*delta_i]; let attribute = match *enable { - true => TextAttribute::Italic(true), - false => TextAttribute::Italic(false), + true => BuildInTextAttribute::Italic(true), + false => BuildInTextAttribute::Italic(false), }; let delta = document.format(*iv, attribute).unwrap(); tracing::trace!("Italic delta: {}", delta.json_str()); @@ -148,21 +147,21 @@ impl TestBuilder { } TestOp::Header(delta_i, iv, level) => { let document = &mut self.documents[*delta_i]; - let attribute = TextAttribute::Header(*level); + let attribute = BuildInTextAttribute::Header(*level); let delta = document.format(*iv, attribute).unwrap(); tracing::trace!("Header delta: {}", delta.json_str()); self.deltas.insert(*delta_i, Some(delta)); } TestOp::Link(delta_i, iv, link) => { let document = &mut self.documents[*delta_i]; - let attribute = TextAttribute::Link(link.to_owned()); + let attribute = BuildInTextAttribute::Link(link.to_owned()); let delta = document.format(*iv, attribute).unwrap(); tracing::trace!("Link delta: {}", delta.json_str()); self.deltas.insert(*delta_i, Some(delta)); } TestOp::Bullet(delta_i, iv, enable) => { let document = &mut self.documents[*delta_i]; - let attribute = TextAttribute::Bullet(*enable); + let attribute = BuildInTextAttribute::Bullet(*enable); let delta = document.format(*iv, attribute).unwrap(); tracing::debug!("Bullet delta: {}", delta.json_str()); @@ -313,18 +312,18 @@ impl Rng { }; match self.0.gen_range(0.0..1.0) { f if f < 0.2 => { - delta.insert(&self.gen_string(i), TextAttributes::default()); + delta.insert(&self.gen_string(i), Attributes::default()); } f if f < 0.4 => { delta.delete(i); } _ => { - delta.retain(i, TextAttributes::default()); + delta.retain(i, Attributes::default()); } } } if self.0.gen_range(0.0..1.0) < 0.3 { - delta.insert(&("1".to_owned() + &self.gen_string(10)), TextAttributes::default()); + delta.insert(&("1".to_owned() + &self.gen_string(10)), Attributes::default()); } delta } diff --git a/frontend/rust-lib/flowy-text-block/tests/editor/op_test.rs b/frontend/rust-lib/flowy-text-block/tests/editor/op_test.rs index 5bd4df0bda..3676bce207 100644 --- a/frontend/rust-lib/flowy-text-block/tests/editor/op_test.rs +++ b/frontend/rust-lib/flowy-text-block/tests/editor/op_test.rs @@ -1,12 +1,8 @@ #![allow(clippy::all)] use crate::editor::{Rng, TestBuilder, TestOp::*}; -use flowy_sync::client_document::{NewlineDoc, PlainDoc}; +use flowy_sync::client_document::{EmptyDoc, NewlineDoc}; use lib_ot::text_delta::TextDeltaBuilder; -use lib_ot::{ - core::Interval, - core::*, - text_delta::{TextAttribute, TextAttributes, TextDelta, TextDeltaAttributeBuilder}, -}; +use lib_ot::{core::Interval, core::*, text_delta::TextDelta}; #[test] fn attributes_insert_text() { @@ -15,7 +11,7 @@ fn attributes_insert_text() { Insert(0, "456", 3), AssertDocJson(0, r#"[{"insert":"123456"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -25,7 +21,7 @@ fn attributes_insert_text_at_head() { Insert(0, "456", 0), AssertDocJson(0, r#"[{"insert":"456123"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -35,7 +31,7 @@ fn attributes_insert_text_at_middle() { Insert(0, "456", 1), AssertDocJson(0, r#"[{"insert":"145623"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -206,9 +202,9 @@ fn delta_utf16_code_unit_seek() { #[test] fn delta_utf16_code_unit_seek_with_attributes() { let mut delta = TextDelta::default(); - let attributes = TextDeltaAttributeBuilder::new() - .add_attr(TextAttribute::Bold(true)) - .add_attr(TextAttribute::Italic(true)) + let attributes = AttributeBuilder::new() + .insert("bold", true) + .insert("italic", true) .build(); delta.add(Operation::insert_with_attributes("1234", attributes.clone())); @@ -304,13 +300,13 @@ fn lengths() { let mut delta = TextDelta::default(); assert_eq!(delta.utf16_base_len, 0); assert_eq!(delta.utf16_target_len, 0); - delta.retain(5, TextAttributes::default()); + delta.retain(5, Attributes::default()); assert_eq!(delta.utf16_base_len, 5); assert_eq!(delta.utf16_target_len, 5); - delta.insert("abc", TextAttributes::default()); + delta.insert("abc", Attributes::default()); assert_eq!(delta.utf16_base_len, 5); assert_eq!(delta.utf16_target_len, 8); - delta.retain(2, TextAttributes::default()); + delta.retain(2, Attributes::default()); assert_eq!(delta.utf16_base_len, 7); assert_eq!(delta.utf16_target_len, 10); delta.delete(2); @@ -320,10 +316,10 @@ fn lengths() { #[test] fn sequence() { let mut delta = TextDelta::default(); - delta.retain(5, TextAttributes::default()); - delta.retain(0, TextAttributes::default()); - delta.insert("appflowy", TextAttributes::default()); - delta.insert("", TextAttributes::default()); + delta.retain(5, Attributes::default()); + delta.retain(0, Attributes::default()); + delta.insert("appflowy", Attributes::default()); + delta.insert("", Attributes::default()); delta.delete(3); delta.delete(0); assert_eq!(delta.ops.len(), 3); @@ -353,15 +349,15 @@ fn apply_test() { #[test] fn base_len_test() { let mut delta_a = TextDelta::default(); - delta_a.insert("a", TextAttributes::default()); - delta_a.insert("b", TextAttributes::default()); - delta_a.insert("c", TextAttributes::default()); + delta_a.insert("a", Attributes::default()); + delta_a.insert("b", Attributes::default()); + delta_a.insert("c", Attributes::default()); let s = "hello world,".to_owned(); delta_a.delete(s.len()); let after_a = delta_a.apply(&s).unwrap(); - delta_a.insert("d", TextAttributes::default()); + delta_a.insert("d", Attributes::default()); assert_eq!("abc", &after_a); } @@ -392,8 +388,8 @@ fn invert_test() { #[test] fn empty_ops() { let mut delta = TextDelta::default(); - delta.retain(0, TextAttributes::default()); - delta.insert("", TextAttributes::default()); + delta.retain(0, Attributes::default()); + delta.insert("", Attributes::default()); delta.delete(0); assert_eq!(delta.ops.len(), 0); } @@ -401,33 +397,33 @@ fn empty_ops() { fn eq() { let mut delta_a = TextDelta::default(); delta_a.delete(1); - delta_a.insert("lo", TextAttributes::default()); - delta_a.retain(2, TextAttributes::default()); - delta_a.retain(3, TextAttributes::default()); + delta_a.insert("lo", Attributes::default()); + delta_a.retain(2, Attributes::default()); + delta_a.retain(3, Attributes::default()); let mut delta_b = TextDelta::default(); delta_b.delete(1); - delta_b.insert("l", TextAttributes::default()); - delta_b.insert("o", TextAttributes::default()); - delta_b.retain(5, TextAttributes::default()); + delta_b.insert("l", Attributes::default()); + delta_b.insert("o", Attributes::default()); + delta_b.retain(5, Attributes::default()); assert_eq!(delta_a, delta_b); delta_a.delete(1); - delta_b.retain(1, TextAttributes::default()); + delta_b.retain(1, Attributes::default()); assert_ne!(delta_a, delta_b); } #[test] fn ops_merging() { let mut delta = TextDelta::default(); assert_eq!(delta.ops.len(), 0); - delta.retain(2, TextAttributes::default()); + delta.retain(2, Attributes::default()); assert_eq!(delta.ops.len(), 1); assert_eq!(delta.ops.last(), Some(&Operation::retain(2))); - delta.retain(3, TextAttributes::default()); + delta.retain(3, Attributes::default()); assert_eq!(delta.ops.len(), 1); assert_eq!(delta.ops.last(), Some(&Operation::retain(5))); - delta.insert("abc", TextAttributes::default()); + delta.insert("abc", Attributes::default()); assert_eq!(delta.ops.len(), 2); assert_eq!(delta.ops.last(), Some(&Operation::insert("abc"))); - delta.insert("xyz", TextAttributes::default()); + delta.insert("xyz", Attributes::default()); assert_eq!(delta.ops.len(), 2); assert_eq!(delta.ops.last(), Some(&Operation::insert("abcxyz"))); delta.delete(1); @@ -442,11 +438,11 @@ fn ops_merging() { fn is_noop() { let mut delta = TextDelta::default(); assert!(delta.is_noop()); - delta.retain(5, TextAttributes::default()); + delta.retain(5, Attributes::default()); assert!(delta.is_noop()); - delta.retain(3, TextAttributes::default()); + delta.retain(3, Attributes::default()); assert!(delta.is_noop()); - delta.insert("lorem", TextAttributes::default()); + delta.insert("lorem", Attributes::default()); assert!(!delta.is_noop()); } #[test] @@ -490,18 +486,13 @@ fn transform_random_delta() { fn transform_with_two_delta() { let mut a = TextDelta::default(); let mut a_s = String::new(); - a.insert( - "123", - TextDeltaAttributeBuilder::new() - .add_attr(TextAttribute::Bold(true)) - .build(), - ); + a.insert("123", AttributeBuilder::new().insert("bold", true).build()); a_s = a.apply(&a_s).unwrap(); assert_eq!(&a_s, "123"); let mut b = TextDelta::default(); let mut b_s = String::new(); - b.insert("456", TextAttributes::default()); + b.insert("456", Attributes::default()); b_s = b.apply(&b_s).unwrap(); assert_eq!(&b_s, "456"); @@ -537,7 +528,7 @@ fn transform_two_plain_delta() { AssertDocJson(0, r#"[{"insert":"123456"}]"#), AssertDocJson(1, r#"[{"insert":"123456"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -551,7 +542,7 @@ fn transform_two_plain_delta2() { AssertDocJson(0, r#"[{"insert":"123456"}]"#), AssertDocJson(1, r#"[{"insert":"123456"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -569,7 +560,7 @@ fn transform_two_non_seq_delta() { AssertDocJson(0, r#"[{"insert":"123456"}]"#), AssertDocJson(1, r#"[{"insert":"123456789"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -584,7 +575,7 @@ fn transform_two_conflict_non_seq_delta() { AssertDocJson(0, r#"[{"insert":"123456"}]"#), AssertDocJson(1, r#"[{"insert":"12378456"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -611,7 +602,7 @@ fn delta_invert_no_attribute_delta2() { Invert(0, 1), AssertDocJson(0, r#"[{"insert":"123"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -619,12 +610,12 @@ fn delta_invert_attribute_delta_with_no_attribute_delta() { let ops = vec![ Insert(0, "123", 0), Bold(0, Interval::new(0, 3), true), - AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":true}}]"#), Insert(1, "4567", 0), Invert(0, 1), - AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":true}}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -636,16 +627,16 @@ fn delta_invert_attribute_delta_with_no_attribute_delta2() { AssertDocJson( 0, r#"[ - {"insert":"123456","attributes":{"bold":"true"}}] + {"insert":"123456","attributes":{"bold":true}}] "#, ), Italic(0, Interval::new(2, 4), true), AssertDocJson( 0, r#"[ - {"insert":"12","attributes":{"bold":"true"}}, - {"insert":"34","attributes":{"bold":"true","italic":"true"}}, - {"insert":"56","attributes":{"bold":"true"}} + {"insert":"12","attributes":{"bold":true}}, + {"insert":"34","attributes":{"bold":true,"italic":true}}, + {"insert":"56","attributes":{"bold":true}} ]"#, ), Insert(1, "abc", 0), @@ -653,13 +644,13 @@ fn delta_invert_attribute_delta_with_no_attribute_delta2() { AssertDocJson( 0, r#"[ - {"insert":"12","attributes":{"bold":"true"}}, - {"insert":"34","attributes":{"bold":"true","italic":"true"}}, - {"insert":"56","attributes":{"bold":"true"}} + {"insert":"12","attributes":{"bold":true}}, + {"insert":"34","attributes":{"bold":true,"italic":true}}, + {"insert":"56","attributes":{"bold":true}} ]"#, ), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -668,11 +659,11 @@ fn delta_invert_no_attribute_delta_with_attribute_delta() { Insert(0, "123", 0), Insert(1, "4567", 0), Bold(1, Interval::new(0, 3), true), - AssertDocJson(1, r#"[{"insert":"456","attributes":{"bold":"true"}},{"insert":"7"}]"#), + AssertDocJson(1, r#"[{"insert":"456","attributes":{"bold":true}},{"insert":"7"}]"#), Invert(0, 1), AssertDocJson(0, r#"[{"insert":"123"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -686,12 +677,12 @@ fn delta_invert_no_attribute_delta_with_attribute_delta2() { Italic(1, Interval::new(1, 3), true), AssertDocJson( 1, - r#"[{"insert":"a","attributes":{"bold":"true"}},{"insert":"bc","attributes":{"bold":"true","italic":"true"}},{"insert":"d","attributes":{"bold":"true"}}]"#, + r#"[{"insert":"a","attributes":{"bold":true}},{"insert":"bc","attributes":{"bold":true,"italic":true}},{"insert":"d","attributes":{"bold":true}}]"#, ), Invert(0, 1), AssertDocJson(0, r#"[{"insert":"123"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -700,14 +691,14 @@ fn delta_invert_attribute_delta_with_attribute_delta() { Insert(0, "123", 0), Bold(0, Interval::new(0, 3), true), Insert(0, "456", 3), - AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#), + AssertDocJson(0, r#"[{"insert":"123456","attributes":{"bold":true}}]"#), Italic(0, Interval::new(2, 4), true), AssertDocJson( 0, r#"[ - {"insert":"12","attributes":{"bold":"true"}}, - {"insert":"34","attributes":{"bold":"true","italic":"true"}}, - {"insert":"56","attributes":{"bold":"true"}} + {"insert":"12","attributes":{"bold":true}}, + {"insert":"34","attributes":{"bold":true,"italic":true}}, + {"insert":"56","attributes":{"bold":true}} ]"#, ), Insert(1, "abc", 0), @@ -717,22 +708,22 @@ fn delta_invert_attribute_delta_with_attribute_delta() { AssertDocJson( 1, r#"[ - {"insert":"a","attributes":{"bold":"true"}}, - {"insert":"bc","attributes":{"bold":"true","italic":"true"}}, - {"insert":"d","attributes":{"bold":"true"}} + {"insert":"a","attributes":{"bold":true}}, + {"insert":"bc","attributes":{"bold":true,"italic":true}}, + {"insert":"d","attributes":{"bold":true}} ]"#, ), Invert(0, 1), AssertDocJson( 0, r#"[ - {"insert":"12","attributes":{"bold":"true"}}, - {"insert":"34","attributes":{"bold":"true","italic":"true"}}, - {"insert":"56","attributes":{"bold":"true"}} + {"insert":"12","attributes":{"bold":true}}, + {"insert":"34","attributes":{"bold":true,"italic":true}}, + {"insert":"56","attributes":{"bold":true}} ]"#, ), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] diff --git a/frontend/rust-lib/flowy-text-block/tests/editor/serde_test.rs b/frontend/rust-lib/flowy-text-block/tests/editor/serde_test.rs index c1f99c83b6..3609f90fff 100644 --- a/frontend/rust-lib/flowy-text-block/tests/editor/serde_test.rs +++ b/frontend/rust-lib/flowy-text-block/tests/editor/serde_test.rs @@ -1,21 +1,21 @@ -use flowy_sync::client_document::{ClientDocument, PlainDoc}; -use lib_ot::text_delta::RichTextOperation; +use flowy_sync::client_document::{ClientDocument, EmptyDoc}; +use lib_ot::text_delta::TextOperation; use lib_ot::{ core::*, - text_delta::{TextAttribute, TextAttributeValue, TextDelta, TextDeltaAttributeBuilder}, + text_delta::{BuildInTextAttribute, TextDelta}, }; #[test] fn operation_insert_serialize_test() { - let attributes = TextDeltaAttributeBuilder::new() - .add_attr(TextAttribute::Bold(true)) - .add_attr(TextAttribute::Italic(true)) + let attributes = AttributeBuilder::new() + .insert("bold", true) + .insert("italic", true) .build(); let operation = Operation::insert_with_attributes("123", attributes); let json = serde_json::to_string(&operation).unwrap(); eprintln!("{}", json); - let insert_op: RichTextOperation = serde_json::from_str(&json).unwrap(); + let insert_op: TextOperation = serde_json::from_str(&json).unwrap(); assert_eq!(insert_op, operation); } @@ -24,23 +24,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: RichTextOperation = serde_json::from_str(&json).unwrap(); + let insert_op: TextOperation = serde_json::from_str(&json).unwrap(); assert_eq!(insert_op, operation); } #[test] fn operation_delete_serialize_test() { - let operation = RichTextOperation::Delete(2); + let operation = TextOperation::Delete(2); let json = serde_json::to_string(&operation).unwrap(); - let insert_op: RichTextOperation = serde_json::from_str(&json).unwrap(); + let insert_op: TextOperation = serde_json::from_str(&json).unwrap(); assert_eq!(insert_op, operation); } #[test] fn attributes_serialize_test() { - let attributes = TextDeltaAttributeBuilder::new() - .add_attr(TextAttribute::Bold(true)) - .add_attr(TextAttribute::Italic(true)) + let attributes = AttributeBuilder::new() + .insert_entry(BuildInTextAttribute::Bold(true)) + .insert_entry(BuildInTextAttribute::Italic(true)) .build(); let retain = Operation::insert_with_attributes("123", attributes); @@ -52,9 +52,9 @@ fn attributes_serialize_test() { fn delta_serialize_multi_attribute_test() { let mut delta = Operations::default(); - let attributes = TextDeltaAttributeBuilder::new() - .add_attr(TextAttribute::Bold(true)) - .add_attr(TextAttribute::Italic(true)) + let attributes = AttributeBuilder::new() + .insert_entry(BuildInTextAttribute::Bold(true)) + .insert_entry(BuildInTextAttribute::Italic(true)) .build(); let retain = Operation::insert_with_attributes("123", attributes); @@ -74,7 +74,7 @@ fn delta_deserialize_test() { let json = r#"[ {"retain":2,"attributes":{"italic":true}}, {"retain":2,"attributes":{"italic":123}}, - {"retain":2,"attributes":{"italic":"true","bold":"true"}}, + {"retain":2,"attributes":{"italic":true,"bold":true}}, {"retain":2,"attributes":{"italic":true,"bold":true}} ]"#; let delta = TextDelta::from_json(json).unwrap(); @@ -88,26 +88,20 @@ fn delta_deserialize_null_test() { ]"#; let delta1 = TextDelta::from_json(json).unwrap(); - let mut attribute = TextAttribute::Bold(true); - attribute.value = TextAttributeValue(None); + let mut attribute = BuildInTextAttribute::Bold(true); + attribute.remove_value(); + let delta2 = OperationBuilder::new() .retain_with_attributes(7, attribute.into()) .build(); - assert_eq!(delta2.json_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#); + assert_eq!(delta2.json_str(), r#"[{"retain":7,"attributes":{"bold":null}}]"#); assert_eq!(delta1, delta2); } -#[test] -fn delta_serde_null_test() { - let mut attribute = TextAttribute::Bold(true); - attribute.value = TextAttributeValue(None); - assert_eq!(attribute.to_json(), r#"{"bold":""}"#); -} - #[test] fn document_insert_serde_test() { - let mut document = ClientDocument::new::(); + let mut document = ClientDocument::new::(); document.insert(0, "\n").unwrap(); document.insert(0, "123").unwrap(); let json = document.delta_str(); diff --git a/frontend/rust-lib/flowy-text-block/tests/editor/undo_redo_test.rs b/frontend/rust-lib/flowy-text-block/tests/editor/undo_redo_test.rs index 78aa034744..4810de202a 100644 --- a/frontend/rust-lib/flowy-text-block/tests/editor/undo_redo_test.rs +++ b/frontend/rust-lib/flowy-text-block/tests/editor/undo_redo_test.rs @@ -1,5 +1,5 @@ use crate::editor::{TestBuilder, TestOp::*}; -use flowy_sync::client_document::{NewlineDoc, PlainDoc, RECORD_THRESHOLD}; +use flowy_sync::client_document::{EmptyDoc, NewlineDoc, RECORD_THRESHOLD}; use lib_ot::core::{Interval, NEW_LINE, WHITESPACE}; #[test] @@ -85,7 +85,7 @@ fn history_bold_redo() { Undo(0), AssertDocJson(0, r#"[{"insert":"\n"}]"#), Redo(0), - AssertDocJson(0, r#" [{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"}]"#), + AssertDocJson(0, r#" [{"insert":"123","attributes":{"bold":true}},{"insert":"\n"}]"#), ]; TestBuilder::new().run_scripts::(ops); } @@ -99,7 +99,7 @@ fn history_bold_redo_with_lagging() { Undo(0), AssertDocJson(0, r#"[{"insert":"123\n"}]"#), Redo(0), - AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"}]"#), + AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":true}},{"insert":"\n"}]"#), ]; TestBuilder::new().run_scripts::(ops); } @@ -115,7 +115,7 @@ fn history_delete_undo() { Undo(0), AssertDocJson(0, r#"[{"insert":"123"}]"#), ]; - TestBuilder::new().run_scripts::(ops); + TestBuilder::new().run_scripts::(ops); } #[test] @@ -127,7 +127,7 @@ fn history_delete_undo_2() { AssertDocJson( 0, r#"[ - {"insert":"23","attributes":{"bold":"true"}}, + {"insert":"23","attributes":{"bold":true}}, {"insert":"\n"}] "#, ), @@ -148,7 +148,7 @@ fn history_delete_undo_with_lagging() { AssertDocJson( 0, r#"[ - {"insert":"23","attributes":{"bold":"true"}}, + {"insert":"23","attributes":{"bold":true}}, {"insert":"\n"}] "#, ), @@ -156,7 +156,7 @@ fn history_delete_undo_with_lagging() { AssertDocJson( 0, r#"[ - {"insert":"123","attributes":{"bold":"true"}}, + {"insert":"123","attributes":{"bold":true}}, {"insert":"\n"}] "#, ), @@ -188,7 +188,7 @@ fn history_replace_undo() { 0, r#"[ {"insert":"ab"}, - {"insert":"3","attributes":{"bold":"true"}},{"insert":"\n"}] + {"insert":"3","attributes":{"bold":true}},{"insert":"\n"}] "#, ), Undo(0), @@ -209,11 +209,11 @@ fn history_replace_undo_with_lagging() { 0, r#"[ {"insert":"ab"}, - {"insert":"3","attributes":{"bold":"true"}},{"insert":"\n"}] + {"insert":"3","attributes":{"bold":true}},{"insert":"\n"}] "#, ), Undo(0), - AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"}]"#), + AssertDocJson(0, r#"[{"insert":"123","attributes":{"bold":true}},{"insert":"\n"}]"#), ]; TestBuilder::new().run_scripts::(ops); } @@ -230,7 +230,7 @@ fn history_replace_redo() { 0, r#"[ {"insert":"ab"}, - {"insert":"3","attributes":{"bold":"true"}},{"insert":"\n"}] + {"insert":"3","attributes":{"bold":true}},{"insert":"\n"}] "#, ), ]; diff --git a/shared-lib/flowy-sync/src/client_document/document_pad.rs b/shared-lib/flowy-sync/src/client_document/document_pad.rs index 0f5a79d095..e15c9beb61 100644 --- a/shared-lib/flowy-sync/src/client_document/document_pad.rs +++ b/shared-lib/flowy-sync/src/client_document/document_pad.rs @@ -7,18 +7,15 @@ use crate::{ errors::CollaborateError, }; use bytes::Bytes; -use lib_ot::{ - core::*, - text_delta::{TextAttribute, TextDelta}, -}; +use lib_ot::{core::*, text_delta::TextDelta}; use tokio::sync::mpsc; pub trait InitialDocumentText { fn initial_delta() -> TextDelta; } -pub struct PlainDoc(); -impl InitialDocumentText for PlainDoc { +pub struct EmptyDoc(); +impl InitialDocumentText for EmptyDoc { fn initial_delta() -> TextDelta { TextDelta::new() } @@ -141,9 +138,9 @@ impl ClientDocument { Ok(delete) } - pub fn format(&mut self, interval: Interval, attribute: TextAttribute) -> Result { + pub fn format(&mut self, interval: Interval, attribute: AttributeEntry) -> Result { let _ = validate_interval(&self.delta, &interval)?; - tracing::trace!("format {} with {}", interval, attribute); + tracing::trace!("format {} with {:?}", interval, attribute); let format_delta = self.view.format(&self.delta, attribute, interval).unwrap(); self.compose_delta(format_delta.clone())?; Ok(format_delta) diff --git a/shared-lib/flowy-sync/src/client_document/extensions/delete/preserve_line_format_merge.rs b/shared-lib/flowy-sync/src/client_document/extensions/delete/preserve_line_format_merge.rs index 47bcc29ac4..4148ee4531 100644 --- a/shared-lib/flowy-sync/src/client_document/extensions/delete/preserve_line_format_merge.rs +++ b/shared-lib/flowy-sync/src/client_document/extensions/delete/preserve_line_format_merge.rs @@ -1,7 +1,7 @@ use crate::{client_document::DeleteExt, util::is_newline}; use lib_ot::{ core::{Interval, OperationAttributes, OperationBuilder, OperationIterator, Utf16CodeUnitMetric, NEW_LINE}, - text_delta::{plain_attributes, TextDelta}, + text_delta::{empty_attributes, TextDelta}, }; pub struct PreserveLineFormatOnMerge {} @@ -37,18 +37,18 @@ impl DeleteExt for PreserveLineFormatOnMerge { // match op.get_data().find(NEW_LINE) { None => { - new_delta.retain(op.len(), plain_attributes()); + new_delta.retain(op.len(), empty_attributes()); continue; } Some(line_break) => { let mut attributes = op.get_attributes(); - attributes.mark_all_as_removed_except(None); + attributes.remove_all_value(); if newline_op.has_attribute() { attributes.extend_other(newline_op.get_attributes()); } - new_delta.retain(line_break, plain_attributes()); + new_delta.retain(line_break, empty_attributes()); new_delta.retain(1, attributes); break; } diff --git a/shared-lib/flowy-sync/src/client_document/extensions/format/resolve_block_format.rs b/shared-lib/flowy-sync/src/client_document/extensions/format/resolve_block_format.rs index 5c290cc6f3..26d0562687 100644 --- a/shared-lib/flowy-sync/src/client_document/extensions/format/resolve_block_format.rs +++ b/shared-lib/flowy-sync/src/client_document/extensions/format/resolve_block_format.rs @@ -1,6 +1,8 @@ +use lib_ot::core::AttributeEntry; +use lib_ot::text_delta::is_block; use lib_ot::{ core::{Interval, OperationBuilder, OperationIterator}, - text_delta::{plain_attributes, AttributeScope, TextAttribute, TextDelta}, + text_delta::{empty_attributes, AttributeScope, TextDelta}, }; use crate::{ @@ -14,8 +16,8 @@ impl FormatExt for ResolveBlockFormat { "ResolveBlockFormat" } - fn apply(&self, delta: &TextDelta, interval: Interval, attribute: &TextAttribute) -> Option { - if attribute.scope != AttributeScope::Block { + fn apply(&self, delta: &TextDelta, interval: Interval, attribute: &AttributeEntry) -> Option { + if !is_block(&attribute.key) { return None; } @@ -26,7 +28,7 @@ impl FormatExt for ResolveBlockFormat { while start < end && iter.has_next() { let next_op = iter.next_op_with_len(end - start).unwrap(); match find_newline(next_op.get_data()) { - None => new_delta.retain(next_op.len(), plain_attributes()), + None => new_delta.retain(next_op.len(), empty_attributes()), Some(_) => { let tmp_delta = line_break(&next_op, attribute, AttributeScope::Block); new_delta.extend(tmp_delta); @@ -40,9 +42,9 @@ impl FormatExt for ResolveBlockFormat { let op = iter.next_op().expect("Unexpected None, iter.has_next() must return op"); match find_newline(op.get_data()) { - None => new_delta.retain(op.len(), plain_attributes()), + None => new_delta.retain(op.len(), empty_attributes()), Some(line_break) => { - new_delta.retain(line_break, plain_attributes()); + new_delta.retain(line_break, empty_attributes()); new_delta.retain(1, attribute.clone().into()); break; } diff --git a/shared-lib/flowy-sync/src/client_document/extensions/format/resolve_inline_format.rs b/shared-lib/flowy-sync/src/client_document/extensions/format/resolve_inline_format.rs index be94706502..c4e7086715 100644 --- a/shared-lib/flowy-sync/src/client_document/extensions/format/resolve_inline_format.rs +++ b/shared-lib/flowy-sync/src/client_document/extensions/format/resolve_inline_format.rs @@ -1,6 +1,8 @@ +use lib_ot::core::AttributeEntry; +use lib_ot::text_delta::is_inline; use lib_ot::{ core::{Interval, OperationBuilder, OperationIterator}, - text_delta::{AttributeScope, TextAttribute, TextDelta}, + text_delta::{AttributeScope, TextDelta}, }; use crate::{ @@ -14,8 +16,8 @@ impl FormatExt for ResolveInlineFormat { "ResolveInlineFormat" } - fn apply(&self, delta: &TextDelta, interval: Interval, attribute: &TextAttribute) -> Option { - if attribute.scope != AttributeScope::Inline { + fn apply(&self, delta: &TextDelta, interval: Interval, attribute: &AttributeEntry) -> Option { + if !is_inline(&attribute.key) { return None; } let mut new_delta = OperationBuilder::new().retain(interval.start).build(); diff --git a/shared-lib/flowy-sync/src/client_document/extensions/helper.rs b/shared-lib/flowy-sync/src/client_document/extensions/helper.rs index d706044387..547eab71e1 100644 --- a/shared-lib/flowy-sync/src/client_document/extensions/helper.rs +++ b/shared-lib/flowy-sync/src/client_document/extensions/helper.rs @@ -1,7 +1,8 @@ use crate::util::find_newline; -use lib_ot::text_delta::{plain_attributes, AttributeScope, RichTextOperation, TextAttribute, TextDelta}; +use lib_ot::core::AttributeEntry; +use lib_ot::text_delta::{empty_attributes, AttributeScope, TextDelta, TextOperation}; -pub(crate) fn line_break(op: &RichTextOperation, attribute: &TextAttribute, scope: AttributeScope) -> TextDelta { +pub(crate) fn line_break(op: &TextOperation, attribute: &AttributeEntry, scope: AttributeScope) -> TextDelta { let mut new_delta = TextDelta::new(); let mut start = 0; let end = op.len(); @@ -11,10 +12,10 @@ pub(crate) fn line_break(op: &RichTextOperation, attribute: &TextAttribute, scop match scope { AttributeScope::Inline => { new_delta.retain(line_break - start, attribute.clone().into()); - new_delta.retain(1, plain_attributes()); + new_delta.retain(1, empty_attributes()); } AttributeScope::Block => { - new_delta.retain(line_break - start, plain_attributes()); + new_delta.retain(line_break - start, empty_attributes()); new_delta.retain(1, attribute.clone().into()); } _ => { @@ -29,7 +30,7 @@ pub(crate) fn line_break(op: &RichTextOperation, attribute: &TextAttribute, scop if start < end { match scope { AttributeScope::Inline => new_delta.retain(end - start, attribute.clone().into()), - AttributeScope::Block => new_delta.retain(end - start, plain_attributes()), + AttributeScope::Block => new_delta.retain(end - start, empty_attributes()), _ => log::error!("Unsupported parser line break for {:?}", scope), } } diff --git a/shared-lib/flowy-sync/src/client_document/extensions/insert/auto_exit_block.rs b/shared-lib/flowy-sync/src/client_document/extensions/insert/auto_exit_block.rs index a9a078cf7c..1a7ce78690 100644 --- a/shared-lib/flowy-sync/src/client_document/extensions/insert/auto_exit_block.rs +++ b/shared-lib/flowy-sync/src/client_document/extensions/insert/auto_exit_block.rs @@ -1,6 +1,6 @@ use crate::{client_document::InsertExt, util::is_newline}; use lib_ot::core::{is_empty_line_at_index, OperationBuilder, OperationIterator}; -use lib_ot::text_delta::{attributes_except_header, TextAttributeKey, TextDelta}; +use lib_ot::text_delta::{attributes_except_header, BuildInTextAttributeKey, TextDelta}; pub struct AutoExitBlock {} @@ -42,7 +42,7 @@ impl InsertExt for AutoExitBlock { } } - attributes.mark_all_as_removed_except(Some(TextAttributeKey::Header)); + attributes.retain_values(&[BuildInTextAttributeKey::Header.as_ref()]); Some( OperationBuilder::new() diff --git a/shared-lib/flowy-sync/src/client_document/extensions/insert/auto_format.rs b/shared-lib/flowy-sync/src/client_document/extensions/insert/auto_format.rs index 6a08c49e62..b2deae600d 100644 --- a/shared-lib/flowy-sync/src/client_document/extensions/insert/auto_format.rs +++ b/shared-lib/flowy-sync/src/client_document/extensions/insert/auto_format.rs @@ -1,7 +1,8 @@ use crate::{client_document::InsertExt, util::is_whitespace}; +use lib_ot::core::Attributes; use lib_ot::{ core::{count_utf16_code_units, OperationBuilder, OperationIterator}, - text_delta::{plain_attributes, TextAttribute, TextAttributes, TextDelta}, + text_delta::{empty_attributes, BuildInTextAttribute, TextDelta}, }; use std::cmp::min; use url::Url; @@ -36,7 +37,7 @@ impl InsertExt for AutoFormatExt { }); let next_attributes = match iter.next_op() { - None => plain_attributes(), + None => empty_attributes(), Some(op) => op.get_attributes(), }; @@ -60,9 +61,9 @@ pub enum AutoFormatter { } impl AutoFormatter { - pub fn to_attributes(&self) -> TextAttributes { + pub fn to_attributes(&self) -> Attributes { match self { - AutoFormatter::Url(url) => TextAttribute::Link(url.as_str()).into(), + AutoFormatter::Url(url) => BuildInTextAttribute::Link(url.as_str()).into(), } } diff --git a/shared-lib/flowy-sync/src/client_document/extensions/insert/default_insert.rs b/shared-lib/flowy-sync/src/client_document/extensions/insert/default_insert.rs index e88cb2c500..b8be2ad5d3 100644 --- a/shared-lib/flowy-sync/src/client_document/extensions/insert/default_insert.rs +++ b/shared-lib/flowy-sync/src/client_document/extensions/insert/default_insert.rs @@ -1,7 +1,8 @@ use crate::client_document::InsertExt; +use lib_ot::core::Attributes; use lib_ot::{ core::{OperationAttributes, OperationBuilder, OperationIterator, NEW_LINE}, - text_delta::{TextAttributeKey, TextAttributes, TextDelta}, + text_delta::{BuildInTextAttributeKey, TextDelta}, }; pub struct DefaultInsertAttribute {} @@ -12,7 +13,7 @@ impl InsertExt for DefaultInsertAttribute { fn apply(&self, delta: &TextDelta, replace_len: usize, text: &str, index: usize) -> Option { let iter = OperationIterator::new(delta); - let mut attributes = TextAttributes::new(); + let mut attributes = Attributes::new(); // Enable each line split by "\n" remains the block attributes. for example: // insert "\n" to "123456" at index 3 @@ -23,7 +24,10 @@ impl InsertExt for DefaultInsertAttribute { match iter.last() { None => {} Some(op) => { - if op.get_attributes().contains_key(&TextAttributeKey::Header) { + if op + .get_attributes() + .contains_key(BuildInTextAttributeKey::Header.as_ref()) + { attributes.extend_other(op.get_attributes()); } } diff --git a/shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_block_format.rs b/shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_block_format.rs index f0ce938ec8..1b8ba41399 100644 --- a/shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_block_format.rs +++ b/shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_block_format.rs @@ -1,9 +1,8 @@ use crate::{client_document::InsertExt, util::is_newline}; +use lib_ot::core::Attributes; use lib_ot::{ core::{OperationBuilder, OperationIterator, NEW_LINE}, - text_delta::{ - attributes_except_header, plain_attributes, TextAttribute, TextAttributeKey, TextAttributes, TextDelta, - }, + text_delta::{attributes_except_header, empty_attributes, BuildInTextAttributeKey, TextDelta}, }; pub struct PreserveBlockFormatOnInsert {} @@ -27,16 +26,16 @@ impl InsertExt for PreserveBlockFormatOnInsert { return None; } - let mut reset_attribute = TextAttributes::new(); - if newline_attributes.contains_key(&TextAttributeKey::Header) { - reset_attribute.add(TextAttribute::Header(1)); + let mut reset_attribute = Attributes::new(); + if newline_attributes.contains_key(BuildInTextAttributeKey::Header.as_ref()) { + reset_attribute.insert(BuildInTextAttributeKey::Header, 1); } let lines: Vec<_> = text.split(NEW_LINE).collect(); let mut new_delta = OperationBuilder::new().retain(index + replace_len).build(); lines.iter().enumerate().for_each(|(i, line)| { if !line.is_empty() { - new_delta.insert(line, plain_attributes()); + new_delta.insert(line, empty_attributes()); } if i == 0 { @@ -48,9 +47,9 @@ impl InsertExt for PreserveBlockFormatOnInsert { } }); if !reset_attribute.is_empty() { - new_delta.retain(offset, plain_attributes()); + new_delta.retain(offset, empty_attributes()); let len = newline_op.get_data().find(NEW_LINE).unwrap(); - new_delta.retain(len, plain_attributes()); + new_delta.retain(len, empty_attributes()); new_delta.retain(1, reset_attribute); } diff --git a/shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_inline_format.rs b/shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_inline_format.rs index 8d7fd877f7..94890b198b 100644 --- a/shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_inline_format.rs +++ b/shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_inline_format.rs @@ -4,7 +4,7 @@ use crate::{ }; use lib_ot::{ core::{OpNewline, OperationBuilder, OperationIterator, NEW_LINE}, - text_delta::{plain_attributes, TextAttributeKey, TextDelta}, + text_delta::{empty_attributes, BuildInTextAttributeKey, TextDelta}, }; pub struct PreserveInlineFormat {} @@ -25,7 +25,7 @@ impl InsertExt for PreserveInlineFormat { } let mut attributes = prev.get_attributes(); - if attributes.is_empty() || !attributes.contains_key(&TextAttributeKey::Link) { + if attributes.is_empty() || !attributes.contains_key(BuildInTextAttributeKey::Link.as_ref()) { return Some( OperationBuilder::new() .retain(index + replace_len) @@ -36,10 +36,10 @@ impl InsertExt for PreserveInlineFormat { let next = iter.next_op(); match &next { - None => attributes = plain_attributes(), + None => attributes = empty_attributes(), Some(next) => { if OpNewline::parse(next).is_equal() { - attributes = plain_attributes(); + attributes = empty_attributes(); } } } @@ -77,11 +77,11 @@ impl InsertExt for PreserveLineFormatOnSplit { } let mut new_delta = TextDelta::new(); - new_delta.retain(index + replace_len, plain_attributes()); + new_delta.retain(index + replace_len, empty_attributes()); if newline_status.is_contain() { debug_assert!(!next.has_attribute()); - new_delta.insert(NEW_LINE, plain_attributes()); + new_delta.insert(NEW_LINE, empty_attributes()); return Some(new_delta); } diff --git a/shared-lib/flowy-sync/src/client_document/extensions/insert/reset_format_on_new_line.rs b/shared-lib/flowy-sync/src/client_document/extensions/insert/reset_format_on_new_line.rs index fe91c0e3f1..0043ed89d0 100644 --- a/shared-lib/flowy-sync/src/client_document/extensions/insert/reset_format_on_new_line.rs +++ b/shared-lib/flowy-sync/src/client_document/extensions/insert/reset_format_on_new_line.rs @@ -1,7 +1,8 @@ use crate::{client_document::InsertExt, util::is_newline}; +use lib_ot::core::Attributes; use lib_ot::{ core::{OperationBuilder, OperationIterator, Utf16CodeUnitMetric, NEW_LINE}, - text_delta::{TextAttributeKey, TextAttributes, TextDelta}, + text_delta::{BuildInTextAttributeKey, TextDelta}, }; pub struct ResetLineFormatOnNewLine {} @@ -22,9 +23,12 @@ impl InsertExt for ResetLineFormatOnNewLine { return None; } - let mut reset_attribute = TextAttributes::new(); - if next_op.get_attributes().contains_key(&TextAttributeKey::Header) { - reset_attribute.delete(&TextAttributeKey::Header); + let mut reset_attribute = Attributes::new(); + if next_op + .get_attributes() + .contains_key(BuildInTextAttributeKey::Header.as_ref()) + { + reset_attribute.remove_value(BuildInTextAttributeKey::Header); } let len = index + replace_len; diff --git a/shared-lib/flowy-sync/src/client_document/extensions/mod.rs b/shared-lib/flowy-sync/src/client_document/extensions/mod.rs index f79424e82e..1933663422 100644 --- a/shared-lib/flowy-sync/src/client_document/extensions/mod.rs +++ b/shared-lib/flowy-sync/src/client_document/extensions/mod.rs @@ -1,10 +1,8 @@ pub use delete::*; pub use format::*; pub use insert::*; -use lib_ot::{ - core::Interval, - text_delta::{TextAttribute, TextDelta}, -}; +use lib_ot::core::AttributeEntry; +use lib_ot::{core::Interval, text_delta::TextDelta}; mod delete; mod format; @@ -22,7 +20,7 @@ pub trait InsertExt { pub trait FormatExt { fn ext_name(&self) -> &str; - fn apply(&self, delta: &TextDelta, interval: Interval, attribute: &TextAttribute) -> Option; + fn apply(&self, delta: &TextDelta, interval: Interval, attribute: &AttributeEntry) -> Option; } pub trait DeleteExt { diff --git a/shared-lib/flowy-sync/src/client_document/view.rs b/shared-lib/flowy-sync/src/client_document/view.rs index 5e32056302..6646d0f4e2 100644 --- a/shared-lib/flowy-sync/src/client_document/view.rs +++ b/shared-lib/flowy-sync/src/client_document/view.rs @@ -1,8 +1,9 @@ use crate::client_document::*; +use lib_ot::core::AttributeEntry; use lib_ot::{ core::{trim, Interval}, errors::{ErrorBuilder, OTError, OTErrorCode}, - text_delta::{TextAttribute, TextDelta}, + text_delta::TextDelta, }; pub const RECORD_THRESHOLD: usize = 400; // in milliseconds @@ -27,7 +28,7 @@ impl ViewExtensions { for ext in &self.insert_exts { if let Some(mut delta) = ext.apply(delta, interval.size(), text, interval.start) { trim(&mut delta); - tracing::debug!("[{} extension]: process: {}", ext.ext_name(), delta); + tracing::trace!("[{}] applied, delta: {}", ext.ext_name(), delta); new_delta = Some(delta); break; } @@ -44,7 +45,7 @@ impl ViewExtensions { for ext in &self.delete_exts { if let Some(mut delta) = ext.apply(delta, interval) { trim(&mut delta); - tracing::trace!("[{}]: applied, delta: {}", ext.ext_name(), delta); + tracing::trace!("[{}] applied, delta: {}", ext.ext_name(), delta); new_delta = Some(delta); break; } @@ -59,14 +60,14 @@ impl ViewExtensions { pub(crate) fn format( &self, delta: &TextDelta, - attribute: TextAttribute, + attribute: AttributeEntry, interval: Interval, ) -> Result { let mut new_delta = None; for ext in &self.format_exts { if let Some(mut delta) = ext.apply(delta, interval, &attribute) { trim(&mut delta); - tracing::trace!("[{}]: applied, delta: {}", ext.ext_name(), delta); + tracing::trace!("[{}] applied, delta: {}", ext.ext_name(), delta); new_delta = Some(delta); break; } diff --git a/shared-lib/flowy-sync/src/server_document/document_manager.rs b/shared-lib/flowy-sync/src/server_document/document_manager.rs index ea1bfab8b7..95a25bb6da 100644 --- a/shared-lib/flowy-sync/src/server_document/document_manager.rs +++ b/shared-lib/flowy-sync/src/server_document/document_manager.rs @@ -11,7 +11,8 @@ use async_stream::stream; use dashmap::DashMap; use futures::stream::StreamExt; use lib_infra::future::BoxResultFuture; -use lib_ot::text_delta::{TextAttributes, TextDelta}; +use lib_ot::core::Attributes; +use lib_ot::text_delta::TextDelta; use std::{collections::HashMap, fmt::Debug, sync::Arc}; use tokio::{ sync::{mpsc, oneshot, RwLock}, @@ -198,7 +199,7 @@ impl std::ops::Drop for ServerDocumentManager { } } -type DocumentRevisionSynchronizer = RevisionSynchronizer; +type DocumentRevisionSynchronizer = RevisionSynchronizer; struct OpenDocumentHandler { doc_id: String, diff --git a/shared-lib/flowy-sync/src/server_document/document_pad.rs b/shared-lib/flowy-sync/src/server_document/document_pad.rs index 20a7a774f4..7a46a8b432 100644 --- a/shared-lib/flowy-sync/src/server_document/document_pad.rs +++ b/shared-lib/flowy-sync/src/server_document/document_pad.rs @@ -1,8 +1,5 @@ use crate::{client_document::InitialDocumentText, errors::CollaborateError, synchronizer::RevisionSyncObject}; -use lib_ot::{ - core::*, - text_delta::{TextAttributes, TextDelta}, -}; +use lib_ot::{core::*, text_delta::TextDelta}; pub struct ServerDocument { doc_id: String, @@ -21,7 +18,7 @@ impl ServerDocument { } } -impl RevisionSyncObject for ServerDocument { +impl RevisionSyncObject for ServerDocument { fn id(&self) -> &str { &self.doc_id } @@ -42,7 +39,7 @@ impl RevisionSyncObject for ServerDocument { self.delta.json_str() } - fn set_delta(&mut self, new_delta: Operations) { + fn set_delta(&mut self, new_delta: Operations) { self.delta = new_delta; } } diff --git a/shared-lib/lib-ot/src/codec/markdown/markdown_encoder.rs b/shared-lib/lib-ot/src/codec/markdown/markdown_encoder.rs index 833a691960..8e4c5eadb8 100644 --- a/shared-lib/lib-ot/src/codec/markdown/markdown_encoder.rs +++ b/shared-lib/lib-ot/src/codec/markdown/markdown_encoder.rs @@ -1,6 +1,8 @@ -use crate::core::{OperationIterator, Operations}; -use crate::text_delta::{is_block, TextAttributeKey, TextAttributeValue, TextAttributes}; +use crate::core::{AttributeKey, AttributeValue, Attributes, OperationIterator, Operations}; +use crate::text_delta::{is_block, TextAttributeKey}; use std::collections::HashMap; +use std::str::FromStr; +use strum_macros::EnumString; const LINEFEEDASCIICODE: i32 = 0x0A; @@ -98,14 +100,14 @@ mod tests { } struct Attribute { - key: TextAttributeKey, - value: TextAttributeValue, + key: AttributeKey, + value: AttributeValue, } -pub fn markdown_encoder(delta: &Operations) -> String { +pub fn markdown_encoder(delta: &Operations) -> String { let mut markdown_buffer = String::new(); let mut line_buffer = String::new(); - let mut current_inline_style = TextAttributes::default(); + let mut current_inline_style = Attributes::default(); let mut current_block_lines: Vec = Vec::new(); let mut iterator = OperationIterator::new(delta); let mut current_block_style: Option = None; @@ -137,23 +139,20 @@ pub fn markdown_encoder(delta: &Operations) -> String { markdown_buffer } -fn handle_inline( - current_inline_style: &mut TextAttributes, - buffer: &mut String, - mut text: String, - attributes: TextAttributes, -) { - let mut marked_for_removal: HashMap = HashMap::new(); +fn handle_inline(current_inline_style: &mut Attributes, buffer: &mut String, mut text: String, attributes: Attributes) { + let mut marked_for_removal: HashMap = HashMap::new(); for key in current_inline_style .clone() .keys() - .collect::>() + .collect::>() .into_iter() .rev() { - if is_block(key) { - continue; + if let Some(attribute) = TextAttributeKey::from_str(key) { + if is_block(&attribute) { + continue; + } } if attributes.contains_key(key) { @@ -161,7 +160,7 @@ fn handle_inline( } let padding = trim_right(buffer); - write_attribute(buffer, key, current_inline_style.get(key).unwrap(), true); + write_attribute(buffer, key, current_inline_style.get(key.as_ref()).unwrap(), true); if !padding.is_empty() { buffer.push_str(&padding) } @@ -175,8 +174,10 @@ fn handle_inline( } for (key, value) in attributes.iter() { - if is_block(key) { - continue; + if let Some(attribute) = TextAttributeKey::from_str(key) { + if is_block(&attribute) { + continue; + } } if current_inline_style.contains_key(key) { continue; @@ -205,7 +206,8 @@ fn trim_right(buffer: &mut String) -> String { " ".repeat(text.len() - result.len()) } -fn write_attribute(buffer: &mut String, key: &TextAttributeKey, value: &TextAttributeValue, close: bool) { +fn write_attribute(buffer: &mut String, key: &AttributeKey, value: &AttributeValue, close: bool) { + let key = TextAttributeKey::from_str(key); match key { TextAttributeKey::Bold => buffer.push_str("**"), TextAttributeKey::Italic => buffer.push('_'), @@ -247,10 +249,10 @@ fn handle_line( buffer: &mut String, markdown_buffer: &mut String, data: String, - attributes: TextAttributes, + attributes: Attributes, current_block_style: &mut Option, current_block_lines: &mut Vec, - current_inline_style: &mut TextAttributes, + current_inline_style: &mut Attributes, ) { let mut span = String::new(); for c in data.chars() { @@ -258,14 +260,15 @@ fn handle_line( if !span.is_empty() { handle_inline(current_inline_style, buffer, span.clone(), attributes.clone()); } - handle_inline( - current_inline_style, - buffer, - String::from(""), - TextAttributes::default(), - ); + handle_inline(current_inline_style, buffer, String::from(""), Attributes::default()); - let line_block_key = attributes.keys().find(|key| is_block(*key)); + let line_block_key = attributes.keys().find(|key| { + if let Some(attribute) = TextAttributeKey::from_str(key) { + is_block(&attribute) + } else { + false + } + }); match (line_block_key, ¤t_block_style) { (Some(line_block_key), Some(current_block_style)) @@ -321,7 +324,7 @@ fn handle_block( markdown_buffer.push_str(¤t_block_lines.join("\n")); markdown_buffer.push('\n'); } - Some(block_style) if block_style.key == TextAttributeKey::CodeBlock => { + Some(block_style) if block_style.key == AttributeKey::CodeBlock => { write_attribute(markdown_buffer, &block_style.key, &block_style.value, false); markdown_buffer.push_str(¤t_block_lines.join("\n")); write_attribute(markdown_buffer, &block_style.key, &block_style.value, true); @@ -342,9 +345,9 @@ fn write_block_tag(buffer: &mut String, block: &Attribute, close: bool) { return; } - if block.key == TextAttributeKey::BlockQuote { + if block.key == AttributeKey::BlockQuote { buffer.push_str("> "); - } else if block.key == TextAttributeKey::List { + } else if block.key == AttributeKey::List { if block.value.0.as_ref().unwrap().eq("bullet") { buffer.push_str("* "); } else if block.value.0.as_ref().unwrap().eq("checked") { @@ -356,14 +359,14 @@ fn write_block_tag(buffer: &mut String, block: &Attribute, close: bool) { } else { buffer.push_str("* "); } - } else if block.key == TextAttributeKey::Header { + } else if block.key == AttributeKey::Header { if block.value.0.as_ref().unwrap().eq("1") { buffer.push_str("# "); } else if block.value.0.as_ref().unwrap().eq("2") { buffer.push_str("## "); } else if block.value.0.as_ref().unwrap().eq("3") { buffer.push_str("### "); - } else if block.key == TextAttributeKey::List { + } else if block.key == AttributeKey::List { } } } diff --git a/shared-lib/lib-ot/src/codec/markdown/mod.rs b/shared-lib/lib-ot/src/codec/markdown/mod.rs index 5a276efda3..c555505482 100644 --- a/shared-lib/lib-ot/src/codec/markdown/mod.rs +++ b/shared-lib/lib-ot/src/codec/markdown/mod.rs @@ -1 +1 @@ -pub mod markdown_encoder; +// pub mod markdown_encoder; diff --git a/shared-lib/lib-ot/src/core/attributes/attribute.rs b/shared-lib/lib-ot/src/core/attributes/attribute.rs index 850ee5a8d7..119be9fcb7 100644 --- a/shared-lib/lib-ot/src/core/attributes/attribute.rs +++ b/shared-lib/lib-ot/src/core/attributes/attribute.rs @@ -1,10 +1,33 @@ -use crate::core::OperationTransform; +use crate::core::{OperationAttributes, OperationTransform}; use crate::errors::OTError; use serde::{Deserialize, Serialize}; -use serde_repr::*; use std::collections::HashMap; +use std::fmt; +use std::fmt::Display; + pub type AttributeMap = HashMap; +#[derive(Debug, Clone)] +pub struct AttributeEntry { + pub key: AttributeKey, + pub value: AttributeValue, +} + +impl AttributeEntry { + pub fn remove_value(&mut self) { + self.value.ty = None; + self.value.value = None; + } +} + +impl std::convert::From for Attributes { + fn from(entry: AttributeEntry) -> Self { + let mut attributes = Attributes::new(); + attributes.insert_entry(entry); + attributes + } +} + #[derive(Default, Clone, Serialize, Deserialize, Eq, PartialEq, Debug)] pub struct Attributes(AttributeMap); @@ -35,12 +58,76 @@ impl Attributes { self.0.insert(key.to_string(), value.into()); } + pub fn insert_entry(&mut self, entry: AttributeEntry) { + self.insert(entry.key, entry.value) + } + + /// Set the key's value to None + pub fn remove_value>(&mut self, key: K) { + // if let Some(mut_value) = self.0.get_mut(key.as_ref()) { + // mut_value.value = None; + // } + self.insert(key.as_ref().to_string(), AttributeValue::none()); + } + + /// Set all key's value to None + pub fn remove_all_value(&mut self) { + self.0.iter_mut().for_each(|(_, v)| { + *v = AttributeValue::none(); + }) + } + + pub fn retain_values(&mut self, retain_keys: &[&str]) { + self.0.iter_mut().for_each(|(k, v)| { + if !retain_keys.contains(&k.as_str()) { + *v = AttributeValue::none(); + } + }) + } + + pub fn remove_key>(&mut self, key: K) { + self.0.remove(key.as_ref()); + } + + /// Create a new key/value map 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) { + if other.is_none() { + return; + } + + let mut new_attributes = other.unwrap().0; + self.0.iter().for_each(|(k, v)| { + new_attributes.insert(k.clone(), v.clone()); + }); + self.0 = new_attributes; + } + pub fn is_empty(&self) -> bool { self.0.is_empty() } +} - pub fn delete(&mut self, key: K) { - self.insert(key.to_string(), AttributeValue::empty()); +impl Display for Attributes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for (key, value) in self.0.iter() { + let _ = f.write_str(&format!("{:?}:{:?}", key, value))?; + } + Ok(()) + } +} + +impl OperationAttributes for Attributes { + fn is_empty(&self) -> bool { + self.is_empty() + } + + fn remove_empty(&mut self) { + self.retain(|_, v| v.value.is_some()); + } + + fn extend_other(&mut self, other: Self) { + self.0.extend(other.0); } } @@ -85,7 +172,7 @@ impl OperationTransform for Attributes { self.iter().fold(base_inverted, |mut attributes, (k, _)| { if other.get(k) != self.get(k) && !other.contains_key(k) { - attributes.delete(k); + attributes.remove_value(k); } attributes }) @@ -94,8 +181,7 @@ impl OperationTransform for Attributes { pub type AttributeKey = String; -#[derive(Eq, PartialEq, Hash, Debug, Clone, Serialize_repr, Deserialize_repr)] -#[repr(u8)] +#[derive(Eq, PartialEq, Hash, Debug, Clone)] pub enum ValueType { IntType = 0, FloatType = 1, @@ -105,41 +191,40 @@ pub enum ValueType { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AttributeValue { - pub ty: ValueType, + pub ty: Option, pub value: Option, } impl AttributeValue { - pub fn empty() -> Self { - Self { - ty: ValueType::StrType, - value: None, - } + pub fn none() -> Self { + Self { ty: None, value: None } } pub fn from_int(val: usize) -> Self { + let value = if val > 0_usize { Some(val.to_string()) } else { None }; Self { - ty: ValueType::IntType, - value: Some(val.to_string()), + ty: Some(ValueType::IntType), + value, } } pub fn from_float(val: f64) -> Self { Self { - ty: ValueType::FloatType, + ty: Some(ValueType::FloatType), value: Some(val.to_string()), } } pub fn from_bool(val: bool) -> Self { + let value = if val { Some(val.to_string()) } else { None }; Self { - ty: ValueType::BoolType, - value: Some(val.to_string()), + ty: Some(ValueType::BoolType), + value, } } pub fn from_string(s: &str) -> Self { let value = if s.is_empty() { None } else { Some(s.to_string()) }; Self { - ty: ValueType::StrType, + ty: Some(ValueType::StrType), value, } } @@ -170,6 +255,24 @@ impl std::convert::From for AttributeValue { } } +impl std::convert::From for AttributeValue { + fn from(value: usize) -> Self { + AttributeValue::from_int(value) + } +} + +impl std::convert::From<&str> for AttributeValue { + fn from(value: &str) -> Self { + AttributeValue::from_string(value) + } +} + +impl std::convert::From for AttributeValue { + fn from(value: String) -> Self { + AttributeValue::from_string(&value) + } +} + #[derive(Default)] pub struct AttributeBuilder { attributes: Attributes, @@ -185,8 +288,13 @@ impl AttributeBuilder { self } - pub fn delete(mut self, key: K) -> Self { - self.attributes.delete(key); + pub fn insert_entry(mut self, entry: AttributeEntry) -> Self { + self.attributes.insert_entry(entry); + self + } + + pub fn delete>(mut self, key: K) -> Self { + self.attributes.remove_value(key); self } diff --git a/shared-lib/lib-ot/src/core/attributes/attribute_serde.rs b/shared-lib/lib-ot/src/core/attributes/attribute_serde.rs index c1bcfb06b1..dd953b7ecd 100644 --- a/shared-lib/lib-ot/src/core/attributes/attribute_serde.rs +++ b/shared-lib/lib-ot/src/core/attributes/attribute_serde.rs @@ -1,4 +1,5 @@ use crate::core::attributes::AttributeValue; + use serde::{ de::{self, MapAccess, Visitor}, Deserialize, Deserializer, Serialize, Serializer, @@ -10,36 +11,38 @@ impl Serialize for AttributeValue { where S: Serializer, { - match self.ty { - super::ValueType::IntType => { - // - if let Some(value) = self.int_value() { - serializer.serialize_i64(value) - } else { - serializer.serialize_none() + match &self.ty { + None => serializer.serialize_none(), + Some(ty) => match ty { + super::ValueType::IntType => { + if let Some(value) = self.int_value() { + serializer.serialize_i64(value) + } else { + serializer.serialize_none() + } } - } - super::ValueType::FloatType => { - if let Some(value) = self.float_value() { - serializer.serialize_f64(value) - } else { - serializer.serialize_none() + super::ValueType::FloatType => { + if let Some(value) = self.float_value() { + serializer.serialize_f64(value) + } else { + serializer.serialize_none() + } } - } - super::ValueType::StrType => { - if let Some(value) = self.str_value() { - serializer.serialize_str(&value) - } else { - serializer.serialize_none() + super::ValueType::StrType => { + if let Some(value) = self.str_value() { + serializer.serialize_str(&value) + } else { + serializer.serialize_none() + } } - } - super::ValueType::BoolType => { - if let Some(value) = self.bool_value() { - serializer.serialize_bool(value) - } else { - serializer.serialize_none() + super::ValueType::BoolType => { + if let Some(value) = self.bool_value() { + serializer.serialize_bool(value) + } else { + serializer.serialize_none() + } } - } + }, } } } @@ -130,7 +133,7 @@ impl<'de> Deserialize<'de> for AttributeValue { where E: de::Error, { - Ok(AttributeValue::empty()) + Ok(AttributeValue::none()) } fn visit_unit(self) -> Result @@ -138,7 +141,7 @@ impl<'de> Deserialize<'de> for AttributeValue { E: de::Error, { // the value that contains null will be processed here. - Ok(AttributeValue::empty()) + Ok(AttributeValue::none()) } fn visit_map(self, map: A) -> Result diff --git a/shared-lib/lib-ot/src/core/delta/builder.rs b/shared-lib/lib-ot/src/core/delta/builder.rs index aeda055258..e00cab532e 100644 --- a/shared-lib/lib-ot/src/core/delta/builder.rs +++ b/shared-lib/lib-ot/src/core/delta/builder.rs @@ -52,9 +52,9 @@ where /// # Examples /// /// ``` - /// use lib_ot::text_delta::{TextAttribute, TextDelta, TextDeltaBuilder}; + /// use lib_ot::text_delta::{BuildInTextAttribute, TextDelta, TextDeltaBuilder}; /// - /// let mut attribute = TextAttribute::Bold(true); + /// let mut attribute = BuildInTextAttribute::Bold(true); /// let delta = TextDeltaBuilder::new().retain_with_attributes(7, attribute.into()).build(); /// /// assert_eq!(delta.json_str(), r#"[{"retain":7,"attributes":{"bold":true}}]"#); @@ -111,7 +111,7 @@ where /// /// ``` /// use lib_ot::core::{OperationTransform, DeltaBuilder}; - /// use lib_ot::text_delta::{TextAttribute, TextDeltaBuilder}; + /// use lib_ot::text_delta::{BuildInTextAttribute, TextDeltaBuilder}; /// let delta = DeltaBuilder::new() /// .retain(3) /// .trim() @@ -119,7 +119,7 @@ where /// assert_eq!(delta.ops.len(), 0); /// /// let delta = TextDeltaBuilder::new() - /// .retain_with_attributes(3, TextAttribute::Bold(true).into()) + /// .retain_with_attributes(3, BuildInTextAttribute::Bold(true).into()) /// .trim() /// .build(); /// assert_eq!(delta.ops.len(), 1); diff --git a/shared-lib/lib-ot/src/core/delta/iterator.rs b/shared-lib/lib-ot/src/core/delta/iterator.rs index 4e9bbee0eb..9063a67bfd 100644 --- a/shared-lib/lib-ot/src/core/delta/iterator.rs +++ b/shared-lib/lib-ot/src/core/delta/iterator.rs @@ -2,7 +2,8 @@ use super::cursor::*; use crate::core::delta::operation::{Operation, OperationAttributes}; use crate::core::delta::{Operations, NEW_LINE}; use crate::core::interval::Interval; -use crate::text_delta::TextAttributes; +use crate::core::Attributes; + use std::ops::{Deref, DerefMut}; pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize; @@ -132,7 +133,7 @@ where } } -pub fn is_empty_line_at_index(delta: &Operations, index: usize) -> bool { +pub fn is_empty_line_at_index(delta: &Operations, index: usize) -> bool { let mut iter = OperationIterator::new(delta); let (prev, next) = (iter.next_op_with_len(index), iter.next_op()); if prev.is_none() { diff --git a/shared-lib/lib-ot/src/core/delta/operation/builder.rs b/shared-lib/lib-ot/src/core/delta/operation/builder.rs index 8c8afe6144..864bc39775 100644 --- a/shared-lib/lib-ot/src/core/delta/operation/builder.rs +++ b/shared-lib/lib-ot/src/core/delta/operation/builder.rs @@ -1,7 +1,6 @@ use crate::core::delta::operation::{EmptyAttributes, Operation, OperationAttributes}; -use crate::text_delta::TextAttributes; -pub type RichTextOpBuilder = OperationsBuilder; +// pub type RichTextOpBuilder = OperationsBuilder; pub type PlainTextOpBuilder = OperationsBuilder; #[derive(Default)] diff --git a/shared-lib/lib-ot/src/core/delta/ops.rs b/shared-lib/lib-ot/src/core/delta/ops.rs index e342b79b66..29e8e0f5ed 100644 --- a/shared-lib/lib-ot/src/core/delta/ops.rs +++ b/shared-lib/lib-ot/src/core/delta/ops.rs @@ -576,7 +576,7 @@ where /// {"retain":7,"attributes":{"bold":null}} /// ]"#; /// let delta = TextDelta::from_json(json).unwrap(); - /// assert_eq!(delta.json_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#); + /// assert_eq!(delta.json_str(), r#"[{"retain":7,"attributes":{"bold":null}}]"#); /// ``` pub fn from_json(json: &str) -> Result { let delta = serde_json::from_str(json).map_err(|e| { diff --git a/shared-lib/lib-ot/src/text_delta/attribute_builder.rs b/shared-lib/lib-ot/src/text_delta/attribute_builder.rs deleted file mode 100644 index 5fe0b2028f..0000000000 --- a/shared-lib/lib-ot/src/text_delta/attribute_builder.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![allow(non_snake_case)] -#![allow(clippy::derivable_impls)] -use crate::text_delta::{TextAttribute, TextAttributes}; - -pub struct TextDeltaAttributeBuilder { - inner: TextAttributes, -} - -impl std::default::Default for TextDeltaAttributeBuilder { - fn default() -> Self { - Self { - inner: TextAttributes::default(), - } - } -} - -impl TextDeltaAttributeBuilder { - pub fn new() -> Self { - TextDeltaAttributeBuilder::default() - } - - pub fn add_attr(mut self, attribute: TextAttribute) -> Self { - self.inner.add(attribute); - self - } - - pub fn build(self) -> TextAttributes { - self.inner - } -} diff --git a/shared-lib/lib-ot/src/text_delta/attributes.rs b/shared-lib/lib-ot/src/text_delta/attributes.rs index c8279ba78d..388f492bdf 100644 --- a/shared-lib/lib-ot/src/text_delta/attributes.rs +++ b/shared-lib/lib-ot/src/text_delta/attributes.rs @@ -1,253 +1,58 @@ #![allow(non_snake_case)] -use crate::core::{Operation, OperationAttributes, OperationTransform}; -use crate::{block_attribute, errors::OTError, ignore_attribute, inline_attribute, list_attribute}; +use crate::core::{AttributeEntry, AttributeKey, Attributes}; +use crate::text_delta::TextOperation; +use crate::{inline_attribute_entry, inline_list_attribute_entry}; use lazy_static::lazy_static; -use std::{ - collections::{HashMap, HashSet}, - fmt, - fmt::Formatter, - iter::FromIterator, -}; -use strum_macros::Display; - -pub type RichTextOperation = Operation; -impl RichTextOperation { - pub fn contain_attribute(&self, attribute: &TextAttribute) -> bool { - self.get_attributes().contains_key(&attribute.key) - } -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct TextAttributes { - pub(crate) inner: HashMap, -} - -impl std::default::Default for TextAttributes { - fn default() -> Self { - Self { - inner: HashMap::with_capacity(0), - } - } -} - -impl fmt::Display for TextAttributes { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("{:?}", self.inner)) - } -} +use std::str::FromStr; +use std::{collections::HashSet, iter::FromIterator}; +use strum_macros::{AsRefStr, Display, EnumString}; #[inline(always)] -pub fn plain_attributes() -> TextAttributes { - TextAttributes::default() +pub fn empty_attributes() -> Attributes { + Attributes::default() } -impl TextAttributes { - pub fn new() -> Self { - TextAttributes { inner: HashMap::new() } - } - - pub fn is_empty(&self) -> bool { - self.inner.is_empty() - } - - pub fn add(&mut self, attribute: TextAttribute) { - let TextAttribute { key, value, scope: _ } = attribute; - self.inner.insert(key, value); - } - - pub fn insert(&mut self, key: TextAttributeKey, value: TextAttributeValue) { - self.inner.insert(key, value); - } - - pub fn delete(&mut self, key: &TextAttributeKey) { - self.inner.insert(key.clone(), TextAttributeValue(None)); - } - - 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); - } - Some(attribute) => { - self.inner.iter_mut().for_each(|(k, v)| { - if k != &attribute { - v.0 = None; - } - }); - } - } - } - - pub fn remove(&mut self, key: TextAttributeKey) { - self.inner.retain(|k, _| k != &key); - } - - // 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) { - if other.is_none() { - return; - } - - let mut new_attributes = other.unwrap().inner; - self.inner.iter().for_each(|(k, v)| { - new_attributes.insert(k.clone(), v.clone()); - }); - self.inner = new_attributes; - } -} - -impl OperationAttributes for TextAttributes { - 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 OperationTransform for TextAttributes { - fn compose(&self, other: &Self) -> Result - where - Self: Sized, - { - let mut attributes = self.clone(); - attributes.extend_other(other.clone()); - Ok(attributes) - } - - fn transform(&self, other: &Self) -> Result<(Self, Self), OTError> - where - Self: Sized, - { - let a = self.iter().fold(TextAttributes::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(TextAttributes::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(TextAttributes::new(), |mut attributes, (k, v)| { - if other.get(k) != self.get(k) && self.contains_key(k) { - attributes.insert(k.clone(), v.clone()); - } - attributes - }); - - let inverted = self.iter().fold(base_inverted, |mut attributes, (k, _)| { - if other.get(k) != self.get(k) && !other.contains_key(k) { - attributes.delete(k); - } - attributes - }); - - inverted - } -} - -impl std::ops::Deref for TextAttributes { - type Target = HashMap; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl std::ops::DerefMut for TextAttributes { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -pub fn attributes_except_header(op: &RichTextOperation) -> TextAttributes { +pub fn attributes_except_header(op: &TextOperation) -> Attributes { let mut attributes = op.get_attributes(); - attributes.remove(TextAttributeKey::Header); + attributes.remove_key(BuildInTextAttributeKey::Header); attributes } #[derive(Debug, Clone)] -pub struct TextAttribute { - pub key: TextAttributeKey, - pub value: TextAttributeValue, - pub scope: AttributeScope, -} +pub struct BuildInTextAttribute(); -impl TextAttribute { - // inline - inline_attribute!(Bold, bool); - inline_attribute!(Italic, bool); - inline_attribute!(Underline, bool); - inline_attribute!(StrikeThrough, bool); - inline_attribute!(Link, &str); - inline_attribute!(Color, String); - inline_attribute!(Font, usize); - inline_attribute!(Size, usize); - inline_attribute!(Background, String); - inline_attribute!(InlineCode, bool); +impl BuildInTextAttribute { + inline_attribute_entry!(Bold, bool); + inline_attribute_entry!(Italic, bool); + inline_attribute_entry!(Underline, bool); + inline_attribute_entry!(StrikeThrough, bool); + inline_attribute_entry!(Link, &str); + inline_attribute_entry!(Color, String); + inline_attribute_entry!(Font, usize); + inline_attribute_entry!(Size, usize); + inline_attribute_entry!(Background, String); + inline_attribute_entry!(InlineCode, bool); - // block - block_attribute!(Header, usize); - block_attribute!(Indent, usize); - block_attribute!(Align, String); - block_attribute!(List, &str); - block_attribute!(CodeBlock, bool); - block_attribute!(BlockQuote, bool); + inline_attribute_entry!(Header, usize); + inline_attribute_entry!(Indent, usize); + inline_attribute_entry!(Align, String); + inline_attribute_entry!(List, &str); + inline_attribute_entry!(CodeBlock, bool); + inline_attribute_entry!(BlockQuote, bool); - // ignore - ignore_attribute!(Width, usize); - ignore_attribute!(Height, usize); + inline_attribute_entry!(Width, usize); + inline_attribute_entry!(Height, usize); // List extension - list_attribute!(Bullet, "bullet"); - list_attribute!(Ordered, "ordered"); - list_attribute!(Checked, "checked"); - list_attribute!(UnChecked, "unchecked"); - - pub fn to_json(&self) -> String { - match serde_json::to_string(self) { - Ok(json) => json, - Err(e) => { - log::error!("Attribute serialize to str failed: {}", e); - "".to_owned() - } - } - } + inline_list_attribute_entry!(Bullet, "bullet"); + inline_list_attribute_entry!(Ordered, "ordered"); + inline_list_attribute_entry!(Checked, "checked"); + inline_list_attribute_entry!(UnChecked, "unchecked"); } -impl fmt::Display for TextAttribute { - 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 TextAttributes { - fn from(attr: TextAttribute) -> Self { - let mut attributes = TextAttributes::new(); - attributes.add(attr); - attributes - } -} - -#[derive(Clone, Debug, Display, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -// #[serde(rename_all = "snake_case")] -pub enum TextAttributeKey { +#[derive(Clone, Debug, Display, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize, AsRefStr, EnumString)] +#[strum(serialize_all = "snake_case")] +pub enum BuildInTextAttributeKey { #[serde(rename = "bold")] Bold, #[serde(rename = "italic")] @@ -286,91 +91,45 @@ pub enum TextAttributeKey { Header, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct TextAttributeValue(pub Option); - -impl std::convert::From<&usize> for TextAttributeValue { - fn from(val: &usize) -> Self { - TextAttributeValue::from(*val) +pub fn is_block(k: &AttributeKey) -> bool { + if let Ok(key) = BuildInTextAttributeKey::from_str(k) { + BLOCK_KEYS.contains(&key) + } else { + false } } -impl std::convert::From for TextAttributeValue { - fn from(val: usize) -> Self { - if val > 0_usize { - TextAttributeValue(Some(format!("{}", val))) - } else { - TextAttributeValue(None) - } +pub fn is_inline(k: &AttributeKey) -> bool { + if let Ok(key) = BuildInTextAttributeKey::from_str(k) { + INLINE_KEYS.contains(&key) + } else { + false } } -impl std::convert::From<&str> for TextAttributeValue { - fn from(val: &str) -> Self { - val.to_owned().into() - } -} - -impl std::convert::From for TextAttributeValue { - fn from(val: String) -> Self { - if val.is_empty() { - TextAttributeValue(None) - } else { - TextAttributeValue(Some(val)) - } - } -} - -impl std::convert::From<&bool> for TextAttributeValue { - fn from(val: &bool) -> Self { - TextAttributeValue::from(*val) - } -} - -impl std::convert::From for TextAttributeValue { - fn from(val: bool) -> Self { - let val = match val { - true => Some("true".to_owned()), - false => None, - }; - TextAttributeValue(val) - } -} - -pub fn is_block_except_header(k: &TextAttributeKey) -> bool { - if k == &TextAttributeKey::Header { - return false; - } - BLOCK_KEYS.contains(k) -} - -pub fn is_block(k: &TextAttributeKey) -> bool { - BLOCK_KEYS.contains(k) -} - lazy_static! { - static ref BLOCK_KEYS: HashSet = HashSet::from_iter(vec![ - TextAttributeKey::Header, - TextAttributeKey::Indent, - TextAttributeKey::Align, - TextAttributeKey::CodeBlock, - TextAttributeKey::List, - TextAttributeKey::BlockQuote, + static ref BLOCK_KEYS: HashSet = HashSet::from_iter(vec![ + BuildInTextAttributeKey::Header, + BuildInTextAttributeKey::Indent, + BuildInTextAttributeKey::Align, + BuildInTextAttributeKey::CodeBlock, + BuildInTextAttributeKey::List, + BuildInTextAttributeKey::BlockQuote, ]); - static ref INLINE_KEYS: HashSet = HashSet::from_iter(vec![ - TextAttributeKey::Bold, - TextAttributeKey::Italic, - TextAttributeKey::Underline, - TextAttributeKey::StrikeThrough, - TextAttributeKey::Link, - TextAttributeKey::Color, - TextAttributeKey::Font, - TextAttributeKey::Size, - TextAttributeKey::Background, - TextAttributeKey::InlineCode, + static ref INLINE_KEYS: HashSet = HashSet::from_iter(vec![ + BuildInTextAttributeKey::Bold, + BuildInTextAttributeKey::Italic, + BuildInTextAttributeKey::Underline, + BuildInTextAttributeKey::StrikeThrough, + BuildInTextAttributeKey::Link, + BuildInTextAttributeKey::Color, + BuildInTextAttributeKey::Font, + BuildInTextAttributeKey::Size, + BuildInTextAttributeKey::Background, + BuildInTextAttributeKey::InlineCode, ]); - static ref INGORE_KEYS: HashSet = - HashSet::from_iter(vec![TextAttributeKey::Width, TextAttributeKey::Height,]); + static ref INGORE_KEYS: HashSet = + HashSet::from_iter(vec![BuildInTextAttributeKey::Width, BuildInTextAttributeKey::Height,]); } #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/shared-lib/lib-ot/src/text_delta/attributes_serde.rs b/shared-lib/lib-ot/src/text_delta/attributes_serde.rs deleted file mode 100644 index 8b4d56d171..0000000000 --- a/shared-lib/lib-ot/src/text_delta/attributes_serde.rs +++ /dev/null @@ -1,231 +0,0 @@ -#[rustfmt::skip] -use crate::text_delta::{TextAttribute, TextAttributeKey, TextAttributes, TextAttributeValue}; -use serde::{ - de, - de::{MapAccess, Visitor}, - ser::SerializeMap, - Deserialize, Deserializer, Serialize, Serializer, -}; -use std::fmt; - -impl Serialize for TextAttribute { - fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> - where - S: Serializer, - { - let mut map = serializer.serialize_map(Some(1))?; - let _ = serial_attribute(&mut map, &self.key, &self.value)?; - map.end() - } -} - -impl Serialize for TextAttributes { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - if self.is_empty() { - return serializer.serialize_none(); - } - - let mut map = serializer.serialize_map(Some(self.inner.len()))?; - for (k, v) in &self.inner { - let _ = serial_attribute(&mut map, k, v)?; - } - map.end() - } -} - -fn serial_attribute(map_serializer: &mut S, key: &TextAttributeKey, value: &TextAttributeValue) -> Result<(), E> -where - S: SerializeMap, - E: From<::Error>, -{ - if let Some(v) = &value.0 { - match key { - TextAttributeKey::Bold - | TextAttributeKey::Italic - | TextAttributeKey::Underline - | TextAttributeKey::StrikeThrough - | TextAttributeKey::CodeBlock - | TextAttributeKey::InlineCode - | TextAttributeKey::BlockQuote => match &v.parse::() { - Ok(value) => map_serializer.serialize_entry(&key, value)?, - Err(e) => log::error!("Serial {:?} failed. {:?}", &key, e), - }, - - TextAttributeKey::Font - | TextAttributeKey::Size - | TextAttributeKey::Header - | TextAttributeKey::Indent - | TextAttributeKey::Width - | TextAttributeKey::Height => match &v.parse::() { - Ok(value) => map_serializer.serialize_entry(&key, value)?, - Err(e) => log::error!("Serial {:?} failed. {:?}", &key, e), - }, - - TextAttributeKey::Link - | TextAttributeKey::Color - | TextAttributeKey::Background - | TextAttributeKey::Align - | TextAttributeKey::List => { - map_serializer.serialize_entry(&key, v)?; - } - } - } else { - map_serializer.serialize_entry(&key, "")?; - } - Ok(()) -} - -impl<'de> Deserialize<'de> for TextAttributes { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct AttributesVisitor; - impl<'de> Visitor<'de> for AttributesVisitor { - type Value = TextAttributes; - 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 = TextAttributes::new(); - while let Some(key) = map.next_key::()? { - let value = map.next_value::()?; - attributes.insert(key, value); - } - - Ok(attributes) - } - } - deserializer.deserialize_map(AttributesVisitor {}) - } -} - -impl Serialize for TextAttributeValue { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match &self.0 { - None => serializer.serialize_none(), - Some(val) => serializer.serialize_str(val), - } - } -} - -impl<'de> Deserialize<'de> for TextAttributeValue { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct AttributeValueVisitor; - impl<'de> Visitor<'de> for AttributeValueVisitor { - type Value = TextAttributeValue; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("bool, usize or string") - } - fn visit_bool(self, value: bool) -> Result - where - E: de::Error, - { - Ok(value.into()) - } - - fn visit_i8(self, value: i8) -> Result - where - E: de::Error, - { - Ok(TextAttributeValue(Some(format!("{}", value)))) - } - - fn visit_i16(self, value: i16) -> Result - where - E: de::Error, - { - Ok(TextAttributeValue(Some(format!("{}", value)))) - } - - fn visit_i32(self, value: i32) -> Result - where - E: de::Error, - { - Ok(TextAttributeValue(Some(format!("{}", value)))) - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - Ok(TextAttributeValue(Some(format!("{}", value)))) - } - - fn visit_u8(self, value: u8) -> Result - where - E: de::Error, - { - Ok(TextAttributeValue(Some(format!("{}", value)))) - } - - fn visit_u16(self, value: u16) -> Result - where - E: de::Error, - { - Ok(TextAttributeValue(Some(format!("{}", value)))) - } - - fn visit_u32(self, value: u32) -> Result - where - E: de::Error, - { - Ok(TextAttributeValue(Some(format!("{}", value)))) - } - - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - Ok(TextAttributeValue(Some(format!("{}", value)))) - } - - fn visit_str(self, s: &str) -> Result - where - E: de::Error, - { - Ok(s.into()) - } - - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(TextAttributeValue(None)) - } - - fn visit_unit(self) -> Result - where - E: de::Error, - { - // the value that contains null will be processed here. - Ok(TextAttributeValue(None)) - } - - fn visit_map(self, map: A) -> Result - where - A: MapAccess<'de>, - { - // https://github.com/serde-rs/json/issues/505 - let mut map = map; - let value = map.next_value::()?; - Ok(value) - } - } - - deserializer.deserialize_any(AttributeValueVisitor) - } -} diff --git a/shared-lib/lib-ot/src/text_delta/delta.rs b/shared-lib/lib-ot/src/text_delta/delta.rs index 8ab66b7e35..c559f9cb0f 100644 --- a/shared-lib/lib-ot/src/text_delta/delta.rs +++ b/shared-lib/lib-ot/src/text_delta/delta.rs @@ -1,5 +1,6 @@ -use crate::core::{OperationBuilder, Operations}; -use crate::text_delta::TextAttributes; +use crate::core::{Attributes, Operation, OperationBuilder, Operations}; -pub type TextDelta = Operations; -pub type TextDeltaBuilder = OperationBuilder; +pub type TextDelta = Operations; +pub type TextDeltaBuilder = OperationBuilder; + +pub type TextOperation = Operation; diff --git a/shared-lib/lib-ot/src/text_delta/macros.rs b/shared-lib/lib-ot/src/text_delta/macros.rs index c8c396427d..26a2ac0886 100644 --- a/shared-lib/lib-ot/src/text_delta/macros.rs +++ b/shared-lib/lib-ot/src/text_delta/macros.rs @@ -1,62 +1,33 @@ #[macro_export] -macro_rules! inline_attribute { +macro_rules! inline_attribute_entry { ( $key: ident, $value: ty ) => { - pub fn $key(value: $value) -> Self { - Self { - key: TextAttributeKey::$key, + pub fn $key(value: $value) -> crate::core::AttributeEntry { + AttributeEntry { + key: BuildInTextAttributeKey::$key.as_ref().to_string(), value: value.into(), - scope: AttributeScope::Inline, } } }; } #[macro_export] -macro_rules! block_attribute { - ( - $key: ident, - $value: ty - ) => { - pub fn $key(value: $value) -> Self { - Self { - key: TextAttributeKey::$key, - value: value.into(), - scope: AttributeScope::Block, - } - } - }; -} - -#[macro_export] -macro_rules! list_attribute { +macro_rules! inline_list_attribute_entry { ( $key: ident, $value: expr ) => { - pub fn $key(b: bool) -> Self { + pub fn $key(b: bool) -> crate::core::AttributeEntry { let value = match b { true => $value, false => "", }; - TextAttribute::List(value) - } - }; -} -#[macro_export] -macro_rules! ignore_attribute { - ( - $key: ident, - $value: ident - ) => { - pub fn $key(value: $value) -> Self { - Self { - key: TextAttributeKey::$key, + AttributeEntry { + key: BuildInTextAttributeKey::List.as_ref().to_string(), value: value.into(), - scope: AttributeScope::Ignore, } } }; diff --git a/shared-lib/lib-ot/src/text_delta/mod.rs b/shared-lib/lib-ot/src/text_delta/mod.rs index 959d0ab92e..06fd687b13 100644 --- a/shared-lib/lib-ot/src/text_delta/mod.rs +++ b/shared-lib/lib-ot/src/text_delta/mod.rs @@ -1,11 +1,8 @@ -mod attribute_builder; mod attributes; -mod attributes_serde; #[macro_use] mod macros; mod delta; -pub use attribute_builder::*; pub use attributes::*; pub use delta::*; diff --git a/shared-lib/lib-ot/tests/node/editor_test.rs b/shared-lib/lib-ot/tests/node/editor_test.rs index 5ace2b27c0..6136fe5efd 100644 --- a/shared-lib/lib-ot/tests/node/editor_test.rs +++ b/shared-lib/lib-ot/tests/node/editor_test.rs @@ -1,7 +1,8 @@ use super::script::{NodeScript::*, *}; +use lib_ot::core::AttributeBuilder; use lib_ot::{ core::{NodeData, Path}, - text_delta::{TextAttribute, TextDeltaAttributeBuilder, TextDeltaBuilder}, + text_delta::TextDeltaBuilder, }; #[test] @@ -14,15 +15,11 @@ fn editor_deserialize_node_test() { .insert("👋 ") .insert_with_attributes( "Welcome to ", - TextDeltaAttributeBuilder::new() - .add_attr(TextAttribute::Bold(true)) - .build(), + AttributeBuilder::new().insert("href", "appflowy.io").build(), ) .insert_with_attributes( "AppFlowy Editor", - TextDeltaAttributeBuilder::new() - .add_attr(TextAttribute::Italic(true)) - .build(), + AttributeBuilder::new().insert("italic", true).build(), ) .build(); @@ -81,7 +78,7 @@ const EXAMPLE_JSON: &str = r#" { "insert": "Welcome to ", "attributes": { - "bold": true + "href": "appflowy.io" } }, { diff --git a/shared-lib/lib-ot/tests/node/operation_test.rs b/shared-lib/lib-ot/tests/node/operation_test.rs index df98d46859..6a7f8bb25a 100644 --- a/shared-lib/lib-ot/tests/node/operation_test.rs +++ b/shared-lib/lib-ot/tests/node/operation_test.rs @@ -41,7 +41,7 @@ fn operation_update_node_attributes_serde_test() { assert_eq!( result, - r#"{"op":"update","path":[0,1],"attributes":{"bold":true},"oldAttributes":{"bold":false}}"# + r#"{"op":"update","path":[0,1],"attributes":{"bold":true},"oldAttributes":{"bold":null}}"# ); }