瀏覽代碼

add format and insert extension

appflowy 3 年之前
父節點
當前提交
1ec4655d1b

+ 15 - 15
app_flowy/packages/flowy_editor/lib/src/model/heuristic/rule.dart

@@ -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;

+ 6 - 16
rust-lib/flowy-ot/src/client/document.rs

@@ -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();
-    }
-}

+ 4 - 4
rust-lib/flowy-ot/src/client/view/extension.rs

@@ -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>;
 }

+ 28 - 0
rust-lib/flowy-ot/src/client/view/format_ext.rs

@@ -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!()
+    }
+}

+ 59 - 7
rust-lib/flowy-ot/src/client/view/insert_ext.rs

@@ -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 {
+    fn apply(&self, delta: &Delta, _replace_len: usize, text: &str, index: usize) -> Option<Delta> {
+        if text.ends_with(NEW_LINE) {
+            return None;
+        }
+
         let attributes = attributes_at_index(delta, index);
-        let mut delta = Delta::new();
-        let insert = Builder::insert(text).attributes(attributes).build();
-        delta.add(insert);
+        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(""));
+        }
 
-        delta
+        let len = index + replace_len;
+        Some(
+            DeltaBuilder::new()
+                .retain(len, Attributes::default())
+                .insert(NEW_LINE, op.get_attributes())
+                .retain(1, reset_attribute)
+                .trim()
+                .build(),
+        )
     }
 }

+ 2 - 0
rust-lib/flowy-ot/src/client/view/mod.rs

@@ -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::*;

+ 72 - 10
rust-lib/flowy-ot/src/client/view/view.rs

@@ -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![
+        //
+
     ]
 }

+ 1 - 1
rust-lib/flowy-ot/src/core/attributes/attributes.rs

@@ -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 }

+ 44 - 0
rust-lib/flowy-ot/src/core/delta/builder.rs

@@ -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();
+    }
+}

+ 2 - 111
rust-lib/flowy-ot/src/core/delta/cursor.rs

@@ -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() {}
-}

+ 0 - 28
rust-lib/flowy-ot/src/core/delta/delta.rs

@@ -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()) }
 }
 

+ 111 - 0
rust-lib/flowy-ot/src/core/delta/iterator.rs

@@ -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,
+    }
+}

+ 4 - 0
rust-lib/flowy-ot/src/core/delta/mod.rs

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

+ 4 - 10
rust-lib/flowy-ot/src/core/operation/operation.rs

@@ -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,
         }
     }
 

+ 2 - 0
rust-lib/flowy-ot/src/errors.rs

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

+ 17 - 5
rust-lib/flowy-ot/tests/helper/mod.rs

@@ -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);
         }

+ 14 - 28
rust-lib/flowy-ot/tests/undo_redo_test.rs

@@ -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);
 }