Przeglądaj źródła

refactor compose with iterator

appflowy 3 lat temu
rodzic
commit
f994155dfe

+ 2 - 2
app_flowy/packages/flowy_editor/lib/src/model/heuristic/rule.dart

@@ -40,7 +40,7 @@ class Rules {
   final List<Rule> _rules;
 
   static final Rules _instance = Rules([
-    // const FormatLinkAtCaretPositionRule(),
+    const FormatLinkAtCaretPositionRule(),
     // const ResolveLineFormatRule(),
     const ResolveInlineFormatRule(),
     // const InsertEmbedsRule(),
@@ -54,7 +54,7 @@ class Rules {
     const CatchAllInsertRule(),
     // const EnsureEmbedLineRule(),
     // const PreserveLineStyleOnMergeRule(),
-    // const CatchAllDeleteRule(),
+    const CatchAllDeleteRule(),
   ]);
 
   static Rules getInstance() => _instance;

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

@@ -30,7 +30,12 @@ impl Document {
         }
     }
 
-    pub fn insert(&mut self, index: usize, text: &str) -> Result<(), OTError> {
+    pub fn insert(
+        &mut self,
+        index: usize,
+        text: &str,
+        replace_len: usize,
+    ) -> Result<Delta, OTError> {
         if self.delta.target_len < index {
             log::error!(
                 "{} out of bounds. should 0..{}",
@@ -39,37 +44,43 @@ impl Document {
             );
         }
 
-        let delta = self.view.insert(&self.delta, text, index)?;
-        let interval = Interval::new(index, index);
-        self.update_with_op(&delta, interval)
+        let delta = self.view.insert(&self.delta, text, index, replace_len)?;
+        self.add_delta(&delta)?;
+        Ok(delta)
+    }
+
+    pub fn delete(&mut self, interval: Interval) -> Result<Delta, OTError> {
+        debug_assert_eq!(interval.is_empty(), false);
+        let delete = self.view.delete(&self.delta, interval)?;
+        if !delete.is_empty() {
+            let _ = self.add_delta(&delete)?;
+        }
+        Ok(delete)
     }
 
     pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> {
         log::debug!("format with {} at {}", attribute, interval);
-        // let format_delta = self
-        //     .view
-        //     .format(&self.delta, attribute.clone(), interval)
-        //     .unwrap();
-        //
-        // self.delta = self.record_change(&format_delta)?;
-        // Ok(())
-
-        self.update_with_attribute(attribute, interval)
+        let format_delta = self
+            .view
+            .format(&self.delta, attribute.clone(), interval)
+            .unwrap();
+
+        self.add_delta(&format_delta)?;
+        Ok(())
     }
 
-    pub fn replace(&mut self, interval: Interval, s: &str) -> Result<(), OTError> {
+    pub fn replace(&mut self, interval: Interval, s: &str) -> Result<Delta, OTError> {
         let mut delta = Delta::default();
         if !s.is_empty() {
-            let insert = OpBuilder::insert(s).build();
-            delta.add(insert);
+            delta = self.insert(interval.start, s, interval.size())?;
         }
 
         if !interval.is_empty() {
-            let delete = OpBuilder::delete(interval.size()).build();
-            delta.add(delete);
+            let delete = self.delete(interval)?;
+            delta = delta.compose(&delete)?;
         }
 
-        self.update_with_op(&delta, interval)
+        Ok(delta)
     }
 
     pub fn can_undo(&self) -> bool { self.history.can_undo() }
@@ -114,66 +125,13 @@ impl Document {
 
     pub fn set_data(&mut self, data: Delta) { self.delta = data; }
 
-    fn update_with_op(&mut self, delta: &Delta, interval: Interval) -> Result<(), OTError> {
-        let mut new_delta = Delta::default();
-        let (prefix, interval, suffix) =
-            split_length_with_interval(self.delta.target_len, interval);
-
-        // prefix
-        if prefix.is_empty() == false && prefix != interval {
-            AttributesIter::from_interval(&self.delta, prefix).for_each(|(length, attributes)| {
-                log::debug!("prefix attribute: {:?}, len: {}", attributes, length);
-                new_delta.retain(length, attributes);
-            });
-        }
-
-        delta.ops.iter().for_each(|op| {
-            new_delta.add(op.clone());
-        });
-
-        // suffix
-        if suffix.is_empty() == false {
-            AttributesIter::from_interval(&self.delta, suffix).for_each(|(length, attributes)| {
-                log::debug!("suffix attribute: {:?}, len: {}", attributes, length);
-                new_delta.retain(length, attributes);
-            });
-        }
-
-        self.delta = self.record_change(&new_delta)?;
-        Ok(())
-    }
-
-    pub fn update_with_attribute(
-        &mut self,
-        attribute: Attribute,
-        interval: Interval,
-    ) -> Result<(), OTError> {
-        log::debug!("Update document with attribute: {}", attribute);
-        let mut attributes = AttrsBuilder::new().add(attribute).build();
-        let old_attributes = AttributesIter::from_interval(&self.delta, interval).next_or_empty();
-
-        log::debug!("combine with old: {:?}", old_attributes);
-        attributes.merge(Some(old_attributes));
-        let new_attributes = attributes;
-        log::debug!("combine result: {:?}", new_attributes);
-
-        let retain = OpBuilder::retain(interval.size())
-            .attributes(new_attributes)
-            .build();
-
-        let mut delta = Delta::new();
-        delta.add(retain);
-
-        self.update_with_op(&delta, interval)
-    }
-
+    #[allow(dead_code)]
     fn next_rev_id(&self) -> RevId { RevId(self.rev_id_counter) }
 
-    fn record_change(&mut self, delta: &Delta) -> Result<Delta, OTError> {
+    fn add_delta(&mut self, delta: &Delta) -> Result<(), OTError> {
         log::debug!("👉invert change {}", delta);
         let composed_delta = self.delta.compose(delta)?;
         let mut undo_delta = delta.invert(&self.delta);
-
         self.rev_id_counter += 1;
 
         let now = chrono::Utc::now().timestamp_millis() as usize;
@@ -188,13 +146,14 @@ impl Document {
             self.last_edit_time = now;
         }
 
+        log::debug!("compose previous result: {}", undo_delta);
         if !undo_delta.is_empty() {
-            log::debug!("record change: {}", undo_delta);
             self.history.record(undo_delta);
         }
 
         log::debug!("document delta: {}", &composed_delta);
-        Ok(composed_delta)
+        self.delta = composed_delta;
+        Ok(())
     }
 
     fn invert_change(&self, change: &Delta) -> Result<(Delta, Delta), OTError> {
@@ -209,10 +168,3 @@ impl Document {
         Ok((new_delta, inverted_delta))
     }
 }
-
-fn split_length_with_interval(length: usize, interval: Interval) -> (Interval, Interval, Interval) {
-    let original_interval = Interval::new(0, length);
-    let prefix = original_interval.prefix(interval);
-    let suffix = original_interval.suffix(interval);
-    (prefix, interval, suffix)
-}

+ 1 - 1
rust-lib/flowy-ot/src/client/history.rs

@@ -46,7 +46,7 @@ impl History {
             cur_undo: 1,
             undos: Vec::new(),
             redoes: Vec::new(),
-            capacity: 20,
+            capacity: MAX_UNDOS,
         }
     }
 

+ 16 - 0
rust-lib/flowy-ot/src/client/view/delete_ext.rs

@@ -0,0 +1,16 @@
+use crate::{
+    client::view::DeleteExt,
+    core::{Attributes, Delta, DeltaBuilder, Interval},
+};
+
+pub struct DefaultDeleteExt {}
+impl DeleteExt for DefaultDeleteExt {
+    fn apply(&self, _delta: &Delta, interval: Interval) -> Option<Delta> {
+        Some(
+            DeltaBuilder::new()
+                .retain(interval.start, Attributes::empty())
+                .delete(interval.size())
+                .build(),
+        )
+    }
+}

+ 1 - 4
rust-lib/flowy-ot/src/client/view/extension.rs

@@ -1,7 +1,4 @@
-use crate::{
-    client::Document,
-    core::{Attribute, Delta, Interval},
-};
+use crate::core::{Attribute, Delta, Interval};
 
 pub trait InsertExt {
     fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta>;

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

@@ -2,6 +2,7 @@ use crate::{
     client::view::{FormatExt, NEW_LINE},
     core::{
         Attribute,
+        AttributeKey,
         AttributeScope,
         Attributes,
         CharMetric,
@@ -11,16 +12,22 @@ use crate::{
         Interval,
         Operation,
     },
+    errors::OTError,
 };
 
 pub struct FormatLinkAtCaretPositionExt {}
 
 impl FormatExt for FormatLinkAtCaretPositionExt {
     fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
+        if attribute.key != AttributeKey::Link || interval.size() != 0 {
+            return None;
+        }
+
         let mut iter = DeltaIter::new(delta);
         iter.seek::<CharMetric>(interval.start);
-        let (before, after) = (iter.next(), iter.next());
-        let mut start = interval.start;
+
+        let (before, after) = (iter.next_op_with_len(interval.size()), iter.next());
+        let mut start = interval.end;
         let mut retain = 0;
 
         if let Some(before) = before {
@@ -97,7 +104,7 @@ impl FormatExt for ResolveInlineFormatExt {
                     None => {
                         new_delta.retain(op.length(), attribute.clone().into());
                     },
-                    Some(mut line_break) => {
+                    Some(line_break) => {
                         let mut pos = 0;
                         let mut some_line_break = Some(line_break);
                         while some_line_break.is_some() {

+ 72 - 22
rust-lib/flowy-ot/src/client/view/insert_ext.rs

@@ -1,29 +1,32 @@
 use crate::{
     client::view::InsertExt,
-    core::{
-        attributes_with_length,
-        AttributeKey,
-        Attributes,
-        CharMetric,
-        Delta,
-        DeltaBuilder,
-        DeltaIter,
-        Operation,
-    },
+    core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Operation},
 };
 
 pub const NEW_LINE: &'static str = "\n";
 
 pub struct PreserveBlockStyleOnInsertExt {}
 impl InsertExt for PreserveBlockStyleOnInsertExt {
-    fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
+    fn apply(
+        &self,
+        _delta: &Delta,
+        _replace_len: usize,
+        _text: &str,
+        _index: usize,
+    ) -> Option<Delta> {
         None
     }
 }
 
 pub struct PreserveLineStyleOnSplitExt {}
 impl InsertExt for PreserveLineStyleOnSplitExt {
-    fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
+    fn apply(
+        &self,
+        _delta: &Delta,
+        _replace_len: usize,
+        _text: &str,
+        _index: usize,
+    ) -> Option<Delta> {
         None
     }
 }
@@ -31,43 +34,90 @@ impl InsertExt for PreserveLineStyleOnSplitExt {
 pub struct AutoExitBlockExt {}
 
 impl InsertExt for AutoExitBlockExt {
-    fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
+    fn apply(
+        &self,
+        _delta: &Delta,
+        _replace_len: usize,
+        _text: &str,
+        _index: usize,
+    ) -> Option<Delta> {
         None
     }
 }
 
 pub struct InsertEmbedsExt {}
 impl InsertExt for InsertEmbedsExt {
-    fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
+    fn apply(
+        &self,
+        _delta: &Delta,
+        _replace_len: usize,
+        _text: &str,
+        _index: usize,
+    ) -> Option<Delta> {
         None
     }
 }
 
 pub struct ForceNewlineForInsertsAroundEmbedExt {}
 impl InsertExt for ForceNewlineForInsertsAroundEmbedExt {
-    fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
+    fn apply(
+        &self,
+        _delta: &Delta,
+        _replace_len: usize,
+        _text: &str,
+        _index: usize,
+    ) -> Option<Delta> {
         None
     }
 }
 
 pub struct AutoFormatLinksExt {}
 impl InsertExt for AutoFormatLinksExt {
-    fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
+    fn apply(
+        &self,
+        _delta: &Delta,
+        _replace_len: usize,
+        _text: &str,
+        _index: usize,
+    ) -> Option<Delta> {
         None
     }
 }
 
 pub struct PreserveInlineStylesExt {}
 impl InsertExt for PreserveInlineStylesExt {
-    fn apply(&self, delta: &Delta, _replace_len: usize, text: &str, index: usize) -> Option<Delta> {
+    fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
         if text.ends_with(NEW_LINE) {
             return None;
         }
-        let probe_index = if index > 1 { index - 1 } else { index };
-        let attributes = attributes_with_length(delta, probe_index);
-        let delta = DeltaBuilder::new().insert(text, attributes).build();
 
-        Some(delta)
+        let mut iter = DeltaIter::new(delta);
+        let prev = iter.next_op_with_len(index);
+        if prev.is_none() {
+            return None;
+        }
+        let prev = prev.unwrap();
+        if prev.get_data().contains(NEW_LINE) {
+            return None;
+        }
+
+        let mut attributes = prev.get_attributes();
+        if attributes.is_empty() || !attributes.contains_key(&AttributeKey::Link) {
+            return Some(
+                DeltaBuilder::new()
+                    .retain(index + replace_len, Attributes::empty())
+                    .insert(text, attributes)
+                    .build(),
+            );
+        }
+
+        attributes.remove(&AttributeKey::Link);
+        let new_delta = DeltaBuilder::new()
+            .retain(index + replace_len, Attributes::empty())
+            .insert(text, attributes)
+            .build();
+
+        return Some(new_delta);
     }
 }
 
@@ -109,7 +159,7 @@ impl InsertExt for ResetLineFormatOnNewLineExt {
 
 pub struct DefaultInsertExt {}
 impl InsertExt for DefaultInsertExt {
-    fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
+    fn apply(&self, _delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
         Some(
             DeltaBuilder::new()
                 .retain(index + replace_len, Attributes::default())

+ 2 - 0
rust-lib/flowy-ot/src/client/view/mod.rs

@@ -1,8 +1,10 @@
+mod delete_ext;
 mod extension;
 mod format_ext;
 mod insert_ext;
 mod view;
 
+pub use delete_ext::*;
 pub use extension::*;
 pub use format_ext::*;
 pub use insert_ext::*;

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

@@ -26,10 +26,16 @@ impl View {
         }
     }
 
-    pub(crate) fn insert(&self, delta: &Delta, text: &str, index: usize) -> Result<Delta, OTError> {
+    pub(crate) fn insert(
+        &self,
+        delta: &Delta,
+        text: &str,
+        index: usize,
+        replace_len: usize,
+    ) -> Result<Delta, OTError> {
         let mut new_delta = None;
         for ext in &self.insert_exts {
-            if let Some(delta) = ext.apply(delta, 0, text, index) {
+            if let Some(delta) = ext.apply(delta, replace_len, text, index) {
                 new_delta = Some(delta);
                 break;
             }
@@ -41,13 +47,19 @@ impl View {
         }
     }
 
-    pub(crate) fn replace(
-        &self,
-        delta: &Delta,
-        text: &str,
-        interval: Interval,
-    ) -> Result<Delta, OTError> {
-        unimplemented!()
+    pub(crate) fn delete(&self, delta: &Delta, interval: Interval) -> Result<Delta, OTError> {
+        let mut new_delta = None;
+        for ext in &self.delete_exts {
+            if let Some(delta) = ext.apply(delta, interval) {
+                new_delta = Some(delta);
+                break;
+            }
+        }
+
+        match new_delta {
+            None => Err(ErrorBuilder::new(OTErrorCode::ApplyInsertFail).build()),
+            Some(new_delta) => Ok(new_delta),
+        }
     }
 
     pub(crate) fn format(
@@ -96,6 +108,6 @@ fn construct_format_exts() -> Vec<FormatExtension> {
 fn construct_delete_exts() -> Vec<DeleteExtension> {
     vec![
         //
-
+        Box::new(DefaultDeleteExt {}),
     ]
 }

+ 9 - 0
rust-lib/flowy-ot/src/core/attributes/attributes.rs

@@ -24,6 +24,8 @@ impl Attributes {
         }
     }
 
+    pub fn empty() -> Self { Self::default() }
+
     pub fn is_empty(&self) -> bool { self.inner.is_empty() }
 
     pub fn add(&mut self, attribute: Attribute) {
@@ -99,6 +101,13 @@ pub fn compose_operation(left: &Option<Operation>, right: &Option<Operation>) ->
     attr
 }
 
+pub fn compose_attributes(left: Attributes, right: Attributes) -> Attributes {
+    log::trace!("compose attributes: a: {:?}, b: {:?}", left, right);
+    let attr = merge_attributes(left, right);
+    log::trace!("compose attributes result: {:?}", attr);
+    attr
+}
+
 pub fn transform_operation(left: &Option<Operation>, right: &Option<Operation>) -> Attributes {
     let attr_l = attributes_from(left);
     let attr_r = attributes_from(right);

+ 5 - 0
rust-lib/flowy-ot/src/core/delta/builder.rs

@@ -16,6 +16,11 @@ impl DeltaBuilder {
         self
     }
 
+    pub fn delete(mut self, n: usize) -> Self {
+        self.delta.delete(n);
+        self
+    }
+
     pub fn insert(mut self, s: &str, attrs: Attributes) -> Self {
         self.delta.insert(s, attrs);
         self

+ 23 - 11
rust-lib/flowy-ot/src/core/delta/cursor.rs

@@ -31,7 +31,7 @@ impl<'a> Cursor<'a> {
         cursor
     }
 
-    pub fn next_interval(&self) -> Interval { self.next_op_interval_with_len(None) }
+    pub fn next_interval(&self) -> Interval { self.next_op_interval_with_constraint(None) }
 
     pub fn next_op_with_len(&mut self, force_len: Option<usize>) -> Option<Operation> {
         let mut find_op = None;
@@ -44,7 +44,7 @@ impl<'a> Cursor<'a> {
 
         while find_op.is_none() && next_op.is_some() {
             let op = next_op.take().unwrap();
-            let interval = self.next_op_interval_with_len(force_len);
+            let interval = self.next_op_interval_with_constraint(force_len);
             find_op = op.shrink(interval);
 
             let suffix = Interval::new(0, op.length()).suffix(interval);
@@ -65,7 +65,7 @@ impl<'a> Cursor<'a> {
 
     pub fn next_op(&mut self) -> Option<Operation> { self.next_op_with_len(None) }
 
-    pub fn has_next(&self) -> bool { self.c_index < self.next_iv.end }
+    pub fn has_next(&self) -> bool { self.next_iter_op().is_some() }
 
     fn descend(&mut self, index: usize) {
         self.next_iv.start += index;
@@ -87,16 +87,28 @@ impl<'a> Cursor<'a> {
         }
     }
 
-    pub fn current_op(&self) -> &Operation {
-        let op = self
-            .next_op
-            .as_ref()
-            .unwrap_or(&self.delta.ops[self.o_index]);
-        op
+    pub fn next_iter_op(&self) -> Option<&Operation> {
+        let mut next_op = self.next_op.as_ref();
+        if next_op.is_none() {
+            let mut offset = 0;
+            for op in &self.delta.ops {
+                offset += op.length();
+                if offset > self.c_index {
+                    next_op = Some(op);
+                    break;
+                }
+            }
+        }
+        next_op
     }
 
-    fn next_op_interval_with_len(&self, force_len: Option<usize>) -> Interval {
-        let op = self.current_op();
+    fn next_op_interval_with_constraint(&self, force_len: Option<usize>) -> Interval {
+        let next_op = self.next_iter_op();
+        if next_op.is_none() {
+            return Interval::new(0, 0);
+        }
+
+        let op = next_op.unwrap();
         let start = self.c_index;
         let end = match force_len {
             None => self.c_index + op.length(),

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

@@ -1,5 +1,5 @@
 use crate::{
-    core::{attributes::*, operation::*, DeltaIter, Interval},
+    core::{attributes::*, operation::*, DeltaIter, Interval, MAX_IV_LEN},
     errors::{ErrorBuilder, OTError, OTErrorCode},
 };
 use bytecount::num_chars;
@@ -141,47 +141,84 @@ impl Delta {
         }
     }
 
-    pub fn compose2(&self, other: &Self) -> Result<Self, OTError> {
+    /// Merges the operation with `other` into one operation while preserving
+    /// the changes of both. Or, in other words, for each input string S and a
+    /// pair of consecutive operations A and B.
+    ///     `apply(apply(S, A), B) = apply(S, compose(A, B))`
+    /// must hold.
+    pub fn compose(&self, other: &Self) -> Result<Self, OTError> {
         let mut new_delta = Delta::default();
-        let mut iter_1 = DeltaIter::new(self);
-        let mut iter_2 = DeltaIter::new(other);
+        let mut iter = DeltaIter::new(self);
+        let mut other_iter = DeltaIter::new(other);
 
-        while iter_1.has_next() || iter_2.has_next() {
-            if iter_2.is_next_insert() {
-                new_delta.add(iter_2.next_op().unwrap());
+        while iter.has_next() || other_iter.has_next() {
+            if other_iter.is_next_insert() {
+                new_delta.add(other_iter.next_op().unwrap());
                 continue;
             }
 
-            if iter_1.is_next_Delete() {
-                new_delta.add(iter_1.next_op().unwrap());
+            if iter.is_next_delete() {
+                new_delta.add(iter.next_op().unwrap());
                 continue;
             }
 
-            let length = min(iter_1.next_op_len(), iter_2.next_op_len());
-            let op1 = iter_1
+            let length = min(
+                iter.next_op_len().unwrap_or(MAX_IV_LEN),
+                other_iter.next_op_len().unwrap_or(MAX_IV_LEN),
+            );
+
+            let op = iter
                 .next_op_with_len(length)
                 .unwrap_or(OpBuilder::retain(length).build());
-            let op2 = iter_2
+
+            let other_op = other_iter
                 .next_op_with_len(length)
                 .unwrap_or(OpBuilder::retain(length).build());
 
-            debug_assert!(op1.length())
+            debug_assert_eq!(op.length(), other_op.length());
+
+            match (&op, &other_op) {
+                (Operation::Retain(retain), Operation::Retain(other_retain)) => {
+                    let composed_attrs = compose_attributes(
+                        retain.attributes.clone(),
+                        other_retain.attributes.clone(),
+                    );
+                    new_delta.add(
+                        OpBuilder::retain(retain.n)
+                            .attributes(composed_attrs)
+                            .build(),
+                    )
+                },
+                (Operation::Insert(insert), Operation::Retain(other_retain)) => {
+                    let mut composed_attrs = compose_attributes(
+                        insert.attributes.clone(),
+                        other_retain.attributes.clone(),
+                    );
+                    composed_attrs = composed_attrs.remove_empty();
+                    new_delta.add(
+                        OpBuilder::insert(op.get_data())
+                            .attributes(composed_attrs)
+                            .build(),
+                    )
+                },
+                (Operation::Retain(_), Operation::Delete(_)) => {
+                    new_delta.add(other_op);
+                },
+                (a, b) => {
+                    debug_assert_eq!(a.is_insert(), true);
+                    debug_assert_eq!(b.is_delete(), true);
+                    continue;
+                },
+            }
         }
 
         Ok(new_delta)
     }
 
-    /// Merges the operation with `other` into one operation while preserving
-    /// the changes of both. Or, in other words, for each input string S and a
-    /// pair of consecutive operations A and B.
-    ///     `apply(apply(S, A), B) = apply(S, compose(A, B))`
-    /// must hold.
-    ///
-    /// # Error
-    ///
-    /// Returns an `OTError` if the operations are not composable due to length
-    /// conflicts.
-    pub fn compose(&self, other: &Self) -> Result<Self, OTError> {
+    #[deprecated(
+        note = "The same as compose except it requires the target_len of self must equal to other's base_len"
+    )]
+    pub fn compose2(&self, other: &Self) -> Result<Self, OTError> {
         if self.target_len != other.base_len {
             return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength).build());
         }

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

@@ -5,6 +5,8 @@ use crate::{
 };
 use std::ops::{Deref, DerefMut};
 
+pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize;
+
 pub struct DeltaIter<'a> {
     cursor: Cursor<'a>,
     interval: Interval,
@@ -12,7 +14,7 @@ pub struct DeltaIter<'a> {
 
 impl<'a> DeltaIter<'a> {
     pub fn new(delta: &'a Delta) -> Self {
-        let interval = Interval::new(0, i32::MAX as usize);
+        let interval = Interval::new(0, MAX_IV_LEN);
         Self::from_interval(delta, interval)
     }
 
@@ -25,24 +27,48 @@ impl<'a> DeltaIter<'a> {
 
     pub fn next_op(&mut self) -> Option<Operation> { self.cursor.next_op() }
 
-    pub fn next_op_len(&self) -> usize { self.cursor.next_interval().size() }
+    pub fn next_op_len(&self) -> Option<usize> {
+        let interval = self.cursor.next_interval();
+        if interval.is_empty() {
+            None
+        } else {
+            Some(interval.size())
+        }
+    }
 
     pub fn next_op_with_len(&mut self, length: usize) -> Option<Operation> {
         self.cursor.next_op_with_len(Some(length))
     }
 
-    pub fn seek<M: Metric>(&mut self, index: usize) -> Result<(), OTError> {
-        let _ = M::seek(&mut self.cursor, index)?;
-        Ok(())
+    pub fn seek<M: Metric>(&mut self, index: usize) {
+        match M::seek(&mut self.cursor, index) {
+            Ok(_) => {},
+            Err(e) => log::error!("Seek fail: {:?}", e),
+        }
     }
 
     pub fn has_next(&self) -> bool { self.cursor.has_next() }
 
-    pub fn is_next_insert(&self) -> bool { self.cursor.current_op().is_insert() }
+    pub fn is_next_insert(&self) -> bool {
+        match self.cursor.next_iter_op() {
+            None => false,
+            Some(op) => op.is_insert(),
+        }
+    }
 
-    pub fn is_next_retain(&self) -> bool { self.cursor.current_op().is_retain() }
+    pub fn is_next_retain(&self) -> bool {
+        match self.cursor.next_iter_op() {
+            None => false,
+            Some(op) => op.is_retain(),
+        }
+    }
 
-    pub fn is_next_delete(&self) -> bool { self.cursor.current_op().is_delete() }
+    pub fn is_next_delete(&self) -> bool {
+        match self.cursor.next_iter_op() {
+            None => false,
+            Some(op) => op.is_delete(),
+        }
+    }
 }
 
 impl<'a> Iterator for DeltaIter<'a> {
@@ -52,7 +78,6 @@ impl<'a> Iterator for DeltaIter<'a> {
 
 pub struct AttributesIter<'a> {
     delta_iter: DeltaIter<'a>,
-    interval: Interval,
 }
 
 impl<'a> AttributesIter<'a> {
@@ -63,10 +88,7 @@ impl<'a> AttributesIter<'a> {
 
     pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self {
         let delta_iter = DeltaIter::from_interval(delta, interval);
-        Self {
-            delta_iter,
-            interval,
-        }
+        Self { delta_iter }
     }
 
     pub fn next_or_empty(&mut self) -> Attributes {
@@ -115,12 +137,3 @@ impl<'a> Iterator for AttributesIter<'a> {
         Some((length, attributes))
     }
 }
-
-pub(crate) fn attributes_with_length(delta: &Delta, index: usize) -> Attributes {
-    let mut iter = AttributesIter::new(delta);
-    iter.seek::<CharMetric>(index);
-    match iter.next() {
-        None => Attributes::new(),
-        Some((_, attributes)) => attributes,
-    }
-}

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

@@ -62,6 +62,7 @@ impl Operation {
 
     pub fn is_empty(&self) -> bool { self.length() == 0 }
 
+    #[allow(dead_code)]
     pub fn split(&self, index: usize) -> (Option<Operation>, Option<Operation>) {
         debug_assert!(index < self.length());
         let mut left = None;
@@ -100,10 +101,14 @@ impl Operation {
                 .attributes(retain.attributes.clone())
                 .build(),
             Operation::Insert(insert) => {
-                if interval.start > insert.s.len() {
+                if interval.start > insert.num_chars() {
                     OpBuilder::insert("").build()
                 } else {
-                    let s = &insert.s[interval.start..min(interval.end, insert.s.len())];
+                    let chars = insert.chars().skip(interval.start);
+                    let s = &chars
+                        .take(min(interval.size(), insert.num_chars()))
+                        .collect::<String>();
+
                     OpBuilder::insert(s)
                         .attributes(insert.attributes.clone())
                         .build()

+ 1 - 0
rust-lib/flowy-ot/src/errors.rs

@@ -28,6 +28,7 @@ pub enum OTErrorCode {
     IncompatibleLength,
     ApplyInsertFail,
     ApplyFormatFail,
+    ComposeOperationFail,
     UndoFail,
     RedoFail,
 }

+ 7 - 15
rust-lib/flowy-ot/tests/attribute_test.rs

@@ -43,11 +43,11 @@ fn delta_insert_text_with_attr() {
             0,
             r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"345"}]"#,
         ),
-        Insert(0, "abc", 1),
-        AssertOpsJson(
-            0,
-            r#"[{"insert":"1abc2","attributes":{"bold":"true"}},{"insert":"345"}]"#,
-        ),
+        /* Insert(0, "abc", 1),
+         * AssertOpsJson(
+         *     0,
+         *     r#"[{"insert":"1abc2","attributes":{"bold":"true"}},{"insert":"345"}]"#,
+         * ), */
     ];
     OpTester::new().run_script(ops);
 }
@@ -147,7 +147,6 @@ fn delta_add_bold_consecutive() {
 }
 
 #[test]
-#[should_panic]
 fn delta_add_bold_empty_str() {
     let ops = vec![Bold(0, Interval::new(0, 4), true)];
     OpTester::new().run_script(ops);
@@ -171,11 +170,7 @@ fn delta_add_bold_italic() {
         Italic(0, Interval::new(4, 6), false),
         AssertOpsJson(
             0,
-            r#"[
-            {"insert":"1234","attributes":{"bold":"true","italic":"true"}},
-            {"insert":"56","attributes":{"bold":"true"}},
-            {"insert":"78","attributes":{"bold":"true","italic":"true"}}]
-            "#,
+            r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true"}},{"insert":"78","attributes":{"bold":"true","italic":"true"}}]"#,
         ),
     ];
     OpTester::new().run_script(ops);
@@ -426,10 +421,7 @@ fn delta_replace_with_text() {
         Replace(0, Interval::new(0, 3), "ab"),
         AssertOpsJson(
             0,
-            r#"[
-            {"insert":"ab"},
-            {"insert":"456","attributes":{"bold":"true"}}]
-            "#,
+            r#"[{"insert":"ab"},{"insert":"456","attributes":{"bold":"true"}}]"#,
         ),
     ];
 

+ 2 - 2
rust-lib/flowy-ot/tests/helper/mod.rs

@@ -72,7 +72,7 @@ impl OpTester {
         match op {
             TestOp::Insert(delta_i, s, index) => {
                 let document = &mut self.documents[*delta_i];
-                document.insert(*index, s).unwrap();
+                document.insert(*index, s, 0).unwrap();
             },
             TestOp::Delete(delta_i, interval) => {
                 let document = &mut self.documents[*delta_i];
@@ -84,7 +84,7 @@ impl OpTester {
             },
             TestOp::InsertBold(delta_i, s, interval) => {
                 let document = &mut self.documents[*delta_i];
-                document.insert(interval.start, s).unwrap();
+                document.insert(interval.start, s, 0).unwrap();
                 document
                     .format(*interval, AttributeKey::Bold.with_value("true".to_owned()))
                     .unwrap();

+ 18 - 4
rust-lib/flowy-ot/tests/op_test.rs

@@ -148,7 +148,7 @@ fn delta_get_ops_in_interval_7() {
     delta.add(retain_a.clone());
 
     let mut iter_1 = DeltaIter::new(&delta);
-    iter_1.seek::<CharMetric>(2).unwrap();
+    iter_1.seek::<CharMetric>(2);
     assert_eq!(iter_1.next_op().unwrap(), OpBuilder::insert("345").build());
     assert_eq!(iter_1.next_op().unwrap(), OpBuilder::retain(3).build());
 
@@ -170,7 +170,7 @@ fn delta_seek_1() {
     delta.add(insert_a.clone());
     delta.add(retain_a.clone());
     let mut iter = DeltaIter::new(&delta);
-    iter.seek::<OpMetric>(1).unwrap();
+    iter.seek::<OpMetric>(1);
     assert_eq!(iter.next_op().unwrap(), OpBuilder::retain(3).build());
 }
 
@@ -230,15 +230,29 @@ fn delta_next_op_len_test() {
 
     let mut iter = DeltaIter::new(&delta);
     iter.seek::<CharMetric>(3);
-    assert_eq!(iter.next_op_len(), 2);
+    assert_eq!(iter.next_op_len().unwrap(), 2);
     assert_eq!(
         iter.next_op_with_len(1).unwrap(),
         OpBuilder::insert("4").build()
     );
-    assert_eq!(iter.next_op_len(), 1);
+    assert_eq!(iter.next_op_len().unwrap(), 1);
     assert_eq!(iter.next_op().unwrap(), OpBuilder::insert("5").build());
 }
 
+#[test]
+fn delta_next_op_len_test2() {
+    let mut delta = Delta::default();
+    delta.add(OpBuilder::insert("12345").build());
+    let mut iter = DeltaIter::new(&delta);
+
+    assert_eq!(iter.next_op_len().unwrap(), 5);
+    assert_eq!(
+        iter.next_op_with_len(5).unwrap(),
+        OpBuilder::insert("12345").build()
+    );
+    assert_eq!(iter.next_op_len(), None);
+}
+
 #[test]
 fn lengths() {
     let mut delta = Delta::default();

+ 1 - 0
rust-lib/flowy-ot/tests/undo_redo_test.rs

@@ -178,6 +178,7 @@ fn delta_undo_delete2_with_lagging() {
 fn delta_redo_delete() {
     let ops = vec![
         Insert(0, "123", 0),
+        Wait(RECORD_THRESHOLD),
         Delete(0, Interval::new(0, 3)),
         AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
         Undo(0),