mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
insert text with index
This commit is contained in:
parent
3770c648c3
commit
4ebeac13f5
@ -1,5 +1,5 @@
|
|||||||
use crate::operation::Operation;
|
use crate::operation::Operation;
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, fmt};
|
||||||
|
|
||||||
const PLAIN: &'static str = "";
|
const PLAIN: &'static str = "";
|
||||||
fn is_plain(s: &str) -> bool { s == PLAIN }
|
fn is_plain(s: &str) -> bool { s == PLAIN }
|
||||||
@ -15,7 +15,7 @@ pub enum Attributes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Attributes {
|
impl Attributes {
|
||||||
pub fn merge(&self, other: Option<Attributes>) -> Attributes {
|
pub fn extend(&self, other: Option<Attributes>) -> Attributes {
|
||||||
let other = other.unwrap_or(Attributes::Empty);
|
let other = other.unwrap_or(Attributes::Empty);
|
||||||
match (self, &other) {
|
match (self, &other) {
|
||||||
(Attributes::Custom(data), Attributes::Custom(o_data)) => {
|
(Attributes::Custom(data), Attributes::Custom(o_data)) => {
|
||||||
@ -42,6 +42,23 @@ impl std::default::Default for Attributes {
|
|||||||
fn default() -> Self { Attributes::Empty }
|
fn default() -> Self { Attributes::Empty }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Attributes {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Attributes::Follow => {
|
||||||
|
f.write_str("")?;
|
||||||
|
},
|
||||||
|
Attributes::Custom(data) => {
|
||||||
|
f.write_fmt(format_args!("{:?}", data.inner))?;
|
||||||
|
},
|
||||||
|
Attributes::Empty => {
|
||||||
|
f.write_str("")?;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct AttributesData {
|
pub struct AttributesData {
|
||||||
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
||||||
@ -132,9 +149,9 @@ pub fn compose_attributes(left: &Option<Operation>, right: &Option<Operation>) -
|
|||||||
let mut attr = match (&attr_l, &attr_r) {
|
let mut attr = match (&attr_l, &attr_r) {
|
||||||
(_, Some(Attributes::Custom(_))) => match &attr_l {
|
(_, Some(Attributes::Custom(_))) => match &attr_l {
|
||||||
None => attr_r.unwrap(),
|
None => attr_r.unwrap(),
|
||||||
Some(_) => attr_l.unwrap().merge(attr_r.clone()),
|
Some(_) => attr_l.unwrap().extend(attr_r.clone()),
|
||||||
},
|
},
|
||||||
(Some(Attributes::Custom(_)), _) => attr_l.unwrap().merge(attr_r),
|
(Some(Attributes::Custom(_)), _) => attr_l.unwrap().extend(attr_r),
|
||||||
_ => Attributes::Empty,
|
_ => Attributes::Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{attributes::*, errors::OTError, operation::*};
|
use crate::{attributes::*, errors::OTError, operation::*};
|
||||||
use bytecount::num_chars;
|
use bytecount::num_chars;
|
||||||
use std::{cmp::Ordering, iter::FromIterator, str::FromStr};
|
use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Delta {
|
pub struct Delta {
|
||||||
@ -33,6 +33,16 @@ impl<T: AsRef<str>> From<T> for Delta {
|
|||||||
fn from(s: T) -> Delta { Delta::from_str(s.as_ref()).unwrap() }
|
fn from(s: T) -> Delta { Delta::from_str(s.as_ref()).unwrap() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Delta {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(&serde_json::to_string(self).unwrap_or("".to_owned()))?;
|
||||||
|
// for op in &self.ops {
|
||||||
|
// f.write_fmt(format_args!("{}", op));
|
||||||
|
// }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromIterator<Operation> for Delta {
|
impl FromIterator<Operation> for Delta {
|
||||||
fn from_iter<T: IntoIterator<Item = Operation>>(ops: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = Operation>>(ops: T) -> Self {
|
||||||
let mut operations = Delta::default();
|
let mut operations = Delta::default();
|
||||||
|
@ -77,9 +77,25 @@ impl Interval {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn union(&self, other: Interval) -> Interval {
|
||||||
|
if self.is_empty() {
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
if other.is_empty() {
|
||||||
|
return *self;
|
||||||
|
}
|
||||||
|
let start = min(self.start, other.start);
|
||||||
|
let end = max(self.end, other.end);
|
||||||
|
Interval { start, end }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> usize { self.end - self.start }
|
pub fn size(&self) -> usize { self.end - self.start }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for Interval {
|
||||||
|
fn default() -> Self { Interval::new(0, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Interval {
|
impl fmt::Display for Interval {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "[{}, {})", self.start(), self.end())
|
write!(f, "[{}, {})", self.start(), self.end())
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::attributes::Attributes;
|
use crate::attributes::Attributes;
|
||||||
use bytecount::num_chars;
|
use bytecount::num_chars;
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
str::Chars,
|
str::Chars,
|
||||||
};
|
};
|
||||||
@ -35,6 +36,18 @@ impl Operation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extend_attributes(&mut self, attributes: Attributes) {
|
||||||
|
match self {
|
||||||
|
Operation::Delete(_) => {},
|
||||||
|
Operation::Retain(retain) => {
|
||||||
|
retain.attributes.extend(Some(attributes));
|
||||||
|
},
|
||||||
|
Operation::Insert(insert) => {
|
||||||
|
insert.attributes.extend(Some(attributes));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_attributes(&mut self, attributes: Attributes) {
|
pub fn set_attributes(&mut self, attributes: Attributes) {
|
||||||
match self {
|
match self {
|
||||||
Operation::Delete(_) => {
|
Operation::Delete(_) => {
|
||||||
@ -67,6 +80,29 @@ impl Operation {
|
|||||||
pub fn is_empty(&self) -> bool { self.length() == 0 }
|
pub fn is_empty(&self) -> bool { self.length() == 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Operation {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Operation::Delete(n) => {
|
||||||
|
f.write_fmt(format_args!("delete: {}", n))?;
|
||||||
|
},
|
||||||
|
Operation::Retain(r) => {
|
||||||
|
f.write_fmt(format_args!(
|
||||||
|
"retain: {}, attributes: {}",
|
||||||
|
r.num, r.attributes
|
||||||
|
))?;
|
||||||
|
},
|
||||||
|
Operation::Insert(i) => {
|
||||||
|
f.write_fmt(format_args!(
|
||||||
|
"insert: {}, attributes: {}",
|
||||||
|
i.s, i.attributes
|
||||||
|
))?;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct OpBuilder {
|
pub struct OpBuilder {
|
||||||
ty: Operation,
|
ty: Operation,
|
||||||
attrs: Attributes,
|
attrs: Attributes,
|
||||||
|
@ -6,10 +6,40 @@ use flowy_ot::{
|
|||||||
operation::{OpBuilder, Operation, Retain},
|
operation::{OpBuilder, Operation, Retain},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_insert_text() {
|
||||||
|
let ops = vec![
|
||||||
|
Insert(0, "123", 0),
|
||||||
|
Insert(0, "456", 3),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"123456"}]"#),
|
||||||
|
];
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_insert_text_at_head() {
|
||||||
|
let ops = vec![
|
||||||
|
Insert(0, "123", 0),
|
||||||
|
Insert(0, "456", 0),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"456123"}]"#),
|
||||||
|
];
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_insert_text_at_middle() {
|
||||||
|
let ops = vec![
|
||||||
|
Insert(0, "123", 0),
|
||||||
|
Insert(0, "456", 2),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"124563"}]"#),
|
||||||
|
];
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_add_bold_and_invert_all() {
|
fn delta_add_bold_and_invert_all() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
Insert(0, "123"),
|
Insert(0, "123", 0),
|
||||||
Bold(0, Interval::new(0, 3), true),
|
Bold(0, Interval::new(0, 3), true),
|
||||||
AssertOpsJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}}]"#),
|
AssertOpsJson(0, r#"[{"insert":"123","attributes":{"bold":"true"}}]"#),
|
||||||
Bold(0, Interval::new(0, 3), false),
|
Bold(0, Interval::new(0, 3), false),
|
||||||
@ -21,7 +51,7 @@ fn delta_add_bold_and_invert_all() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn delta_add_bold_and_invert_partial_suffix() {
|
fn delta_add_bold_and_invert_partial_suffix() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
Insert(0, "1234"),
|
Insert(0, "1234", 0),
|
||||||
Bold(0, Interval::new(0, 4), true),
|
Bold(0, Interval::new(0, 4), true),
|
||||||
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
|
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
|
||||||
Bold(0, Interval::new(2, 4), false),
|
Bold(0, Interval::new(2, 4), false),
|
||||||
@ -36,7 +66,7 @@ fn delta_add_bold_and_invert_partial_suffix() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn delta_add_bold_and_invert_partial_suffix2() {
|
fn delta_add_bold_and_invert_partial_suffix2() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
Insert(0, "1234"),
|
Insert(0, "1234", 0),
|
||||||
Bold(0, Interval::new(0, 4), true),
|
Bold(0, Interval::new(0, 4), true),
|
||||||
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
|
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
|
||||||
Bold(0, Interval::new(2, 4), false),
|
Bold(0, Interval::new(2, 4), false),
|
||||||
@ -53,7 +83,7 @@ fn delta_add_bold_and_invert_partial_suffix2() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn delta_add_bold_and_invert_partial_prefix() {
|
fn delta_add_bold_and_invert_partial_prefix() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
Insert(0, "1234"),
|
Insert(0, "1234", 0),
|
||||||
Bold(0, Interval::new(0, 4), true),
|
Bold(0, Interval::new(0, 4), true),
|
||||||
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
|
AssertOpsJson(0, r#"[{"insert":"1234","attributes":{"bold":"true"}}]"#),
|
||||||
Bold(0, Interval::new(0, 2), false),
|
Bold(0, Interval::new(0, 2), false),
|
||||||
@ -68,7 +98,7 @@ fn delta_add_bold_and_invert_partial_prefix() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn delta_add_bold_consecutive() {
|
fn delta_add_bold_consecutive() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
Insert(0, "1234"),
|
Insert(0, "1234", 0),
|
||||||
Bold(0, Interval::new(0, 1), true),
|
Bold(0, Interval::new(0, 1), true),
|
||||||
AssertOpsJson(
|
AssertOpsJson(
|
||||||
0,
|
0,
|
||||||
@ -93,14 +123,14 @@ fn delta_add_bold_empty_str() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn delta_add_bold_italic() {
|
fn delta_add_bold_italic() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
Insert(0, "1234"),
|
Insert(0, "1234", 0),
|
||||||
Bold(0, Interval::new(0, 4), true),
|
Bold(0, Interval::new(0, 4), true),
|
||||||
Italic(0, Interval::new(0, 4), true),
|
Italic(0, Interval::new(0, 4), true),
|
||||||
AssertOpsJson(
|
AssertOpsJson(
|
||||||
0,
|
0,
|
||||||
r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}}]"#,
|
r#"[{"insert":"1234","attributes":{"italic":"true","bold":"true"}}]"#,
|
||||||
),
|
),
|
||||||
Insert(0, "5678"),
|
Insert(0, "5678", 4),
|
||||||
AssertOpsJson(
|
AssertOpsJson(
|
||||||
0,
|
0,
|
||||||
r#"[{"insert":"12345678","attributes":{"italic":"true","bold":"true"}}]"#,
|
r#"[{"insert":"12345678","attributes":{"italic":"true","bold":"true"}}]"#,
|
||||||
@ -114,6 +144,74 @@ fn delta_add_bold_italic() {
|
|||||||
MergeTest::new().run_script(ops);
|
MergeTest::new().run_script(ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_add_bold_italic2() {
|
||||||
|
let ops = vec![
|
||||||
|
Insert(0, "123456", 0),
|
||||||
|
Bold(0, Interval::new(0, 6), true),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
|
||||||
|
Italic(0, Interval::new(0, 2), true),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"3456","attributes":{"bold":"true"}}]"#,
|
||||||
|
),
|
||||||
|
Italic(0, Interval::new(4, 6), true),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"12","attributes":{"bold":"true","italic":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true"}}]"#,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_add_bold_italic3() {
|
||||||
|
let ops = vec![
|
||||||
|
Insert(0, "123456789", 0),
|
||||||
|
Bold(0, Interval::new(0, 5), true),
|
||||||
|
Italic(0, Interval::new(0, 2), true),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"12","attributes":{"bold":"true","italic":"true"}},{"insert":"345","attributes":{"bold":"true"}},{"insert":"6789"}]"#,
|
||||||
|
),
|
||||||
|
Italic(0, Interval::new(2, 4), true),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"5","attributes":{"bold":"true"}},{"insert":"6789"}]"#,
|
||||||
|
),
|
||||||
|
Bold(0, Interval::new(7, 9), true),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"5","attributes":{"bold":"true"}},{"insert":"67"},{"insert":"89","attributes":{"bold":"true"}}]"#,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_add_bold_italic_delete() {
|
||||||
|
let ops = vec![
|
||||||
|
Insert(0, "123456789", 0),
|
||||||
|
Bold(0, Interval::new(0, 5), true),
|
||||||
|
Italic(0, Interval::new(0, 2), true),
|
||||||
|
Italic(0, Interval::new(2, 4), true),
|
||||||
|
Bold(0, Interval::new(7, 9), true),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"5","attributes":{"bold":"true"}},{"insert":"67"},{"insert":"89","attributes":{"bold":"true"}}]"#,
|
||||||
|
),
|
||||||
|
Delete(0, Interval::new(0, 5)),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"67","attributes":{"bold":"true"}},{"insert":"89"}]"#,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_merge_inserted_text_with_same_attribute() {
|
fn delta_merge_inserted_text_with_same_attribute() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
@ -125,21 +223,6 @@ fn delta_merge_inserted_text_with_same_attribute() {
|
|||||||
MergeTest::new().run_script(ops);
|
MergeTest::new().run_script(ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn delta_compose_attr_delta_with_no_attr_delta_test() {
|
|
||||||
let expected = r#"[{"insert":"123456","attributes":{"bold":"true"}},{"insert":"7"}]"#;
|
|
||||||
let ops = vec![
|
|
||||||
InsertBold(0, "123456", Interval::new(0, 6)),
|
|
||||||
AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
|
|
||||||
Insert(1, "7"),
|
|
||||||
AssertOpsJson(1, r#"[{"insert":"7"}]"#),
|
|
||||||
Transform(0, 1),
|
|
||||||
AssertOpsJson(0, expected),
|
|
||||||
AssertOpsJson(1, expected),
|
|
||||||
];
|
|
||||||
MergeTest::new().run_script(ops);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_compose_attr_delta_with_attr_delta_test() {
|
fn delta_compose_attr_delta_with_attr_delta_test() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
@ -155,6 +238,48 @@ fn delta_compose_attr_delta_with_attr_delta_test() {
|
|||||||
MergeTest::new().run_script(ops);
|
MergeTest::new().run_script(ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_compose_attr_delta_with_attr_delta_test2() {
|
||||||
|
let ops = vec![
|
||||||
|
Insert(0, "123456", 0),
|
||||||
|
Bold(0, Interval::new(0, 6), true),
|
||||||
|
Italic(0, Interval::new(0, 2), true),
|
||||||
|
Italic(0, Interval::new(4, 6), true),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"12","attributes":{"bold":"true","italic":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true"}}]"#,
|
||||||
|
),
|
||||||
|
InsertBold(1, "7", Interval::new(0, 1)),
|
||||||
|
AssertOpsJson(1, r#"[{"insert":"7","attributes":{"bold":"true"}}]"#),
|
||||||
|
Transform(0, 1),
|
||||||
|
AssertOpsJson(
|
||||||
|
0,
|
||||||
|
r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true"}},{"insert":"7","attributes":{"bold":"true"}}]"#,
|
||||||
|
),
|
||||||
|
AssertOpsJson(
|
||||||
|
1,
|
||||||
|
r#"[{"insert":"12","attributes":{"italic":"true","bold":"true"}},{"insert":"34","attributes":{"bold":"true"}},{"insert":"56","attributes":{"italic":"true"}},{"insert":"7","attributes":{"bold":"true"}}]"#,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_compose_attr_delta_with_no_attr_delta_test() {
|
||||||
|
let expected = r#"[{"insert":"123456","attributes":{"bold":"true"}},{"insert":"7"}]"#;
|
||||||
|
let ops = vec![
|
||||||
|
InsertBold(0, "123456", Interval::new(0, 6)),
|
||||||
|
AssertOpsJson(0, r#"[{"insert":"123456","attributes":{"bold":"true"}}]"#),
|
||||||
|
Insert(1, "7", 0),
|
||||||
|
AssertOpsJson(1, r#"[{"insert":"7"}]"#),
|
||||||
|
Transform(0, 1),
|
||||||
|
AssertOpsJson(0, expected),
|
||||||
|
AssertOpsJson(1, expected),
|
||||||
|
];
|
||||||
|
MergeTest::new().run_script(ops);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_delete_heading() {
|
fn delta_delete_heading() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
|
@ -9,7 +9,7 @@ use std::sync::Once;
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum MergeTestOp {
|
pub enum MergeTestOp {
|
||||||
Insert(usize, &'static str),
|
Insert(usize, &'static str, usize),
|
||||||
// delta_i, s, start, length,
|
// delta_i, s, start, length,
|
||||||
InsertBold(usize, &'static str, Interval),
|
InsertBold(usize, &'static str, Interval),
|
||||||
// delta_i, start, length, enable
|
// delta_i, start, length, enable
|
||||||
@ -43,9 +43,8 @@ impl MergeTest {
|
|||||||
|
|
||||||
pub fn run_op(&mut self, op: &MergeTestOp) {
|
pub fn run_op(&mut self, op: &MergeTestOp) {
|
||||||
match op {
|
match op {
|
||||||
MergeTestOp::Insert(delta_i, s) => {
|
MergeTestOp::Insert(delta_i, s, index) => {
|
||||||
let delta = &mut self.deltas[*delta_i];
|
self.update_delta_with_insert(*delta_i, s, *index);
|
||||||
delta.insert(s, Attributes::Follow);
|
|
||||||
},
|
},
|
||||||
MergeTestOp::Delete(delta_i, interval) => {
|
MergeTestOp::Delete(delta_i, interval) => {
|
||||||
//
|
//
|
||||||
@ -88,8 +87,8 @@ impl MergeTest {
|
|||||||
let target_delta: Delta = serde_json::from_str(&delta_i_json).unwrap();
|
let target_delta: Delta = serde_json::from_str(&delta_i_json).unwrap();
|
||||||
|
|
||||||
if expected_delta != target_delta {
|
if expected_delta != target_delta {
|
||||||
log::error!("✅ {}", expected);
|
log::error!("✅ expect: {}", expected,);
|
||||||
log::error!("❌ {}", delta_i_json);
|
log::error!("❌ receive: {}", delta_i_json);
|
||||||
}
|
}
|
||||||
assert_eq!(target_delta, expected_delta);
|
assert_eq!(target_delta, expected_delta);
|
||||||
},
|
},
|
||||||
@ -102,6 +101,43 @@ impl MergeTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if old_delta.target_len < index {
|
||||||
|
log::error!("{} out of bounds {}", index, target_interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut attributes = attributes_in_delta(old_delta, &Interval::new(index, index + 1));
|
||||||
|
if attributes == Attributes::Empty {
|
||||||
|
attributes = Attributes::Follow;
|
||||||
|
}
|
||||||
|
let insert = OpBuilder::insert(s).attributes(attributes).build();
|
||||||
|
|
||||||
|
let mut new_delta = Delta::default();
|
||||||
|
let prefix = Interval::new(0, index);
|
||||||
|
let suffix = Interval::new(index, old_delta.target_len);
|
||||||
|
|
||||||
|
split_interval_with_delta(old_delta, &prefix)
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|interval| {
|
||||||
|
let attrs = attributes_in_delta(old_delta, &interval);
|
||||||
|
new_delta.retain(interval.size() as u64, attrs);
|
||||||
|
});
|
||||||
|
|
||||||
|
new_delta.add(insert);
|
||||||
|
|
||||||
|
split_interval_with_delta(old_delta, &suffix)
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|interval| {
|
||||||
|
let attrs = attributes_in_delta(old_delta, &interval);
|
||||||
|
new_delta.retain(interval.size() as u64, attrs);
|
||||||
|
});
|
||||||
|
|
||||||
|
new_delta = old_delta.compose(&new_delta).unwrap();
|
||||||
|
self.deltas[delta_index] = new_delta;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_delta_with_attribute(
|
pub fn update_delta_with_attribute(
|
||||||
&mut self,
|
&mut self,
|
||||||
delta_index: usize,
|
delta_index: usize,
|
||||||
@ -109,58 +145,93 @@ impl MergeTest {
|
|||||||
interval: &Interval,
|
interval: &Interval,
|
||||||
) {
|
) {
|
||||||
let old_delta = &self.deltas[delta_index];
|
let old_delta = &self.deltas[delta_index];
|
||||||
let retain = OpBuilder::retain(interval.size() as u64)
|
let mut retain = OpBuilder::retain(interval.size() as u64)
|
||||||
.attributes(attributes)
|
.attributes(attributes)
|
||||||
.build();
|
.build();
|
||||||
let new_delta = make_delta_with_op(old_delta, retain, interval);
|
|
||||||
|
let attrs = attributes_in_delta(old_delta, &interval);
|
||||||
|
retain.extend_attributes(attrs);
|
||||||
|
|
||||||
|
let new_delta = new_delta_with_op(old_delta, retain, interval);
|
||||||
self.deltas[delta_index] = new_delta;
|
self.deltas[delta_index] = new_delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_delta_with_delete(&mut self, delta_index: usize, interval: &Interval) {
|
pub fn update_delta_with_delete(&mut self, delta_index: usize, interval: &Interval) {
|
||||||
let old_delta = &self.deltas[delta_index];
|
let old_delta = &self.deltas[delta_index];
|
||||||
let delete = OpBuilder::delete(interval.size() as u64).build();
|
let mut delete = OpBuilder::delete(interval.size() as u64).build();
|
||||||
let new_delta = make_delta_with_op(old_delta, delete, interval);
|
let attrs = attributes_in_delta(old_delta, &interval);
|
||||||
|
delete.extend_attributes(attrs);
|
||||||
|
|
||||||
|
let new_delta = new_delta_with_op(old_delta, delete, interval);
|
||||||
self.deltas[delta_index] = new_delta;
|
self.deltas[delta_index] = new_delta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_delta_with_op(delta: &Delta, op: Operation, interval: &Interval) -> Delta {
|
fn new_delta_with_op(delta: &Delta, op: Operation, interval: &Interval) -> Delta {
|
||||||
let mut new_delta = Delta::default();
|
let mut new_delta = Delta::default();
|
||||||
let (prefix, suffix) = length_split_with_interval(delta.target_len, interval);
|
let (prefix, interval, suffix) = target_length_split_with_interval(delta.target_len, *interval);
|
||||||
|
|
||||||
// prefix
|
// prefix
|
||||||
if prefix.is_empty() == false && prefix != *interval {
|
if prefix.is_empty() == false && prefix != interval {
|
||||||
let size = prefix.size();
|
let intervals = split_interval_with_delta(delta, &prefix);
|
||||||
let attrs = attributes_in_interval(delta, &prefix);
|
intervals.into_iter().for_each(|interval| {
|
||||||
new_delta.retain(size as u64, attrs);
|
let attrs = attributes_in_delta(delta, &interval);
|
||||||
|
new_delta.retain(interval.size() as u64, attrs);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
new_delta.add(op);
|
new_delta.add(op);
|
||||||
|
|
||||||
// suffix
|
// suffix
|
||||||
if suffix.is_empty() == false {
|
if suffix.is_empty() == false {
|
||||||
let size = suffix.size();
|
let intervals = split_interval_with_delta(delta, &suffix);
|
||||||
let attrs = attributes_in_interval(delta, &suffix);
|
intervals.into_iter().for_each(|interval| {
|
||||||
new_delta.retain(size as u64, attrs);
|
let attrs = attributes_in_delta(delta, &interval);
|
||||||
|
new_delta.retain(interval.size() as u64, attrs);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
delta.compose(&new_delta).unwrap()
|
delta.compose(&new_delta).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn length_split_with_interval(length: usize, interval: &Interval) -> (Interval, Interval) {
|
fn split_interval_with_delta(delta: &Delta, interval: &Interval) -> Vec<Interval> {
|
||||||
|
let mut start = 0;
|
||||||
|
let mut new_intervals = vec![];
|
||||||
|
delta.ops.iter().for_each(|op| match op {
|
||||||
|
Operation::Delete(_) => {},
|
||||||
|
Operation::Retain(_) => {},
|
||||||
|
Operation::Insert(insert) => {
|
||||||
|
let len = insert.num_chars() as usize;
|
||||||
|
let end = start + len;
|
||||||
|
let insert_interval = Interval::new(start, end);
|
||||||
|
let new_interval = interval.intersect(insert_interval);
|
||||||
|
|
||||||
|
if !new_interval.is_empty() {
|
||||||
|
new_intervals.push(new_interval)
|
||||||
|
}
|
||||||
|
start += len;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
new_intervals
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target_length_split_with_interval(
|
||||||
|
length: usize,
|
||||||
|
interval: Interval,
|
||||||
|
) -> (Interval, Interval, Interval) {
|
||||||
let original_interval = Interval::new(0, length);
|
let original_interval = Interval::new(0, length);
|
||||||
let prefix = original_interval.prefix(*interval);
|
let prefix = original_interval.prefix(interval);
|
||||||
let suffix = original_interval.suffix(*interval);
|
let suffix = original_interval.suffix(interval);
|
||||||
(prefix, suffix)
|
(prefix, interval, suffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_print_delta(delta: &Delta) {
|
pub fn debug_print_delta(delta: &Delta) {
|
||||||
log::debug!("😁 {}", serde_json::to_string(delta).unwrap());
|
log::debug!("😁 {}", serde_json::to_string(delta).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Attributes {
|
pub fn attributes_in_delta(delta: &Delta, interval: &Interval) -> Attributes {
|
||||||
let mut attributes_data = AttributesData::new();
|
let mut attributes_data = AttributesData::new();
|
||||||
let mut offset = 0;
|
let mut offset: usize = 0;
|
||||||
|
|
||||||
delta.ops.iter().for_each(|op| match op {
|
delta.ops.iter().for_each(|op| match op {
|
||||||
Operation::Delete(_n) => {},
|
Operation::Delete(_n) => {},
|
||||||
@ -178,10 +249,14 @@ pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Attributes
|
|||||||
Operation::Insert(insert) => match &insert.attributes {
|
Operation::Insert(insert) => match &insert.attributes {
|
||||||
Attributes::Follow => {},
|
Attributes::Follow => {},
|
||||||
Attributes::Custom(data) => {
|
Attributes::Custom(data) => {
|
||||||
if interval.start >= offset && insert.num_chars() > (interval.end as u64 - 1) {
|
let end = insert.num_chars() as usize;
|
||||||
|
if !interval
|
||||||
|
.intersect(Interval::new(offset, offset + end))
|
||||||
|
.is_empty()
|
||||||
|
{
|
||||||
attributes_data.extend(data.clone());
|
attributes_data.extend(data.clone());
|
||||||
}
|
}
|
||||||
offset += insert.num_chars() as usize;
|
offset += end;
|
||||||
},
|
},
|
||||||
Attributes::Empty => {},
|
Attributes::Empty => {},
|
||||||
},
|
},
|
||||||
@ -193,6 +268,13 @@ pub fn attributes_in_interval(delta: &Delta, interval: &Interval) -> Attributes
|
|||||||
Attributes::Custom(attributes_data)
|
Attributes::Custom(attributes_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn attributes_in_operation(op: &Operation, interval: &Interval) -> Attributes {
|
||||||
|
match op {
|
||||||
|
Operation::Delete(_) => Attributes::Empty,
|
||||||
|
Operation::Retain(retain) => Attributes::Empty,
|
||||||
|
Operation::Insert(insert) => Attributes::Empty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Rng(StdRng);
|
pub struct Rng(StdRng);
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ pub mod helper;
|
|||||||
|
|
||||||
use crate::helper::MergeTestOp::*;
|
use crate::helper::MergeTestOp::*;
|
||||||
use bytecount::num_chars;
|
use bytecount::num_chars;
|
||||||
use flowy_ot::{attributes::*, delta::Delta, operation::OpBuilder};
|
use flowy_ot::{attributes::*, delta::Delta, interval::Interval, operation::OpBuilder};
|
||||||
use helper::*;
|
use helper::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -188,8 +188,8 @@ fn transform() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn transform2() {
|
fn transform2() {
|
||||||
let ops = vec![
|
let ops = vec![
|
||||||
Insert(0, "123"),
|
Insert(0, "123", 0),
|
||||||
Insert(1, "456"),
|
Insert(1, "456", 0),
|
||||||
Transform(0, 1),
|
Transform(0, 1),
|
||||||
AssertStr(0, "123456"),
|
AssertStr(0, "123456"),
|
||||||
AssertStr(1, "123456"),
|
AssertStr(1, "123456"),
|
||||||
|
Loading…
Reference in New Issue
Block a user