add format and insert extension

This commit is contained in:
appflowy 2021-08-11 23:34:35 +08:00
parent e4a64fd06a
commit 1ec4655d1b
17 changed files with 386 additions and 236 deletions

View File

@ -40,21 +40,21 @@ class Rules {
final List<Rule> _rules;
static final Rules _instance = Rules([
// const FormatLinkAtCaretPositionRule(),
// const ResolveLineFormatRule(),
// const ResolveInlineFormatRule(),
// const InsertEmbedsRule(),
// const ForceNewlineForInsertsAroundEmbedRule(),
// const AutoExitBlockRule(),
// const PreserveBlockStyleOnInsertRule(),
// const PreserveLineStyleOnSplitRule(),
// const ResetLineFormatOnNewLineRule(),
// const AutoFormatLinksRule(),
// const PreserveInlineStylesRule(),
// const CatchAllInsertRule(),
// const EnsureEmbedLineRule(),
// const PreserveLineStyleOnMergeRule(),
// const CatchAllDeleteRule(),
const FormatLinkAtCaretPositionRule(),
const ResolveLineFormatRule(),
const ResolveInlineFormatRule(),
const InsertEmbedsRule(),
const ForceNewlineForInsertsAroundEmbedRule(),
const AutoExitBlockRule(),
const PreserveBlockStyleOnInsertRule(),
const PreserveLineStyleOnSplitRule(),
const ResetLineFormatOnNewLineRule(),
const AutoFormatLinksRule(),
const PreserveInlineStylesRule(),
const CatchAllInsertRule(),
const EnsureEmbedLineRule(),
const PreserveLineStyleOnMergeRule(),
const CatchAllDeleteRule(),
]);
static Rules getInstance() => _instance;

View File

@ -17,6 +17,10 @@ pub struct Document {
impl Document {
pub fn new() -> Self {
let delta = Delta::new();
Self::from_delta(delta)
}
pub fn from_delta(delta: Delta) -> Self {
Document {
delta,
history: History::new(),
@ -35,7 +39,7 @@ impl Document {
);
}
let delta = self.view.handle_insert(&self.delta, text, index);
let delta = self.view.insert(&self.delta, text, index)?;
let interval = Interval::new(index, index);
self.update_with_op(&delta, interval)
}
@ -139,7 +143,7 @@ impl Document {
) -> Result<(), OTError> {
log::debug!("Update document with attribute: {}", attribute);
let mut attributes = AttrsBuilder::new().add(attribute).build();
let old_attributes = self.delta.get_attributes(interval);
let old_attributes = AttributesIter::from_interval(&self.delta, interval).next_or_empty();
log::debug!("combine with old: {:?}", old_attributes);
attributes.merge(Some(old_attributes));
@ -202,17 +206,3 @@ fn split_length_with_interval(length: usize, interval: Interval) -> (Interval, I
let suffix = original_interval.suffix(interval);
(prefix, interval, suffix)
}
pub fn trim(delta: &mut Delta) {
let remove_last = match delta.ops.last() {
None => false,
Some(op) => match op {
Operation::Delete(_) => false,
Operation::Retain(retain) => retain.is_plain(),
Operation::Insert(_) => false,
},
};
if remove_last {
delta.ops.pop();
}
}

View File

@ -1,16 +1,16 @@
use crate::{
client::Document,
core::{Attributes, Delta, Interval},
core::{Attribute, Delta, Interval},
};
pub trait InsertExt {
fn apply(&self, delta: &Delta, s: &str, index: usize) -> Delta;
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta>;
}
pub trait FormatExt {
fn apply(&self, document: &Document, interval: Interval, attributes: Attributes);
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta>;
}
pub trait DeleteExt {
fn apply(&self, document: &Document, interval: Interval);
fn apply(&self, delta: &Delta, interval: Interval) -> Option<Delta>;
}

View File

@ -0,0 +1,28 @@
use crate::{
client::view::FormatExt,
core::{Attribute, Delta, Interval},
};
pub struct FormatLinkAtCaretPositionExt {}
impl FormatExt for FormatLinkAtCaretPositionExt {
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
unimplemented!()
}
}
pub struct ResolveLineFormatExt {}
impl FormatExt for ResolveLineFormatExt {
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
unimplemented!()
}
}
pub struct ResolveInlineFormatExt {}
impl FormatExt for ResolveInlineFormatExt {
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
unimplemented!()
}
}

View File

@ -1,21 +1,73 @@
use crate::{
client::view::InsertExt,
core::{attributes_at_index, Attributes, AttributesIter, Builder, Delta, Interval},
core::{
attributes_at_index,
AttributeKey,
Attributes,
Delta,
DeltaBuilder,
DeltaIter,
Operation,
},
};
pub struct PreserveInlineStyleExt {}
pub const NEW_LINE: &'static str = "\n";
pub struct PreserveInlineStyleExt {}
impl PreserveInlineStyleExt {
pub fn new() -> Self { Self {} }
}
impl InsertExt for PreserveInlineStyleExt {
fn apply(&self, delta: &Delta, text: &str, index: usize) -> Delta {
let attributes = attributes_at_index(delta, index);
let mut delta = Delta::new();
let insert = Builder::insert(text).attributes(attributes).build();
delta.add(insert);
fn apply(&self, delta: &Delta, _replace_len: usize, text: &str, index: usize) -> Option<Delta> {
if text.ends_with(NEW_LINE) {
return None;
}
delta
let attributes = attributes_at_index(delta, index);
let delta = DeltaBuilder::new().insert(text, attributes).build();
Some(delta)
}
}
pub struct ResetLineFormatOnNewLineExt {}
impl ResetLineFormatOnNewLineExt {
pub fn new() -> Self { Self {} }
}
impl InsertExt for ResetLineFormatOnNewLineExt {
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
if text != NEW_LINE {
return None;
}
let mut iter = DeltaIter::new(delta);
iter.seek_to(index);
let maybe_next_op = iter.next();
if maybe_next_op.is_none() {
return None;
}
let op = maybe_next_op.unwrap();
if !op.get_data().starts_with(NEW_LINE) {
return None;
}
let mut reset_attribute = Attributes::new();
if op.get_attributes().contains_key(&AttributeKey::Header) {
reset_attribute.add(AttributeKey::Header.with_value(""));
}
let len = index + replace_len;
Some(
DeltaBuilder::new()
.retain(len, Attributes::default())
.insert(NEW_LINE, op.get_attributes())
.retain(1, reset_attribute)
.trim()
.build(),
)
}
}

View File

@ -1,7 +1,9 @@
mod extension;
mod format_ext;
mod insert_ext;
mod view;
pub use extension::*;
pub use format_ext::*;
pub use insert_ext::*;
pub use view::*;

View File

@ -1,32 +1,94 @@
use crate::{
client::view::{InsertExt, PreserveInlineStyleExt},
core::Delta,
client::view::{DeleteExt, FormatExt, InsertExt, *},
core::{Attribute, Delta, Interval},
errors::{ErrorBuilder, OTError, OTErrorCode},
};
type InsertExtension = Box<dyn InsertExt>;
type FormatExtension = Box<dyn FormatExt>;
type DeleteExtension = Box<dyn DeleteExt>;
pub struct View {
insert_exts: Vec<InsertExtension>,
format_exts: Vec<FormatExtension>,
delete_exts: Vec<DeleteExtension>,
}
impl View {
pub(crate) fn new() -> Self {
let insert_exts = construct_insert_exts();
Self { insert_exts }
let format_exts = construct_format_exts();
let delete_exts = construct_delete_exts();
Self {
insert_exts,
format_exts,
delete_exts,
}
}
pub(crate) fn handle_insert(&self, delta: &Delta, s: &str, index: usize) -> Delta {
let mut new_delta = Delta::new();
self.insert_exts.iter().for_each(|ext| {
new_delta = ext.apply(delta, s, index);
});
new_delta
pub(crate) fn insert(&self, delta: &Delta, text: &str, index: usize) -> Result<Delta, OTError> {
let mut new_delta = None;
for ext in &self.insert_exts {
if let Some(delta) = ext.apply(delta, 0, text, index) {
new_delta = Some(delta);
break;
}
}
match new_delta {
None => Err(ErrorBuilder::new(OTErrorCode::ApplyInsertFail).build()),
Some(new_delta) => Ok(new_delta),
}
}
pub(crate) fn replace(
&self,
delta: &Delta,
text: &str,
interval: Interval,
) -> Result<Delta, OTError> {
unimplemented!()
}
pub(crate) fn format(
&self,
delta: &Delta,
attribute: Attribute,
interval: Interval,
) -> Result<Delta, OTError> {
let mut new_delta = None;
for ext in &self.format_exts {
if let Some(delta) = ext.apply(delta, interval, &attribute) {
new_delta = Some(delta);
break;
}
}
match new_delta {
None => Err(ErrorBuilder::new(OTErrorCode::ApplyFormatFail).build()),
Some(new_delta) => Ok(new_delta),
}
}
}
fn construct_insert_exts() -> Vec<InsertExtension> {
vec![
//
Box::new(PreserveInlineStyleExt::new()),
Box::new(ResetLineFormatOnNewLineExt::new()),
]
}
fn construct_format_exts() -> Vec<FormatExtension> {
vec![
Box::new(FormatLinkAtCaretPositionExt {}),
Box::new(ResolveLineFormatExt {}),
Box::new(ResolveInlineFormatExt {}),
]
}
fn construct_delete_exts() -> Vec<DeleteExtension> {
vec![
//
]
}

View File

@ -1,5 +1,5 @@
use crate::core::{Attribute, AttributeKey, Operation};
use std::{collections::HashMap, fmt, fmt::Formatter};
use std::{collections::HashMap, fmt};
pub const REMOVE_FLAG: &'static str = "";
pub(crate) fn should_remove(s: &str) -> bool { s == REMOVE_FLAG }

View File

@ -0,0 +1,44 @@
use crate::core::{Attributes, Delta, Operation};
pub struct DeltaBuilder {
delta: Delta,
}
impl DeltaBuilder {
pub fn new() -> Self {
Self {
delta: Delta::new(),
}
}
pub fn retain(mut self, n: usize, attrs: Attributes) -> Self {
self.delta.retain(n, attrs);
self
}
pub fn insert(mut self, s: &str, attrs: Attributes) -> Self {
self.delta.insert(s, attrs);
self
}
pub fn trim(mut self) -> Self {
trim(&mut self.delta);
self
}
pub fn build(self) -> Delta { self.delta }
}
pub fn trim(delta: &mut Delta) {
let remove_last = match delta.ops.last() {
None => false,
Some(op) => match op {
Operation::Delete(_) => false,
Operation::Retain(retain) => retain.is_plain(),
Operation::Insert(_) => false,
},
};
if remove_last {
delta.ops.pop();
}
}

View File

@ -1,12 +1,8 @@
use crate::{
core::{Attributes, Delta, Interval, Operation},
core::{Delta, Interval, Operation},
errors::{ErrorBuilder, OTError, OTErrorCode},
};
use std::{
cmp::min,
ops::{Deref, DerefMut},
slice::Iter,
};
use std::{cmp::min, slice::Iter};
pub struct Cursor<'a> {
delta: &'a Delta,
@ -111,108 +107,3 @@ impl<'a> Cursor<'a> {
Ok(())
}
}
pub struct DeltaIter<'a> {
cursor: Cursor<'a>,
interval: Interval,
}
impl<'a> DeltaIter<'a> {
pub fn new(delta: &'a Delta) -> Self {
let interval = Interval::new(0, usize::MAX);
Self::from_interval(delta, interval)
}
pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self {
let cursor = Cursor::new(delta, interval);
Self { cursor, interval }
}
pub fn ops(&mut self) -> Vec<Operation> { self.collect::<Vec<_>>() }
pub fn seek_to(&mut self, n_char: usize) -> Result<(), OTError> {
let _ = self.cursor.seek_to(n_char)?;
Ok(())
}
}
impl<'a> Iterator for DeltaIter<'a> {
type Item = Operation;
fn next(&mut self) -> Option<Self::Item> { self.cursor.next_op() }
}
pub struct AttributesIter<'a> {
delta_iter: DeltaIter<'a>,
interval: Interval,
}
impl<'a> AttributesIter<'a> {
pub fn new(delta: &'a Delta) -> Self {
let interval = Interval::new(0, usize::MAX);
Self::from_interval(delta, interval)
}
pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self {
let delta_iter = DeltaIter::from_interval(delta, interval);
Self {
delta_iter,
interval,
}
}
}
impl<'a> Deref for AttributesIter<'a> {
type Target = DeltaIter<'a>;
fn deref(&self) -> &Self::Target { &self.delta_iter }
}
impl<'a> DerefMut for AttributesIter<'a> {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.delta_iter }
}
impl<'a> Iterator for AttributesIter<'a> {
type Item = (usize, Attributes);
fn next(&mut self) -> Option<Self::Item> {
let next_op = self.delta_iter.next();
if next_op.is_none() {
return None;
}
let mut length: usize = 0;
let mut attributes = Attributes::new();
match next_op.unwrap() {
Operation::Delete(_n) => {},
Operation::Retain(retain) => {
log::debug!("extend retain attributes with {} ", &retain.attributes);
attributes.extend(retain.attributes.clone());
length = retain.n;
},
Operation::Insert(insert) => {
log::debug!("extend insert attributes with {} ", &insert.attributes);
attributes.extend(insert.attributes.clone());
length = insert.num_chars();
},
}
Some((length, attributes))
}
}
pub(crate) fn attributes_at_index(delta: &Delta, index: usize) -> Attributes {
let mut iter = AttributesIter::new(delta);
iter.seek_to(index);
match iter.next() {
// None => Attributes::Follow,
None => Attributes::new(),
Some((_, attributes)) => attributes,
}
}
#[cfg(test)]
mod tests {
#[test]
fn test() {}
}

