From eae0c17ddac863d5bbde5e0d78c66380fbf48e53 Mon Sep 17 00:00:00 2001 From: appflowy Date: Sun, 1 Aug 2021 10:45:15 +0800 Subject: [PATCH] config delta --- rust-lib/flowy-ot/src/attributes.rs | 4 +- rust-lib/flowy-ot/src/delta.rs | 387 +++++++++++++--------------- rust-lib/flowy-ot/src/operation.rs | 157 +++++------ rust-lib/flowy-ot/tests/op_test.rs | 31 ++- 4 files changed, 290 insertions(+), 289 deletions(-) diff --git a/rust-lib/flowy-ot/src/attributes.rs b/rust-lib/flowy-ot/src/attributes.rs index ef274a7bd6..67042af043 100644 --- a/rust-lib/flowy-ot/src/attributes.rs +++ b/rust-lib/flowy-ot/src/attributes.rs @@ -12,7 +12,7 @@ impl Attributes { } } - pub fn remove_empty_value(&mut self) { self.inner.retain((|_, v| v.is_empty())); } + pub fn remove_empty_value(&mut self) { self.inner.retain(|_, v| v.is_empty()); } pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); } } @@ -134,7 +134,7 @@ pub fn invert_attributes(attr: Option, base: Option) -> attributes }); - let inverted = attr.iter().fold(base_inverted, |mut attributes, (k, v)| { + 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()); } diff --git a/rust-lib/flowy-ot/src/delta.rs b/rust-lib/flowy-ot/src/delta.rs index d7f6d501a4..850abc0a88 100644 --- a/rust-lib/flowy-ot/src/delta.rs +++ b/rust-lib/flowy-ot/src/delta.rs @@ -1,6 +1,6 @@ use crate::{attributes::*, errors::OTError, operation::*}; use bytecount::num_chars; -use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator}; +use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator, str::FromStr}; #[derive(Clone, Debug, PartialEq)] pub struct Delta { @@ -19,15 +19,29 @@ impl Default for Delta { } } -// impl FromIterator for Delta { -// fn from_iter>(ops: T) -> Self { -// let mut operations = Delta::default(); -// for op in ops { -// operations.add(op); -// } -// operations -// } -// } +impl FromStr for Delta { + type Err = (); + + fn from_str(s: &str) -> Result { + let mut delta = Delta::with_capacity(1); + delta.add(Operation::Insert(s.into())); + Ok(delta) + } +} + +impl> From for Delta { + fn from(s: T) -> Delta { Delta::from_str(s.as_ref()).unwrap() } +} + +impl FromIterator for Delta { + fn from_iter>(ops: T) -> Self { + let mut operations = Delta::default(); + for op in ops { + operations.add(op); + } + operations + } +} impl Delta { #[inline] @@ -39,28 +53,24 @@ impl Delta { } } - // fn add(&mut self, op: OpType) { - // match op { - // OpType::Delete(i) => self.delete(i), - // OpType::Insert(s) => self.insert(&s), - // OpType::Retain(i) => self.retain(i), - // } - // } + fn add(&mut self, op: Operation) { + match op { + Operation::Delete(i) => self.delete(i), + Operation::Insert(i) => self.insert(&i.s, i.attrs), + Operation::Retain(r) => self.retain(r.n, r.attrs), + } + } pub fn delete(&mut self, n: u64) { if n == 0 { return; } self.base_len += n as usize; - - if let Some(operation) = self.ops.last_mut() { - if operation.ty.is_delete() { - operation.delete(n); - return; - } + if let Some(Operation::Delete(n_last)) = self.ops.last_mut() { + *n_last += n; + } else { + self.ops.push(OpBuilder::delete(n).build()); } - - self.ops.push(OperationBuilder::delete(n).build()); } pub fn insert(&mut self, s: &str, attrs: Option) { @@ -68,30 +78,25 @@ impl Delta { return; } self.target_len += num_chars(s.as_bytes()); - let new_last = match self - .ops - .iter_mut() - .map(|op| &mut op.ty) - .collect::>() - .as_mut_slice() - { - [.., OpType::Insert(s_last)] => { - *s_last += &s; + + let new_last = match self.ops.as_mut_slice() { + [.., Operation::Insert(s_last)] => { + s_last.s += &s; return; }, - [.., OpType::Insert(s_pre_last), OpType::Delete(_)] => { - *s_pre_last += s; + [.., Operation::Insert(s_pre_last), Operation::Delete(_)] => { + s_pre_last.s += s; return; }, - [.., op_last @ OpType::Delete(_)] => { + [.., op_last @ Operation::Delete(_)] => { let new_last = op_last.clone(); - *(*op_last) = OpType::Insert(s.to_owned()); + *op_last = Operation::Insert(s.into()); new_last }, - _ => OpType::Insert(s.to_owned()), + _ => Operation::Insert(s.into()), }; self.ops - .push(OperationBuilder::new(new_last).with_attrs(attrs).build()); + .push(OpBuilder::new(new_last).with_attrs(attrs).build()); } pub fn retain(&mut self, n: u64, attrs: Option) { @@ -101,16 +106,13 @@ impl Delta { self.base_len += n as usize; self.target_len += n as usize; - if let Some(operation) = self.ops.last_mut() { - if operation.ty.is_retain() { - operation.retain(n); - operation.set_attrs(attrs); - return; - } + if let Some(Operation::Retain(i_last)) = self.ops.last_mut() { + i_last.n += n; + i_last.attrs = attrs; + } else { + self.ops + .push(OpBuilder::retain(n).with_attrs(attrs).build()); } - - self.ops - .push(OperationBuilder::retain(n).with_attrs(attrs).build()); } /// Merges the operation with `other` into one operation while preserving @@ -132,115 +134,102 @@ impl Delta { let mut ops1 = self.ops.iter().cloned(); let mut ops2 = other.ops.iter().cloned(); - let mut maybe_op1 = ops1.next(); - let mut maybe_op2 = ops2.next(); + let mut next_op1 = ops1.next(); + let mut next_op2 = ops2.next(); loop { - match ( - &maybe_op1.as_ref().map(|o| &o.ty), - &maybe_op2.as_ref().map(|o| &o.ty), - ) { + match (&next_op1, &next_op2) { (None, None) => break, - (Some(OpType::Delete(i)), _) => { + (Some(Operation::Delete(i)), _) => { new_delta.delete(*i); - maybe_op1 = ops1.next(); + next_op1 = ops1.next(); }, - (_, Some(OpType::Insert(s))) => { - new_delta.insert(s, operation_attrs(&maybe_op2)); - maybe_op2 = ops2.next(); + (_, Some(Operation::Insert(insert))) => { + new_delta.insert(&insert.s, get_attrs(&next_op2)); + next_op2 = ops2.next(); }, (None, _) | (_, None) => { return Err(OTError); }, - (Some(OpType::Retain(i)), Some(OpType::Retain(j))) => { - let new_attrs = compose_attributes( - operation_attrs(&maybe_op1), - operation_attrs(&maybe_op2), - true, - ); + (Some(Operation::Retain(i)), Some(Operation::Retain(j))) => { + let new_attrs = + compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true); match i.cmp(&j) { Ordering::Less => { - new_delta.retain(*i, new_attrs); - maybe_op2 = Some(OperationBuilder::retain(*j - *i).build()); - maybe_op1 = ops1.next(); + new_delta.retain(i.n, new_attrs); + next_op2 = Some(OpBuilder::retain(j.n - i.n).build()); + next_op1 = ops1.next(); }, std::cmp::Ordering::Equal => { - new_delta.retain(*i, new_attrs); - maybe_op1 = ops1.next(); - maybe_op2 = ops2.next(); + new_delta.retain(i.n, new_attrs); + next_op1 = ops1.next(); + next_op2 = ops2.next(); }, std::cmp::Ordering::Greater => { - new_delta.retain(*j, new_attrs); - maybe_op1 = Some(OperationBuilder::retain(*i - *j).build()); - maybe_op2 = ops2.next(); + new_delta.retain(j.n, new_attrs); + next_op1 = Some(OpBuilder::retain(i.n - j.n).build()); + next_op2 = ops2.next(); }, } }, - (Some(OpType::Insert(s)), Some(OpType::Delete(j))) => { - match (num_chars(s.as_bytes()) as u64).cmp(j) { + (Some(Operation::Insert(insert)), Some(Operation::Delete(j))) => { + match (num_chars(insert.as_bytes()) as u64).cmp(j) { Ordering::Less => { - maybe_op2 = Some( - OperationBuilder::delete(*j - num_chars(s.as_bytes()) as u64) - .build(), + next_op2 = Some( + OpBuilder::delete(*j - num_chars(insert.as_bytes()) as u64).build(), ); - maybe_op1 = ops1.next(); + next_op1 = ops1.next(); }, Ordering::Equal => { - maybe_op1 = ops1.next(); - maybe_op2 = ops2.next(); + next_op1 = ops1.next(); + next_op2 = ops2.next(); }, Ordering::Greater => { - maybe_op1 = Some( - OperationBuilder::insert(s.chars().skip(*j as usize).collect()) + next_op1 = Some( + OpBuilder::insert(insert.chars().skip(*j as usize).collect()) .build(), ); - maybe_op2 = ops2.next(); + next_op2 = ops2.next(); }, } }, - (Some(OpType::Insert(s)), Some(OpType::Retain(j))) => { - let new_attrs = compose_attributes( - operation_attrs(&maybe_op1), - operation_attrs(&maybe_op2), - false, - ); - match (num_chars(s.as_bytes()) as u64).cmp(j) { + (Some(Operation::Insert(insert)), Some(Operation::Retain(j))) => { + let new_attrs = + compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), false); + match (insert.num_chars()).cmp(j) { Ordering::Less => { - new_delta.insert(s, new_attrs); - maybe_op2 = Some( - OperationBuilder::retain(*j - num_chars(s.as_bytes()) as u64) - .build(), - ); - maybe_op1 = ops1.next(); + new_delta.insert(&insert.s, new_attrs); + next_op2 = Some(OpBuilder::retain(j.n - insert.num_chars()).build()); + next_op1 = ops1.next(); }, Ordering::Equal => { - new_delta.insert(s, new_attrs); - maybe_op1 = ops1.next(); - maybe_op2 = ops2.next(); + new_delta.insert(&insert.s, new_attrs); + next_op1 = ops1.next(); + next_op2 = ops2.next(); }, Ordering::Greater => { - let chars = &mut s.chars(); + let chars = &mut insert.chars(); new_delta - .insert(&chars.take(*j as usize).collect::(), new_attrs); - maybe_op1 = Some(OperationBuilder::insert(chars.collect()).build()); - maybe_op2 = ops2.next(); + .insert(&chars.take(j.n as usize).collect::(), new_attrs); + next_op1 = Some(OpBuilder::insert(chars.collect()).build()); + next_op2 = ops2.next(); }, } }, - (Some(OpType::Retain(i)), Some(OpType::Delete(j))) => match i.cmp(&j) { + (Some(Operation::Retain(i)), Some(Operation::Delete(j))) => match i.cmp(&j) { Ordering::Less => { - new_delta.delete(*i); - maybe_op2 = Some(OperationBuilder::delete(*j - *i).build()); - maybe_op1 = ops1.next(); + new_delta.delete(i.n); + next_op2 = Some(OpBuilder::delete(*j - i.n).build()); + next_op1 = ops1.next(); }, Ordering::Equal => { new_delta.delete(*j); - maybe_op2 = ops2.next(); - maybe_op1 = ops1.next(); + next_op2 = ops2.next(); + next_op1 = ops1.next(); }, Ordering::Greater => { new_delta.delete(*j); - maybe_op1 = Some(OperationBuilder::retain(*i - *j).build()); - maybe_op2 = ops2.next(); + next_op1 = Some(OpBuilder::retain(i.n - *j).build()); + next_op2 = ops2.next(); }, }, }; @@ -268,33 +257,24 @@ impl Delta { let mut ops1 = self.ops.iter().cloned(); let mut ops2 = other.ops.iter().cloned(); - let mut maybe_op1 = ops1.next(); - let mut maybe_op2 = ops2.next(); + let mut next_op1 = ops1.next(); + let mut next_op2 = ops2.next(); loop { - match ( - &maybe_op1.as_ref().map(|o| &o.ty), - &maybe_op2.as_ref().map(|o| &o.ty), - ) { + match (&next_op1, &next_op2) { (None, None) => break, - (Some(OpType::Insert(s)), _) => { - let new_attrs = compose_attributes( - operation_attrs(&maybe_op1), - operation_attrs(&maybe_op2), - true, - ); - a_prime.insert(s, new_attrs.clone()); - b_prime.retain(num_chars(s.as_bytes()) as _, new_attrs.clone()); - maybe_op1 = ops1.next(); + (Some(Operation::Insert(insert)), _) => { + let new_attrs = + compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true); + a_prime.insert(&insert.s, new_attrs.clone()); + b_prime.retain(insert.num_chars(), new_attrs.clone()); + next_op1 = ops1.next(); }, - (_, Some(OpType::Insert(s))) => { - let new_attrs = compose_attributes( - operation_attrs(&maybe_op1), - operation_attrs(&maybe_op2), - true, - ); - a_prime.retain(num_chars(s.as_bytes()) as _, new_attrs.clone()); - b_prime.insert(s, new_attrs.clone()); - maybe_op2 = ops2.next(); + (_, Some(Operation::Insert(insert))) => { + let new_attrs = + compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true); + a_prime.retain(insert.num_chars(), new_attrs.clone()); + b_prime.insert(&insert.s, new_attrs.clone()); + next_op2 = ops2.next(); }, (None, _) => { return Err(OTError); @@ -302,82 +282,79 @@ impl Delta { (_, None) => { return Err(OTError); }, - (Some(OpType::Retain(i)), Some(OpType::Retain(j))) => { - let new_attrs = compose_attributes( - operation_attrs(&maybe_op1), - operation_attrs(&maybe_op2), - true, - ); + (Some(Operation::Retain(i)), Some(Operation::Retain(j))) => { + let new_attrs = + compose_attributes(get_attrs(&next_op1), get_attrs(&next_op2), true); match i.cmp(&j) { Ordering::Less => { - a_prime.retain(*i, new_attrs.clone()); - b_prime.retain(*i, new_attrs.clone()); - maybe_op2 = Some(OperationBuilder::retain(*j - *i).build()); - maybe_op1 = ops1.next(); + a_prime.retain(i.n, new_attrs.clone()); + b_prime.retain(i.n, new_attrs.clone()); + next_op2 = Some(OpBuilder::retain(j.n - i.n).build()); + next_op1 = ops1.next(); }, Ordering::Equal => { - a_prime.retain(*i, new_attrs.clone()); - b_prime.retain(*i, new_attrs.clone()); - maybe_op1 = ops1.next(); - maybe_op2 = ops2.next(); + a_prime.retain(i.n, new_attrs.clone()); + b_prime.retain(i.n, new_attrs.clone()); + next_op1 = ops1.next(); + next_op2 = ops2.next(); }, Ordering::Greater => { - a_prime.retain(*j, new_attrs.clone()); - b_prime.retain(*j, new_attrs.clone()); - maybe_op1 = Some(OperationBuilder::retain(*i - *j).build()); - maybe_op2 = ops2.next(); + a_prime.retain(j.n, new_attrs.clone()); + b_prime.retain(j.n, new_attrs.clone()); + next_op1 = Some(OpBuilder::retain(i.n - j.n).build()); + next_op2 = ops2.next(); }, }; }, - (Some(OpType::Delete(i)), Some(OpType::Delete(j))) => match i.cmp(&j) { + (Some(Operation::Delete(i)), Some(Operation::Delete(j))) => match i.cmp(&j) { Ordering::Less => { - maybe_op2 = Some(OperationBuilder::delete(*j - *i).build()); - maybe_op1 = ops1.next(); + next_op2 = Some(OpBuilder::delete(*j - *i).build()); + next_op1 = ops1.next(); }, Ordering::Equal => { - maybe_op1 = ops1.next(); - maybe_op2 = ops2.next(); + next_op1 = ops1.next(); + next_op2 = ops2.next(); }, Ordering::Greater => { - maybe_op1 = Some(OperationBuilder::delete(*i - *j).build()); - maybe_op2 = ops2.next(); + next_op1 = Some(OpBuilder::delete(*i - *j).build()); + next_op2 = ops2.next(); }, }, - (Some(OpType::Delete(i)), Some(OpType::Retain(j))) => { + (Some(Operation::Delete(i)), Some(Operation::Retain(j))) => { match i.cmp(&j) { Ordering::Less => { a_prime.delete(*i); - maybe_op2 = Some(OperationBuilder::retain(*j - *i).build()); - maybe_op1 = ops1.next(); + next_op2 = Some(OpBuilder::retain(j.n - *i).build()); + next_op1 = ops1.next(); }, Ordering::Equal => { a_prime.delete(*i); - maybe_op1 = ops1.next(); - maybe_op2 = ops2.next(); + next_op1 = ops1.next(); + next_op2 = ops2.next(); }, Ordering::Greater => { - a_prime.delete(*j); - maybe_op1 = Some(OperationBuilder::delete(*i - *j).build()); - maybe_op2 = ops2.next(); + a_prime.delete(j.n); + next_op1 = Some(OpBuilder::delete(*i - j.n).build()); + next_op2 = ops2.next(); }, }; }, - (Some(OpType::Retain(i)), Some(OpType::Delete(j))) => { + (Some(Operation::Retain(i)), Some(Operation::Delete(j))) => { match i.cmp(&j) { Ordering::Less => { - b_prime.delete(*i); - maybe_op2 = Some(OperationBuilder::delete(*j - *i).build()); - maybe_op1 = ops1.next(); + b_prime.delete(i.n); + next_op2 = Some(OpBuilder::delete(*j - i.n).build()); + next_op1 = ops1.next(); }, Ordering::Equal => { - b_prime.delete(*i); - maybe_op1 = ops1.next(); - maybe_op2 = ops2.next(); + b_prime.delete(i.n); + next_op1 = ops1.next(); + next_op2 = ops2.next(); }, Ordering::Greater => { b_prime.delete(*j); - maybe_op1 = Some(OperationBuilder::retain(*i - *j).build()); - maybe_op2 = ops2.next(); + next_op1 = Some(OpBuilder::retain(i.n - *j).build()); + next_op2 = ops2.next(); }, }; }, @@ -400,19 +377,19 @@ impl Delta { let mut new_s = String::new(); let chars = &mut s.chars(); for op in &self.ops { - match &op.ty { - OpType::Retain(retain) => { - for c in chars.take(*retain as usize) { + match &op { + Operation::Retain(retain) => { + for c in chars.take(retain.n as usize) { new_s.push(c); } }, - OpType::Delete(delete) => { + Operation::Delete(delete) => { for _ in 0..*delete { chars.next(); } }, - OpType::Insert(insert) => { - new_s += insert; + Operation::Insert(insert) => { + new_s += &insert.s; }, } } @@ -422,42 +399,36 @@ impl Delta { /// Computes the inverse of an operation. The inverse of an operation is the /// operation that reverts the effects of the operation pub fn invert(&self, s: &str) -> Self { - let mut inverse = Delta::default(); + let mut inverted = Delta::default(); let chars = &mut s.chars(); for op in &self.ops { - match &op.ty { - OpType::Retain(retain) => { - inverse.retain(*retain, op.attrs.clone()); - for _ in 0..*retain { + match &op { + Operation::Retain(retain) => { + inverted.retain(retain.n, None); + for _ in 0..retain.n { chars.next(); } }, - OpType::Insert(insert) => { - inverse.delete(num_chars(insert.as_bytes()) as u64); + Operation::Insert(insert) => { + inverted.delete(insert.num_chars()); }, - OpType::Delete(delete) => { - inverse.insert( + Operation::Delete(delete) => { + inverted.insert( &chars.take(*delete as usize).collect::(), - op.attrs.clone(), + op.attrs(), ); }, } } - inverse + inverted } /// Checks if this operation has no effect. #[inline] pub fn is_noop(&self) -> bool { - match self - .ops - .iter() - .map(|op| &op.ty) - .collect::>() - .as_slice() - { + match self.ops.as_slice() { [] => true, - [OpType::Retain(_)] => true, + [Operation::Retain(_)] => true, _ => false, } } @@ -474,11 +445,13 @@ impl Delta { /// Returns the wrapped sequence of operations. #[inline] pub fn ops(&self) -> &[Operation] { &self.ops } + + pub fn is_empty(&self) -> bool { self.ops.is_empty() } } -pub fn operation_attrs(operation: &Option) -> Option { +pub fn get_attrs(operation: &Option) -> Option { match operation { None => None, - Some(operation) => operation.attrs.clone(), + Some(operation) => operation.attrs(), } } diff --git a/rust-lib/flowy-ot/src/operation.rs b/rust-lib/flowy-ot/src/operation.rs index a2bb2d9408..d9427b504a 100644 --- a/rust-lib/flowy-ot/src/operation.rs +++ b/rust-lib/flowy-ot/src/operation.rs @@ -1,105 +1,118 @@ use crate::attributes::Attributes; +use bytecount::num_chars; use std::{ cmp::Ordering, collections::{hash_map::RandomState, HashMap}, - ops::Deref, + ops::{Deref, DerefMut}, + str::Chars, }; -#[derive(Clone, Debug, PartialEq)] -pub struct Operation { - pub ty: OpType, - pub attrs: Option, +#[derive(Debug, Clone, PartialEq)] +pub enum Operation { + Delete(u64), + Retain(Retain), + Insert(Insert), } impl Operation { - pub fn delete(&mut self, n: u64) { self.ty.delete(n); } - - pub fn retain(&mut self, n: u64) { self.ty.retain(n); } - - pub fn set_attrs(&mut self, attrs: Option) { self.attrs = attrs; } - - pub fn is_plain(&self) -> bool { - match self.attrs { - None => true, - Some(ref attrs) => attrs.is_empty(), + pub fn is_delete(&self) -> bool { + match self { + Operation::Delete(_) => true, + _ => false, } } pub fn is_noop(&self) -> bool { - match self.ty { - OpType::Retain(_) => true, + match self { + Operation::Retain(_) => true, _ => false, } } + + pub fn attrs(&self) -> Option { + match self { + Operation::Delete(_) => None, + Operation::Retain(retain) => retain.attrs.clone(), + Operation::Insert(insert) => insert.attrs.clone(), + } + } + + pub fn is_plain(&self) -> bool { self.attrs().is_none() } } -#[derive(Debug, Clone, PartialEq)] -pub enum OpType { - Delete(u64), - Retain(u64), - Insert(String), -} - -impl OpType { - pub fn is_delete(&self) -> bool { - match self { - OpType::Delete(_) => true, - _ => false, - } - } - - pub fn is_retain(&self) -> bool { - match self { - OpType::Retain(_) => true, - _ => false, - } - } - - pub fn is_insert(&self) -> bool { - match self { - OpType::Insert(_) => true, - _ => false, - } - } - - pub fn delete(&mut self, n: u64) { - debug_assert_eq!(self.is_delete(), true); - if let OpType::Delete(n_last) = self { - *n_last += n; - } - } - - pub fn retain(&mut self, n: u64) { - debug_assert_eq!(self.is_retain(), true); - if let OpType::Retain(i_last) = self { - *i_last += n; - } - } -} - -pub struct OperationBuilder { - ty: OpType, +pub struct OpBuilder { + ty: Operation, attrs: Option, } -impl OperationBuilder { - pub fn new(ty: OpType) -> OperationBuilder { OperationBuilder { ty, attrs: None } } +impl OpBuilder { + pub fn new(ty: Operation) -> OpBuilder { OpBuilder { ty, attrs: None } } - pub fn retain(n: u64) -> OperationBuilder { OperationBuilder::new(OpType::Retain(n)) } + pub fn retain(n: u64) -> OpBuilder { OpBuilder::new(Operation::Retain(n.into())) } - pub fn delete(n: u64) -> OperationBuilder { OperationBuilder::new(OpType::Delete(n)) } + pub fn delete(n: u64) -> OpBuilder { OpBuilder::new(Operation::Delete(n)) } - pub fn insert(s: String) -> OperationBuilder { OperationBuilder::new(OpType::Insert(s)) } + pub fn insert(s: String) -> OpBuilder { OpBuilder::new(Operation::Insert(s.into())) } - pub fn with_attrs(mut self, attrs: Option) -> OperationBuilder { + pub fn with_attrs(mut self, attrs: Option) -> OpBuilder { self.attrs = attrs; self } pub fn build(self) -> Operation { - Operation { - ty: self.ty, - attrs: self.attrs, + let mut operation = self.ty; + match &mut operation { + Operation::Delete(_) => {}, + Operation::Retain(retain) => retain.attrs = self.attrs, + Operation::Insert(insert) => insert.attrs = self.attrs, + } + operation + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Retain { + pub n: u64, + pub(crate) attrs: Option, +} + +impl std::convert::From for Retain { + fn from(n: u64) -> Self { Retain { n, attrs: None } } +} + +impl Deref for Retain { + type Target = u64; + + fn deref(&self) -> &Self::Target { &self.n } +} + +impl DerefMut for Retain { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.n } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Insert { + pub s: String, + pub attrs: Option, +} + +impl Insert { + pub fn as_bytes(&self) -> &[u8] { self.s.as_bytes() } + + pub fn chars(&self) -> Chars<'_> { self.s.chars() } + + pub fn num_chars(&self) -> u64 { num_chars(self.s.as_bytes()) as _ } +} + +impl std::convert::From for Insert { + fn from(s: String) -> Self { Insert { s, attrs: None } } +} + +impl std::convert::From<&str> for Insert { + fn from(s: &str) -> Self { + Insert { + s: s.to_owned(), + attrs: None, } } } diff --git a/rust-lib/flowy-ot/tests/op_test.rs b/rust-lib/flowy-ot/tests/op_test.rs index d458c5fc39..eadfdce0f5 100644 --- a/rust-lib/flowy-ot/tests/op_test.rs +++ b/rust-lib/flowy-ot/tests/op_test.rs @@ -5,7 +5,7 @@ use bytecount::num_chars; use flowy_ot::{ attributes::*, delta::Delta, - operation::{OpType, OperationBuilder}, + operation::{OpBuilder, Operation}, }; #[test] @@ -31,7 +31,7 @@ fn sequence() { let mut delta = Delta::default(); delta.retain(5, None); delta.retain(0, None); - delta.insert("lorem", None); + delta.insert("appflowy", None); delta.insert("", None); delta.delete(3); delta.delete(0); @@ -64,6 +64,21 @@ fn apply() { assert_eq!("hello world,appflowy", &after_b); } +#[test] +fn base_len_test() { + let mut delta_a = Delta::default(); + delta_a.insert("a", None); + delta_a.insert("b", None); + delta_a.insert("c", None); + + let s = "hello world,".to_owned(); + delta_a.delete(s.len() as u64); + let after_a = delta_a.apply(&s).unwrap(); + + delta_a.insert("d", None); + assert_eq!("abc", &after_a); +} + #[test] fn invert() { for _ in 0..1000 { @@ -107,28 +122,28 @@ fn ops_merging() { assert_eq!(delta.ops.len(), 0); delta.retain(2, None); assert_eq!(delta.ops.len(), 1); - assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(2).build())); + assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(2).build())); delta.retain(3, None); assert_eq!(delta.ops.len(), 1); - assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(5).build())); + assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build())); delta.insert("abc", None); assert_eq!(delta.ops.len(), 2); assert_eq!( delta.ops.last(), - Some(&OperationBuilder::insert("abc".to_owned()).build()) + Some(&OpBuilder::insert("abc".to_owned()).build()) ); delta.insert("xyz", None); assert_eq!(delta.ops.len(), 2); assert_eq!( delta.ops.last(), - Some(&OperationBuilder::insert("abcxyz".to_owned()).build()) + Some(&OpBuilder::insert("abcxyz".to_owned()).build()) ); delta.delete(1); assert_eq!(delta.ops.len(), 3); - assert_eq!(delta.ops.last(), Some(&OperationBuilder::delete(1).build())); + assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(1).build())); delta.delete(1); assert_eq!(delta.ops.len(), 3); - assert_eq!(delta.ops.last(), Some(&OperationBuilder::delete(2).build())); + assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(2).build())); } #[test] fn is_noop() {