Bladeren bron

extend cursor with generic metric

appflowy 4 jaren geleden
bovenliggende
commit
bc7da582a3

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

@@ -47,6 +47,8 @@ impl Document {
     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, interval)?;
+
         self.update_with_attribute(attribute, interval)
     }
 

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

@@ -1,13 +1,43 @@
 use crate::{
     client::view::FormatExt,
-    core::{Attribute, Delta, Interval},
+    core::{Attribute, AttributeScope, Attributes, Delta, DeltaBuilder, DeltaIter, Interval},
 };
 
 pub struct FormatLinkAtCaretPositionExt {}
 
 impl FormatExt for FormatLinkAtCaretPositionExt {
     fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
-        unimplemented!()
+        let mut iter = DeltaIter::new(delta);
+        iter.seek(interval.start);
+        let (before, after) = (iter.next(), iter.next());
+        let mut start = interval.start;
+        let mut retain = 0;
+
+        if let Some(before) = before {
+            if before.contain_attribute(attribute) {
+                start -= before.length();
+                retain += before.length();
+            }
+        }
+
+        if let Some(after) = after {
+            if after.contain_attribute(attribute) {
+                if retain != 0 {
+                    retain += after.length();
+                }
+            }
+        }
+
+        if retain == 0 {
+            return None;
+        }
+
+        Some(
+            DeltaBuilder::new()
+                .retain(start, Attributes::default())
+                .retain(retain, (attribute.clone()).into())
+                .build(),
+        )
     }
 }
 
@@ -15,7 +45,17 @@ pub struct ResolveLineFormatExt {}
 
 impl FormatExt for ResolveLineFormatExt {
     fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
-        unimplemented!()
+        if attribute.scope != AttributeScope::Block {
+            return None;
+        }
+
+        let mut new_delta = Delta::new();
+        new_delta.retain(interval.start, Attributes::default());
+
+        let mut iter = DeltaIter::new(delta);
+        iter.seek(interval.start);
+
+        None
     }
 }
 

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

@@ -44,7 +44,7 @@ impl InsertExt for ResetLineFormatOnNewLineExt {
         }
 
         let mut iter = DeltaIter::new(delta);
-        iter.seek_to(index);
+        iter.seek(index);
         let maybe_next_op = iter.next();
         if maybe_next_op.is_none() {
             return None;

+ 10 - 2
rust-lib/flowy-ot/src/core/attributes/builder.rs

@@ -71,7 +71,7 @@ pub enum AttributeKey {
     UnChecked,
 }
 
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Eq, Clone)]
 pub enum AttributeScope {
     Inline,
     Block,
@@ -79,7 +79,7 @@ pub enum AttributeScope {
     Ignore,
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Attribute {
     pub key: AttributeKey,
     pub value: String,
@@ -93,6 +93,14 @@ impl fmt::Display for Attribute {
     }
 }
 
+impl std::convert::Into<Attributes> for Attribute {
+    fn into(self) -> Attributes {
+        let mut attributes = Attributes::new();
+        attributes.add(self);
+        attributes
+    }
+}
+
 pub struct AttrsBuilder {
     inner: Attributes,
 }

+ 86 - 30
rust-lib/flowy-ot/src/core/delta/cursor.rs

@@ -8,8 +8,9 @@ pub struct Cursor<'a> {
     delta: &'a Delta,
     interval: Interval,
     iterator: Iter<'a, Operation>,
-    offset: usize,
-    offset_op: Option<&'a Operation>,
+    char_index: usize,
+    op_index: usize,
+    current_op: Option<&'a Operation>,
 }
 
 impl<'a> Cursor<'a> {
@@ -18,29 +19,31 @@ impl<'a> Cursor<'a> {
             delta,
             interval,
             iterator: delta.ops.iter(),
-            offset: 0,
-            offset_op: None,
+            char_index: 0,
+            op_index: 0,
+            current_op: None,
         };
         cursor
     }
 
     pub fn next_op(&mut self) -> Option<Operation> {
-        let mut next_op = self.offset_op.take();
-
+        let mut next_op = self.current_op.take();
         if next_op.is_none() {
             next_op = self.iterator.next();
         }
 
         let mut find_op = None;
         while find_op.is_none() && next_op.is_some() {
+            self.op_index += 1;
+
             let op = next_op.unwrap();
-            if self.offset < self.interval.start {
-                let intersect =
-                    Interval::new(self.offset, self.offset + op.length()).intersect(self.interval);
+            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.offset += op.length();
+                    self.char_index += op.length();
                 } else {
-                    if let Some(new_op) = op.shrink(intersect.translate_neg(self.offset)) {
+                    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  │
@@ -50,11 +53,11 @@ impl<'a> Cursor<'a> {
                         // op = "45"
                         find_op = Some(new_op);
                     }
-                    self.offset = intersect.end;
+                    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.offset)) {
+                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
@@ -67,43 +70,96 @@ impl<'a> Cursor<'a> {
                 // └──────┘  └─▲────┘  └───▲──┘
                 //             │  [2, 5)   │
                 //
-                self.offset += min(self.interval.size(), op.length());
+                self.char_index += min(self.interval.size(), op.length());
             }
 
             match find_op {
                 None => next_op = self.iterator.next(),
-                Some(_) => self.interval.start = self.offset,
+                Some(_) => self.interval.start = self.char_index,
             }
         }
 
         find_op
     }
 
-    pub fn seek_to(&mut self, index: usize) -> Result<(), OTError> {
-        if self.offset > index {
-            let msg = format!(
-                "{} should be greater than current offset: {}",
-                index, self.offset
-            );
-            return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength)
-                .msg(&msg)
-                .build());
+    pub fn seek<M: Metric>(&mut self, index: usize) -> Result<(), OTError> {
+        self.current_op = M::seek(self, index)?;
+        Ok(())
+    }
+}
+
+pub trait Metric {
+    fn seek<'a, 'b>(
+        cursor: &'b mut Cursor<'a>,
+        index: usize,
+    ) -> Result<Option<&'a Operation>, OTError>;
+}
+
+pub struct 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 {
+                break;
+            }
         }
 