View File

@ -525,34 +525,6 @@ impl Delta {
pub fn is_empty(&self) -> bool { self.ops.is_empty() }
pub fn get_attributes(&self, interval: Interval) -> Attributes {
let mut attributes = Attributes::new();
let mut offset: usize = 0;
log::debug!("Get attributes at {:?}", interval);
self.ops.iter().for_each(|op| match op {
Operation::Delete(_n) => {},
Operation::Retain(retain) => {
if interval.contains_range(offset, offset + retain.n) {
log::debug!("extend retain attributes with {} ", &retain.attributes);
attributes.extend(retain.attributes.clone());
}
offset += retain.n;
},
Operation::Insert(insert) => {
let end = insert.num_chars() as usize;
if interval.contains_range(offset, offset + end) {
log::debug!("extend insert attributes with {} ", &insert.attributes);
attributes.extend(insert.attributes.clone());
}
offset += end;
},
});
log::debug!("Get attributes result: {} ", &attributes);
attributes
}
pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap_or("".to_owned()) }
}

View File

@ -0,0 +1,111 @@
use super::cursor::*;
use crate::{
core::{Attributes, Delta, Interval, Operation},
errors::OTError,
};
use std::ops::{Deref, DerefMut};
pub struct DeltaIter<'a> {
cursor: Cursor<'a>,
interval: Interval,
}
impl<'a> DeltaIter<'a> {
pub fn new(delta: &'a Delta) -> Self {
let interval = Interval::new(0, usize::MAX);
Self::from_interval(delta, interval)
}
pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self {
let cursor = Cursor::new(delta, interval);
Self { cursor, interval }
}
pub fn ops(&mut self) -> Vec<Operation> { self.collect::<Vec<_>>() }
pub fn seek_to(&mut self, n_char: usize) -> Result<(), OTError> {
let _ = self.cursor.seek_to(n_char)?;
Ok(())
}
}
impl<'a> Iterator for DeltaIter<'a> {
type Item = Operation;
fn next(&mut self) -> Option<Self::Item> { self.cursor.next_op() }
}
pub struct AttributesIter<'a> {
delta_iter: DeltaIter<'a>,
interval: Interval,
}
impl<'a> AttributesIter<'a> {
pub fn new(delta: &'a Delta) -> Self {
let interval = Interval::new(0, usize::MAX);
Self::from_interval(delta, interval)
}
pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self {
let delta_iter = DeltaIter::from_interval(delta, interval);
Self {
delta_iter,
interval,
}
}
pub fn next_or_empty(&mut self) -> Attributes {
match self.next() {
None => Attributes::default(),
Some((_, attributes)) => attributes,
}
}
}
impl<'a> Deref for AttributesIter<'a> {
type Target = DeltaIter<'a>;
fn deref(&self) -> &Self::Target { &self.delta_iter }
}
impl<'a> DerefMut for AttributesIter<'a> {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.delta_iter }
}
impl<'a> Iterator for AttributesIter<'a> {
type Item = (usize, Attributes);
fn next(&mut self) -> Option<Self::Item> {
let next_op = self.delta_iter.next();
if next_op.is_none() {
return None;
}
let mut length: usize = 0;
let mut attributes = Attributes::new();
match next_op.unwrap() {
Operation::Delete(_n) => {},
Operation::Retain(retain) => {
log::debug!("extend retain attributes with {} ", &retain.attributes);
attributes.extend(retain.attributes.clone());
length = retain.n;
},
Operation::Insert(insert) => {
log::debug!("extend insert attributes with {} ", &insert.attributes);
attributes.extend(insert.attributes.clone());
length = insert.num_chars();
},
}
Some((length, attributes))
}
}
pub(crate) fn attributes_at_index(delta: &Delta, index: usize) -> Attributes {
let mut iter = AttributesIter::new(delta);
iter.seek_to(index);
match iter.next() {
// None => Attributes::Follow,
None => Attributes::new(),
Some((_, attributes)) => attributes,
}
}

