mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-11-04 03:54:44 +00:00 
			
		
		
		
	config ot invert detal
This commit is contained in:
		
							parent
							
								
									958918e0a0
								
							
						
					
					
						commit
						80a94880fc
					
				@ -27,6 +27,7 @@ impl Attributes {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // remove attribute if the value is PLAIN
 | 
			
		||||
    // { "k": PLAIN } -> {}
 | 
			
		||||
    pub fn remove_plain(&mut self) {
 | 
			
		||||
        match self {
 | 
			
		||||
            Attributes::Follow => {},
 | 
			
		||||
@ -36,6 +37,14 @@ impl Attributes {
 | 
			
		||||
            Attributes::Empty => {},
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_attributes_data(&self) -> Option<AttributesData> {
 | 
			
		||||
        match self {
 | 
			
		||||
            Attributes::Follow => None,
 | 
			
		||||
            Attributes::Custom(data) => Some(data.clone()),
 | 
			
		||||
            Attributes::Empty => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::default::Default for Attributes {
 | 
			
		||||
@ -131,7 +140,7 @@ impl AttrsBuilder {
 | 
			
		||||
    pub fn build(self) -> Attributes { Attributes::Custom(self.inner) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn attributes_from(operation: &Option<Operation>) -> Option<Attributes> {
 | 
			
		||||
pub(crate) fn attributes_from(operation: &Option<Operation>) -> Option<Attributes> {
 | 
			
		||||
    match operation {
 | 
			
		||||
        None => None,
 | 
			
		||||
        Some(operation) => Some(operation.get_attributes()),
 | 
			
		||||
@ -196,6 +205,37 @@ pub fn transform_attributes(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn invert_attributes(attr: Attributes, base: Attributes) -> Attributes {
 | 
			
		||||
    let attr = attr.get_attributes_data();
 | 
			
		||||
    let base = base.get_attributes_data();
 | 
			
		||||
 | 
			
		||||
    if attr.is_none() && base.is_none() {
 | 
			
		||||
        return Attributes::Empty;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let attr = attr.unwrap_or(AttributesData::new());
 | 
			
		||||
    let base = base.unwrap_or(AttributesData::new());
 | 
			
		||||
 | 
			
		||||
    let base_inverted = base
 | 
			
		||||
        .iter()
 | 
			
		||||
        .fold(AttributesData::new(), |mut attributes, (k, v)| {
 | 
			
		||||
            if base.get(k) != attr.get(k) && attr.contains_key(k) {
 | 
			
		||||
                attributes.insert(k.clone(), v.clone());
 | 
			
		||||
            }
 | 
			
		||||
            attributes
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    let inverted = attr.iter().fold(base_inverted, |mut attributes, (k, _)| {
 | 
			
		||||
        if base.get(k) != attr.get(k) && !base.contains_key(k) {
 | 
			
		||||
            // attributes.insert(k.clone(), "".to_owned());
 | 
			
		||||
            attributes.remove(k);
 | 
			
		||||
        }
 | 
			
		||||
        attributes
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return Attributes::Custom(inverted);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn transform_attribute_data(left: AttributesData, right: AttributesData) -> AttributesData {
 | 
			
		||||
    let result = right
 | 
			
		||||
        .iter()
 | 
			
		||||
@ -207,29 +247,3 @@ fn transform_attribute_data(left: AttributesData, right: AttributesData) -> Attr
 | 
			
		||||
        });
 | 
			
		||||
    result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pub fn invert_attributes(
 | 
			
		||||
//     attr: Option<AttributesData>,
 | 
			
		||||
//     base: Option<AttributesData>,
 | 
			
		||||
// ) -> AttributesData {
 | 
			
		||||
//     let attr = attr.unwrap_or(AttributesData::new());
 | 
			
		||||
//     let base = base.unwrap_or(AttributesData::new());
 | 
			
		||||
//
 | 
			
		||||
//     let base_inverted = base
 | 
			
		||||
//         .iter()
 | 
			
		||||
//         .fold(AttributesData::new(), |mut attributes, (k, v)| {
 | 
			
		||||
//             if base.get(k) != attr.get(k) && attr.contains_key(k) {
 | 
			
		||||
//                 attributes.insert(k.clone(), v.clone());
 | 
			
		||||
//             }
 | 
			
		||||
//             attributes
 | 
			
		||||
//         });
 | 
			
		||||
//
 | 
			
		||||
//     let inverted = attr.iter().fold(base_inverted, |mut attributes, (k, _)| {
 | 
			
		||||
//         if base.get(k) != attr.get(k) && !base.contains_key(k) {
 | 
			
		||||
//             attributes.insert(k.clone(), "".to_owned());
 | 
			
		||||
//         }
 | 
			
		||||
//         attributes
 | 
			
		||||
//     });
 | 
			
		||||
//
 | 
			
		||||
//     return inverted;
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    core::{attributes::*, operation::*},
 | 
			
		||||
    core::{attributes::*, operation::*, Interval},
 | 
			
		||||
    errors::OTError,
 | 
			
		||||
};
 | 
			
		||||
use bytecount::num_chars;
 | 
			
		||||
@ -70,7 +70,7 @@ impl Delta {
 | 
			
		||||
        match op {
 | 
			
		||||
            Operation::Delete(i) => self.delete(i),
 | 
			
		||||
            Operation::Insert(i) => self.insert(&i.s, i.attributes),
 | 
			
		||||
            Operation::Retain(r) => self.retain(r.num, r.attributes),
 | 
			
		||||
            Operation::Retain(r) => self.retain(r.n, r.attributes),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -95,11 +95,11 @@ impl Delta {
 | 
			
		||||
        let new_last = match self.ops.as_mut_slice() {
 | 
			
		||||
            [.., Operation::Insert(insert)] => {
 | 
			
		||||
                //
 | 
			
		||||
                merge_insert_or_new_op(insert, s, attrs)
 | 
			
		||||
                insert.merge_or_new_op(s, attrs)
 | 
			
		||||
            },
 | 
			
		||||
            [.., Operation::Insert(pre_insert), Operation::Delete(_)] => {
 | 
			
		||||
                //
 | 
			
		||||
                merge_insert_or_new_op(pre_insert, s, attrs)
 | 
			
		||||
                pre_insert.merge_or_new_op(s, attrs)
 | 
			
		||||
            },
 | 
			
		||||
            [.., op_last @ Operation::Delete(_)] => {
 | 
			
		||||
                let new_last = op_last.clone();
 | 
			
		||||
@ -123,9 +123,8 @@ impl Delta {
 | 
			
		||||
        self.target_len += n as usize;
 | 
			
		||||
 | 
			
		||||
        if let Some(Operation::Retain(retain)) = self.ops.last_mut() {
 | 
			
		||||
            match merge_retain_or_new_op(retain, n, attrs) {
 | 
			
		||||
                None => {},
 | 
			
		||||
                Some(new_op) => self.ops.push(new_op),
 | 
			
		||||
            if let Some(new_op) = retain.merge_or_new_op(n, attrs) {
 | 
			
		||||
                self.ops.push(new_op);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            self.ops
 | 
			
		||||
@ -175,24 +174,24 @@ impl Delta {
 | 
			
		||||
                    let composed_attrs = compose_attributes(&next_op1, &next_op2);
 | 
			
		||||
                    log::debug!(
 | 
			
		||||
                        "[retain:{} - retain:{}]: {:?}",
 | 
			
		||||
                        retain.num,
 | 
			
		||||
                        o_retain.num,
 | 
			
		||||
                        retain.n,
 | 
			
		||||
                        o_retain.n,
 | 
			
		||||
                        composed_attrs
 | 
			
		||||
                    );
 | 
			
		||||
                    match retain.cmp(&o_retain) {
 | 
			
		||||
                        Ordering::Less => {
 | 
			
		||||
                            new_delta.retain(retain.num, composed_attrs);
 | 
			
		||||
                            next_op2 = Some(OpBuilder::retain(o_retain.num - retain.num).build());
 | 
			
		||||
                            new_delta.retain(retain.n, composed_attrs);
 | 
			
		||||
                            next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build());
 | 
			
		||||
                            next_op1 = ops1.next();
 | 
			
		||||
                        },
 | 
			
		||||
                        std::cmp::Ordering::Equal => {
 | 
			
		||||
                            new_delta.retain(retain.num, composed_attrs);
 | 
			
		||||
                            new_delta.retain(retain.n, composed_attrs);
 | 
			
		||||
                            next_op1 = ops1.next();
 | 
			
		||||
                            next_op2 = ops2.next();
 | 
			
		||||
                        },
 | 
			
		||||
                        std::cmp::Ordering::Greater => {
 | 
			
		||||
                            new_delta.retain(o_retain.num, composed_attrs);
 | 
			
		||||
                            next_op1 = Some(OpBuilder::retain(retain.num - o_retain.num).build());
 | 
			
		||||
                            new_delta.retain(o_retain.n, composed_attrs);
 | 
			
		||||
                            next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build());
 | 
			
		||||
                            next_op2 = ops2.next();
 | 
			
		||||
                        },
 | 
			
		||||
                    }
 | 
			
		||||
@ -227,14 +226,14 @@ impl Delta {
 | 
			
		||||
                    log::debug!(
 | 
			
		||||
                        "[insert:{} - retain:{}]: {:?}",
 | 
			
		||||
                        insert.s,
 | 
			
		||||
                        o_retain.num,
 | 
			
		||||
                        o_retain.n,
 | 
			
		||||
                        composed_attrs
 | 
			
		||||
                    );
 | 
			
		||||
                    match (insert.num_chars()).cmp(o_retain) {
 | 
			
		||||
                        Ordering::Less => {
 | 
			
		||||
                            new_delta.insert(&insert.s, composed_attrs.clone());
 | 
			
		||||
                            next_op2 = Some(
 | 
			
		||||
                                OpBuilder::retain(o_retain.num - insert.num_chars())
 | 
			
		||||
                                OpBuilder::retain(o_retain.n - insert.num_chars())
 | 
			
		||||
                                    .attributes(composed_attrs.clone())
 | 
			
		||||
                                    .build(),
 | 
			
		||||
                            );
 | 
			
		||||
@ -248,7 +247,7 @@ impl Delta {
 | 
			
		||||
                        Ordering::Greater => {
 | 
			
		||||
                            let chars = &mut insert.chars();
 | 
			
		||||
                            new_delta.insert(
 | 
			
		||||
                                &chars.take(o_retain.num as usize).collect::<String>(),
 | 
			
		||||
                                &chars.take(o_retain.n as usize).collect::<String>(),
 | 
			
		||||
                                composed_attrs,
 | 
			
		||||
                            );
 | 
			
		||||
                            next_op1 = Some(
 | 
			
		||||
@ -263,8 +262,8 @@ impl Delta {
 | 
			
		||||
                (Some(Operation::Retain(retain)), Some(Operation::Delete(o_num))) => {
 | 
			
		||||
                    match retain.cmp(&o_num) {
 | 
			
		||||
                        Ordering::Less => {
 | 
			
		||||
                            new_delta.delete(retain.num);
 | 
			
		||||
                            next_op2 = Some(OpBuilder::delete(*o_num - retain.num).build());
 | 
			
		||||
                            new_delta.delete(retain.n);
 | 
			
		||||
                            next_op2 = Some(OpBuilder::delete(*o_num - retain.n).build());
 | 
			
		||||
                            next_op1 = ops1.next();
 | 
			
		||||
                        },
 | 
			
		||||
                        Ordering::Equal => {
 | 
			
		||||
@ -274,7 +273,7 @@ impl Delta {
 | 
			
		||||
                        },
 | 
			
		||||
                        Ordering::Greater => {
 | 
			
		||||
                            new_delta.delete(*o_num);
 | 
			
		||||
                            next_op1 = Some(OpBuilder::retain(retain.num - *o_num).build());
 | 
			
		||||
                            next_op1 = Some(OpBuilder::retain(retain.n - *o_num).build());
 | 
			
		||||
                            next_op2 = ops2.next();
 | 
			
		||||
                        },
 | 
			
		||||
                    }
 | 
			
		||||
@ -331,21 +330,21 @@ impl Delta {
 | 
			
		||||
                    let composed_attrs = transform_attributes(&next_op1, &next_op2, true);
 | 
			
		||||
                    match retain.cmp(&o_retain) {
 | 
			
		||||
                        Ordering::Less => {
 | 
			
		||||
                            a_prime.retain(retain.num, composed_attrs.clone());
 | 
			
		||||
                            b_prime.retain(retain.num, composed_attrs.clone());
 | 
			
		||||
                            next_op2 = Some(OpBuilder::retain(o_retain.num - retain.num).build());
 | 
			
		||||
                            a_prime.retain(retain.n, composed_attrs.clone());
 | 
			
		||||
                            b_prime.retain(retain.n, composed_attrs.clone());
 | 
			
		||||
                            next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build());
 | 
			
		||||
                            next_op1 = ops1.next();
 | 
			
		||||
                        },
 | 
			
		||||
                        Ordering::Equal => {
 | 
			
		||||
                            a_prime.retain(retain.num, composed_attrs.clone());
 | 
			
		||||
                            b_prime.retain(retain.num, composed_attrs.clone());
 | 
			
		||||
                            a_prime.retain(retain.n, composed_attrs.clone());
 | 
			
		||||
                            b_prime.retain(retain.n, composed_attrs.clone());
 | 
			
		||||
                            next_op1 = ops1.next();
 | 
			
		||||
                            next_op2 = ops2.next();
 | 
			
		||||
                        },
 | 
			
		||||
                        Ordering::Greater => {
 | 
			
		||||
                            a_prime.retain(o_retain.num, composed_attrs.clone());
 | 
			
		||||
                            b_prime.retain(o_retain.num, composed_attrs.clone());
 | 
			
		||||
                            next_op1 = Some(OpBuilder::retain(retain.num - o_retain.num).build());
 | 
			
		||||
                            a_prime.retain(o_retain.n, composed_attrs.clone());
 | 
			
		||||
                            b_prime.retain(o_retain.n, composed_attrs.clone());
 | 
			
		||||
                            next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build());
 | 
			
		||||
                            next_op2 = ops2.next();
 | 
			
		||||
                        },
 | 
			
		||||
                    };
 | 
			
		||||
@ -368,7 +367,7 @@ impl Delta {
 | 
			
		||||
                    match i.cmp(&o_retain) {
 | 
			
		||||
                        Ordering::Less => {
 | 
			
		||||
                            a_prime.delete(*i);
 | 
			
		||||
                            next_op2 = Some(OpBuilder::retain(o_retain.num - *i).build());
 | 
			
		||||
                            next_op2 = Some(OpBuilder::retain(o_retain.n - *i).build());
 | 
			
		||||
                            next_op1 = ops1.next();
 | 
			
		||||
                        },
 | 
			
		||||
                        Ordering::Equal => {
 | 
			
		||||
@ -377,8 +376,8 @@ impl Delta {
 | 
			
		||||
                            next_op2 = ops2.next();
 | 
			
		||||
                        },
 | 
			
		||||
                        Ordering::Greater => {
 | 
			
		||||
                            a_prime.delete(o_retain.num);
 | 
			
		||||
                            next_op1 = Some(OpBuilder::delete(*i - o_retain.num).build());
 | 
			
		||||
                            a_prime.delete(o_retain.n);
 | 
			
		||||
                            next_op1 = Some(OpBuilder::delete(*i - o_retain.n).build());
 | 
			
		||||
                            next_op2 = ops2.next();
 | 
			
		||||
                        },
 | 
			
		||||
                    };
 | 
			
		||||
@ -386,18 +385,18 @@ impl Delta {
 | 
			
		||||
                (Some(Operation::Retain(retain)), Some(Operation::Delete(j))) => {
 | 
			
		||||
                    match retain.cmp(&j) {
 | 
			
		||||
                        Ordering::Less => {
 | 
			
		||||
                            b_prime.delete(retain.num);
 | 
			
		||||
                            next_op2 = Some(OpBuilder::delete(*j - retain.num).build());
 | 
			
		||||
                            b_prime.delete(retain.n);
 | 
			
		||||
                            next_op2 = Some(OpBuilder::delete(*j - retain.n).build());
 | 
			
		||||
                            next_op1 = ops1.next();
 | 
			
		||||
                        },
 | 
			
		||||
                        Ordering::Equal => {
 | 
			
		||||
                            b_prime.delete(retain.num);
 | 
			
		||||
                            b_prime.delete(retain.n);
 | 
			
		||||
                            next_op1 = ops1.next();
 | 
			
		||||
                            next_op2 = ops2.next();
 | 
			
		||||
                        },
 | 
			
		||||
                        Ordering::Greater => {
 | 
			
		||||
                            b_prime.delete(*j);
 | 
			
		||||
                            next_op1 = Some(OpBuilder::retain(retain.num - *j).build());
 | 
			
		||||
                            next_op1 = Some(OpBuilder::retain(retain.n - *j).build());
 | 
			
		||||
                            next_op2 = ops2.next();
 | 
			
		||||
                        },
 | 
			
		||||
                    };
 | 
			
		||||
@ -423,7 +422,7 @@ impl Delta {
 | 
			
		||||
        for op in &self.ops {
 | 
			
		||||
            match &op {
 | 
			
		||||
                Operation::Retain(retain) => {
 | 
			
		||||
                    for c in chars.take(retain.num as usize) {
 | 
			
		||||
                    for c in chars.take(retain.n as usize) {
 | 
			
		||||
                        new_s.push(c);
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
@ -448,8 +447,11 @@ impl Delta {
 | 
			
		||||
        for op in &self.ops {
 | 
			
		||||
            match &op {
 | 
			
		||||
                Operation::Retain(retain) => {
 | 
			
		||||
                    inverted.retain(retain.num, Attributes::Follow);
 | 
			
		||||
                    for _ in 0..retain.num {
 | 
			
		||||
                    inverted.retain(retain.n, Attributes::Follow);
 | 
			
		||||
 | 
			
		||||
                    // TODO: use advance_by instead, but it's unstable now
 | 
			
		||||
                    // chars.advance_by(retain.num)
 | 
			
		||||
                    for _ in 0..retain.n {
 | 
			
		||||
                        chars.next();
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
@ -467,6 +469,54 @@ impl Delta {
 | 
			
		||||
        inverted
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn invert_delta(&self, other: &Delta) -> Delta {
 | 
			
		||||
        let mut inverted = Delta::default();
 | 
			
		||||
        if other.is_empty() {
 | 
			
		||||
            return inverted;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let a = |inverted: &mut Delta, op: &Operation, index: usize, op_len: usize| {
 | 
			
		||||
            let ops = other.ops_in_interval(Interval::new(index, op_len));
 | 
			
		||||
            ops.into_iter().for_each(|other_op| match op {
 | 
			
		||||
                Operation::Delete(_) => {
 | 
			
		||||
                    inverted.add(other_op);
 | 
			
		||||
                },
 | 
			
		||||
                Operation::Retain(_) => {},
 | 
			
		||||
                Operation::Insert(_) => {
 | 
			
		||||
                    if !op.is_plain() {
 | 
			
		||||
                        let inverted_attrs =
 | 
			
		||||
                            invert_attributes(op.get_attributes(), other_op.get_attributes());
 | 
			
		||||
                        inverted.retain(other_op.length(), inverted_attrs);
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let mut index = 0;
 | 
			
		||||
        for op in &self.ops {
 | 
			
		||||
            let op_len: usize = op.length() as usize;
 | 
			
		||||
            match op {
 | 
			
		||||
                Operation::Delete(_) => {
 | 
			
		||||
                    a(&mut inverted, op, index, op_len);
 | 
			
		||||
                    index += op_len;
 | 
			
		||||
                },
 | 
			
		||||
                Operation::Retain(_) => {
 | 
			
		||||
                    if op.is_plain() {
 | 
			
		||||
                        inverted.retain(op_len as u64, Attributes::Empty);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        a(&mut inverted, op, index, op_len as usize);
 | 
			
		||||
                    }
 | 
			
		||||
                    index += op_len;
 | 
			
		||||
                },
 | 
			
		||||
                Operation::Insert(insert) => {
 | 
			
		||||
                    inverted.delete(op_len as u64);
 | 
			
		||||
                },
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        inverted
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Checks if this operation has no effect.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn is_noop(&self) -> bool {
 | 
			
		||||
@ -477,66 +527,64 @@ impl Delta {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the length of a string these operations can be applied to
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn base_len(&self) -> usize { self.base_len }
 | 
			
		||||
 | 
			
		||||
    /// Returns the length of the resulting string after the operations have
 | 
			
		||||
    /// been applied.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn target_len(&self) -> usize { self.target_len }
 | 
			
		||||
 | 
			
		||||
    /// Returns the wrapped sequence of operations.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn ops(&self) -> &[Operation] { &self.ops }
 | 
			
		||||
 | 
			
		||||
    pub fn is_empty(&self) -> bool { self.ops.is_empty() }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn merge_insert_or_new_op(
 | 
			
		||||
    insert: &mut Insert,
 | 
			
		||||
    s: &str,
 | 
			
		||||
    attributes: Attributes,
 | 
			
		||||
) -> Option<Operation> {
 | 
			
		||||
    match &attributes {
 | 
			
		||||
        Attributes::Follow => {
 | 
			
		||||
            insert.s += s;
 | 
			
		||||
            return None;
 | 
			
		||||
        },
 | 
			
		||||
        Attributes::Custom(_) | Attributes::Empty => {
 | 
			
		||||
            if insert.attributes == attributes {
 | 
			
		||||
                insert.s += s;
 | 
			
		||||
                None
 | 
			
		||||
            } else {
 | 
			
		||||
                Some(OpBuilder::insert(s).attributes(attributes).build())
 | 
			
		||||
    pub fn ops_in_interval(&self, interval: Interval) -> Vec<Operation> {
 | 
			
		||||
        let mut ops: Vec<Operation> = Vec::with_capacity(self.ops.len());
 | 
			
		||||
        let mut offset: usize = 0;
 | 
			
		||||
        let mut ops_iter = self.ops.iter();
 | 
			
		||||
        let mut op = ops_iter.next();
 | 
			
		||||
 | 
			
		||||
        while offset < interval.end && op.is_some() {
 | 
			
		||||
            if let Some(op) = op {
 | 
			
		||||
                if offset < interval.start {
 | 
			
		||||
                    offset += op.length() as usize;
 | 
			
		||||
                } else {
 | 
			
		||||
                    ops.push(op.clone());
 | 
			
		||||
                    offset += op.length() as usize;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn merge_retain_or_new_op(
 | 
			
		||||
    retain: &mut Retain,
 | 
			
		||||
    n: u64,
 | 
			
		||||
    attributes: Attributes,
 | 
			
		||||
) -> Option<Operation> {
 | 
			
		||||
    log::debug!(
 | 
			
		||||
        "merge_retain_or_new_op: {:?}, {:?}",
 | 
			
		||||
        retain.attributes,
 | 
			
		||||
        attributes
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    match &attributes {
 | 
			
		||||
        Attributes::Follow => {
 | 
			
		||||
            retain.num += n;
 | 
			
		||||
            None
 | 
			
		||||
        },
 | 
			
		||||
        Attributes::Custom(_) | Attributes::Empty => {
 | 
			
		||||
            if retain.attributes == attributes {
 | 
			
		||||
                retain.num += n;
 | 
			
		||||
                None
 | 
			
		||||
            } else {
 | 
			
		||||
                Some(OpBuilder::retain(n).attributes(attributes).build())
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
            op = ops_iter.next();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ops
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn attributes_in_interval(&self, interval: Interval) -> Attributes {
 | 
			
		||||
        let mut attributes_data = AttributesData::new();
 | 
			
		||||
        let mut offset: usize = 0;
 | 
			
		||||
 | 
			
		||||
        self.ops.iter().for_each(|op| match op {
 | 
			
		||||
            Operation::Delete(_n) => {},
 | 
			
		||||
            Operation::Retain(_retain) => {
 | 
			
		||||
                unimplemented!()
 | 
			
		||||
                // if interval.contains(retain.n as usize) {
 | 
			
		||||
                //     match &retain.attributes {
 | 
			
		||||
                //         Attributes::Follow => {},
 | 
			
		||||
                //         Attributes::Custom(data) => {
 | 
			
		||||
                //             attributes_data.extend(data.clone());
 | 
			
		||||
                //         },
 | 
			
		||||
                //         Attributes::Empty => {},
 | 
			
		||||
                //     }
 | 
			
		||||
                // }
 | 
			
		||||
            },
 | 
			
		||||
            Operation::Insert(insert) => match &insert.attributes {
 | 
			
		||||
                Attributes::Follow => {},
 | 
			
		||||
                Attributes::Custom(data) => {
 | 
			
		||||
                    let end = insert.num_chars() as usize;
 | 
			
		||||
                    if interval.contains_range(offset, offset + end) {
 | 
			
		||||
                        attributes_data.extend(data.clone());
 | 
			
		||||
                    }
 | 
			
		||||
                    offset += end;
 | 
			
		||||
                },
 | 
			
		||||
                Attributes::Empty => {},
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if attributes_data.is_plain() {
 | 
			
		||||
            Attributes::Empty
 | 
			
		||||
        } else {
 | 
			
		||||
            Attributes::Custom(attributes_data)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,10 @@ impl Interval {
 | 
			
		||||
 | 
			
		||||
    pub fn contains(&self, val: usize) -> bool { self.start <= val && val < self.end }
 | 
			
		||||
 | 
			
		||||
    pub fn contains_range(&self, start: usize, end: usize) -> bool {
 | 
			
		||||
        !self.intersect(Interval::new(start, end)).is_empty()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn is_after(&self, val: usize) -> bool { self.start > val }
 | 
			
		||||
 | 
			
		||||
    pub fn is_empty(&self) -> bool { self.end <= self.start }
 | 
			
		||||
 | 
			
		||||
@ -73,10 +73,11 @@ impl Operation {
 | 
			
		||||
    pub fn length(&self) -> u64 {
 | 
			
		||||
        match self {
 | 
			
		||||
            Operation::Delete(n) => *n,
 | 
			
		||||
            Operation::Retain(r) => r.num,
 | 
			
		||||
            Operation::Retain(r) => r.n,
 | 
			
		||||
            Operation::Insert(i) => i.num_chars(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn is_empty(&self) -> bool { self.length() == 0 }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -89,7 +90,7 @@ impl fmt::Display for Operation {
 | 
			
		||||
            Operation::Retain(r) => {
 | 
			
		||||
                f.write_fmt(format_args!(
 | 
			
		||||
                    "retain: {}, attributes: {}",
 | 
			
		||||
                    r.num, r.attributes
 | 
			
		||||
                    r.n, r.attributes
 | 
			
		||||
                ))?;
 | 
			
		||||
            },
 | 
			
		||||
            Operation::Insert(i) => {
 | 
			
		||||
@ -141,15 +142,40 @@ impl OpBuilder {
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
 | 
			
		||||
pub struct Retain {
 | 
			
		||||
    #[serde(rename(serialize = "retain", deserialize = "retain"))]
 | 
			
		||||
    pub num: u64,
 | 
			
		||||
    pub n: u64,
 | 
			
		||||
    #[serde(skip_serializing_if = "is_empty")]
 | 
			
		||||
    pub attributes: Attributes,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Retain {
 | 
			
		||||
    pub fn merge_or_new_op(&mut self, n: u64, attributes: Attributes) -> Option<Operation> {
 | 
			
		||||
        log::debug!(
 | 
			
		||||
            "merge_retain_or_new_op: {:?}, {:?}",
 | 
			
		||||
            self.attributes,
 | 
			
		||||
            attributes
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        match &attributes {
 | 
			
		||||
            Attributes::Follow => {
 | 
			
		||||
                self.n += n;
 | 
			
		||||
                None
 | 
			
		||||
            },
 | 
			
		||||
            Attributes::Custom(_) | Attributes::Empty => {
 | 
			
		||||
                if self.attributes == attributes {
 | 
			
		||||
                    self.n += n;
 | 
			
		||||
                    None
 | 
			
		||||
                } else {
 | 
			
		||||
                    Some(OpBuilder::retain(n).attributes(attributes).build())
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::convert::From<u64> for Retain {
 | 
			
		||||
    fn from(n: u64) -> Self {
 | 
			
		||||
        Retain {
 | 
			
		||||
            num: n,
 | 
			
		||||
            n,
 | 
			
		||||
            attributes: Attributes::default(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -158,11 +184,11 @@ impl std::convert::From<u64> for Retain {
 | 
			
		||||
impl Deref for Retain {
 | 
			
		||||
    type Target = u64;
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &Self::Target { &self.num }
 | 
			
		||||
    fn deref(&self) -> &Self::Target { &self.n }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DerefMut for Retain {
 | 
			
		||||
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.num }
 | 
			
		||||
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.n }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
 | 
			
		||||
@ -180,6 +206,23 @@ impl Insert {
 | 
			
		||||
    pub fn chars(&self) -> Chars<'_> { self.s.chars() }
 | 
			
		||||
 | 
			
		||||
    pub fn num_chars(&self) -> u64 { num_chars(self.s.as_bytes()) as _ }
 | 
			
		||||
 | 
			
		||||
    pub fn merge_or_new_op(&mut self, s: &str, attributes: Attributes) -> Option<Operation> {
 | 
			
		||||
        match &attributes {
 | 
			
		||||
            Attributes::Follow => {
 | 
			
		||||
                self.s += s;
 | 
			
		||||
                return None;
 | 
			
		||||
            },
 | 
			
		||||
            Attributes::Custom(_) | Attributes::Empty => {
 | 
			
		||||
                if self.attributes == attributes {
 | 
			
		||||
                    self.s += s;
 | 
			
		||||
                    None
 | 
			
		||||
                } else {
 | 
			
		||||
                    Some(OpBuilder::insert(s).attributes(attributes).build())
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::convert::From<String> for Insert {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
pub mod helper;
 | 
			
		||||
 | 
			
		||||
use crate::helper::{MergeTestOp::*, *};
 | 
			
		||||
use crate::helper::{TestOp::*, *};
 | 
			
		||||
use flowy_ot::core::Interval;
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -10,7 +10,7 @@ fn delta_insert_text() {
 | 
			
		||||
        Insert(0, "456", 3),
 | 
			
		||||
        AssertOpsJson(0, r#"[{"insert":"123456"}]"#),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -20,7 +20,7 @@ fn delta_insert_text_at_head() {
 | 
			
		||||
        Insert(0, "456", 0),
 | 
			
		||||
        AssertOpsJson(0, r#"[{"insert":"456123"}]"#),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -30,7 +30,7 @@ fn delta_insert_text_at_middle() {
 | 
			
		||||
        Insert(0, "456", 1),
 | 
			
		||||
        AssertOpsJson(0, r#"[{"insert":"145623"}]"#),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -49,7 +49,7 @@ fn delta_insert_text_with_attr() {
 | 
			
		||||
            r#"[{"insert":"1abc2","attributes":{"bold":"true"}},{"insert":"345"}]"#,
 | 
			
		||||
        ),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -61,7 +61,7 @@ fn delta_add_bold_and_invert_all() {
 | 
			
		||||
        Bold(0, Interval::new(0, 3), false),
 | 
			
		||||
        AssertOpsJson(0, r#"[{"insert":"123"}]"#),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -76,7 +76,7 @@ fn delta_add_bold_and_invert_partial_suffix() {
 | 
			
		||||
            r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
 | 
			
		||||
        ),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -93,7 +93,7 @@ fn delta_add_bold_and_invert_partial_suffix2() {
 | 
			
		||||
        Bold(0, Interval::new(2, 4), true),
 | 
			
		||||
        AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -108,7 +108,7 @@ fn delta_add_bold_and_invert_partial_prefix() {
 | 
			
		||||
            r#"[{"insert":"12"},{"insert":"34","attributes":{"bold":"true"}}]"#,
 | 
			
		||||
        ),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -126,14 +126,14 @@ fn delta_add_bold_consecutive() {
 | 
			
		||||
            r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"34"}]"#,
 | 
			
		||||
        ),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
#[should_panic]
 | 
			
		||||
fn delta_add_bold_empty_str() {
 | 
			
		||||
    let ops = vec![Bold(0, Interval::new(0, 4), true)];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -157,7 +157,7 @@ fn delta_add_bold_italic() {
 | 
			
		||||
            r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}},{"insert":"56"},{"insert":"78","attributes":{"bold":"true","italic":"true"}}]"#,
 | 
			
		||||
        ),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -178,7 +178,7 @@ fn delta_add_bold_italic2() {
 | 
			
		||||
        ),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -203,7 +203,7 @@ fn delta_add_bold_italic3() {
 | 
			
		||||
        ),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -225,7 +225,7 @@ fn delta_add_bold_italic_delete() {
 | 
			
		||||
        ),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -236,7 +236,7 @@ fn delta_merge_inserted_text_with_same_attribute() {
 | 
			
		||||
        InsertBold(0, "456", Interval::new(3, 6)),
 | 
			
		||||
        AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -251,7 +251,7 @@ fn delta_compose_attr_delta_with_attr_delta_test() {
 | 
			
		||||
        AssertOpsJson(1, r#"[{"insert":"1234567","attributes":{"bold":"true"}}]"#),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -278,7 +278,7 @@ fn delta_compose_attr_delta_with_attr_delta_test2() {
 | 
			
		||||
        ),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -293,7 +293,7 @@ fn delta_compose_attr_delta_with_no_attr_delta_test() {
 | 
			
		||||
        AssertOpsJson(0, expected),
 | 
			
		||||
        AssertOpsJson(1, expected),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -305,7 +305,7 @@ fn delta_delete_heading() {
 | 
			
		||||
        AssertOpsJson(0, r#"[{"insert":"3456","attributes":{"bold":"true"}}]"#),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -317,7 +317,7 @@ fn delta_delete_trailing() {
 | 
			
		||||
        AssertOpsJson(0, r#"[{"insert":"12345","attributes":{"bold":"true"}}]"#),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -331,7 +331,7 @@ fn delta_delete_middle() {
 | 
			
		||||
        AssertOpsJson(0, r#"[{"insert":"34","attributes":{"bold":"true"}}]"#),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
@ -343,5 +343,5 @@ fn delta_delete_all() {
 | 
			
		||||
        AssertOpsJson(0, r#"[]"#),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ use rand::{prelude::*, Rng as WrappedRng};
 | 
			
		||||
use std::sync::Once;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub enum MergeTestOp {
 | 
			
		||||
pub enum TestOp {
 | 
			
		||||
    Insert(usize, &'static str, usize),
 | 
			
		||||
    // delta_i, s, start, length,
 | 
			
		||||
    InsertBold(usize, &'static str, Interval),
 | 
			
		||||
@ -16,11 +16,11 @@ pub enum MergeTestOp {
 | 
			
		||||
    AssertOpsJson(usize, &'static str),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct MergeTest {
 | 
			
		||||
pub struct OpTester {
 | 
			
		||||
    deltas: Vec<Delta>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MergeTest {
 | 
			
		||||
impl OpTester {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        static INIT: Once = Once::new();
 | 
			
		||||
        INIT.call_once(|| {
 | 
			
		||||
@ -36,29 +36,29 @@ impl MergeTest {
 | 
			
		||||
        Self { deltas }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn run_op(&mut self, op: &MergeTestOp) {
 | 
			
		||||
    pub fn run_op(&mut self, op: &TestOp) {
 | 
			
		||||
        match op {
 | 
			
		||||
            MergeTestOp::Insert(delta_i, s, index) => {
 | 
			
		||||
            TestOp::Insert(delta_i, s, index) => {
 | 
			
		||||
                self.update_delta_with_insert(*delta_i, s, *index);
 | 
			
		||||
            },
 | 
			
		||||
            MergeTestOp::Delete(delta_i, interval) => {
 | 
			
		||||
            TestOp::Delete(delta_i, interval) => {
 | 
			
		||||
                //
 | 
			
		||||
                self.update_delta_with_delete(*delta_i, interval);
 | 
			
		||||
            },
 | 
			
		||||
            MergeTestOp::InsertBold(delta_i, s, _interval) => {
 | 
			
		||||
            TestOp::InsertBold(delta_i, s, _interval) => {
 | 
			
		||||
                let attrs = AttrsBuilder::new().bold(true).build();
 | 
			
		||||
                let delta = &mut self.deltas[*delta_i];
 | 
			
		||||
                delta.insert(s, attrs);
 | 
			
		||||
            },
 | 
			
		||||
            MergeTestOp::Bold(delta_i, interval, enable) => {
 | 
			
		||||
            TestOp::Bold(delta_i, interval, enable) => {
 | 
			
		||||
                let attrs = AttrsBuilder::new().bold(*enable).build();
 | 
			
		||||
                self.update_delta_with_attribute(*delta_i, attrs, interval);
 | 
			
		||||
            },
 | 
			
		||||
            MergeTestOp::Italic(delta_i, interval, enable) => {
 | 
			
		||||
            TestOp::Italic(delta_i, interval, enable) => {
 | 
			
		||||
                let attrs = AttrsBuilder::new().italic(*enable).build();
 | 
			
		||||
                self.update_delta_with_attribute(*delta_i, attrs, interval);
 | 
			
		||||
            },
 | 
			
		||||
            MergeTestOp::Transform(delta_a_i, delta_b_i) => {
 | 
			
		||||
            TestOp::Transform(delta_a_i, delta_b_i) => {
 | 
			
		||||
                let delta_a = &self.deltas[*delta_a_i];
 | 
			
		||||
                let delta_b = &self.deltas[*delta_b_i];
 | 
			
		||||
 | 
			
		||||
@ -70,12 +70,12 @@ impl MergeTest {
 | 
			
		||||
                self.deltas[*delta_a_i] = new_delta_a;
 | 
			
		||||
                self.deltas[*delta_b_i] = new_delta_b;
 | 
			
		||||
            },
 | 
			
		||||
            MergeTestOp::AssertStr(delta_i, expected) => {
 | 
			
		||||
            TestOp::AssertStr(delta_i, expected) => {
 | 
			
		||||
                let s = self.deltas[*delta_i].apply("").unwrap();
 | 
			
		||||
                assert_eq!(&s, expected);
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            MergeTestOp::AssertOpsJson(delta_i, expected) => {
 | 
			
		||||
            TestOp::AssertOpsJson(delta_i, expected) => {
 | 
			
		||||
                let delta_i_json = serde_json::to_string(&self.deltas[*delta_i]).unwrap();
 | 
			
		||||
 | 
			
		||||
                let expected_delta: Delta = serde_json::from_str(expected).unwrap();
 | 
			
		||||
@ -90,12 +90,14 @@ impl MergeTest {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn run_script(&mut self, script: Vec<MergeTestOp>) {
 | 
			
		||||
    pub fn run_script(&mut self, script: Vec<TestOp>) {
 | 
			
		||||
        for (_i, op) in script.iter().enumerate() {
 | 
			
		||||
            self.run_op(op);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_delta(&mut self, index: usize) -> &mut Delta { &mut self.deltas[index] }
 | 
			
		||||
 | 
			
		||||
    pub fn update_delta_with_insert(&mut self, delta_index: usize, s: &str, index: usize) {
 | 
			
		||||
        let old_delta = &mut self.deltas[delta_index];
 | 
			
		||||
        let target_interval = Interval::new(0, old_delta.target_len);
 | 
			
		||||
@ -103,7 +105,7 @@ impl MergeTest {
 | 
			
		||||
            log::error!("{} out of bounds {}", index, target_interval);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut attributes = attributes_in_delta(old_delta, &Interval::new(index, index + 1));
 | 
			
		||||
        let mut attributes = old_delta.attributes_in_interval(Interval::new(index, index + 1));
 | 
			
		||||
        if attributes == Attributes::Empty {
 | 
			
		||||
            attributes = Attributes::Follow;
 | 
			
		||||
        }
 | 
			
		||||
@ -123,8 +125,8 @@ impl MergeTest {
 | 
			
		||||
            .attributes(attributes)
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
        let attrs = attributes_in_delta(old_delta, &interval);
 | 
			
		||||
        retain.extend_attributes(attrs);
 | 
			
		||||
        let attributes = old_delta.attributes_in_interval(*interval);
 | 
			
		||||
        retain.extend_attributes(attributes);
 | 
			
		||||
 | 
			
		||||
        let new_delta = new_delta_with_op(old_delta, retain, *interval);
 | 
			
		||||
        self.deltas[delta_index] = new_delta;
 | 
			
		||||
@ -133,8 +135,8 @@ impl MergeTest {
 | 
			
		||||
    pub fn update_delta_with_delete(&mut self, delta_index: usize, interval: &Interval) {
 | 
			
		||||
        let old_delta = &self.deltas[delta_index];
 | 
			
		||||
        let mut delete = OpBuilder::delete(interval.size() as u64).build();
 | 
			
		||||
        let attrs = attributes_in_delta(old_delta, &interval);
 | 
			
		||||
        delete.extend_attributes(attrs);
 | 
			
		||||
        let attributes = old_delta.attributes_in_interval(*interval);
 | 
			
		||||
        delete.extend_attributes(attributes);
 | 
			
		||||
 | 
			
		||||
        let new_delta = new_delta_with_op(old_delta, delete, *interval);
 | 
			
		||||
        self.deltas[delta_index] = new_delta;
 | 
			
		||||
@ -149,8 +151,8 @@ fn new_delta_with_op(delta: &Delta, op: Operation, interval: Interval) -> Delta
 | 
			
		||||
    if prefix.is_empty() == false && prefix != interval {
 | 
			
		||||
        let intervals = split_interval_with_delta(delta, &prefix);
 | 
			
		||||
        intervals.into_iter().for_each(|interval| {
 | 
			
		||||
            let attrs = attributes_in_delta(delta, &interval);
 | 
			
		||||
            new_delta.retain(interval.size() as u64, attrs);
 | 
			
		||||
            let attributes = delta.attributes_in_interval(interval);
 | 
			
		||||
            new_delta.retain(interval.size() as u64, attributes);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -160,8 +162,8 @@ fn new_delta_with_op(delta: &Delta, op: Operation, interval: Interval) -> Delta
 | 
			
		||||
    if suffix.is_empty() == false {
 | 
			
		||||
        let intervals = split_interval_with_delta(delta, &suffix);
 | 
			
		||||
        intervals.into_iter().for_each(|interval| {
 | 
			
		||||
            let attrs = attributes_in_delta(delta, &interval);
 | 
			
		||||
            new_delta.retain(interval.size() as u64, attrs);
 | 
			
		||||
            let attributes = delta.attributes_in_interval(interval);
 | 
			
		||||
            new_delta.retain(interval.size() as u64, attributes);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -200,47 +202,7 @@ pub fn target_length_split_with_interval(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn debug_print_delta(delta: &Delta) {
 | 
			
		||||
    log::debug!("😁 {}", serde_json::to_string(delta).unwrap());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn attributes_in_delta(delta: &Delta, interval: &Interval) -> Attributes {
 | 
			
		||||
    let mut attributes_data = AttributesData::new();
 | 
			
		||||
    let mut offset: usize = 0;
 | 
			
		||||
 | 
			
		||||
    delta.ops.iter().for_each(|op| match op {
 | 
			
		||||
        Operation::Delete(_n) => {},
 | 
			
		||||
        Operation::Retain(retain) => {
 | 
			
		||||
            if interval.contains(retain.num as usize) {
 | 
			
		||||
                match &retain.attributes {
 | 
			
		||||
                    Attributes::Follow => {},
 | 
			
		||||
                    Attributes::Custom(data) => {
 | 
			
		||||
                        attributes_data.extend(data.clone());
 | 
			
		||||
                    },
 | 
			
		||||
                    Attributes::Empty => {},
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        Operation::Insert(insert) => match &insert.attributes {
 | 
			
		||||
            Attributes::Follow => {},
 | 
			
		||||
            Attributes::Custom(data) => {
 | 
			
		||||
                let end = insert.num_chars() as usize;
 | 
			
		||||
                if !interval
 | 
			
		||||
                    .intersect(Interval::new(offset, offset + end))
 | 
			
		||||
                    .is_empty()
 | 
			
		||||
                {
 | 
			
		||||
                    attributes_data.extend(data.clone());
 | 
			
		||||
                }
 | 
			
		||||
                offset += end;
 | 
			
		||||
            },
 | 
			
		||||
            Attributes::Empty => {},
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if attributes_data.is_plain() {
 | 
			
		||||
        Attributes::Empty
 | 
			
		||||
    } else {
 | 
			
		||||
        Attributes::Custom(attributes_data)
 | 
			
		||||
    }
 | 
			
		||||
    eprintln!("😁 {}", serde_json::to_string(delta).unwrap());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Rng(StdRng);
 | 
			
		||||
@ -259,7 +221,7 @@ impl Rng {
 | 
			
		||||
    pub fn gen_delta(&mut self, s: &str) -> Delta {
 | 
			
		||||
        let mut delta = Delta::default();
 | 
			
		||||
        loop {
 | 
			
		||||
            let left = s.chars().count() - delta.base_len();
 | 
			
		||||
            let left = s.chars().count() - delta.base_len;
 | 
			
		||||
            if left == 0 {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										68
									
								
								rust-lib/flowy-ot/tests/invert_test.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								rust-lib/flowy-ot/tests/invert_test.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,68 @@
 | 
			
		||||
pub mod helper;
 | 
			
		||||
use crate::helper::{TestOp::*, *};
 | 
			
		||||
use flowy_ot::core::{Delta, Interval, OpBuilder};
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn delta_invert_delta_test() {
 | 
			
		||||
    let mut delta = Delta::default();
 | 
			
		||||
    delta.add(OpBuilder::insert("123").build());
 | 
			
		||||
 | 
			
		||||
    let mut change = Delta::default();
 | 
			
		||||
    change.add(OpBuilder::retain(3).build());
 | 
			
		||||
    change.add(OpBuilder::insert("456").build());
 | 
			
		||||
    let undo = change.invert_delta(&delta);
 | 
			
		||||
 | 
			
		||||
    let new_delta = delta.compose(&change).unwrap();
 | 
			
		||||
    let delta_after_undo = new_delta.compose(&undo).unwrap();
 | 
			
		||||
 | 
			
		||||
    assert_eq!(delta_after_undo, delta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn delta_get_ops_in_interval_1() {
 | 
			
		||||
    let mut delta = Delta::default();
 | 
			
		||||
    let insert_a = OpBuilder::insert("123").build();
 | 
			
		||||
    let insert_b = OpBuilder::insert("4").build();
 | 
			
		||||
 | 
			
		||||
    delta.add(insert_a.clone());
 | 
			
		||||
    delta.add(insert_b.clone());
 | 
			
		||||
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        delta.ops_in_interval(Interval::new(0, 3)),
 | 
			
		||||
        vec![delta.ops.last().unwrap().clone()]
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn delta_get_ops_in_interval_2() {
 | 
			
		||||
    let mut delta = Delta::default();
 | 
			
		||||
    let insert_a = OpBuilder::insert("123").build();
 | 
			
		||||
    let insert_b = OpBuilder::insert("4").build();
 | 
			
		||||
    let insert_c = OpBuilder::insert("5").build();
 | 
			
		||||
    let retain_a = OpBuilder::retain(3).build();
 | 
			
		||||
 | 
			
		||||
    delta.add(insert_a.clone());
 | 
			
		||||
    delta.add(retain_a.clone());
 | 
			
		||||
    delta.add(insert_b.clone());
 | 
			
		||||
    delta.add(insert_c.clone());
 | 
			
		||||
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        delta.ops_in_interval(Interval::new(0, 3)),
 | 
			
		||||
        vec![insert_a.clone()]
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        delta.ops_in_interval(Interval::new(0, 4)),
 | 
			
		||||
        vec![insert_a.clone(), retain_a.clone()]
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        delta.ops_in_interval(Interval::new(0, 7)),
 | 
			
		||||
        vec![
 | 
			
		||||
            insert_a.clone(),
 | 
			
		||||
            retain_a.clone(),
 | 
			
		||||
            // insert_b and insert_c will be merged into one. insert: "45"
 | 
			
		||||
            delta.ops.last().unwrap().clone()
 | 
			
		||||
        ]
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
pub mod helper;
 | 
			
		||||
 | 
			
		||||
use crate::helper::MergeTestOp::*;
 | 
			
		||||
use crate::helper::TestOp::*;
 | 
			
		||||
use bytecount::num_chars;
 | 
			
		||||
use flowy_ot::core::*;
 | 
			
		||||
use helper::*;
 | 
			
		||||
@ -196,7 +196,7 @@ fn transform2() {
 | 
			
		||||
        AssertOpsJson(0, r#"[{"insert":"123456"}]"#),
 | 
			
		||||
        AssertOpsJson(1, r#"[{"insert":"123456"}]"#),
 | 
			
		||||
    ];
 | 
			
		||||
    MergeTest::new().run_script(ops);
 | 
			
		||||
    OpTester::new().run_script(ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user