mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: add documentation for lib-ot crate
This commit is contained in:
parent
7af259c056
commit
3c5b9e6b50
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::{apply_cell_data_changeset, decode_any_cell_data, CellDataOperation};
|
||||
use crate::services::cell::CellDataOperation;
|
||||
use crate::services::field::type_options::checkbox_type_option::*;
|
||||
use crate::services::field::FieldBuilder;
|
||||
use flowy_grid_data_model::revision::FieldRevision;
|
||||
|
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::entities::FieldType;
|
||||
use crate::services::cell::{CellDataChangeset, CellDataOperation};
|
||||
use crate::services::cell::CellDataOperation;
|
||||
use crate::services::field::*;
|
||||
// use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat};
|
||||
use flowy_grid_data_model::revision::FieldRevision;
|
||||
|
@ -2,6 +2,7 @@
|
||||
use crate::editor::{Rng, TestBuilder, TestOp::*};
|
||||
use flowy_sync::client_document::{NewlineDoc, PlainDoc};
|
||||
use lib_ot::{
|
||||
core::Interval,
|
||||
core::*,
|
||||
rich_text::{AttributeBuilder, RichTextAttribute, RichTextAttributes, RichTextDelta},
|
||||
};
|
||||
@ -39,23 +40,23 @@ fn attributes_insert_text_at_middle() {
|
||||
#[test]
|
||||
fn delta_get_ops_in_interval_1() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
let insert_a = OpBuilder::insert("123").build();
|
||||
let insert_b = OpBuilder::insert("4").build();
|
||||
let insert_a = OperationBuilder::insert("123").build();
|
||||
let insert_b = OperationBuilder::insert("4").build();
|
||||
|
||||
delta.add(insert_a.clone());
|
||||
delta.add(insert_b.clone());
|
||||
|
||||
let mut iterator = DeltaIter::from_interval(&delta, Interval::new(0, 4));
|
||||
let mut iterator = DeltaIterator::from_interval(&delta, Interval::new(0, 4));
|
||||
assert_eq!(iterator.ops(), delta.ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_get_ops_in_interval_2() {
|
||||
let mut delta = RichTextDelta::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();
|
||||
let insert_a = OperationBuilder::insert("123").build();
|
||||
let insert_b = OperationBuilder::insert("4").build();
|
||||
let insert_c = OperationBuilder::insert("5").build();
|
||||
let retain_a = OperationBuilder::retain(3).build();
|
||||
|
||||
delta.add(insert_a.clone());
|
||||
delta.add(retain_a.clone());
|
||||
@ -63,32 +64,32 @@ fn delta_get_ops_in_interval_2() {
|
||||
delta.add(insert_c.clone());
|
||||
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(0, 2)).ops(),
|
||||
vec![OpBuilder::insert("12").build()]
|
||||
DeltaIterator::from_interval(&delta, Interval::new(0, 2)).ops(),
|
||||
vec![OperationBuilder::insert("12").build()]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(1, 3)).ops(),
|
||||
vec![OpBuilder::insert("23").build()]
|
||||
DeltaIterator::from_interval(&delta, Interval::new(1, 3)).ops(),
|
||||
vec![OperationBuilder::insert("23").build()]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(0, 3)).ops(),
|
||||
DeltaIterator::from_interval(&delta, Interval::new(0, 3)).ops(),
|
||||
vec![insert_a.clone()]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(0, 4)).ops(),
|
||||
vec![insert_a.clone(), OpBuilder::retain(1).build()]
|
||||
DeltaIterator::from_interval(&delta, Interval::new(0, 4)).ops(),
|
||||
vec![insert_a.clone(), OperationBuilder::retain(1).build()]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(0, 6)).ops(),
|
||||
DeltaIterator::from_interval(&delta, Interval::new(0, 6)).ops(),
|
||||
vec![insert_a.clone(), retain_a.clone()]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(0, 7)).ops(),
|
||||
DeltaIterator::from_interval(&delta, Interval::new(0, 7)).ops(),
|
||||
vec![insert_a.clone(), retain_a.clone(), insert_b.clone()]
|
||||
);
|
||||
}
|
||||
@ -96,54 +97,60 @@ fn delta_get_ops_in_interval_2() {
|
||||
#[test]
|
||||
fn delta_get_ops_in_interval_3() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
let insert_a = OpBuilder::insert("123456").build();
|
||||
let insert_a = OperationBuilder::insert("123456").build();
|
||||
delta.add(insert_a.clone());
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(3, 5)).ops(),
|
||||
vec![OpBuilder::insert("45").build()]
|
||||
DeltaIterator::from_interval(&delta, Interval::new(3, 5)).ops(),
|
||||
vec![OperationBuilder::insert("45").build()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_get_ops_in_interval_4() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
let insert_a = OpBuilder::insert("12").build();
|
||||
let insert_b = OpBuilder::insert("34").build();
|
||||
let insert_c = OpBuilder::insert("56").build();
|
||||
let insert_a = OperationBuilder::insert("12").build();
|
||||
let insert_b = OperationBuilder::insert("34").build();
|
||||
let insert_c = OperationBuilder::insert("56").build();
|
||||
|
||||
delta.ops.push(insert_a.clone());
|
||||
delta.ops.push(insert_b.clone());
|
||||
delta.ops.push(insert_c.clone());
|
||||
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(0, 2)).ops(),
|
||||
DeltaIterator::from_interval(&delta, Interval::new(0, 2)).ops(),
|
||||
vec![insert_a]
|
||||
);
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(2, 4)).ops(),
|
||||
DeltaIterator::from_interval(&delta, Interval::new(2, 4)).ops(),
|
||||
vec![insert_b]
|
||||
);
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(4, 6)).ops(),
|
||||
DeltaIterator::from_interval(&delta, Interval::new(4, 6)).ops(),
|
||||
vec![insert_c]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(2, 5)).ops(),
|
||||
vec![OpBuilder::insert("34").build(), OpBuilder::insert("5").build()]
|
||||
DeltaIterator::from_interval(&delta, Interval::new(2, 5)).ops(),
|
||||
vec![
|
||||
OperationBuilder::insert("34").build(),
|
||||
OperationBuilder::insert("5").build()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_get_ops_in_interval_5() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
let insert_a = OpBuilder::insert("123456").build();
|
||||
let insert_b = OpBuilder::insert("789").build();
|
||||
let insert_a = OperationBuilder::insert("123456").build();
|
||||
let insert_b = OperationBuilder::insert("789").build();
|
||||
delta.ops.push(insert_a.clone());
|
||||
delta.ops.push(insert_b.clone());
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(4, 8)).ops(),
|
||||
vec![OpBuilder::insert("56").build(), OpBuilder::insert("78").build()]
|
||||
DeltaIterator::from_interval(&delta, Interval::new(4, 8)).ops(),
|
||||
vec![
|
||||
OperationBuilder::insert("56").build(),
|
||||
OperationBuilder::insert("78").build()
|
||||
]
|
||||
);
|
||||
|
||||
// assert_eq!(
|
||||
@ -155,54 +162,60 @@ fn delta_get_ops_in_interval_5() {
|
||||
#[test]
|
||||
fn delta_get_ops_in_interval_6() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
let insert_a = OpBuilder::insert("12345678").build();
|
||||
let insert_a = OperationBuilder::insert("12345678").build();
|
||||
delta.add(insert_a.clone());
|
||||
assert_eq!(
|
||||
DeltaIter::from_interval(&delta, Interval::new(4, 6)).ops(),
|
||||
vec![OpBuilder::insert("56").build()]
|
||||
DeltaIterator::from_interval(&delta, Interval::new(4, 6)).ops(),
|
||||
vec![OperationBuilder::insert("56").build()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_get_ops_in_interval_7() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
let insert_a = OpBuilder::insert("12345").build();
|
||||
let retain_a = OpBuilder::retain(3).build();
|
||||
let insert_a = OperationBuilder::insert("12345").build();
|
||||
let retain_a = OperationBuilder::retain(3).build();
|
||||
|
||||
delta.add(insert_a.clone());
|
||||
delta.add(retain_a.clone());
|
||||
|
||||
let mut iter_1 = DeltaIter::from_offset(&delta, 2);
|
||||
assert_eq!(iter_1.next_op().unwrap(), OpBuilder::insert("345").build());
|
||||
assert_eq!(iter_1.next_op().unwrap(), OpBuilder::retain(3).build());
|
||||
let mut iter_1 = DeltaIterator::from_offset(&delta, 2);
|
||||
assert_eq!(iter_1.next_op().unwrap(), OperationBuilder::insert("345").build());
|
||||
assert_eq!(iter_1.next_op().unwrap(), OperationBuilder::retain(3).build());
|
||||
|
||||
let mut iter_2 = DeltaIter::new(&delta);
|
||||
assert_eq!(iter_2.next_op_with_len(2).unwrap(), OpBuilder::insert("12").build());
|
||||
assert_eq!(iter_2.next_op().unwrap(), OpBuilder::insert("345").build());
|
||||
let mut iter_2 = DeltaIterator::new(&delta);
|
||||
assert_eq!(
|
||||
iter_2.next_op_with_len(2).unwrap(),
|
||||
OperationBuilder::insert("12").build()
|
||||
);
|
||||
assert_eq!(iter_2.next_op().unwrap(), OperationBuilder::insert("345").build());
|
||||
|
||||
assert_eq!(iter_2.next_op().unwrap(), OpBuilder::retain(3).build());
|
||||
assert_eq!(iter_2.next_op().unwrap(), OperationBuilder::retain(3).build());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_op_seek() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
let insert_a = OpBuilder::insert("12345").build();
|
||||
let retain_a = OpBuilder::retain(3).build();
|
||||
let insert_a = OperationBuilder::insert("12345").build();
|
||||
let retain_a = OperationBuilder::retain(3).build();
|
||||
delta.add(insert_a.clone());
|
||||
delta.add(retain_a.clone());
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
let mut iter = DeltaIterator::new(&delta);
|
||||
iter.seek::<OpMetric>(1);
|
||||
assert_eq!(iter.next_op().unwrap(), OpBuilder::retain(3).build());
|
||||
assert_eq!(iter.next_op().unwrap(), retain_a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_utf16_code_unit_seek() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
delta.add(OpBuilder::insert("12345").build());
|
||||
delta.add(OperationBuilder::insert("12345").build());
|
||||
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
let mut iter = DeltaIterator::new(&delta);
|
||||
iter.seek::<Utf16CodeUnitMetric>(3);
|
||||
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("45").build());
|
||||
assert_eq!(
|
||||
iter.next_op_with_len(2).unwrap(),
|
||||
OperationBuilder::insert("45").build()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -213,77 +226,92 @@ fn delta_utf16_code_unit_seek_with_attributes() {
|
||||
.add_attr(RichTextAttribute::Italic(true))
|
||||
.build();
|
||||
|
||||
delta.add(OpBuilder::insert("1234").attributes(attributes.clone()).build());
|
||||
delta.add(OpBuilder::insert("\n").build());
|
||||
delta.add(OperationBuilder::insert("1234").attributes(attributes.clone()).build());
|
||||
delta.add(OperationBuilder::insert("\n").build());
|
||||
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
let mut iter = DeltaIterator::new(&delta);
|
||||
iter.seek::<Utf16CodeUnitMetric>(0);
|
||||
|
||||
assert_eq!(
|
||||
iter.next_op_with_len(4).unwrap(),
|
||||
OpBuilder::insert("1234").attributes(attributes).build(),
|
||||
OperationBuilder::insert("1234").attributes(attributes).build(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_next_op_len() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
delta.add(OpBuilder::insert("12345").build());
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("12").build());
|
||||
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("34").build());
|
||||
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("5").build());
|
||||
delta.add(OperationBuilder::insert("12345").build());
|
||||
let mut iter = DeltaIterator::new(&delta);
|
||||
assert_eq!(
|
||||
iter.next_op_with_len(2).unwrap(),
|
||||
OperationBuilder::insert("12").build()
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next_op_with_len(2).unwrap(),
|
||||
OperationBuilder::insert("34").build()
|
||||
);
|
||||
assert_eq!(iter.next_op_with_len(2).unwrap(), OperationBuilder::insert("5").build());
|
||||
assert_eq!(iter.next_op_with_len(1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_next_op_len_with_chinese() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
delta.add(OpBuilder::insert("你好").build());
|
||||
delta.add(OperationBuilder::insert("你好").build());
|
||||
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
let mut iter = DeltaIterator::new(&delta);
|
||||
assert_eq!(iter.next_op_len().unwrap(), 2);
|
||||
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("你好").build());
|
||||
assert_eq!(
|
||||
iter.next_op_with_len(2).unwrap(),
|
||||
OperationBuilder::insert("你好").build()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_next_op_len_with_english() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
delta.add(OpBuilder::insert("ab").build());
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
delta.add(OperationBuilder::insert("ab").build());
|
||||
let mut iter = DeltaIterator::new(&delta);
|
||||
assert_eq!(iter.next_op_len().unwrap(), 2);
|
||||
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("ab").build());
|
||||
assert_eq!(
|
||||
iter.next_op_with_len(2).unwrap(),
|
||||
OperationBuilder::insert("ab").build()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_next_op_len_after_seek() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
delta.add(OpBuilder::insert("12345").build());
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
delta.add(OperationBuilder::insert("12345").build());
|
||||
let mut iter = DeltaIterator::new(&delta);
|
||||
assert_eq!(iter.next_op_len().unwrap(), 5);
|
||||
iter.seek::<Utf16CodeUnitMetric>(3);
|
||||
assert_eq!(iter.next_op_len().unwrap(), 2);
|
||||
assert_eq!(iter.next_op_with_len(1).unwrap(), OpBuilder::insert("4").build());
|
||||
assert_eq!(iter.next_op_with_len(1).unwrap(), OperationBuilder::insert("4").build());
|
||||
assert_eq!(iter.next_op_len().unwrap(), 1);
|
||||
assert_eq!(iter.next_op().unwrap(), OpBuilder::insert("5").build());
|
||||
assert_eq!(iter.next_op().unwrap(), OperationBuilder::insert("5").build());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_next_op_len_none() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
delta.add(OpBuilder::insert("12345").build());
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
delta.add(OperationBuilder::insert("12345").build());
|
||||
let mut iter = DeltaIterator::new(&delta);
|
||||
|
||||
assert_eq!(iter.next_op_len().unwrap(), 5);
|
||||
assert_eq!(iter.next_op_with_len(5).unwrap(), OpBuilder::insert("12345").build());
|
||||
assert_eq!(
|
||||
iter.next_op_with_len(5).unwrap(),
|
||||
OperationBuilder::insert("12345").build()
|
||||
);
|
||||
assert_eq!(iter.next_op_len(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_next_op_with_len_zero() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
delta.add(OpBuilder::insert("12345").build());
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
delta.add(OperationBuilder::insert("12345").build());
|
||||
let mut iter = DeltaIterator::new(&delta);
|
||||
assert_eq!(iter.next_op_with_len(0), None,);
|
||||
assert_eq!(iter.next_op_len().unwrap(), 5);
|
||||
}
|
||||
@ -291,14 +319,14 @@ fn delta_next_op_with_len_zero() {
|
||||
#[test]
|
||||
fn delta_next_op_with_len_cross_op_return_last() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
delta.add(OpBuilder::insert("12345").build());
|
||||
delta.add(OpBuilder::retain(1).build());
|
||||
delta.add(OpBuilder::insert("678").build());
|
||||
delta.add(OperationBuilder::insert("12345").build());
|
||||
delta.add(OperationBuilder::retain(1).build());
|
||||
delta.add(OperationBuilder::insert("678").build());
|
||||
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
let mut iter = DeltaIterator::new(&delta);
|
||||
iter.seek::<Utf16CodeUnitMetric>(4);
|
||||
assert_eq!(iter.next_op_len().unwrap(), 1);
|
||||
assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::retain(1).build());
|
||||
assert_eq!(iter.next_op_with_len(2).unwrap(), OperationBuilder::retain(1).build());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -342,18 +370,17 @@ fn apply_1000() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply() {
|
||||
let s = "hello world,".to_owned();
|
||||
let mut delta_a = RichTextDelta::default();
|
||||
delta_a.insert(&s, RichTextAttributes::default());
|
||||
fn apply_test() {
|
||||
let s = "hello";
|
||||
let delta_a = PlainTextDeltaBuilder::new().insert(s).build();
|
||||
let delta_b = PlainTextDeltaBuilder::new()
|
||||
.retain(s.len())
|
||||
.insert(", AppFlowy")
|
||||
.build();
|
||||
|
||||
let mut delta_b = RichTextDelta::default();
|
||||
delta_b.retain(s.len(), RichTextAttributes::default());
|
||||
delta_b.insert("appflowy", RichTextAttributes::default());
|
||||
|
||||
let after_a = delta_a.apply("").unwrap();
|
||||
let after_a = delta_a.to_str().unwrap();
|
||||
let after_b = delta_b.apply(&after_a).unwrap();
|
||||
assert_eq!("hello world,appflowy", &after_b);
|
||||
assert_eq!("hello, AppFlowy", &after_b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -384,6 +411,17 @@ fn invert() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invert_test() {
|
||||
let s = "hello world";
|
||||
let delta = PlainTextDeltaBuilder::new().insert(s).build();
|
||||
let invert_delta = delta.invert_str("");
|
||||
assert_eq!(delta.utf16_base_len, invert_delta.utf16_target_len);
|
||||
assert_eq!(delta.utf16_target_len, invert_delta.utf16_base_len);
|
||||
|
||||
assert_eq!(invert_delta.apply(s).unwrap(), "")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_ops() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
@ -415,23 +453,24 @@ fn ops_merging() {
|
||||
assert_eq!(delta.ops.len(), 0);
|
||||
delta.retain(2, RichTextAttributes::default());
|
||||
assert_eq!(delta.ops.len(), 1);
|
||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(2).build()));
|
||||
assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(2).build()));
|
||||
delta.retain(3, RichTextAttributes::default());
|
||||
assert_eq!(delta.ops.len(), 1);
|
||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build()));
|
||||
assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(5).build()));
|
||||
delta.insert("abc", RichTextAttributes::default());
|
||||
assert_eq!(delta.ops.len(), 2);
|
||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abc").build()));
|
||||
assert_eq!(delta.ops.last(), Some(&OperationBuilder::insert("abc").build()));
|
||||
delta.insert("xyz", RichTextAttributes::default());
|
||||
assert_eq!(delta.ops.len(), 2);
|
||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abcxyz").build()));
|
||||
assert_eq!(delta.ops.last(), Some(&OperationBuilder::insert("abcxyz").build()));
|
||||
delta.delete(1);
|
||||
assert_eq!(delta.ops.len(), 3);
|
||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(1).build()));
|
||||
assert_eq!(delta.ops.last(), Some(&OperationBuilder::delete(1).build()));
|
||||
delta.delete(1);
|
||||
assert_eq!(delta.ops.len(), 3);
|
||||
assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(2).build()));
|
||||
assert_eq!(delta.ops.last(), Some(&OperationBuilder::delete(2).build()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_noop() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
@ -582,11 +621,11 @@ fn transform_two_conflict_non_seq_delta() {
|
||||
#[test]
|
||||
fn delta_invert_no_attribute_delta() {
|
||||
let mut delta = RichTextDelta::default();
|
||||
delta.add(OpBuilder::insert("123").build());
|
||||
delta.add(OperationBuilder::insert("123").build());
|
||||
|
||||
let mut change = RichTextDelta::default();
|
||||
change.add(OpBuilder::retain(3).build());
|
||||
change.add(OpBuilder::insert("456").build());
|
||||
change.add(OperationBuilder::retain(3).build());
|
||||
change.add(OperationBuilder::insert("456").build());
|
||||
let undo = change.invert(&delta);
|
||||
|
||||
let new_delta = delta.compose(&change).unwrap();
|
||||
|
@ -11,7 +11,7 @@ fn operation_insert_serialize_test() {
|
||||
.add_attr(RichTextAttribute::Bold(true))
|
||||
.add_attr(RichTextAttribute::Italic(true))
|
||||
.build();
|
||||
let operation = OpBuilder::insert("123").attributes(attributes).build();
|
||||
let operation = OperationBuilder::insert("123").attributes(attributes).build();
|
||||
let json = serde_json::to_string(&operation).unwrap();
|
||||
eprintln!("{}", json);
|
||||
|
||||
@ -42,7 +42,7 @@ fn attributes_serialize_test() {
|
||||
.add_attr(RichTextAttribute::Bold(true))
|
||||
.add_attr(RichTextAttribute::Italic(true))
|
||||
.build();
|
||||
let retain = OpBuilder::insert("123").attributes(attributes).build();
|
||||
let retain = OperationBuilder::insert("123").attributes(attributes).build();
|
||||
|
||||
let json = serde_json::to_string(&retain).unwrap();
|
||||
eprintln!("{}", json);
|
||||
@ -56,7 +56,7 @@ fn delta_serialize_multi_attribute_test() {
|
||||
.add_attr(RichTextAttribute::Bold(true))
|
||||
.add_attr(RichTextAttribute::Italic(true))
|
||||
.build();
|
||||
let retain = OpBuilder::insert("123").attributes(attributes).build();
|
||||
let retain = OperationBuilder::insert("123").attributes(attributes).build();
|
||||
|
||||
delta.add(retain);
|
||||
delta.add(Operation::Retain(5.into()));
|
||||
|
@ -108,6 +108,7 @@ fn history_bold_redo_with_lagging() {
|
||||
fn history_delete_undo() {
|
||||
let ops = vec![
|
||||
Insert(0, "123", 0),
|
||||
Wait(RECORD_THRESHOLD),
|
||||
AssertDocJson(0, r#"[{"insert":"123"}]"#),
|
||||
Delete(0, Interval::new(0, 3)),
|
||||
AssertDocJson(0, r#"[]"#),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{client_document::DeleteExt, util::is_newline};
|
||||
use lib_ot::{
|
||||
core::{Attributes, DeltaBuilder, DeltaIter, Interval, Utf16CodeUnitMetric, NEW_LINE},
|
||||
core::{Attributes, DeltaBuilder, DeltaIterator, Interval, Utf16CodeUnitMetric, NEW_LINE},
|
||||
rich_text::{plain_attributes, RichTextDelta},
|
||||
};
|
||||
|
||||
@ -16,7 +16,7 @@ impl DeleteExt for PreserveLineFormatOnMerge {
|
||||
}
|
||||
|
||||
// seek to the interval start pos. e.g. You backspace enter pos
|
||||
let mut iter = DeltaIter::from_offset(delta, interval.start);
|
||||
let mut iter = DeltaIterator::from_offset(delta, interval.start);
|
||||
|
||||
// op will be the "\n"
|
||||
let newline_op = iter.next_op_with_len(1)?;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use lib_ot::{
|
||||
core::{DeltaBuilder, DeltaIter, Interval},
|
||||
core::{DeltaBuilder, DeltaIterator, Interval},
|
||||
rich_text::{plain_attributes, AttributeScope, RichTextAttribute, RichTextDelta},
|
||||
};
|
||||
|
||||
@ -20,7 +20,7 @@ impl FormatExt for ResolveBlockFormat {
|
||||
}
|
||||
|
||||
let mut new_delta = DeltaBuilder::new().retain(interval.start).build();
|
||||
let mut iter = DeltaIter::from_offset(delta, interval.start);
|
||||
let mut iter = DeltaIterator::from_offset(delta, interval.start);
|
||||
let mut start = 0;
|
||||
let end = interval.size();
|
||||
while start < end && iter.has_next() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use lib_ot::{
|
||||
core::{DeltaBuilder, DeltaIter, Interval},
|
||||
core::{DeltaBuilder, DeltaIterator, Interval},
|
||||
rich_text::{AttributeScope, RichTextAttribute, RichTextDelta},
|
||||
};
|
||||
|
||||
@ -19,7 +19,7 @@ impl FormatExt for ResolveInlineFormat {
|
||||
return None;
|
||||
}
|
||||
let mut new_delta = DeltaBuilder::new().retain(interval.start).build();
|
||||
let mut iter = DeltaIter::from_offset(delta, interval.start);
|
||||
let mut iter = DeltaIterator::from_offset(delta, interval.start);
|
||||
let mut start = 0;
|
||||
let end = interval.size();
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
use crate::{client_document::InsertExt, util::is_newline};
|
||||
use lib_ot::{
|
||||
core::{is_empty_line_at_index, DeltaBuilder, DeltaIter},
|
||||
rich_text::{attributes_except_header, RichTextAttributeKey, RichTextDelta},
|
||||
};
|
||||
use lib_ot::core::{is_empty_line_at_index, DeltaBuilder, DeltaIterator};
|
||||
use lib_ot::rich_text::{attributes_except_header, RichTextAttributeKey, RichTextDelta};
|
||||
|
||||
pub struct AutoExitBlock {}
|
||||
|
||||
@ -21,7 +19,7 @@ impl InsertExt for AutoExitBlock {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut iter = DeltaIter::from_offset(delta, index);
|
||||
let mut iter = DeltaIterator::from_offset(delta, index);
|
||||
let next = iter.next_op()?;
|
||||
let mut attributes = next.get_attributes();
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{client_document::InsertExt, util::is_whitespace};
|
||||
use lib_ot::{
|
||||
core::{count_utf16_code_units, DeltaBuilder, DeltaIter},
|
||||
core::{count_utf16_code_units, DeltaBuilder, DeltaIterator},
|
||||
rich_text::{plain_attributes, RichTextAttribute, RichTextAttributes, RichTextDelta},
|
||||
};
|
||||
use std::cmp::min;
|
||||
@ -17,7 +17,7 @@ impl InsertExt for AutoFormatExt {
|
||||
if !is_whitespace(text) {
|
||||
return None;
|
||||
}
|
||||
let mut iter = DeltaIter::new(delta);
|
||||
let mut iter = DeltaIterator::new(delta);
|
||||
if let Some(prev) = iter.next_op_with_len(index) {
|
||||
match AutoFormat::parse(prev.get_data()) {
|
||||
None => {}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::client_document::InsertExt;
|
||||
use lib_ot::{
|
||||
core::{Attributes, DeltaBuilder, DeltaIter, NEW_LINE},
|
||||
core::{Attributes, DeltaBuilder, DeltaIterator, NEW_LINE},
|
||||
rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta},
|
||||
};
|
||||
|
||||
@ -11,7 +11,7 @@ impl InsertExt for DefaultInsertAttribute {
|
||||
}
|
||||
|
||||
fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option<RichTextDelta> {
|
||||
let iter = DeltaIter::new(delta);
|
||||
let iter = DeltaIterator::new(delta);
|
||||
let mut attributes = RichTextAttributes::new();
|
||||
|
||||
// Enable each line split by "\n" remains the block attributes. for example:
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{client_document::InsertExt, util::is_newline};
|
||||
use lib_ot::{
|
||||
core::{DeltaBuilder, DeltaIter, NEW_LINE},
|
||||
core::{DeltaBuilder, DeltaIterator, NEW_LINE},
|
||||
rich_text::{
|
||||
attributes_except_header, plain_attributes, RichTextAttribute, RichTextAttributeKey, RichTextAttributes,
|
||||
RichTextDelta,
|
||||
@ -18,7 +18,7 @@ impl InsertExt for PreserveBlockFormatOnInsert {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut iter = DeltaIter::from_offset(delta, index);
|
||||
let mut iter = DeltaIterator::from_offset(delta, index);
|
||||
match iter.next_op_with_newline() {
|
||||
None => {}
|
||||
Some((newline_op, offset)) => {
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
util::{contain_newline, is_newline},
|
||||
};
|
||||
use lib_ot::{
|
||||
core::{DeltaBuilder, DeltaIter, OpNewline, NEW_LINE},
|
||||
core::{DeltaBuilder, DeltaIterator, OpNewline, NEW_LINE},
|
||||
rich_text::{plain_attributes, RichTextAttributeKey, RichTextDelta},
|
||||
};
|
||||
|
||||
@ -18,7 +18,7 @@ impl InsertExt for PreserveInlineFormat {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut iter = DeltaIter::new(delta);
|
||||
let mut iter = DeltaIterator::new(delta);
|
||||
let prev = iter.next_op_with_len(index)?;
|
||||
if OpNewline::parse(&prev).is_contain() {
|
||||
return None;
|
||||
@ -64,7 +64,7 @@ impl InsertExt for PreserveLineFormatOnSplit {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut iter = DeltaIter::new(delta);
|
||||
let mut iter = DeltaIterator::new(delta);
|
||||
let prev = iter.next_op_with_len(index)?;
|
||||
if OpNewline::parse(&prev).is_end() {
|
||||
return None;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{client_document::InsertExt, util::is_newline};
|
||||
use lib_ot::{
|
||||
core::{DeltaBuilder, DeltaIter, Utf16CodeUnitMetric, NEW_LINE},
|
||||
core::{DeltaBuilder, DeltaIterator, Utf16CodeUnitMetric, NEW_LINE},
|
||||
rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta},
|
||||
};
|
||||
|
||||
@ -15,7 +15,7 @@ impl InsertExt for ResetLineFormatOnNewLine {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut iter = DeltaIter::new(delta);
|
||||
let mut iter = DeltaIterator::new(delta);
|
||||
iter.seek::<Utf16CodeUnitMetric>(index);
|
||||
let next_op = iter.next_op()?;
|
||||
if !next_op.get_data().starts_with(NEW_LINE) {
|
||||
|
@ -1,7 +1,12 @@
|
||||
use crate::core::{trim, Attributes, Delta, PlainTextAttributes};
|
||||
use crate::core::delta::{trim, Delta};
|
||||
use crate::core::operation::{Attributes, PlainTextAttributes};
|
||||
|
||||
pub type PlainTextDeltaBuilder = DeltaBuilder<PlainTextAttributes>;
|
||||
|
||||
/// A builder for creating new [Delta] objects.
|
||||
///
|
||||
/// Note that all edit operations must be sorted; the start point of each
|
||||
/// interval must be no less than the end point of the previous one.
|
||||
pub struct DeltaBuilder<T: Attributes> {
|
||||
delta: Delta<T>,
|
||||
}
|
||||
@ -23,6 +28,8 @@ where
|
||||
DeltaBuilder::default()
|
||||
}
|
||||
|
||||
/// Retain the 'n' characters with the attributes. Use 'retain' instead if you don't
|
||||
/// need any attributes.
|
||||
pub fn retain_with_attributes(mut self, n: usize, attrs: T) -> Self {
|
||||
self.delta.retain(n, attrs);
|
||||
self
|
||||
@ -33,11 +40,14 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
/// Deletes the given interval. Panics if interval is not properly sorted.
|
||||
pub fn delete(mut self, n: usize) -> Self {
|
||||
self.delta.delete(n);
|
||||
self
|
||||
}
|
||||
|
||||
/// Insert the string with attributes. Use 'insert' instead if you don't
|
||||
/// need any attributes.
|
||||
pub fn insert_with_attributes(mut self, s: &str, attrs: T) -> Self {
|
||||
self.delta.insert(s, attrs);
|
||||
self
|
||||
@ -53,6 +63,7 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the `Delta`
|
||||
pub fn build(self) -> Delta<T> {
|
||||
self.delta
|
||||
}
|
||||
|
@ -1,33 +1,51 @@
|
||||
#![allow(clippy::while_let_on_iterator)]
|
||||
use crate::{
|
||||
core::{Attributes, Delta, Interval, Operation},
|
||||
errors::{ErrorBuilder, OTError, OTErrorCode},
|
||||
};
|
||||
use crate::core::delta::Delta;
|
||||
use crate::core::interval::Interval;
|
||||
use crate::core::operation::{Attributes, Operation};
|
||||
use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
|
||||
use std::{cmp::min, iter::Enumerate, slice::Iter};
|
||||
|
||||
/// A [DeltaCursor] is used to iterate the delta and return the corresponding delta.
|
||||
#[derive(Debug)]
|
||||
pub struct OpCursor<'a, T: Attributes> {
|
||||
pub struct DeltaCursor<'a, T: Attributes> {
|
||||
pub(crate) delta: &'a Delta<T>,
|
||||
pub(crate) origin_iv: Interval,
|
||||
pub(crate) consume_iv: Interval,
|
||||
pub(crate) consume_count: usize,
|
||||
pub(crate) op_index: usize,
|
||||
pub(crate) op_offset: usize,
|
||||
iter: Enumerate<Iter<'a, Operation<T>>>,
|
||||
next_op: Option<Operation<T>>,
|
||||
}
|
||||
|
||||
impl<'a, T> OpCursor<'a, T>
|
||||
impl<'a, T> DeltaCursor<'a, T>
|
||||
where
|
||||
T: Attributes,
|
||||
{
|
||||
pub fn new(delta: &'a Delta<T>, interval: Interval) -> OpCursor<'a, T> {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `delta`: The delta you want to iterate over.
|
||||
/// * `interval`: The range for the cursor movement.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use lib_ot::core::{DeltaIterator, Interval, OperationBuilder};
|
||||
/// use lib_ot::rich_text::RichTextDelta;
|
||||
/// let mut delta = RichTextDelta::default();
|
||||
/// let op_1 = OperationBuilder::insert("123").build();
|
||||
/// let op_2 = OperationBuilder::insert("4").build();
|
||||
/// delta.add(op_1.clone());
|
||||
/// delta.add(op_2.clone());
|
||||
/// assert_eq!(DeltaIterator::from_interval(&delta, Interval::new(0, 3)).ops(), vec![op_1.clone()]);
|
||||
/// ```
|
||||
pub fn new(delta: &'a Delta<T>, interval: Interval) -> DeltaCursor<'a, T> {
|
||||
// debug_assert!(interval.start <= delta.target_len);
|
||||
let mut cursor = Self {
|
||||
delta,
|
||||
origin_iv: interval,
|
||||
consume_iv: interval,
|
||||
consume_count: 0,
|
||||
op_index: 0,
|
||||
op_offset: 0,
|
||||
iter: delta.ops.iter().enumerate(),
|
||||
next_op: None,
|
||||
};
|
||||
@ -35,17 +53,37 @@ where
|
||||
cursor
|
||||
}
|
||||
|
||||
// get the next operation interval
|
||||
/// Return the next operation interval
|
||||
pub fn next_iv(&self) -> Interval {
|
||||
self.next_iv_with_len(None).unwrap_or_else(|| Interval::new(0, 0))
|
||||
}
|
||||
|
||||
pub fn next_op(&mut self) -> Option<Operation<T>> {
|
||||
/// Return the next operation
|
||||
pub fn get_next_op(&mut self) -> Option<Operation<T>> {
|
||||
self.next_with_len(None)
|
||||
}
|
||||
|
||||
// get the last operation before the end.
|
||||
// checkout the delta_next_op_with_len_cross_op_return_last test for more detail
|
||||
/// Return the reference of the next operation
|
||||
pub fn next_op(&self) -> Option<&Operation<T>> {
|
||||
let mut next_op = self.next_op.as_ref();
|
||||
if next_op.is_none() {
|
||||
let mut offset = 0;
|
||||
for op in &self.delta.ops {
|
||||
offset += op.len();
|
||||
if offset > self.consume_count {
|
||||
next_op = Some(op);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
next_op
|
||||
}
|
||||
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `expected_len`: Return the next operation with the specified length.
|
||||
///
|
||||
///
|
||||
pub fn next_with_len(&mut self, expected_len: Option<usize>) -> Option<Operation<T>> {
|
||||
let mut find_op = None;
|
||||
let holder = self.next_op.clone();
|
||||
@ -97,17 +135,24 @@ where
|
||||
}
|
||||
|
||||
pub fn has_next(&self) -> bool {
|
||||
self.next_iter_op().is_some()
|
||||
self.next_op().is_some()
|
||||
}
|
||||
|
||||
fn descend(&mut self, index: usize) {
|
||||
self.consume_iv.start += index;
|
||||
/// Find the op within the current offset.
|
||||
/// This function sets the start of the consume_iv to the offset, update the consume_count
|
||||
/// and the next_op reference.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `offset`: Represents the offset of the delta string, in Utf16CodeUnit unit.
|
||||
fn descend(&mut self, offset: usize) {
|
||||
self.consume_iv.start += offset;
|
||||
|
||||
if self.consume_count >= self.consume_iv.start {
|
||||
return;
|
||||
}
|
||||
while let Some((o_index, op)) = self.iter.next() {
|
||||
self.op_index = o_index;
|
||||
self.op_offset = o_index;
|
||||
let start = self.consume_count;
|
||||
let end = start + op.len();
|
||||
let intersect = Interval::new(start, end).intersect(self.consume_iv);
|
||||
@ -121,7 +166,7 @@ where
|
||||
}
|
||||
|
||||
fn next_iv_with_len(&self, expected_len: Option<usize>) -> Option<Interval> {
|
||||
let op = self.next_iter_op()?;
|
||||
let op = self.next_op()?;
|
||||
let start = self.consume_count;
|
||||
let end = match expected_len {
|
||||
None => self.consume_count + op.len(),
|
||||
@ -132,31 +177,16 @@ where
|
||||
let interval = intersect.translate_neg(start);
|
||||
Some(interval)
|
||||
}
|
||||
|
||||
pub fn next_iter_op(&self) -> Option<&Operation<T>> {
|
||||
let mut next_op = self.next_op.as_ref();
|
||||
if next_op.is_none() {
|
||||
let mut offset = 0;
|
||||
for op in &self.delta.ops {
|
||||
offset += op.len();
|
||||
if offset > self.consume_count {
|
||||
next_op = Some(op);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
next_op
|
||||
}
|
||||
}
|
||||
|
||||
fn find_next<'a, T>(cursor: &mut OpCursor<'a, T>) -> Option<&'a Operation<T>>
|
||||
fn find_next<'a, T>(cursor: &mut DeltaCursor<'a, T>) -> Option<&'a Operation<T>>
|
||||
where
|
||||
T: Attributes,
|
||||
{
|
||||
match cursor.iter.next() {
|
||||
None => None,
|
||||
Some((o_index, op)) => {
|
||||
cursor.op_index = o_index;
|
||||
cursor.op_offset = o_index;
|
||||
Some(op)
|
||||
}
|
||||
}
|
||||
@ -164,31 +194,34 @@ where
|
||||
|
||||
type SeekResult = Result<(), OTError>;
|
||||
pub trait Metric {
|
||||
fn seek<T: Attributes>(cursor: &mut OpCursor<T>, offset: usize) -> SeekResult;
|
||||
fn seek<T: Attributes>(cursor: &mut DeltaCursor<T>, offset: usize) -> SeekResult;
|
||||
}
|
||||
|
||||
/// [OpMetric] is used by [DeltaIterator] for seeking operations
|
||||
/// The unit of the movement is Operation
|
||||
pub struct OpMetric();
|
||||
|
||||
impl Metric for OpMetric {
|
||||
fn seek<T: Attributes>(cursor: &mut OpCursor<T>, offset: usize) -> SeekResult {
|
||||
let _ = check_bound(cursor.op_index, offset)?;
|
||||
let mut seek_cursor = OpCursor::new(cursor.delta, cursor.origin_iv);
|
||||
let mut cur_offset = 0;
|
||||
fn seek<T: Attributes>(cursor: &mut DeltaCursor<T>, op_offset: usize) -> SeekResult {
|
||||
let _ = check_bound(cursor.op_offset, op_offset)?;
|
||||
let mut seek_cursor = DeltaCursor::new(cursor.delta, cursor.origin_iv);
|
||||
|
||||
while let Some((_, op)) = seek_cursor.iter.next() {
|
||||
cur_offset += op.len();
|
||||
if cur_offset > offset {
|
||||
cursor.descend(op.len());
|
||||
if cursor.op_offset >= op_offset {
|
||||
break;
|
||||
}
|
||||
}
|
||||
cursor.descend(cur_offset);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// [Utf16CodeUnitMetric] is used by [DeltaIterator] for seeking operations.
|
||||
/// The unit of the movement is Utf16CodeUnit
|
||||
pub struct Utf16CodeUnitMetric();
|
||||
|
||||
impl Metric for Utf16CodeUnitMetric {
|
||||
fn seek<T: Attributes>(cursor: &mut OpCursor<T>, offset: usize) -> SeekResult {
|
||||
fn seek<T: Attributes>(cursor: &mut DeltaCursor<T>, offset: usize) -> SeekResult {
|
||||
if offset > 0 {
|
||||
let _ = check_bound(cursor.consume_count, offset)?;
|
||||
let _ = cursor.next_with_len(Some(offset));
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::{
|
||||
core::{operation::*, DeltaIter, FlowyStr, Interval, OperationTransformable, MAX_IV_LEN},
|
||||
errors::{ErrorBuilder, OTError, OTErrorCode},
|
||||
};
|
||||
use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
|
||||
|
||||
use crate::core::delta::{DeltaIterator, MAX_IV_LEN};
|
||||
use crate::core::flowy_str::FlowyStr;
|
||||
use crate::core::interval::Interval;
|
||||
use crate::core::operation::{Attributes, Operation, OperationBuilder, OperationTransformable, PlainTextAttributes};
|
||||
use bytes::Bytes;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::{
|
||||
@ -15,11 +16,20 @@ use std::{
|
||||
|
||||
pub type PlainTextDelta = Delta<PlainTextAttributes>;
|
||||
|
||||
// TODO: optimize the memory usage with Arc::make_mut or Cow
|
||||
/// A [Delta] contains list of operations that consists of 'Retain', 'Delete' and 'Insert' operation.
|
||||
/// Check out the [Operation] for more details. It describes the document as a sequence of
|
||||
/// operations.
|
||||
///
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Delta<T: Attributes> {
|
||||
pub ops: Vec<Operation<T>>,
|
||||
|
||||
/// 'Delete' and 'Retain' operation will update the [utf16_base_len]
|
||||
/// Transforming the other delta, it requires the utf16_base_len must be equal.
|
||||
pub utf16_base_len: usize,
|
||||
|
||||
/// Represents the current len of the delta.
|
||||
/// 'Insert' and 'Retain' operation will update the [utf16_target_len]
|
||||
pub utf16_target_len: usize,
|
||||
}
|
||||
|
||||
@ -81,6 +91,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Adding an operation. It will be added in sequence.
|
||||
pub fn add(&mut self, op: Operation<T>) {
|
||||
match op {
|
||||
Operation::Delete(i) => self.delete(i),
|
||||
@ -89,6 +100,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Creating a [Delete] operation with len [n]
|
||||
pub fn delete(&mut self, n: usize) {
|
||||
if n == 0 {
|
||||
return;
|
||||
@ -97,10 +109,11 @@ where
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
/// Creating a [Insert] operation with string, [s].
|
||||
pub fn insert(&mut self, s: &str, attributes: T) {
|
||||
let s: FlowyStr = s.into();
|
||||
if s.is_empty() {
|
||||
@ -119,10 +132,10 @@ where
|
||||
}
|
||||
[.., op_last @ Operation::<T>::Delete(_)] => {
|
||||
let new_last = op_last.clone();
|
||||
*op_last = OpBuilder::<T>::insert(&s).attributes(attributes).build();
|
||||
*op_last = OperationBuilder::<T>::insert(&s).attributes(attributes).build();
|
||||
Some(new_last)
|
||||
}
|
||||
_ => Some(OpBuilder::<T>::insert(&s).attributes(attributes).build()),
|
||||
_ => Some(OperationBuilder::<T>::insert(&s).attributes(attributes).build()),
|
||||
};
|
||||
|
||||
match new_last {
|
||||
@ -131,6 +144,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Creating a [Retain] operation with len, [n].
|
||||
pub fn retain(&mut self, n: usize, attributes: T) {
|
||||
if n == 0 {
|
||||
return;
|
||||
@ -143,24 +157,48 @@ where
|
||||
self.ops.push(new_op);
|
||||
}
|
||||
} else {
|
||||
self.ops.push(OpBuilder::<T>::retain(n).attributes(attributes).build());
|
||||
self.ops
|
||||
.push(OperationBuilder::<T>::retain(n).attributes(attributes).build());
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies an operation to a string, returning a new string.
|
||||
pub fn apply(&self, s: &str) -> Result<String, OTError> {
|
||||
let s: FlowyStr = s.into();
|
||||
if s.utf16_size() != self.utf16_base_len {
|
||||
/// Return the a new string described by this delta. The new string will contains the input string.
|
||||
/// The length of the [applied_str] must be equal to the the [utf16_base_len].
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `applied_str`: A string represents the utf16_base_len content. it will be consumed by the [retain]
|
||||
/// or [delete] operations.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use lib_ot::core::PlainTextDeltaBuilder;
|
||||
/// let s = "hello";
|
||||
/// let delta_a = PlainTextDeltaBuilder::new().insert(s).build();
|
||||
/// let delta_b = PlainTextDeltaBuilder::new()
|
||||
/// .retain(s.len())
|
||||
/// .insert(", AppFlowy")
|
||||
/// .build();
|
||||
///
|
||||
/// let after_a = delta_a.to_str().unwrap();
|
||||
/// let after_b = delta_b.apply(&after_a).unwrap();
|
||||
/// assert_eq!("hello, AppFlowy", &after_b);
|
||||
/// ```
|
||||
pub fn apply(&self, applied_str: &str) -> Result<String, OTError> {
|
||||
let applied_str: FlowyStr = applied_str.into();
|
||||
if applied_str.utf16_size() != self.utf16_base_len {
|
||||
return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength)
|
||||
.msg(format!(
|
||||
"Expected: {}, received: {}",
|
||||
"Expected: {}, but received: {}",
|
||||
self.utf16_base_len,
|
||||
s.utf16_size()
|
||||
applied_str.utf16_size()
|
||||
))
|
||||
.build());
|
||||
}
|
||||
let mut new_s = String::new();
|
||||
let code_point_iter = &mut s.utf16_code_unit_iter();
|
||||
let code_point_iter = &mut applied_str.utf16_code_unit_iter();
|
||||
for op in &self.ops {
|
||||
match &op {
|
||||
Operation::Retain(retain) => {
|
||||
@ -181,34 +219,60 @@ where
|
||||
Ok(new_s)
|
||||
}
|
||||
|
||||
/// Computes the inverse of an operation. The inverse of an operation is the
|
||||
/// operation that reverts the effects of the operation
|
||||
pub fn invert_str(&self, s: &str) -> Self {
|
||||
/// Computes the inverse [Delta]. The inverse of an operation is the
|
||||
/// operation that reverts the effects of the operation
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `inverted_s`: A string represents the utf16_base_len content. The len of [inverted_s]
|
||||
/// must equal to the [utf16_base_len], it will be consumed by the [retain] or [delete] operations.
|
||||
///
|
||||
/// If the delta's operations just contain a insert operation. The inverted_s must be empty string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use lib_ot::core::PlainTextDeltaBuilder;
|
||||
/// let s = "hello world";
|
||||
/// let delta = PlainTextDeltaBuilder::new().insert(s).build();
|
||||
/// let invert_delta = delta.invert_str(s);
|
||||
/// assert_eq!(delta.utf16_base_len, invert_delta.utf16_target_len);
|
||||
/// assert_eq!(delta.utf16_target_len, invert_delta.utf16_base_len);
|
||||
///
|
||||
/// assert_eq!(invert_delta.apply(s).unwrap(), "")
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
pub fn invert_str(&self, inverted_s: &str) -> Self {
|
||||
let mut inverted = Delta::default();
|
||||
let chars = &mut s.chars();
|
||||
let inverted_s: FlowyStr = inverted_s.into();
|
||||
let code_point_iter = &mut inverted_s.utf16_code_unit_iter();
|
||||
|
||||
for op in &self.ops {
|
||||
match &op {
|
||||
Operation::Retain(retain) => {
|
||||
inverted.retain(retain.n, T::default());
|
||||
// TODO: use advance_by instead, but it's unstable now
|
||||
// chars.advance_by(retain.num)
|
||||
for _ in 0..retain.n {
|
||||
chars.next();
|
||||
code_point_iter.next();
|
||||
}
|
||||
}
|
||||
Operation::Insert(insert) => {
|
||||
inverted.delete(insert.utf16_size());
|
||||
}
|
||||
Operation::Delete(delete) => {
|
||||
inverted.insert(&chars.take(*delete as usize).collect::<String>(), op.get_attributes());
|
||||
let bytes = code_point_iter
|
||||
.take(*delete as usize)
|
||||
.into_iter()
|
||||
.flat_map(|a| str::from_utf8(a.0).ok())
|
||||
.collect::<String>();
|
||||
|
||||
inverted.insert(&bytes, op.get_attributes());
|
||||
}
|
||||
}
|
||||
}
|
||||
inverted
|
||||
}
|
||||
|
||||
/// Checks if this operation has no effect.
|
||||
#[inline]
|
||||
/// Return true if the delta doesn't contain any [Insert] or [Delete] operations.
|
||||
pub fn is_noop(&self) -> bool {
|
||||
matches!(self.ops.as_slice(), [] | [Operation::Retain(_)])
|
||||
}
|
||||
@ -231,8 +295,8 @@ where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut new_delta = Delta::default();
|
||||
let mut iter = DeltaIter::new(self);
|
||||
let mut other_iter = DeltaIter::new(other);
|
||||
let mut iter = DeltaIterator::new(self);
|
||||
let mut other_iter = DeltaIterator::new(other);
|
||||
|
||||
while iter.has_next() || other_iter.has_next() {
|
||||
if other_iter.is_next_insert() {
|
||||
@ -252,10 +316,10 @@ where
|
||||
|
||||
let op = iter
|
||||
.next_op_with_len(length)
|
||||
.unwrap_or_else(|| OpBuilder::retain(length).build());
|
||||
.unwrap_or_else(|| OperationBuilder::retain(length).build());
|
||||
let other_op = other_iter
|
||||
.next_op_with_len(length)
|
||||
.unwrap_or_else(|| OpBuilder::retain(length).build());
|
||||
.unwrap_or_else(|| OperationBuilder::retain(length).build());
|
||||
|
||||
// debug_assert_eq!(op.len(), other_op.len(), "Composing delta failed,");
|
||||
|
||||
@ -263,12 +327,16 @@ where
|
||||
(Operation::Retain(retain), Operation::Retain(other_retain)) => {
|
||||
let composed_attrs = retain.attributes.compose(&other_retain.attributes)?;
|
||||
|
||||
new_delta.add(OpBuilder::retain(retain.n).attributes(composed_attrs).build())
|
||||
new_delta.add(OperationBuilder::retain(retain.n).attributes(composed_attrs).build())
|
||||
}
|
||||
(Operation::Insert(insert), Operation::Retain(other_retain)) => {
|
||||
let mut composed_attrs = insert.attributes.compose(&other_retain.attributes)?;
|
||||
composed_attrs.remove_empty();
|
||||
new_delta.add(OpBuilder::insert(op.get_data()).attributes(composed_attrs).build())
|
||||
new_delta.add(
|
||||
OperationBuilder::insert(op.get_data())
|
||||
.attributes(composed_attrs)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
(Operation::Retain(_), Operation::Delete(_)) => {
|
||||
new_delta.add(other_op);
|
||||
@ -331,7 +399,7 @@ where
|
||||
Ordering::Less => {
|
||||
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_op2 = Some(OperationBuilder::retain(o_retain.n - retain.n).build());
|
||||
next_op1 = ops1.next();
|
||||
}
|
||||
Ordering::Equal => {
|
||||
@ -343,14 +411,14 @@ where
|
||||
Ordering::Greater => {
|
||||
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_op1 = Some(OperationBuilder::retain(retain.n - o_retain.n).build());
|
||||
next_op2 = ops2.next();
|
||||
}
|
||||
};
|
||||
}
|
||||
(Some(Operation::Delete(i)), Some(Operation::Delete(j))) => match i.cmp(j) {
|
||||
Ordering::Less => {
|
||||
next_op2 = Some(OpBuilder::delete(*j - *i).build());
|
||||
next_op2 = Some(OperationBuilder::delete(*j - *i).build());
|
||||
next_op1 = ops1.next();
|
||||
}
|
||||
Ordering::Equal => {
|
||||
@ -358,7 +426,7 @@ where
|
||||
next_op2 = ops2.next();
|
||||
}
|
||||
Ordering::Greater => {
|
||||
next_op1 = Some(OpBuilder::delete(*i - *j).build());
|
||||
next_op1 = Some(OperationBuilder::delete(*i - *j).build());
|
||||
next_op2 = ops2.next();
|
||||
}
|
||||
},
|
||||
@ -366,7 +434,7 @@ where
|
||||
match i.cmp(o_retain) {
|
||||
Ordering::Less => {
|
||||
a_prime.delete(*i);
|
||||
next_op2 = Some(OpBuilder::retain(o_retain.n - *i).build());
|
||||
next_op2 = Some(OperationBuilder::retain(o_retain.n - *i).build());
|
||||
next_op1 = ops1.next();
|
||||
}
|
||||
Ordering::Equal => {
|
||||
@ -376,7 +444,7 @@ where
|
||||
}
|
||||
Ordering::Greater => {
|
||||
a_prime.delete(o_retain.n);
|
||||
next_op1 = Some(OpBuilder::delete(*i - o_retain.n).build());
|
||||
next_op1 = Some(OperationBuilder::delete(*i - o_retain.n).build());
|
||||
next_op2 = ops2.next();
|
||||
}
|
||||
};
|
||||
@ -385,7 +453,7 @@ where
|
||||
match retain.cmp(j) {
|
||||
Ordering::Less => {
|
||||
b_prime.delete(retain.n);
|
||||
next_op2 = Some(OpBuilder::delete(*j - retain.n).build());
|
||||
next_op2 = Some(OperationBuilder::delete(*j - retain.n).build());
|
||||
next_op1 = ops1.next();
|
||||
}
|
||||
Ordering::Equal => {
|
||||
@ -395,7 +463,7 @@ where
|
||||
}
|
||||
Ordering::Greater => {
|
||||
b_prime.delete(*j);
|
||||
next_op1 = Some(OpBuilder::retain(retain.n - *j).build());
|
||||
next_op1 = Some(OperationBuilder::retain(retain.n - *j).build());
|
||||
next_op2 = ops2.next();
|
||||
}
|
||||
};
|
||||
@ -407,21 +475,17 @@ where
|
||||
|
||||
fn invert(&self, other: &Self) -> Self {
|
||||
let mut inverted = Delta::default();
|
||||
if other.is_empty() {
|
||||
return inverted;
|
||||
}
|
||||
|
||||
let mut index = 0;
|
||||
for op in &self.ops {
|
||||
let len: usize = op.len() as usize;
|
||||
match op {
|
||||
Operation::Delete(n) => {
|
||||
invert_from_other(&mut inverted, other, op, index, index + *n);
|
||||
invert_other(&mut inverted, other, op, index, index + *n);
|
||||
index += len;
|
||||
}
|
||||
Operation::Retain(_) => {
|
||||
match op.has_attribute() {
|
||||
true => invert_from_other(&mut inverted, other, op, index, index + len),
|
||||
true => invert_other(&mut inverted, other, op, index, index + len),
|
||||
false => {
|
||||
// tracing::trace!("invert retain: {} by retain {} {}", op, len,
|
||||
// op.get_attributes());
|
||||
@ -452,7 +516,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn invert_from_other<T: Attributes>(
|
||||
fn invert_other<T: Attributes>(
|
||||
base: &mut Delta<T>,
|
||||
other: &Delta<T>,
|
||||
operation: &Operation<T>,
|
||||
@ -460,7 +524,7 @@ fn invert_from_other<T: Attributes>(
|
||||
end: usize,
|
||||
) {
|
||||
tracing::trace!("invert op: {} [{}:{}]", operation, start, end);
|
||||
let other_ops = DeltaIter::from_interval(other, Interval::new(start, end)).ops();
|
||||
let other_ops = DeltaIterator::from_interval(other, Interval::new(start, end)).ops();
|
||||
other_ops.into_iter().for_each(|other_op| match operation {
|
||||
Operation::Delete(_n) => {
|
||||
// tracing::trace!("invert delete: {} by add {}", n, other_op);
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::core::{Attributes, Delta};
|
||||
use crate::core::delta::Delta;
|
||||
use crate::core::operation::Attributes;
|
||||
use serde::{
|
||||
de::{SeqAccess, Visitor},
|
||||
ser::SerializeSeq,
|
||||
|
@ -1,17 +1,17 @@
|
||||
use super::cursor::*;
|
||||
use crate::{
|
||||
core::{Attributes, Delta, Interval, Operation, NEW_LINE},
|
||||
rich_text::RichTextAttributes,
|
||||
};
|
||||
use crate::core::delta::{Delta, NEW_LINE};
|
||||
use crate::core::interval::Interval;
|
||||
use crate::core::operation::{Attributes, Operation};
|
||||
use crate::rich_text::RichTextAttributes;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize;
|
||||
|
||||
pub struct DeltaIter<'a, T: Attributes> {
|
||||
cursor: OpCursor<'a, T>,
|
||||
pub struct DeltaIterator<'a, T: Attributes> {
|
||||
cursor: DeltaCursor<'a, T>,
|
||||
}
|
||||
|
||||
impl<'a, T> DeltaIter<'a, T>
|
||||
impl<'a, T> DeltaIterator<'a, T>
|
||||
where
|
||||
T: Attributes,
|
||||
{
|
||||
@ -28,7 +28,7 @@ where
|
||||
}
|
||||
|
||||
pub fn from_interval(delta: &'a Delta<T>, interval: Interval) -> Self {
|
||||
let cursor = OpCursor::new(delta, interval);
|
||||
let cursor = DeltaCursor::new(delta, interval);
|
||||
Self { cursor }
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ where
|
||||
}
|
||||
|
||||
pub fn next_op(&mut self) -> Option<Operation<T>> {
|
||||
self.cursor.next_op()
|
||||
self.cursor.get_next_op()
|
||||
}
|
||||
|
||||
pub fn next_op_with_len(&mut self, len: usize) -> Option<Operation<T>> {
|
||||
@ -80,28 +80,28 @@ where
|
||||
}
|
||||
|
||||
pub fn is_next_insert(&self) -> bool {
|
||||
match self.cursor.next_iter_op() {
|
||||
match self.cursor.next_op() {
|
||||
None => false,
|
||||
Some(op) => op.is_insert(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_next_retain(&self) -> bool {
|
||||
match self.cursor.next_iter_op() {
|
||||
match self.cursor.next_op() {
|
||||
None => false,
|
||||
Some(op) => op.is_retain(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_next_delete(&self) -> bool {
|
||||
match self.cursor.next_iter_op() {
|
||||
match self.cursor.next_op() {
|
||||
None => false,
|
||||
Some(op) => op.is_delete(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for DeltaIter<'a, T>
|
||||
impl<'a, T> Iterator for DeltaIterator<'a, T>
|
||||
where
|
||||
T: Attributes,
|
||||
{
|
||||
@ -112,7 +112,7 @@ where
|
||||
}
|
||||
|
||||
pub fn is_empty_line_at_index(delta: &Delta<RichTextAttributes>, index: usize) -> bool {
|
||||
let mut iter = DeltaIter::new(delta);
|
||||
let mut iter = DeltaIterator::new(delta);
|
||||
let (prev, next) = (iter.next_op_with_len(index), iter.next_op());
|
||||
if prev.is_none() {
|
||||
return true;
|
||||
@ -128,7 +128,7 @@ pub fn is_empty_line_at_index(delta: &Delta<RichTextAttributes>, index: usize) -
|
||||
}
|
||||
|
||||
pub struct AttributesIter<'a, T: Attributes> {
|
||||
delta_iter: DeltaIter<'a, T>,
|
||||
delta_iter: DeltaIterator<'a, T>,
|
||||
}
|
||||
|
||||
impl<'a, T> AttributesIter<'a, T>
|
||||
@ -141,7 +141,7 @@ where
|
||||
}
|
||||
|
||||
pub fn from_interval(delta: &'a Delta<T>, interval: Interval) -> Self {
|
||||
let delta_iter = DeltaIter::from_interval(delta, interval);
|
||||
let delta_iter = DeltaIterator::from_interval(delta, interval);
|
||||
Self { delta_iter }
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ impl<'a, T> Deref for AttributesIter<'a, T>
|
||||
where
|
||||
T: Attributes,
|
||||
{
|
||||
type Target = DeltaIter<'a, T>;
|
||||
type Target = DeltaIterator<'a, T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.delta_iter
|
||||
|
@ -177,7 +177,7 @@ impl<'a> FlowyUtf16CodePointIterator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::core::Interval;
|
||||
use crate::core::interval::Interval;
|
||||
use std::str;
|
||||
|
||||
impl<'a> Iterator for FlowyUtf16CodePointIterator<'a> {
|
||||
@ -226,7 +226,8 @@ pub fn len_utf8_from_first_byte(b: u8) -> usize {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::core::{FlowyStr, Interval};
|
||||
use crate::core::flowy_str::FlowyStr;
|
||||
use crate::core::interval::Interval;
|
||||
|
||||
#[test]
|
||||
fn flowy_str_code_unit() {
|
||||
|
@ -157,7 +157,7 @@ impl From<RangeToInclusive<usize>> for Interval {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::core::Interval;
|
||||
use crate::core::interval::Interval;
|
||||
|
||||
#[test]
|
||||
fn contains() {
|
||||
|
@ -3,28 +3,7 @@ mod flowy_str;
|
||||
mod interval;
|
||||
mod operation;
|
||||
|
||||
use crate::errors::OTError;
|
||||
pub use delta::*;
|
||||
pub use flowy_str::*;
|
||||
pub use interval::*;
|
||||
pub use operation::*;
|
||||
|
||||
pub trait OperationTransformable {
|
||||
/// Merges the operation with `other` into one operation while preserving
|
||||
/// the changes of both.
|
||||
fn compose(&self, other: &Self) -> Result<Self, OTError>
|
||||
where
|
||||
Self: Sized;
|
||||
/// Transforms two operations a and b that happened concurrently and
|
||||
/// produces two operations a' and b'.
|
||||
/// (a', b') = a.transform(b)
|
||||
/// a.compose(b') = b.compose(a')
|
||||
fn transform(&self, other: &Self) -> Result<(Self, Self), OTError>
|
||||
where
|
||||
Self: Sized;
|
||||
/// Inverts the operation with `other` to produces undo operation.
|
||||
/// undo = a.invert(b)
|
||||
/// new_b = b.compose(a)
|
||||
/// b = new_b.compose(undo)
|
||||
fn invert(&self, other: &Self) -> Self;
|
||||
}
|
||||
|
@ -1,40 +1,38 @@
|
||||
use crate::{
|
||||
core::{Attributes, Operation, PlainTextAttributes},
|
||||
rich_text::RichTextAttributes,
|
||||
};
|
||||
use crate::core::operation::{Attributes, Operation, PlainTextAttributes};
|
||||
use crate::rich_text::RichTextAttributes;
|
||||
|
||||
pub type RichTextOpBuilder = OpBuilder<RichTextAttributes>;
|
||||
pub type PlainTextOpBuilder = OpBuilder<PlainTextAttributes>;
|
||||
pub type RichTextOpBuilder = OperationBuilder<RichTextAttributes>;
|
||||
pub type PlainTextOpBuilder = OperationBuilder<PlainTextAttributes>;
|
||||
|
||||
pub struct OpBuilder<T: Attributes> {
|
||||
pub struct OperationBuilder<T: Attributes> {
|
||||
ty: Operation<T>,
|
||||
attrs: T,
|
||||
}
|
||||
|
||||
impl<T> OpBuilder<T>
|
||||
impl<T> OperationBuilder<T>
|
||||
where
|
||||
T: Attributes,
|
||||
{
|
||||
pub fn new(ty: Operation<T>) -> OpBuilder<T> {
|
||||
OpBuilder {
|
||||
pub fn new(ty: Operation<T>) -> OperationBuilder<T> {
|
||||
OperationBuilder {
|
||||
ty,
|
||||
attrs: T::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retain(n: usize) -> OpBuilder<T> {
|
||||
OpBuilder::new(Operation::Retain(n.into()))
|
||||
pub fn retain(n: usize) -> OperationBuilder<T> {
|
||||
OperationBuilder::new(Operation::Retain(n.into()))
|
||||
}
|
||||
|
||||
pub fn delete(n: usize) -> OpBuilder<T> {
|
||||
OpBuilder::new(Operation::Delete(n))
|
||||
pub fn delete(n: usize) -> OperationBuilder<T> {
|
||||
OperationBuilder::new(Operation::Delete(n))
|
||||
}
|
||||
|
||||
pub fn insert(s: &str) -> OpBuilder<T> {
|
||||
OpBuilder::new(Operation::Insert(s.into()))
|
||||
pub fn insert(s: &str) -> OperationBuilder<T> {
|
||||
OperationBuilder::new(Operation::Insert(s.into()))
|
||||
}
|
||||
|
||||
pub fn attributes(mut self, attrs: T) -> OpBuilder<T> {
|
||||
pub fn attributes(mut self, attrs: T) -> OperationBuilder<T> {
|
||||
self.attrs = attrs;
|
||||
self
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::{
|
||||
core::{FlowyStr, Interval, OpBuilder, OperationTransformable},
|
||||
errors::OTError,
|
||||
};
|
||||
use crate::core::flowy_str::FlowyStr;
|
||||
use crate::core::interval::Interval;
|
||||
use crate::core::operation::OperationBuilder;
|
||||
use crate::errors::OTError;
|
||||
use serde::{Deserialize, Serialize, __private::Formatter};
|
||||
use std::fmt::Display;
|
||||
use std::{
|
||||
cmp::min,
|
||||
fmt,
|
||||
@ -10,13 +11,73 @@ use std::{
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
pub trait Attributes: fmt::Display + Eq + PartialEq + Default + Clone + Debug + OperationTransformable {
|
||||
fn is_empty(&self) -> bool;
|
||||
pub trait OperationTransformable {
|
||||
/// Merges the operation with `other` into one operation while preserving
|
||||
/// the changes of both.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `other`: The delta gonna to merge.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use lib_ot::core::{OperationTransformable, PlainTextDeltaBuilder};
|
||||
/// let document = PlainTextDeltaBuilder::new().build();
|
||||
/// let delta = PlainTextDeltaBuilder::new().insert("abc").build();
|
||||
/// let new_document = document.compose(&delta).unwrap();
|
||||
/// assert_eq!(new_document.to_str().unwrap(), "abc".to_owned());
|
||||
/// ```
|
||||
fn compose(&self, other: &Self) -> Result<Self, OTError>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
// Remove the empty attribute which value is None.
|
||||
fn remove_empty(&mut self);
|
||||
/// Transforms two operations a and b that happened concurrently and
|
||||
/// produces two operations a' and b'.
|
||||
/// (a', b') = a.transform(b)
|
||||
/// a.compose(b') = b.compose(a')
|
||||
///
|
||||
fn transform(&self, other: &Self) -> Result<(Self, Self), OTError>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn extend_other(&mut self, other: Self);
|
||||
/// Return the invert delta from the other. It can be used to do the undo operation.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `other`: Generate the undo delta for [Other]. [Other] can compose the undo delta to return
|
||||
/// to the previous state.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use lib_ot::core::{OperationTransformable, PlainTextDeltaBuilder};
|
||||
/// let original_document = PlainTextDeltaBuilder::new().build();
|
||||
/// let delta = PlainTextDeltaBuilder::new().insert("abc").build();
|
||||
///
|
||||
/// let undo_delta = delta.invert(&original_document);
|
||||
/// let new_document = original_document.compose(&delta).unwrap();
|
||||
/// let document = new_document.compose(&undo_delta).unwrap();
|
||||
///
|
||||
/// assert_eq!(original_document, document);
|
||||
///
|
||||
/// ```
|
||||
fn invert(&self, other: &Self) -> Self;
|
||||
}
|
||||
|
||||
pub trait Attributes: Default + Display + Eq + PartialEq + Clone + Debug + OperationTransformable {
|
||||
fn is_empty(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Remove the empty attribute which value is None.
|
||||
fn remove_empty(&mut self) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
fn extend_other(&mut self, _other: Self) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
@ -77,22 +138,22 @@ where
|
||||
let right;
|
||||
match self {
|
||||
Operation::Delete(n) => {
|
||||
left = Some(OpBuilder::<T>::delete(index).build());
|
||||
right = Some(OpBuilder::<T>::delete(*n - index).build());
|
||||
left = Some(OperationBuilder::<T>::delete(index).build());
|
||||
right = Some(OperationBuilder::<T>::delete(*n - index).build());
|
||||
}
|
||||
Operation::Retain(retain) => {
|
||||
left = Some(OpBuilder::<T>::delete(index).build());
|
||||
right = Some(OpBuilder::<T>::delete(retain.n - index).build());
|
||||
left = Some(OperationBuilder::<T>::delete(index).build());
|
||||
right = Some(OperationBuilder::<T>::delete(retain.n - index).build());
|
||||
}
|
||||
Operation::Insert(insert) => {
|
||||
let attributes = self.get_attributes();
|
||||
left = Some(
|
||||
OpBuilder::<T>::insert(&insert.s[0..index])
|
||||
OperationBuilder::<T>::insert(&insert.s[0..index])
|
||||
.attributes(attributes.clone())
|
||||
.build(),
|
||||
);
|
||||
right = Some(
|
||||
OpBuilder::<T>::insert(&insert.s[index..insert.utf16_size()])
|
||||
OperationBuilder::<T>::insert(&insert.s[index..insert.utf16_size()])
|
||||
.attributes(attributes)
|
||||
.build(),
|
||||
);
|
||||
@ -104,16 +165,18 @@ where
|
||||
|
||||
pub fn shrink(&self, interval: Interval) -> Option<Operation<T>> {
|
||||
let op = match self {
|
||||
Operation::Delete(n) => OpBuilder::delete(min(*n, interval.size())).build(),
|
||||
Operation::Retain(retain) => OpBuilder::retain(min(retain.n, interval.size()))
|
||||
Operation::Delete(n) => OperationBuilder::delete(min(*n, interval.size())).build(),
|
||||
Operation::Retain(retain) => OperationBuilder::retain(min(retain.n, interval.size()))
|
||||
.attributes(retain.attributes.clone())
|
||||
.build(),
|
||||
Operation::Insert(insert) => {
|
||||
if interval.start > insert.utf16_size() {
|
||||
OpBuilder::insert("").build()
|
||||
OperationBuilder::insert("").build()
|
||||
} else {
|
||||
let s = insert.s.sub_str(interval).unwrap_or_else(|| "".to_owned());
|
||||
OpBuilder::insert(&s).attributes(insert.attributes.clone()).build()
|
||||
OperationBuilder::insert(&s)
|
||||
.attributes(insert.attributes.clone())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -212,7 +275,7 @@ where
|
||||
self.n += n;
|
||||
None
|
||||
} else {
|
||||
Some(OpBuilder::retain(n).attributes(attributes).build())
|
||||
Some(OperationBuilder::retain(n).attributes(attributes).build())
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,7 +359,7 @@ where
|
||||
self.s += s;
|
||||
None
|
||||
} else {
|
||||
Some(OpBuilder::<T>::insert(s).attributes(attributes).build())
|
||||
Some(OperationBuilder::<T>::insert(s).attributes(attributes).build())
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,15 +409,7 @@ impl fmt::Display for PlainTextAttributes {
|
||||
}
|
||||
}
|
||||
|
||||
impl Attributes for PlainTextAttributes {
|
||||
fn is_empty(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn remove_empty(&mut self) {}
|
||||
|
||||
fn extend_other(&mut self, _other: Self) {}
|
||||
}
|
||||
impl Attributes for PlainTextAttributes {}
|
||||
|
||||
impl OperationTransformable for PlainTextAttributes {
|
||||
fn compose(&self, _other: &Self) -> Result<Self, OTError> {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::core::{Attributes, FlowyStr, Insert, Operation, Retain};
|
||||
use crate::core::flowy_str::FlowyStr;
|
||||
use crate::core::operation::{Attributes, Insert, Operation, Retain};
|
||||
use serde::{
|
||||
de,
|
||||
de::{MapAccess, SeqAccess, Visitor},
|
||||
|
@ -1,10 +1,6 @@
|
||||
#![allow(non_snake_case)]
|
||||
use crate::{
|
||||
block_attribute,
|
||||
core::{Attributes, Operation, OperationTransformable},
|
||||
errors::OTError,
|
||||
ignore_attribute, inline_attribute, list_attribute,
|
||||
};
|
||||
use crate::core::{Attributes, Operation, OperationTransformable};
|
||||
use crate::{block_attribute, errors::OTError, ignore_attribute, inline_attribute, list_attribute};
|
||||
use lazy_static::lazy_static;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
|
@ -1,7 +1,5 @@
|
||||
use crate::{
|
||||
core::{Delta, DeltaBuilder},
|
||||
rich_text::RichTextAttributes,
|
||||
};
|
||||
use crate::core::{Delta, DeltaBuilder};
|
||||
use crate::rich_text::RichTextAttributes;
|
||||
|
||||
pub type RichTextDelta = Delta<RichTextAttributes>;
|
||||
pub type RichTextDeltaBuilder = DeltaBuilder<RichTextAttributes>;
|
||||
|
Loading…
Reference in New Issue
Block a user