-        let mut offset = 0;
-        while let Some(op) = self.iterator.next() {
-            if offset != 0 {
-                self.offset = offset;
+        Ok(op_at_index)
+    }
+}
+
+pub struct 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();
-            self.offset_op = Some(op);
+            op_at_index = Some(op);
 
             if offset >= index {
                 break;
             }
         }
 
-        Ok(())
+        Ok(op_at_index)
+    }
+}
+
+fn check_bound(current: usize, target: usize) -> Result<(), OTError> {
+    if current > target {
+        let msg = format!("{} should be greater than current: {}", target, current);
+        return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength)
+            .msg(&msg)
+            .build());
     }
+    Ok(())
 }

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

@@ -23,8 +23,8 @@ impl<'a> DeltaIter<'a> {
 
     pub fn ops(&mut self) -> Vec<Operation> { self.collect::<Vec<_>>() }
 
-    pub fn seek_to(&mut self, n_char: usize) -> Result<(), OTError> {
-        let _ = self.cursor.seek_to(n_char)?;
+    pub fn seek(&mut self, n_char: usize) -> Result<(), OTError> {
+        let _ = self.cursor.seek::<CharMetric>(n_char)?;
         Ok(())
     }
 }
@@ -102,7 +102,7 @@ impl<'a> Iterator for AttributesIter<'a> {
 
 pub(crate) fn attributes_at_index(delta: &Delta, index: usize) -> Attributes {
     let mut iter = AttributesIter::new(delta);
-    iter.seek_to(index);
+    iter.seek(index);
     match iter.next() {
         // None => Attributes::Follow,
         None => Attributes::new(),

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

@@ -1,4 +1,4 @@
-use crate::core::{Attributes, Builder, Interval};
+use crate::core::{Attribute, Attributes, Builder, Interval};
 use bytecount::num_chars;
 use serde::__private::Formatter;
 use std::{
@@ -48,6 +48,10 @@ impl Operation {
 
     pub fn has_attribute(&self) -> bool { !self.get_attributes().is_empty() }
 
+    pub fn contain_attribute(&self, attribute: &Attribute) -> bool {
+        self.get_attributes().contains_key(&attribute.key)
+    }
+
     pub fn length(&self) -> usize {
         match self {
             Operation::Delete(n) => *n,