瀏覽代碼

cursor: next op with length

appflowy 3 年之前
父節點
當前提交
93cf9712dc

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

@@ -40,21 +40,21 @@ class Rules {
   final List<Rule> _rules;
   final List<Rule> _rules;
 
 
   static final Rules _instance = Rules([
   static final Rules _instance = Rules([
-    const FormatLinkAtCaretPositionRule(),
-    const ResolveLineFormatRule(),
+    // const FormatLinkAtCaretPositionRule(),
+    // const ResolveLineFormatRule(),
     const ResolveInlineFormatRule(),
     const ResolveInlineFormatRule(),
-    const InsertEmbedsRule(),
-    const ForceNewlineForInsertsAroundEmbedRule(),
-    const AutoExitBlockRule(),
-    const PreserveBlockStyleOnInsertRule(),
-    const PreserveLineStyleOnSplitRule(),
+    // const InsertEmbedsRule(),
+    // const ForceNewlineForInsertsAroundEmbedRule(),
+    // const AutoExitBlockRule(),
+    // const PreserveBlockStyleOnInsertRule(),
+    // const PreserveLineStyleOnSplitRule(),
     const ResetLineFormatOnNewLineRule(),
     const ResetLineFormatOnNewLineRule(),
-    const AutoFormatLinksRule(),
+    // const AutoFormatLinksRule(),
     const PreserveInlineStylesRule(),
     const PreserveInlineStylesRule(),
     const CatchAllInsertRule(),
     const CatchAllInsertRule(),
-    const EnsureEmbedLineRule(),
-    const PreserveLineStyleOnMergeRule(),
-    const CatchAllDeleteRule(),
+    // const EnsureEmbedLineRule(),
+    // const PreserveLineStyleOnMergeRule(),
+    // const CatchAllDeleteRule(),
   ]);
   ]);
 
 
   static Rules getInstance() => _instance;
   static Rules getInstance() => _instance;

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

@@ -46,9 +46,12 @@ impl Document {
 
 
     pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> {
     pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> {
         log::debug!("format with {} at {}", attribute, interval);
         log::debug!("format with {} at {}", attribute, interval);
-
-        // let format_delta = self.view.format(&self.delta, attribute, interval)?;
-
+        // let format_delta = self
+        //     .view
+        //     .format(&self.delta, attribute.clone(), interval)
+        //     .unwrap();
+        // let a = self.delta.compose(&format_delta).unwrap();
+        // println!("{:?}", a);
         self.update_with_attribute(attribute, interval)
         self.update_with_attribute(attribute, interval)
     }
     }
 
 
@@ -165,7 +168,10 @@ impl Document {
     fn next_rev_id(&self) -> RevId { RevId(self.rev_id_counter) }
     fn next_rev_id(&self) -> RevId { RevId(self.rev_id_counter) }
 
 
     fn record_change(&mut self, delta: &Delta) -> Result<Delta, OTError> {
     fn record_change(&mut self, delta: &Delta) -> Result<Delta, OTError> {
-        let (composed_delta, mut undo_delta) = self.invert_change(&delta)?;
+        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;
         self.rev_id_counter += 1;
 
 
         let now = chrono::Utc::now().timestamp_millis() as usize;
         let now = chrono::Utc::now().timestamp_millis() as usize;

+ 63 - 6
rust-lib/flowy-ot/src/client/view/format_ext.rs

@@ -1,6 +1,16 @@
 use crate::{
 use crate::{
-    client::view::FormatExt,
-    core::{Attribute, AttributeScope, Attributes, Delta, DeltaBuilder, DeltaIter, Interval},
+    client::view::{FormatExt, NEW_LINE},
+    core::{
+        Attribute,
+        AttributeScope,
+        Attributes,
+        CharMetric,
+        Delta,
+        DeltaBuilder,
+        DeltaIter,
+        Interval,
+        Operation,
+    },
 };
 };
 
 
 pub struct FormatLinkAtCaretPositionExt {}
 pub struct FormatLinkAtCaretPositionExt {}
@@ -8,7 +18,7 @@ pub struct FormatLinkAtCaretPositionExt {}
 impl FormatExt for FormatLinkAtCaretPositionExt {
 impl FormatExt for FormatLinkAtCaretPositionExt {
     fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
     fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
         let mut iter = DeltaIter::new(delta);
         let mut iter = DeltaIter::new(delta);
-        iter.seek(interval.start);
+        iter.seek::<CharMetric>(interval.start);
         let (before, after) = (iter.next(), iter.next());
         let (before, after) = (iter.next(), iter.next());
         let mut start = interval.start;
         let mut start = interval.start;
         let mut retain = 0;
         let mut retain = 0;
@@ -42,7 +52,6 @@ impl FormatExt for FormatLinkAtCaretPositionExt {
 }
 }
 
 
 pub struct ResolveLineFormatExt {}
 pub struct ResolveLineFormatExt {}
-
 impl FormatExt for ResolveLineFormatExt {
 impl FormatExt for ResolveLineFormatExt {
     fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
     fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
         if attribute.scope != AttributeScope::Block {
         if attribute.scope != AttributeScope::Block {
@@ -53,7 +62,7 @@ impl FormatExt for ResolveLineFormatExt {
         new_delta.retain(interval.start, Attributes::default());
         new_delta.retain(interval.start, Attributes::default());
 
 
         let mut iter = DeltaIter::new(delta);
         let mut iter = DeltaIter::new(delta);
-        iter.seek(interval.start);
+        iter.seek::<CharMetric>(interval.start);
 
 
         None
         None
     }
     }
@@ -63,6 +72,54 @@ pub struct ResolveInlineFormatExt {}
 
 
 impl FormatExt for ResolveInlineFormatExt {
 impl FormatExt for ResolveInlineFormatExt {
     fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
     fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
-        unimplemented!()
+        if attribute.scope != AttributeScope::Inline {
+            return None;
+        }
+        let mut new_delta = DeltaBuilder::new()
+            .retain(interval.start, Attributes::default())
+            .build();
+
+        let mut iter = DeltaIter::new(delta);
+        iter.seek::<CharMetric>(interval.start);
+
+        let mut cur = 0;
+        let len = interval.size();
+
+        while cur < len && iter.has_next() {
+            let some_op = iter.next_op_with_length(len - cur);
+            if some_op.is_none() {
+                return Some(new_delta);
+            }
+            let op = some_op.unwrap();
+            if let Operation::Insert(insert) = &op {
+                let mut s = insert.s.as_str();
+                match s.find(NEW_LINE) {
+                    None => {
+                        new_delta.retain(op.length(), attribute.clone().into());
+                    },
+                    Some(mut line_break) => {
+                        let mut pos = 0;
+                        let mut some_line_break = Some(line_break);
+                        while some_line_break.is_some() {
+                            let line_break = some_line_break.unwrap();
+                            new_delta.retain(line_break - pos, attribute.clone().into());
+                            new_delta.retain(1, Attributes::default());
+                            pos = line_break + 1;
+
+                            s = &s[pos..s.len()];
+                            some_line_break = s.find(NEW_LINE);
+                        }
+
+                        if pos < op.length() {
+                            new_delta.retain(op.length() - pos, attribute.clone().into());
+                        }
+                    },
+                }
+            }
+
+            cur += op.length();
+        }
+
+        Some(new_delta)
     }
     }
 }
 }

+ 60 - 13
rust-lib/flowy-ot/src/client/view/insert_ext.rs

@@ -1,9 +1,10 @@
 use crate::{
 use crate::{
     client::view::InsertExt,
     client::view::InsertExt,
     core::{
     core::{
-        attributes_at_index,
+        attributes_with_length,
         AttributeKey,
         AttributeKey,
         Attributes,
         Attributes,
+        CharMetric,
         Delta,
         Delta,
         DeltaBuilder,
         DeltaBuilder,
         DeltaIter,
         DeltaIter,
@@ -13,18 +14,57 @@ use crate::{
 
 
 pub const NEW_LINE: &'static str = "\n";
 pub const NEW_LINE: &'static str = "\n";
 
 
-pub struct PreserveInlineStyleExt {}
-impl PreserveInlineStyleExt {
-    pub fn new() -> Self { Self {} }
+pub struct PreserveBlockStyleOnInsertExt {}
+impl InsertExt for PreserveBlockStyleOnInsertExt {
+    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> {
+        None
+    }
+}
+
+pub struct AutoExitBlockExt {}
+
+impl InsertExt for AutoExitBlockExt {
+    fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
+        None
+    }
 }
 }
 
 
-impl InsertExt for PreserveInlineStyleExt {
+pub struct InsertEmbedsExt {}
+impl InsertExt for InsertEmbedsExt {
+    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> {
+        None
+    }
+}
+
+pub struct AutoFormatLinksExt {}
+impl InsertExt for AutoFormatLinksExt {
+    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) {
         if text.ends_with(NEW_LINE) {
             return None;
             return None;
         }
         }
-
-        let attributes = attributes_at_index(delta, index);
+        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();
         let delta = DeltaBuilder::new().insert(text, attributes).build();
 
 
         Some(delta)
         Some(delta)
@@ -32,11 +72,6 @@ impl InsertExt for PreserveInlineStyleExt {
 }
 }
 
 
 pub struct ResetLineFormatOnNewLineExt {}
 pub struct ResetLineFormatOnNewLineExt {}
-
-impl ResetLineFormatOnNewLineExt {
-    pub fn new() -> Self { Self {} }
-}
-
 impl InsertExt for ResetLineFormatOnNewLineExt {
 impl InsertExt for ResetLineFormatOnNewLineExt {
     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 != NEW_LINE {
         if text != NEW_LINE {
@@ -44,7 +79,7 @@ impl InsertExt for ResetLineFormatOnNewLineExt {
         }
         }
 
 
         let mut iter = DeltaIter::new(delta);
         let mut iter = DeltaIter::new(delta);
-        iter.seek(index);
+        iter.seek::<CharMetric>(index);
         let maybe_next_op = iter.next();
         let maybe_next_op = iter.next();
         if maybe_next_op.is_none() {
         if maybe_next_op.is_none() {
             return None;
             return None;
@@ -71,3 +106,15 @@ 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> {
+        Some(
+            DeltaBuilder::new()
+                .retain(index + replace_len, Attributes::default())
+                .insert(text, Attributes::default())
+                .build(),
+        )
+    }
+}

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

@@ -73,8 +73,15 @@ impl View {
 
 
 fn construct_insert_exts() -> Vec<InsertExtension> {
 fn construct_insert_exts() -> Vec<InsertExtension> {
     vec![
     vec![
-        Box::new(PreserveInlineStyleExt::new()),
-        Box::new(ResetLineFormatOnNewLineExt::new()),
+        Box::new(InsertEmbedsExt {}),
+        Box::new(ForceNewlineForInsertsAroundEmbedExt {}),
+        Box::new(AutoExitBlockExt {}),
+        Box::new(PreserveBlockStyleOnInsertExt {}),
+        Box::new(PreserveLineStyleOnSplitExt {}),
+        Box::new(ResetLineFormatOnNewLineExt {}),
+        Box::new(AutoFormatLinksExt {}),
+        Box::new(PreserveInlineStylesExt {}),
+        Box::new(DefaultInsertExt {}),
     ]
     ]
 }
 }
 
 

+ 81 - 107
rust-lib/flowy-ot/src/core/delta/cursor.rs

@@ -4,153 +4,127 @@ use crate::{
 };
 };
 use std::{cmp::min, slice::Iter};
 use std::{cmp::min, slice::Iter};
 
 
+#[derive(Debug)]
 pub struct Cursor<'a> {
 pub struct Cursor<'a> {
-    delta: &'a Delta,
-    interval: Interval,
-    iterator: Iter<'a, Operation>,
-    char_index: usize,
-    op_index: usize,
-    current_op: Option<&'a Operation>,
+    pub(crate) delta: &'a Delta,
+    pub(crate) origin_iv: Interval,
+    pub(crate) next_iv: Interval,
+    pub(crate) c_index: usize,
+    pub(crate) o_index: usize,
+    iter: Iter<'a, Operation>,
+    next_op: Option<Operation>,
 }
 }
 
 
 impl<'a> Cursor<'a> {
 impl<'a> Cursor<'a> {
     pub fn new(delta: &'a Delta, interval: Interval) -> Cursor<'a> {
     pub fn new(delta: &'a Delta, interval: Interval) -> Cursor<'a> {
-        let cursor = Self {
+        // debug_assert!(interval.start <= delta.target_len);
+        let mut cursor = Self {
             delta,
             delta,
-            interval,
-            iterator: delta.ops.iter(),
-            char_index: 0,
-            op_index: 0,
-            current_op: None,
+            origin_iv: interval,
+            next_iv: interval,
+            c_index: 0,
+            o_index: 0,
+            iter: delta.ops.iter(),
+            next_op: None,
         };
         };
+        cursor.descend(0);
         cursor
         cursor
     }
     }
 
 
-    pub fn next_op(&mut self) -> Option<Operation> {
-        let mut next_op = self.current_op.take();
-        if next_op.is_none() {
-            next_op = self.iterator.next();
+    fn descend(&mut self, index: usize) {
+        self.next_iv.start += index;
+        if self.c_index >= self.next_iv.start {
+            return;
         }
         }
 
 
+        while let Some(op) = self.iter.next() {
+            self.o_index += 1;
+            let start = self.c_index;
+            let end = start + op.length();
+            let intersect = Interval::new(start, end).intersect(self.next_iv);
+            if intersect.is_empty() {
+                self.c_index += op.length();
+            } else {
+                self.next_op = Some(op.clone());
+                break;
+            }
+        }
+    }
+
+    pub fn next_op_with_length(&mut self, length: Option<usize>) -> Option<Operation> {
         let mut find_op = None;
         let mut find_op = None;
-        while find_op.is_none() && next_op.is_some() {
-            self.op_index += 1;
+        let next_op = self.next_op.take();
+        let mut next_op = next_op.as_ref();
+        if next_op.is_none() {
+            next_op = self.iter.next();
+            self.o_index += 1;
+        }
 
 
+        while find_op.is_none() && next_op.is_some() {
             let op = next_op.unwrap();
             let op = next_op.unwrap();
-            if self.char_index < self.interval.start {
-                let intersect = Interval::new(self.char_index, self.char_index + op.length())
-                    .intersect(self.interval);
-                if intersect.is_empty() {
-                    self.char_index += op.length();
-                } else {
-                    if let Some(new_op) = op.shrink(intersect.translate_neg(self.char_index)) {
-                        // shrink the op to fit the intersect range
-                        // ┌──────────────┐
-                        // │ 1 2 3 4 5 6  │
-                        // └───────▲───▲──┘
-                        //         │   │
-                        //        [3, 5)
-                        // op = "45"
-                        find_op = Some(new_op);
-                    }
-                    self.char_index = intersect.end;
-                }
-            } else {
-                // the interval passed in the shrink function is base on the op not the delta.
-                if let Some(new_op) = op.shrink(self.interval.translate_neg(self.char_index)) {
-                    find_op = Some(new_op);
-                }
-                // for example: extract the ops from three insert ops with interval [2,5). the
-                // interval size is larger than the op. Moving the offset to extract each part.
-                // Each step would be the small value between interval.size() and
-                // next_op.length(). Checkout the delta_get_ops_in_interval_4 for more details.
-                //
-                // ┌──────┐  ┌──────┐  ┌──────┐
-                // │ 1 2  │  │ 3 4  │  │ 5 6  │
-                // └──────┘  └─▲────┘  └───▲──┘
-                //             │  [2, 5)   │
-                //
-                self.char_index += min(self.interval.size(), op.length());
+            let start = self.c_index;
+            let end = match length {
+                None => self.c_index + op.length(),
+                Some(length) => self.c_index + min(length, op.length()),
+            };
+            let intersect = Interval::new(start, end).intersect(self.next_iv);
+            let interval = intersect.translate_neg(start);
+
+            let op_interval = Interval::new(0, op.length());
+            let suffix = op_interval.suffix(interval);
+
+            find_op = op.shrink(interval);
+
+            if !suffix.is_empty() {
+                self.next_op = op.shrink(suffix);
             }
             }
 
 
-            match find_op {
-                None => next_op = self.iterator.next(),
-                Some(_) => self.interval.start = self.char_index,
+            self.c_index = intersect.end;
+            self.next_iv.start = intersect.end;
+
+            if find_op.is_none() {
+                next_op = self.iter.next();
             }
             }
         }
         }
 
 
         find_op
         find_op
     }
     }
 
 
-    pub fn seek<M: Metric>(&mut self, index: usize) -> Result<(), OTError> {
-        self.current_op = M::seek(self, index)?;
-        Ok(())
-    }
+    pub fn next_op(&mut self) -> Option<Operation> { self.next_op_with_length(None) }
+
+    pub fn has_next(&self) -> bool { self.c_index < self.next_iv.end }
 }
 }
 
 
+type SeekResult = Result<(), OTError>;
 pub trait Metric {
 pub trait Metric {
-    fn seek<'a, 'b>(
-        cursor: &'b mut Cursor<'a>,
-        index: usize,
-    ) -> Result<Option<&'a Operation>, OTError>;
+    fn seek(cursor: &mut Cursor, index: usize) -> SeekResult;
 }
 }
 
 
 pub struct OpMetric {}
 pub struct OpMetric {}
 
 
 impl Metric for OpMetric {
 impl Metric for OpMetric {
-    fn seek<'a, 'b>(
-        cursor: &'b mut Cursor<'a>,
-        index: usize,
-    ) -> Result<Option<&'a Operation>, OTError> {
-        let _ = check_bound(cursor.op_index, index)?;
-
-        let mut offset = cursor.op_index;
-        let mut op_at_index = None;
-
-        while let Some(op) = cursor.iterator.next() {
-            if offset != cursor.op_index {
-                cursor.char_index += op.length();
-                cursor.op_index = offset;
-            }
-
-            offset += 1;
-            op_at_index = Some(op);
-
-            if offset >= index {
+    fn seek(cursor: &mut Cursor, index: usize) -> SeekResult {
+        let _ = check_bound(cursor.o_index, index)?;
+        let mut temp_cursor = Cursor::new(cursor.delta, cursor.origin_iv);
+        let mut offset = 0;
+        while let Some(op) = temp_cursor.iter.next() {
+            offset += op.length();
+            if offset > index {
                 break;
                 break;
             }
             }
         }
         }
-
-        Ok(op_at_index)
+        cursor.descend(offset);
+        Ok(())
     }
     }
 }
 }
 
 
 pub struct CharMetric {}
 pub struct CharMetric {}
 
 
 impl Metric for CharMetric {
 impl Metric for CharMetric {
-    fn seek<'a, 'b>(
-        cursor: &'b mut Cursor<'a>,
-        index: usize,
-    ) -> Result<Option<&'a Operation>, OTError> {
-        let _ = check_bound(cursor.char_index, index)?;
-
-        let mut offset = cursor.char_index;
-        let mut op_at_index = None;
-        while let Some(op) = cursor.iterator.next() {
-            if offset != cursor.char_index {
-                cursor.char_index = offset;
-                cursor.op_index += 1;
-            }
-
-            offset += op.length();
-            op_at_index = Some(op);
-
-            if offset >= index {
-                break;
-            }
-        }
-
-        Ok(op_at_index)
+    fn seek(cursor: &mut Cursor, index: usize) -> SeekResult {
+        let _ = check_bound(cursor.c_index, index)?;
+        let _ = cursor.next_op_with_length(Some(index));
+        Ok(())
     }
     }
 }
 }
 
 

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

@@ -12,7 +12,7 @@ pub struct DeltaIter<'a> {
 
 
 impl<'a> DeltaIter<'a> {
 impl<'a> DeltaIter<'a> {
     pub fn new(delta: &'a Delta) -> Self {
     pub fn new(delta: &'a Delta) -> Self {
-        let interval = Interval::new(0, usize::MAX);
+        let interval = Interval::new(0, i32::MAX as usize);
         Self::from_interval(delta, interval)
         Self::from_interval(delta, interval)
     }
     }
 
 
@@ -23,15 +23,23 @@ impl<'a> DeltaIter<'a> {
 
 
     pub fn ops(&mut self) -> Vec<Operation> { self.collect::<Vec<_>>() }
     pub fn ops(&mut self) -> Vec<Operation> { self.collect::<Vec<_>>() }
 
 
-    pub fn seek(&mut self, n_char: usize) -> Result<(), OTError> {
-        let _ = self.cursor.seek::<CharMetric>(n_char)?;
+    pub fn next_op(&mut self) -> Option<Operation> { self.cursor.next_op() }
+
+    pub fn next_op_with_length(&mut self, length: usize) -> Option<Operation> {
+        self.cursor.next_op_with_length(Some(length))
+    }
+
+    pub fn seek<M: Metric>(&mut self, index: usize) -> Result<(), OTError> {
+        let _ = M::seek(&mut self.cursor, index)?;
         Ok(())
         Ok(())
     }
     }
+
+    pub fn has_next(&self) -> bool { self.cursor.has_next() }
 }
 }
 
 
 impl<'a> Iterator for DeltaIter<'a> {
 impl<'a> Iterator for DeltaIter<'a> {
     type Item = Operation;
     type Item = Operation;
-    fn next(&mut self) -> Option<Self::Item> { self.cursor.next_op() }
+    fn next(&mut self) -> Option<Self::Item> { self.next_op() }
 }
 }
 
 
 pub struct AttributesIter<'a> {
 pub struct AttributesIter<'a> {
@@ -100,11 +108,10 @@ impl<'a> Iterator for AttributesIter<'a> {
     }
     }
 }
 }
 
 
-pub(crate) fn attributes_at_index(delta: &Delta, index: usize) -> Attributes {
+pub(crate) fn attributes_with_length(delta: &Delta, index: usize) -> Attributes {
     let mut iter = AttributesIter::new(delta);
     let mut iter = AttributesIter::new(delta);
-    iter.seek(index);
+    iter.seek::<CharMetric>(index);
     match iter.next() {
     match iter.next() {
-        // None => Attributes::Follow,
         None => Attributes::new(),
         None => Attributes::new(),
         Some((_, attributes)) => attributes,
         Some((_, attributes)) => attributes,
     }
     }

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

@@ -62,6 +62,37 @@ impl Operation {
 
 
     pub fn is_empty(&self) -> bool { self.length() == 0 }
     pub fn is_empty(&self) -> bool { self.length() == 0 }
 
 
+    pub fn split(&self, index: usize) -> (Option<Operation>, Option<Operation>) {
+        debug_assert!(index < self.length());
+        let mut left = None;
+        let mut right = None;
+        match self {
+            Operation::Delete(n) => {
+                left = Some(Builder::delete(index).build());
+                right = Some(Builder::delete(*n - index).build());
+            },
+            Operation::Retain(retain) => {
+                left = Some(Builder::delete(index).build());
+                right = Some(Builder::delete(retain.n - index).build());
+            },
+            Operation::Insert(insert) => {
+                let attributes = self.get_attributes();
+                left = Some(
+                    Builder::insert(&insert.s[0..index])
+                        .attributes(attributes.clone())
+                        .build(),
+                );
+                right = Some(
+                    Builder::insert(&insert.s[index..insert.num_chars()])
+                        .attributes(attributes)
+                        .build(),
+                );
+            },
+        }
+
+        (left, right)
+    }
+
     pub fn shrink(&self, interval: Interval) -> Option<Operation> {
     pub fn shrink(&self, interval: Interval) -> Option<Operation> {
         let op = match self {
         let op = match self {
             Operation::Delete(n) => Builder::delete(min(*n, interval.size())).build(),
             Operation::Delete(n) => Builder::delete(min(*n, interval.size())).build(),

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

@@ -60,7 +60,7 @@ impl OpTester {
         static INIT: Once = Once::new();
         static INIT: Once = Once::new();
         INIT.call_once(|| {
         INIT.call_once(|| {
             color_eyre::install().unwrap();
             color_eyre::install().unwrap();
-            std::env::set_var("RUST_LOG", "debug");
+            std::env::set_var("RUST_LOG", "info");
             env_logger::init();
             env_logger::init();
         });
         });
 
 

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

@@ -36,6 +36,11 @@ fn delta_get_ops_in_interval_2() {
         vec![Builder::insert("12").build()]
         vec![Builder::insert("12").build()]
     );
     );
 
 
+    assert_eq!(
+        DeltaIter::from_interval(&delta, Interval::new(1, 3)).ops(),
+        vec![Builder::insert("23").build()]
+    );
+
     assert_eq!(
     assert_eq!(
         DeltaIter::from_interval(&delta, Interval::new(0, 3)).ops(),
         DeltaIter::from_interval(&delta, Interval::new(0, 3)).ops(),
         vec![insert_a.clone()]
         vec![insert_a.clone()]
@@ -110,10 +115,10 @@ fn delta_get_ops_in_interval_5() {
         vec![Builder::insert("56").build(), Builder::insert("78").build()]
         vec![Builder::insert("56").build(), Builder::insert("78").build()]
     );
     );
 
 
-    assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(8, 9)).ops(),
-        vec![Builder::insert("9").build()]
-    );
+    // assert_eq!(
+    //     DeltaIter::from_interval(&delta, Interval::new(8, 9)).ops(),
+    //     vec![Builder::insert("9").build()]
+    // );
 }
 }
 
 
 #[test]
 #[test]
@@ -127,6 +132,91 @@ fn delta_get_ops_in_interval_6() {
     );
     );
 }
 }
 
 
+#[test]
+fn delta_get_ops_in_interval_7() {
+    let mut delta = Delta::default();
+    let insert_a = Builder::insert("12345").build();
+    let retain_a = Builder::retain(3).build();
+
+    delta.add(insert_a.clone());
+    delta.add(retain_a.clone());
+
+    let mut iter_1 = DeltaIter::new(&delta);
+    iter_1.seek::<CharMetric>(2).unwrap();
+    assert_eq!(iter_1.next_op().unwrap(), Builder::insert("345").build());
+    assert_eq!(iter_1.next_op().unwrap(), Builder::retain(3).build());
+
+    let mut iter_2 = DeltaIter::new(&delta);
+    assert_eq!(
+        iter_2.next_op_with_length(2).unwrap(),
+        Builder::insert("12").build()
+    );
+    assert_eq!(iter_2.next_op().unwrap(), Builder::insert("345").build());
+
+    assert_eq!(iter_2.next_op().unwrap(), Builder::retain(3).build());
+}
+
+#[test]
+fn delta_seek_1() {
+    let mut delta = Delta::default();
+    let insert_a = Builder::insert("12345").build();
+    let retain_a = Builder::retain(3).build();
+    delta.add(insert_a.clone());
+    delta.add(retain_a.clone());
+    let mut iter = DeltaIter::new(&delta);
+    iter.seek::<OpMetric>(1).unwrap();
+    assert_eq!(iter.next_op().unwrap(), Builder::retain(3).build());
+}
+
+#[test]
+fn delta_seek_2() {
+    let mut delta = Delta::default();
+    delta.add(Builder::insert("12345").build());
+
+    let mut iter = DeltaIter::new(&delta);
+    assert_eq!(
+        iter.next_op_with_length(1).unwrap(),
+        Builder::insert("1").build()
+    );
+}
+
+#[test]
+fn delta_seek_3() {
+    let mut delta = Delta::default();
+    delta.add(Builder::insert("12345").build());
+
+    let mut iter = DeltaIter::new(&delta);
+    assert_eq!(
+        iter.next_op_with_length(2).unwrap(),
+        Builder::insert("12").build()
+    );
+
+    assert_eq!(
+        iter.next_op_with_length(2).unwrap(),
+        Builder::insert("34").build()
+    );
+
+    assert_eq!(
+        iter.next_op_with_length(2).unwrap(),
+        Builder::insert("5").build()
+    );
+
+    assert_eq!(iter.next_op_with_length(1), None);
+}
+
+#[test]
+fn delta_seek_4() {
+    let mut delta = Delta::default();
+    delta.add(Builder::insert("12345").build());
+
+    let mut iter = DeltaIter::new(&delta);
+    iter.seek::<CharMetric>(3);
+    assert_eq!(
+        iter.next_op_with_length(2).unwrap(),
+        Builder::insert("45").build()
+    );
+}
+
 #[test]
 #[test]
 fn lengths() {
 fn lengths() {
     let mut delta = Delta::default();
     let mut delta = Delta::default();