Browse Source

compare delta between flutter and rust

appflowy 3 years ago
parent
commit
87d3f4fc0d

+ 18 - 4
app_flowy/lib/workspace/domain/i_doc.dart

@@ -2,6 +2,7 @@ import 'dart:convert';
 
 import 'package:flowy_editor/flowy_editor.dart';
 import 'package:dartz/dartz.dart';
+// ignore: implementation_imports
 import 'package:flowy_editor/src/model/quill_delta.dart';
 import 'package:flowy_log/flowy_log.dart';
 import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart';
@@ -18,16 +19,29 @@ class FlowyDoc implements EditorChangesetSender {
   String get id => doc.id;
 
   @override
-  void sendDelta(Delta delta) {
-    final json = jsonEncode(delta.toJson());
+  void sendDelta(Delta changeset, Delta delta) async {
+    final json = jsonEncode(changeset.toJson());
     Log.debug("Send json: $json");
-    iDocImpl.applyChangeset(json: json);
+    final result = await iDocImpl.applyChangeset(json: json);
+
+    result.fold((rustDoc) {
+      // final json = utf8.decode(doc.data);
+      final rustDelta = Delta.fromJson(jsonDecode(utf8.decode(rustDoc.data)));
+
+      if (delta != rustDelta) {
+        Log.error("Receive : $rustDelta");
+        Log.error("Expected : $delta");
+      } else {
+        Log.info("Receive : $rustDelta");
+        Log.info("Expected : $delta");
+      }
+    }, (r) => null);
   }
 }
 
 abstract class IDoc {
   Future<Either<Doc, WorkspaceError>> readDoc();
   Future<Either<Unit, WorkspaceError>> saveDoc({String? json});
-  Future<Either<Unit, WorkspaceError>> applyChangeset({String? json});
+  Future<Either<Doc, WorkspaceError>> applyChangeset({String? json});
   Future<Either<Unit, WorkspaceError>> closeDoc();
 }

+ 1 - 1
app_flowy/lib/workspace/infrastructure/i_doc_impl.dart

@@ -33,7 +33,7 @@ class IDocImpl extends IDoc {
   }
 
   @override
-  Future<Either<Unit, WorkspaceError>> applyChangeset({String? json}) {
+  Future<Either<Doc, WorkspaceError>> applyChangeset({String? json}) {
     return repo.applyChangeset(data: _encodeText(json));
   }
 }

+ 1 - 1
app_flowy/lib/workspace/infrastructure/repos/doc_repo.dart

@@ -25,7 +25,7 @@ class DocRepository {
     return WorkspaceEventSaveViewData(request).send();
   }
 
-  Future<Either<Unit, WorkspaceError>> applyChangeset(
+  Future<Either<Doc, WorkspaceError>> applyChangeset(
       {required Uint8List data}) {
     final request = ApplyChangesetRequest.create()
       ..viewId = docId

+ 5 - 2
app_flowy/packages/flowy_editor/lib/src/model/document/document.dart

@@ -15,7 +15,7 @@ import 'node/node.dart';
 import 'package:flowy_log/flowy_log.dart';
 
 abstract class EditorChangesetSender {
-  void sendDelta(Delta delta);
+  void sendDelta(Delta changeset, Delta delta);
 }
 
 /// The rich text document
@@ -182,8 +182,11 @@ class Document {
     }
 
     try {
-      sender?.sendDelta(delta);
+      final changeset = delta;
+
       _delta = _delta.compose(delta);
+
+      sender?.sendDelta(changeset, _delta);
     } catch (e) {
       throw '_delta compose failed';
     }

+ 2 - 2
app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart

@@ -275,14 +275,14 @@ class WorkspaceEventApplyChangeset {
      ApplyChangesetRequest request;
      WorkspaceEventApplyChangeset(this.request);
 
-    Future<Either<Unit, WorkspaceError>> send() {
+    Future<Either<Doc, WorkspaceError>> send() {
     final request = FFIRequest.create()
           ..event = WorkspaceEvent.ApplyChangeset.toString()
           ..payload = requestToBytes(this.request);
 
     return Dispatch.asyncRequest(request)
         .then((bytesResult) => bytesResult.fold(
-           (bytes) => left(unit),
+           (okBytes) => left(Doc.fromBuffer(okBytes)),
            (errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
         ));
     }

+ 14 - 3
rust-lib/flowy-document/src/module.rs

@@ -9,6 +9,7 @@ use crate::{
 };
 use diesel::SqliteConnection;
 use flowy_database::ConnectionPool;
+use flowy_ot::client::Document;
 use std::sync::Arc;
 use tokio::sync::RwLock;
 
@@ -54,15 +55,25 @@ impl FlowyDocument {
         Ok(())
     }
 
-    pub async fn apply_changeset(&self, params: ApplyChangesetParams) -> Result<(), DocError> {
+    pub async fn apply_changeset(&self, params: ApplyChangesetParams) -> Result<Doc, DocError> {
         let _ = self
             .cache
             .mut_doc(&params.id, |doc| {
-                log::debug!("Document:{} applying delta", &params.id);
                 let _ = doc.apply_changeset(params.data.clone())?;
                 Ok(())
             })
             .await?;
-        Ok(())
+
+        let doc_str = match self.cache.read_doc(&params.id).await? {
+            None => "".to_owned(),
+            Some(doc_json) => doc_json,
+        };
+
+        let doc = Doc {
+            id: params.id,
+            data: doc_str.as_bytes().to_vec(),
+        };
+
+        Ok(doc)
     }
 }

+ 25 - 6
rust-lib/flowy-document/src/services/doc_cache.rs

@@ -11,7 +11,7 @@ use tokio::sync::RwLock;
 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
 pub struct DocId(pub(crate) String);
 
-pub struct DocInfo {
+pub struct OpenDocument {
     document: Document,
 }
 
@@ -23,7 +23,7 @@ where
 }
 
 pub(crate) struct DocCache {
-    inner: DashMap<DocId, RwLock<DocInfo>>,
+    inner: DashMap<DocId, RwLock<OpenDocument>>,
 }
 
 impl DocCache {
@@ -37,7 +37,7 @@ impl DocCache {
         let doc_id = id.into();
         let delta = data.try_into()?;
         let document = Document::from_delta(delta);
-        let doc_info = DocInfo { document };
+        let doc_info = OpenDocument { document };
         self.inner.insert(doc_id, RwLock::new(doc_info));
         Ok(())
     }
@@ -49,9 +49,7 @@ impl DocCache {
     {
         let doc_id = id.into();
         match self.inner.get(&doc_id) {
-            None => Err(ErrorBuilder::new(ErrorCode::DocNotfound)
-                .msg("Doc is close or you should call open first")
-                .build()),
+            None => Err(doc_not_found()),
             Some(doc_info) => {
                 let mut write_guard = doc_info.write().await;
                 f(&mut write_guard.document)
@@ -59,6 +57,21 @@ impl DocCache {
         }
     }
 
+    pub(crate) async fn read_doc<T>(&self, id: T) -> Result<Option<String>, DocError>
+    where
+        T: Into<DocId>,
+    {
+        let doc_id = id.into();
+        match self.inner.get(&doc_id) {
+            None => Err(doc_not_found()),
+            Some(doc_info) => {
+                let mut write_guard = doc_info.read().await;
+                let doc = &(*write_guard).document;
+                Ok(Some(doc.to_json()))
+            },
+        }
+    }
+
     pub(crate) fn close<T>(&self, id: T) -> Result<(), DocError>
     where
         T: Into<DocId>,
@@ -68,3 +81,9 @@ impl DocCache {
         Ok(())
     }
 }
+
+fn doc_not_found() -> DocError {
+    ErrorBuilder::new(ErrorCode::DocNotfound)
+        .msg("Doc is close or you should call open first")
+        .build()
+}

+ 15 - 15
rust-lib/flowy-ot/src/client/document/document.rs

@@ -51,14 +51,16 @@ impl Document {
 
     pub fn to_json(&self) -> String { self.delta.to_json() }
 
+    pub fn to_string(&self) -> String { self.delta.apply("").unwrap() }
+
     pub fn apply_changeset<T>(&mut self, changeset: T) -> Result<(), OTError>
     where
         T: TryInto<Delta, Error = OTError>,
     {
         let new_delta: Delta = changeset.try_into()?;
+        log::debug!("Apply delta: {}", new_delta);
         self.add_delta(&new_delta);
-
-        log::info!("Current delta: {:?}", self.to_json());
+        log::debug!("Current delta: {}", self.to_json());
         Ok(())
     }
 
@@ -68,7 +70,7 @@ impl Document {
 
         let text = data.into_string()?;
         let delta = self.view.insert(&self.delta, &text, interval)?;
-        log::debug!("👉 receive change: {}", delta);
+        log::trace!("👉 receive change: {}", delta);
         self.add_delta(&delta)?;
         Ok(delta)
     }
@@ -78,7 +80,7 @@ impl Document {
         debug_assert_eq!(interval.is_empty(), false);
         let delete = self.view.delete(&self.delta, interval)?;
         if !delete.is_empty() {
-            log::debug!("👉 receive change: {}", delete);
+            log::trace!("👉 receive change: {}", delete);
             let _ = self.add_delta(&delete)?;
         }
         Ok(delete)
@@ -86,10 +88,10 @@ impl Document {
 
     pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> {
         let _ = validate_interval(&self.delta, &interval)?;
-        log::debug!("format with {} at {}", attribute, interval);
+        log::trace!("format with {} at {}", attribute, interval);
         let format_delta = self.view.format(&self.delta, attribute.clone(), interval).unwrap();
 
-        log::debug!("👉 receive change: {}", format_delta);
+        log::trace!("👉 receive change: {}", format_delta);
         self.add_delta(&format_delta)?;
         Ok(())
     }
@@ -100,7 +102,7 @@ impl Document {
         let text = data.into_string()?;
         if !text.is_empty() {
             delta = self.view.insert(&self.delta, &text, interval)?;
-            log::debug!("👉 receive change: {}", delta);
+            log::trace!("👉 receive change: {}", delta);
             self.add_delta(&delta)?;
         }
 
@@ -144,8 +146,6 @@ impl Document {
         }
     }
 
-    pub fn to_string(&self) -> String { self.delta.apply("").unwrap() }
-
     pub fn data(&self) -> &Delta { &self.delta }
 
     pub fn set_data(&mut self, data: Delta) { self.delta = data; }
@@ -160,21 +160,21 @@ impl Document {
         let now = chrono::Utc::now().timestamp_millis() as usize;
         if now - self.last_edit_time < RECORD_THRESHOLD {
             if let Some(last_delta) = self.history.undo() {
-                log::debug!("compose previous change");
-                log::debug!("current = {}", undo_delta);
-                log::debug!("previous = {}", last_delta);
+                log::trace!("compose previous change");
+                log::trace!("current = {}", undo_delta);
+                log::trace!("previous = {}", last_delta);
                 undo_delta = undo_delta.compose(&last_delta)?;
             }
         } else {
             self.last_edit_time = now;
         }
 
-        log::debug!("👉 receive change undo: {}", undo_delta);
+        log::trace!("👉 receive change undo: {}", undo_delta);
         if !undo_delta.is_empty() {
             self.history.record(undo_delta);
         }
 
-        log::debug!("document delta: {}", &composed_delta);
+        log::trace!("document delta: {}", &composed_delta);
         self.delta = composed_delta;
         Ok(())
     }
@@ -183,7 +183,7 @@ impl Document {
         // c = a.compose(b)
         // d = b.invert(a)
         // a = c.compose(d)
-        log::debug!("👉invert change {}", change);
+        log::trace!("👉invert change {}", change);
         let new_delta = self.delta.compose(change)?;
         let inverted_delta = change.invert(&self.delta);
         Ok((new_delta, inverted_delta))

+ 3 - 3
rust-lib/flowy-ot/src/client/view.rs

@@ -25,7 +25,7 @@ impl View {
         let mut new_delta = None;
         for ext in &self.insert_exts {
             if let Some(delta) = ext.apply(delta, interval.size(), text, interval.start) {
-                log::debug!("[{}]: applied, delta: {}", ext.ext_name(), delta);
+                log::trace!("[{}]: applied, delta: {}", ext.ext_name(), delta);
                 new_delta = Some(delta);
                 break;
             }
@@ -41,7 +41,7 @@ impl View {
         let mut new_delta = None;
         for ext in &self.delete_exts {
             if let Some(delta) = ext.apply(delta, interval) {
-                log::debug!("[{}]: applied, delta: {}", ext.ext_name(), delta);
+                log::trace!("[{}]: applied, delta: {}", ext.ext_name(), delta);
                 new_delta = Some(delta);
                 break;
             }
@@ -57,7 +57,7 @@ impl View {
         let mut new_delta = None;
         for ext in &self.format_exts {
             if let Some(delta) = ext.apply(delta, interval, &attribute) {
-                log::debug!("[{}]: applied, delta: {}", ext.ext_name(), delta);
+                log::trace!("[{}]: applied, delta: {}", ext.ext_name(), delta);
                 new_delta = Some(delta);
                 break;
             }

+ 13 - 13
rust-lib/flowy-ot/src/core/delta/delta.rs

@@ -252,7 +252,7 @@ impl Delta {
                 },
                 (Some(Operation::Retain(retain)), Some(Operation::Retain(o_retain))) => {
                     let composed_attrs = compose_operation(&next_op1, &next_op2);
-                    log::debug!("[retain:{} - retain:{}]: {:?}", retain.n, o_retain.n, composed_attrs);
+                    log::trace!("[retain:{} - retain:{}]: {:?}", retain.n, o_retain.n, composed_attrs);
                     match retain.cmp(&o_retain) {
                         Ordering::Less => {
                             new_delta.retain(retain.n, composed_attrs);
@@ -299,7 +299,7 @@ impl Delta {
                     let mut composed_attrs = compose_operation(&next_op1, &next_op2);
                     composed_attrs.remove_empty();
 
-                    log::debug!("compose: [{} - {}], composed_attrs: {}", insert, o_retain, composed_attrs);
+                    log::trace!("compose: [{} - {}], composed_attrs: {}", insert, o_retain, composed_attrs);
                     match (insert.num_chars()).cmp(o_retain) {
                         Ordering::Less => {
                             new_delta.insert(&insert.s, composed_attrs.clone());
@@ -533,9 +533,9 @@ impl Delta {
         if other.is_empty() {
             return inverted;
         }
-        log::debug!("🌜Calculate invert delta");
-        log::debug!("current: {}", self);
-        log::debug!("other: {}", other);
+        log::trace!("🌜Calculate invert delta");
+        log::trace!("current: {}", self);
+        log::trace!("other: {}", other);
         let mut index = 0;
         for op in &self.ops {
             let len: usize = op.len() as usize;
@@ -548,20 +548,20 @@ impl Delta {
                     match op.has_attribute() {
                         true => invert_from_other(&mut inverted, other, op, index, index + len),
                         false => {
-                            log::debug!("invert retain: {} by retain {} {}", op, len, op.get_attributes());
+                            log::trace!("invert retain: {} by retain {} {}", op, len, op.get_attributes());
                             inverted.retain(len as usize, op.get_attributes())
                         },
                     }
                     index += len;
                 },
                 Operation::Insert(_) => {
-                    log::debug!("invert insert: {} by delete {}", op, len);
+                    log::trace!("invert insert: {} by delete {}", op, len);
                     inverted.delete(len as usize);
                 },
             }
         }
 
-        log::debug!("🌛invert result: {}", inverted);
+        log::trace!("🌛invert result: {}", inverted);
         inverted
     }
 
@@ -581,22 +581,22 @@ impl Delta {
 }
 
 fn invert_from_other(base: &mut Delta, other: &Delta, operation: &Operation, start: usize, end: usize) {
-    log::debug!("invert op: {} [{}:{}]", operation, start, end);
+    log::trace!("invert op: {} [{}:{}]", operation, start, end);
     let other_ops = DeltaIter::from_interval(other, Interval::new(start, end)).ops();
     other_ops.into_iter().for_each(|other_op| match operation {
         Operation::Delete(n) => {
-            log::debug!("invert delete: {} by add {}", n, other_op);
+            log::trace!("invert delete: {} by add {}", n, other_op);
             base.add(other_op);
         },
         Operation::Retain(retain) => {
-            log::debug!(
+            log::trace!(
                 "invert attributes: {:?}, {:?}",
                 operation.get_attributes(),
                 other_op.get_attributes()
             );
             let inverted_attrs = invert_attributes(operation.get_attributes(), other_op.get_attributes());
-            log::debug!("invert attributes result: {:?}", inverted_attrs);
-            log::debug!("invert retain: {} by retain len: {}, {}", retain, other_op.len(), inverted_attrs);
+            log::trace!("invert attributes result: {:?}", inverted_attrs);
+            log::trace!("invert retain: {} by retain len: {}, {}", retain, other_op.len(), inverted_attrs);
             base.retain(other_op.len(), inverted_attrs);
         },
         Operation::Insert(_) => {

+ 2 - 2
rust-lib/flowy-ot/src/core/delta/iterator.rs

@@ -154,13 +154,13 @@ impl<'a> Iterator for AttributesIter<'a> {
         match next_op.unwrap() {
             Operation::Delete(_n) => {},
             Operation::Retain(retain) => {
-                log::debug!("extend retain attributes with {} ", &retain.attributes);
+                log::trace!("extend retain attributes with {} ", &retain.attributes);
                 attributes.extend(retain.attributes.clone());
 
                 length = retain.n;
             },
             Operation::Insert(insert) => {
-                log::debug!("extend insert attributes with {} ", &insert.attributes);
+                log::trace!("extend insert attributes with {} ", &insert.attributes);
                 attributes.extend(insert.attributes.clone());
                 length = insert.num_chars();
             },

+ 1 - 1
rust-lib/flowy-ot/src/core/operation/operation.rs

@@ -161,7 +161,7 @@ impl fmt::Display for Retain {
 
 impl Retain {
     pub fn merge_or_new(&mut self, n: usize, attributes: Attributes) -> Option<Operation> {
-        log::debug!("merge_retain_or_new_op: len: {:?}, l: {} - r: {}", n, self.attributes, attributes);
+        log::trace!("merge_retain_or_new_op: len: {:?}, l: {} - r: {}", n, self.attributes, attributes);
 
         if self.attributes == attributes {
             self.n += n;

+ 2 - 2
rust-lib/flowy-ot/tests/op_test.rs

@@ -490,11 +490,11 @@ fn delta_transform_test() {
 
     let (a_prime, b_prime) = a.transform(&b).unwrap();
     assert_eq!(
-        r#"[{"insert":"123","attributes":{"bold":"true"}},{"retain":3}]"#,
+        r#"[{"insert":"123","attributes":{"bold":true}},{"retain":3}]"#,
         serde_json::to_string(&a_prime).unwrap()
     );
     assert_eq!(
-        r#"[{"retain":3,"attributes":{"bold":"true"}},{"insert":"456"}]"#,
+        r#"[{"retain":3,"attributes":{"bold":true}},{"insert":"456"}]"#,
         serde_json::to_string(&b_prime).unwrap()
     );
 }

+ 2 - 2
rust-lib/flowy-ot/tests/serde_test.rs

@@ -82,10 +82,10 @@ fn delta_deserialize_test() {
 #[test]
 fn delta_deserialize_null_test() {
     let json = r#"[
-        {"retain":2,"attributes":{"italic":null}}
+        {"retain":7,"attributes":{"bold":null}}
      ]"#;
     let delta = Delta::from_json(json).unwrap();
-    eprintln!("{}", delta);
+    println!("{}", delta);
 }
 
 #[test]

+ 1 - 0
rust-lib/flowy-sdk/src/lib.rs

@@ -40,6 +40,7 @@ fn crate_log_filter(level: Option<String>) -> String {
     filters.push(format!("flowy_user={}", level));
     filters.push(format!("flowy_document={}", level));
     filters.push(format!("flowy_observable={}", level));
+    filters.push(format!("flowy_ot={}", level));
     filters.push(format!("info"));
     filters.join(",")
 }

+ 3 - 7
rust-lib/flowy-workspace/src/entities/view/parser/delta_data.rs

@@ -1,16 +1,12 @@
 use flowy_ot::core::Delta;
 
 #[derive(Debug)]
-pub struct DeltaData(pub Delta);
+pub struct DeltaData(pub Vec<u8>);
 
 impl DeltaData {
     pub fn parse(data: Vec<u8>) -> Result<DeltaData, String> {
-        let delta = Delta::from_bytes(data).map_err(|e| format!("{:?}", e))?;
+        // let _ = Delta::from_bytes(data.clone()).map_err(|e| format!("{:?}", e))?;
 
-        Ok(Self(delta))
+        Ok(Self(data))
     }
 }
-
-impl AsRef<Delta> for DeltaData {
-    fn as_ref(&self) -> &Delta { &self.0 }
-}

+ 2 - 4
rust-lib/flowy-workspace/src/entities/view/view_update.rs

@@ -130,8 +130,7 @@ impl TryInto<SaveDocParams> for SaveViewDataRequest {
         // Opti: Vec<u8> -> Delta -> Vec<u8>
         let data = DeltaData::parse(self.data)
             .map_err(|e| ErrorBuilder::new(ErrorCode::ViewDataInvalid).msg(e).build())?
-            .0
-            .into_bytes();
+            .0;
 
         Ok(SaveDocParams { id: view_id, data })
     }
@@ -157,8 +156,7 @@ impl TryInto<ApplyChangesetParams> for ApplyChangesetRequest {
         // Opti: Vec<u8> -> Delta -> Vec<u8>
         let data = DeltaData::parse(self.data)
             .map_err(|e| ErrorBuilder::new(ErrorCode::ViewDataInvalid).msg(e).build())?
-            .0
-            .into_bytes();
+            .0;
 
         Ok(ApplyChangesetParams { id: view_id, data })
     }

+ 1 - 1
rust-lib/flowy-workspace/src/event.rs

@@ -52,6 +52,6 @@ pub enum WorkspaceEvent {
     #[event(input = "SaveViewDataRequest")]
     SaveViewData      = 206,
 
-    #[event(input = "ApplyChangesetRequest")]
+    #[event(input = "ApplyChangesetRequest", output = "Doc")]
     ApplyChangeset    = 207,
 }

+ 3 - 3
rust-lib/flowy-workspace/src/handlers/view_handler.rs

@@ -69,10 +69,10 @@ pub(crate) async fn update_view_data_handler(
 pub(crate) async fn apply_changeset_handler(
     data: Data<ApplyChangesetRequest>,
     controller: Unit<Arc<ViewController>>,
-) -> Result<(), WorkspaceError> {
+) -> DataResult<Doc, WorkspaceError> {
     let params: ApplyChangesetParams = data.into_inner().try_into()?;
-    let _ = controller.apply_changeset(params).await?;
-    Ok(())
+    let doc = controller.apply_changeset(params).await?;
+    data_result(doc)
 }
 
 #[tracing::instrument(skip(data, controller), err)]

+ 3 - 3
rust-lib/flowy-workspace/src/services/view_controller.rs

@@ -129,9 +129,9 @@ impl ViewController {
         Ok(())
     }
 
-    pub(crate) async fn apply_changeset(&self, params: ApplyChangesetParams) -> Result<(), WorkspaceError> {
-        let _ = self.document.apply_changeset(params).await?;
-        Ok(())
+    pub(crate) async fn apply_changeset(&self, params: ApplyChangesetParams) -> Result<Doc, WorkspaceError> {
+        let doc = self.document.apply_changeset(params).await?;
+        Ok(doc)
     }
 }