View File

@ -1,5 +1,9 @@
mod builder;
mod cursor;
mod delta;
mod iterator;
pub use builder::*;
pub use cursor::*;
pub use delta::*;
pub use iterator::*;

View File

@ -16,17 +16,11 @@ pub enum Operation {
}
impl Operation {
pub fn is_delete(&self) -> bool {
pub fn get_data(&self) -> &str {
match self {
Operation::Delete(_) => true,
_ => false,
}
}
pub fn is_noop(&self) -> bool {
match self {
Operation::Retain(_) => true,
_ => false,
Operation::Delete(_) => "",
Operation::Retain(_) => "",
Operation::Insert(insert) => &insert.s,
}
}

View File

@ -26,6 +26,8 @@ impl Error for OTError {
#[derive(Debug, Clone)]
pub enum OTErrorCode {
IncompatibleLength,
ApplyInsertFail,
ApplyFormatFail,
UndoFail,
RedoFail,
}

View File

@ -64,11 +64,7 @@ impl OpTester {
env_logger::init();
});
let mut documents = Vec::with_capacity(2);
for _ in 0..2 {
documents.push(Document::new());
}
Self { documents }
Self { documents: vec![] }
}
pub fn run_op(&mut self, op: &TestOp) {
@ -173,6 +169,22 @@ impl OpTester {
}
pub fn run_script(&mut self, script: Vec<TestOp>) {
let delta = Delta::new();
self.run(script, delta);
}
pub fn run_script_with_newline(&mut self, script: Vec<TestOp>) {
let mut delta = Delta::new();
delta.insert("\n", Attributes::default());
self.run(script, delta);
}
fn run(&mut self, script: Vec<TestOp>, delta: Delta) {
let mut documents = Vec::with_capacity(2);
for _ in 0..2 {
documents.push(Document::from_delta(delta.clone()));
}
self.documents = documents;
for (_i, op) in script.iter().enumerate() {
self.run_op(op);
}

View File

@ -6,18 +6,16 @@ use flowy_ot::{client::RECORD_THRESHOLD, core::Interval};
#[test]
fn delta_undo_insert() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Undo(0),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_undo_insert2() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Wait(RECORD_THRESHOLD),
Insert(0, "456", 0),
@ -26,13 +24,12 @@ fn delta_undo_insert2() {
Undo(0),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_redo_insert() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
AssertOpsJson(0, r#"[{"insert":"123\n"}]"#),
Undo(0),
@ -40,13 +37,12 @@ fn delta_redo_insert() {
Redo(0),
AssertOpsJson(0, r#"[{"insert":"123\n"}]"#),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_redo_insert_with_lagging() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Wait(RECORD_THRESHOLD),
Insert(0, "456", 3),
@ -60,38 +56,35 @@ fn delta_redo_insert_with_lagging() {
Undo(0),
AssertOpsJson(0, r#"[{"insert":"123\n"}]"#),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_undo_attributes() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true),
Undo(0),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_undo_attributes_with_lagging() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Wait(RECORD_THRESHOLD),
Bold(0, Interval::new(0, 3), true),
Undo(0),
AssertOpsJson(0, r#"[{"insert":"123\n"}]"#),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_redo_attributes() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true),
Undo(0),
@ -102,13 +95,12 @@ fn delta_redo_attributes() {
r#" [{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"}]"#,
),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_redo_attributes_with_lagging() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Wait(RECORD_THRESHOLD),
Bold(0, Interval::new(0, 3), true),
@ -120,7 +112,7 @@ fn delta_redo_attributes_with_lagging() {
r#"[{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"}]"#,
),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
@ -139,7 +131,6 @@ fn delta_undo_delete() {
#[test]
fn delta_undo_delete2() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true),
Delete(0, Interval::new(0, 1)),
@ -153,13 +144,12 @@ fn delta_undo_delete2() {
Undo(0),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_undo_delete2_with_lagging() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Wait(RECORD_THRESHOLD),
Bold(0, Interval::new(0, 3), true),
@ -181,13 +171,12 @@ fn delta_undo_delete2_with_lagging() {
"#,
),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_redo_delete() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Delete(0, Interval::new(0, 3)),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
@ -195,13 +184,12 @@ fn delta_redo_delete() {
Redo(0),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_undo_replace() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true),
Replace(0, Interval::new(0, 2), "ab"),
@ -215,13 +203,12 @@ fn delta_undo_replace() {
Undo(0),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_undo_replace_with_lagging() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Wait(RECORD_THRESHOLD),
Bold(0, Interval::new(0, 3), true),
@ -240,13 +227,12 @@ fn delta_undo_replace_with_lagging() {
r#"[{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"}]"#,
),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn delta_redo_replace() {
let ops = vec![
Insert(0, "\n", 0),
Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true),
Replace(0, Interval::new(0, 2), "ab"),
@ -260,5 +246,5 @@ fn delta_redo_replace() {
"#,
),
];
OpTester::new().run_script(ops);
OpTester::new().run_script_with_newline(ops);
}