refactor compose with iterator

This commit is contained in:
appflowy 2021-08-14 16:44:39 +08:00
parent b03dcde824
commit f994155dfe
20 changed files with 341 additions and 216 deletions

View File

@ -40,7 +40,7 @@ class Rules {
final List<Rule> _rules;
static final Rules _instance = Rules([
// const FormatLinkAtCaretPositionRule(),
const FormatLinkAtCaretPositionRule(),
// const ResolveLineFormatRule(),
const ResolveInlineFormatRule(),
// const InsertEmbedsRule(),
@ -54,7 +54,7 @@ class Rules {
const CatchAllInsertRule(),
// const EnsureEmbedLineRule(),
// const PreserveLineStyleOnMergeRule(),
// const CatchAllDeleteRule(),
const CatchAllDeleteRule(),
]);
static Rules getInstance() => _instance;

View File

@ -30,7 +30,12 @@ impl Document {
}
}
pub fn insert(&mut self, index: usize, text: &str) -> Result<(), OTError> {
pub fn insert(
&mut self,
index: usize,
text: &str,
replace_len: usize,
) -> Result<Delta, OTError> {
if self.delta.target_len < index {
log::error!(
"{} out of bounds. should 0..{}",
@ -39,37 +44,43 @@ impl Document {
);
}
let delta = self.view.insert(&self.delta, text, index)?;
let interval = Interval::new(index, index);
self.update_with_op(&delta, interval)
let delta = self.view.insert(&self.delta, text, index, replace_len)?;
self.add_delta(&delta)?;
Ok(delta)
}
pub fn delete(&mut self, interval: Interval) -> Result<Delta, OTError> {
debug_assert_eq!(interval.is_empty(), false);
let delete = self.view.delete(&self.delta, interval)?;
if !delete.is_empty() {
let _ = self.add_delta(&delete)?;
}
Ok(delete)
}
pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> {
log::debug!("format with {} at {}", attribute, interval);
// let format_delta = self
// .view
// .format(&self.delta, attribute.clone(), interval)
// .unwrap();
//
// self.delta = self.record_change(&format_delta)?;
// Ok(())
let format_delta = self
.view
.format(&self.delta, attribute.clone(), interval)
.unwrap();
self.update_with_attribute(attribute, interval)
self.add_delta(&format_delta)?;
Ok(())
}
pub fn replace(&mut self, interval: Interval, s: &str) -> Result<(), OTError> {
pub fn replace(&mut self, interval: Interval, s: &str) -> Result<Delta, OTError> {
let mut delta = Delta::default();
if !s.is_empty() {
let insert = OpBuilder::insert(s).build();
delta.add(insert);
delta = self.insert(interval.start, s, interval.size())?;
}
if !interval.is_empty() {
let delete = OpBuilder::delete(interval.size()).build();
delta.add(delete);
let delete = self.delete(interval)?;
delta = delta.compose(&delete)?;
}
self.update_with_op(&delta, interval)
Ok(delta)
}
pub fn can_undo(&self) -> bool { self.history.can_undo() }
@ -114,66 +125,13 @@ impl Document {
pub fn set_data(&mut self, data: Delta) { self.delta = data; }
fn update_with_op(&mut self, delta: &Delta, interval: Interval) -> Result<(), OTError> {
let mut new_delta = Delta::default();
let (prefix, interval, suffix) =
split_length_with_interval(self.delta.target_len, interval);
// prefix
if prefix.is_empty() == false && prefix != interval {
AttributesIter::from_interval(&self.delta, prefix).for_each(|(length, attributes)| {
log::debug!("prefix attribute: {:?}, len: {}", attributes, length);
new_delta.retain(length, attributes);
});
}
delta.ops.iter().for_each(|op| {
new_delta.add(op.clone());
});
// suffix
if suffix.is_empty() == false {
AttributesIter::from_interval(&self.delta, suffix).for_each(|(length, attributes)| {
log::debug!("suffix attribute: {:?}, len: {}", attributes, length);
new_delta.retain(length, attributes);
});
}
self.delta = self.record_change(&new_delta)?;
Ok(())
}
pub fn update_with_attribute(
&mut self,
attribute: Attribute,
interval: Interval,
) -> Result<(), OTError> {
log::debug!("Update document with attribute: {}", attribute);
let mut attributes = AttrsBuilder::new().add(attribute).build();
let old_attributes = AttributesIter::from_interval(&self.delta, interval).next_or_empty();
log::debug!("combine with old: {:?}", old_attributes);
attributes.merge(Some(old_attributes));
let new_attributes = attributes;
log::debug!("combine result: {:?}", new_attributes);
let retain = OpBuilder::retain(interval.size())
.attributes(new_attributes)
.build();
let mut delta = Delta::new();
delta.add(retain);
self.update_with_op(&delta, interval)
}
#[allow(dead_code)]
fn next_rev_id(&self) -> RevId { RevId(self.rev_id_counter) }
fn record_change(&mut self, delta: &Delta) -> Result<Delta, OTError> {
fn add_delta(&mut self, delta: &Delta) -> Result<(), OTError> {
log::debug!("👉invert change {}", delta);
let composed_delta = self.delta.compose(delta)?;
let mut undo_delta = delta.invert(&self.delta);
self.rev_id_counter += 1;
let now = chrono::Utc::now().timestamp_millis() as usize;
@ -188,13 +146,14 @@ impl Document {
self.last_edit_time = now;
}
log::debug!("compose previous result: {}", undo_delta);
if !undo_delta.is_empty() {
log::debug!("record change: {}", undo_delta);
self.history.record(undo_delta);
}
log::debug!("document delta: {}", &composed_delta);
Ok(composed_delta)
self.delta = composed_delta;
Ok(())
}
fn invert_change(&self, change: &Delta) -> Result<(Delta, Delta), OTError> {
@ -209,10 +168,3 @@ impl Document {
Ok((new_delta, inverted_delta))
}
}
fn split_length_with_interval(length: usize, interval: Interval) -> (Interval, Interval, Interval) {
let original_interval = Interval::new(0, length);
let prefix = original_interval.prefix(interval);
let suffix = original_interval.suffix(interval);
(prefix, interval, suffix)
}

View File

@ -46,7 +46,7 @@ impl History {
cur_undo: 1,
undos: Vec::new(),
redoes: Vec::new(),
capacity: 20,
capacity: MAX_UNDOS,
}
}

View File

@ -0,0 +1,16 @@
use crate::{
client::view::DeleteExt,
core::{Attributes, Delta, DeltaBuilder, Interval},
};
pub struct DefaultDeleteExt {}
impl DeleteExt for DefaultDeleteExt {
fn apply(&self, _delta: &Delta, interval: Interval) -> Option<Delta> {
Some(
DeltaBuilder::new()
.retain(interval.start, Attributes::empty())
.delete(interval.size())
.build(),
)
}
}

View File

@ -1,7 +1,4 @@
use crate::{
client::Document,
core::{Attribute, Delta, Interval},
};
use crate::core::{Attribute, Delta, Interval};
pub trait InsertExt {
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta>;

View File

@ -2,6 +2,7 @@ use crate::{
client::view::{FormatExt, NEW_LINE},
core::{
Attribute,
AttributeKey,
AttributeScope,
Attributes,
CharMetric,
@ -11,16 +12,22 @@ use crate::{
Interval,
Operation,
},
errors::OTError,
};
pub struct FormatLinkAtCaretPositionExt {}
impl FormatExt for FormatLinkAtCaretPositionExt {
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
if attribute.key != AttributeKey::Link || interval.size() != 0 {
return None;
}
let mut iter = DeltaIter::new(delta);
iter.seek::<CharMetric>(interval.start);
let (before, after) = (iter.next(), iter.next());
let mut start = interval.start;
let (before, after) = (iter.next_op_with_len(interval.size()), iter.next());
let mut start = interval.end;
let mut retain = 0;
if let Some(before) = before {
@ -97,7 +104,7 @@ impl FormatExt for ResolveInlineFormatExt {
None => {
new_delta.retain(op.length(), attribute.clone().into());
},
Some(mut line_break) => {
Some(line_break) => {
let mut pos = 0;
let mut some_line_break = Some(line_break);
while some_line_break.is_some() {

View File

@ -1,29 +1,32 @@
use crate::{
client::view::InsertExt,
core::{
attributes_with_length,
AttributeKey,
Attributes,
CharMetric,
Delta,
DeltaBuilder,
DeltaIter,
Operation,
},
core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Operation},
};
pub const NEW_LINE: &'static str = "\n";
pub struct PreserveBlockStyleOnInsertExt {}
impl InsertExt for PreserveBlockStyleOnInsertExt {
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
fn apply(
&self,
_delta: &Delta,
_replace_len: usize,
_text: &str,
_index: usize,
) -> Option<Delta> {
None
}
}
pub struct PreserveLineStyleOnSplitExt {}
impl InsertExt for PreserveLineStyleOnSplitExt {
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
fn apply(
&self,
_delta: &Delta,
_replace_len: usize,
_text: &str,
_index: usize,
) -> Option<Delta> {
None
}
}
@ -31,43 +34,90 @@ impl InsertExt for PreserveLineStyleOnSplitExt {
pub struct AutoExitBlockExt {}
impl InsertExt for AutoExitBlockExt {
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
fn apply(
&self,
_delta: &Delta,
_replace_len: usize,
_text: &str,
_index: usize,
) -> Option<Delta> {
None
}
}
pub struct InsertEmbedsExt {}
impl InsertExt for InsertEmbedsExt {
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
fn apply(
&self,
_delta: &Delta,
_replace_len: usize,
_text: &str,
_index: usize,
) -> Option<Delta> {
None
}
}
pub struct ForceNewlineForInsertsAroundEmbedExt {}
impl InsertExt for ForceNewlineForInsertsAroundEmbedExt {
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
fn apply(
&self,
_delta: &Delta,
_replace_len: usize,
_text: &str,
_index: usize,
) -> Option<Delta> {
None
}
}
pub struct AutoFormatLinksExt {}
impl InsertExt for AutoFormatLinksExt {
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
fn apply(
&self,
_delta: &Delta,
_replace_len: usize,
_text: &str,
_index: usize,
) -> Option<Delta> {
None
}
}
pub struct PreserveInlineStylesExt {}
impl InsertExt for PreserveInlineStylesExt {
fn apply(&self, delta: &Delta, _replace_len: usize, text: &str, index: usize) -> Option<Delta> {
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
if text.ends_with(NEW_LINE) {
return None;
}
let probe_index = if index > 1 { index - 1 } else { index };
let attributes = attributes_with_length(delta, probe_index);
let delta = DeltaBuilder::new().insert(text, attributes).build();
Some(delta)
let mut iter = DeltaIter::new(delta);
let prev = iter.next_op_with_len(index);
if prev.is_none() {
return None;
}
let prev = prev.unwrap();
if prev.get_data().contains(NEW_LINE) {
return None;
}
let mut attributes = prev.get_attributes();
if attributes.is_empty() || !attributes.contains_key(&AttributeKey::Link) {
return Some(
DeltaBuilder::new()
.retain(index + replace_len, Attributes::empty())
.insert(text, attributes)
.build(),
);
}
attributes.remove(&AttributeKey::Link);
let new_delta = DeltaBuilder::new()
.retain(index + replace_len, Attributes::empty())
.insert(text, attributes)
.build();
return Some(new_delta);
}
}
@ -109,7 +159,7 @@ impl InsertExt for ResetLineFormatOnNewLineExt {
pub struct DefaultInsertExt {}
impl InsertExt for DefaultInsertExt {
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
fn apply(&self, _delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
Some(
DeltaBuilder::new()
.retain(index + replace_len, Attributes::default())

View File

@ -1,8 +1,10 @@
mod delete_ext;
mod extension;
mod format_ext;
mod insert_ext;
mod view;
pub use delete_ext::*;
pub use extension::*;
pub use format_ext::*;
pub use insert_ext::*;

View File

@ -26,10 +26,16 @@ impl View {
}
}
pub(crate) fn insert(&self, delta: &Delta, text: &str, index: usize) -> Result<Delta, OTError> {
pub(crate) fn insert(
&self,
delta: &Delta,
text: &str,
index: usize,
replace_len: 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) {
if let Some(delta) = ext.apply(delta, replace_len, text, index) {
new_delta = Some(delta);
break;
}
@ -41,13 +47,19 @@ impl View {
}
}
pub(crate) fn replace(
&self,
delta: &Delta,
text: &str,
interval: Interval,
) -> Result<Delta, OTError> {
unimplemented!()
pub(crate) fn delete(&self, delta: &Delta, interval: Interval) -> Result<Delta, OTError> {
let mut new_delta = None;
for ext in &self.delete_exts {
if let Some(delta) = ext.apply(delta, interval) {
new_delta = Some(delta);
break;
}
}
match new_delta {
None => Err(ErrorBuilder::new(OTErrorCode::ApplyInsertFail).build()),
Some(new_delta) => Ok(new_delta),
}
}
pub(crate) fn format(
@ -96,6 +108,6 @@ fn construct_format_exts() -> Vec<FormatExtension> {
fn construct_delete_exts() -> Vec<DeleteExtension> {
vec![
//
Box::new(DefaultDeleteExt {}),
]
}

View File

@ -24,6 +24,8 @@ impl Attributes {
}
}
pub fn empty() -> Self { Self::default() }
pub fn is_empty(&self) -> bool { self.inner.is_empty() }
pub fn add(&mut self, attribute: Attribute) {
@ -99,6 +101,13 @@ pub fn compose_operation(left: &Option<Operation>, right: &Option<Operation>) ->
attr
}
pub fn compose_attributes(left: Attributes, right: Attributes) -> Attributes {
log::trace!("compose attributes: a: {:?}, b: {:?}", left, right);
let attr = merge_attributes(left, right);
log::trace!("compose attributes result: {:?}", attr);
attr
}
pub fn transform_operation(left: &Option<Operation>, right: &Option<Operation>) -> Attributes {
let attr_l = attributes_from(left);
let attr_r = attributes_from(right);

View File

@ -16,6 +16,11 @@ impl DeltaBuilder {
self
}
pub fn delete(mut self, n: usize) -> Self {
self.delta.delete(n);
self
}
pub fn insert(mut self, s: &str, attrs: Attributes) -> Self {
self.delta.insert(s, attrs);
self

View File

@ -31,7 +31,7 @@ impl<'a> Cursor<'a> {
cursor
}
pub fn next_interval(&self) -> Interval { self.next_op_interval_with_len(None) }
pub fn next_interval(&self) -> Interval { self.next_op_interval_with_constraint(None) }
pub fn next_op_with_len(&mut self, force_len: Option<usize>) -> Option<Operation> {
let mut find_op = None;
@ -44,7 +44,7 @@ impl<'a> Cursor<'a> {
while find_op.is_none() && next_op.is_some() {
let op = next_op.take().unwrap();
let interval = self.next_op_interval_with_len(force_len);
let interval = self.next_op_interval_with_constraint(force_len);
find_op = op.shrink(interval);
let suffix = Interval::new(0, op.length()).suffix(interval);
@ -65,7 +65,7 @@ impl<'a> Cursor<'a> {
pub fn next_op(&mut self) -> Option<Operation> { self.next_op_with_len(None) }
pub fn has_next(&self) -> bool { self.c_index < self.next_iv.end }
pub fn has_next(&self) -> bool { self.next_iter_op().is_some() }
fn descend(&mut self, index: usize) {
self.next_iv.start += index;
@ -87,16 +87,28 @@ impl<'a> Cursor<'a> {
}
}
pub fn current_op(&self) -> &Operation {
let op = self
.next_op
.as_ref()
.unwrap_or(&self.delta.ops[self.o_index]);
op
pub fn next_iter_op(&self) -> Option<&Operation> {
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.length();
if offset > self.c_index {
next_op = Some(op);
break;
}
}
}
next_op
}
fn next_op_interval_with_len(&self, force_len: Option<usize>) -> Interval {
let op = self.current_op();
fn next_op_interval_with_constraint(&self, force_len: Option<usize>) -> Interval {
let next_op = self.next_iter_op();
if next_op.is_none() {
return Interval::new(0, 0);
}
let op = next_op.unwrap();
let start = self.c_index;
let end = match force_len {
None => self.c_index + op.length(),

View File

@ -1,5 +1,5 @@
use crate::{
core::{attributes::*, operation::*, DeltaIter, Interval},
core::{attributes::*, operation::*, DeltaIter, Interval, MAX_IV_LEN},
errors::{ErrorBuilder, OTError, OTErrorCode},
};
use bytecount::num_chars;
@ -141,47 +141,84 @@ impl Delta {
}
}
pub fn compose2(&self, other: &Self) -> Result<Self, OTError> {
let mut new_delta = Delta::default();
let mut iter_1 = DeltaIter::new(self);
let mut iter_2 = DeltaIter::new(other);
while iter_1.has_next() || iter_2.has_next() {
if iter_2.is_next_insert() {
new_delta.add(iter_2.next_op().unwrap());
continue;
}
if iter_1.is_next_Delete() {
new_delta.add(iter_1.next_op().unwrap());
continue;
}
let length = min(iter_1.next_op_len(), iter_2.next_op_len());
let op1 = iter_1
.next_op_with_len(length)
.unwrap_or(OpBuilder::retain(length).build());
let op2 = iter_2
.next_op_with_len(length)
.unwrap_or(OpBuilder::retain(length).build());
debug_assert!(op1.length())
}
Ok(new_delta)
}
/// Merges the operation with `other` into one operation while preserving
/// the changes of both. Or, in other words, for each input string S and a
/// pair of consecutive operations A and B.
/// `apply(apply(S, A), B) = apply(S, compose(A, B))`
/// must hold.
///
/// # Error
///
/// Returns an `OTError` if the operations are not composable due to length
/// conflicts.
pub fn compose(&self, other: &Self) -> Result<Self, OTError> {
let mut new_delta = Delta::default();
let mut iter = DeltaIter::new(self);
let mut other_iter = DeltaIter::new(other);
while iter.has_next() || other_iter.has_next() {
if other_iter.is_next_insert() {
new_delta.add(other_iter.next_op().unwrap());
continue;
}
if iter.is_next_delete() {
new_delta.add(iter.next_op().unwrap());
continue;
}
let length = min(
iter.next_op_len().unwrap_or(MAX_IV_LEN),
other_iter.next_op_len().unwrap_or(MAX_IV_LEN),
);
let op = iter
.next_op_with_len(length)
.unwrap_or(OpBuilder::retain(length).build());
let other_op = other_iter
.next_op_with_len(length)
.unwrap_or(OpBuilder::retain(length).build());
debug_assert_eq!(op.length(), other_op.length());
match (&op, &other_op) {
(Operation::Retain(retain), Operation::Retain(other_retain)) => {
let composed_attrs = compose_attributes(
retain.attributes.clone(),
other_retain.attributes.clone(),
);
new_delta.add(
OpBuilder::retain(retain.n)
.attributes(composed_attrs)
.build(),
)
},
(Operation::Insert(insert), Operation::Retain(other_retain)) => {
let mut composed_attrs = compose_attributes(
insert.attributes.clone(),
other_retain.attributes.clone(),
);
composed_attrs = composed_attrs.remove_empty();
new_delta.add(
OpBuilder::insert(op.get_data())
.attributes(composed_attrs)
.build(),
)
},
(Operation::Retain(_), Operation::Delete(_)) => {
new_delta.add(other_op);
},
(a, b) => {
debug_assert_eq!(a.is_insert(), true);
debug_assert_eq!(b.is_delete(), true);
continue;
},
}
}
Ok(new_delta)
}
#[deprecated(
note = "The same as compose except it requires the target_len of self must equal to other's base_len"
)]
pub fn compose2(&self, other: &Self) -> Result<Self, OTError> {
if self.target_len != other.base_len {
return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength).build());
}

View File

@ -5,6 +5,8 @@ use crate::{
};
use std::ops::{Deref, DerefMut};
pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize;
pub struct DeltaIter<'a> {
cursor: Cursor<'a>,
interval: Interval,
@ -12,7 +14,7 @@ pub struct DeltaIter<'a> {
impl<'a> DeltaIter<'a> {
pub fn new(delta: &'a Delta) -> Self {
let interval = Interval::new(0, i32::MAX as usize);
let interval = Interval::new(0, MAX_IV_LEN);
Self::from_interval(delta, interval)
}
@ -25,24 +27,48 @@ impl<'a> DeltaIter<'a> {
pub fn next_op(&mut self) -> Option<Operation> { self.cursor.next_op() }
pub fn next_op_len(&self) -> usize { self.cursor.next_interval().size() }
pub fn next_op_len(&self) -> Option<usize> {
let interval = self.cursor.next_interval();
if interval.is_empty() {
None
} else {
Some(interval.size())
}
}
pub fn next_op_with_len(&mut self, length: usize) -> Option<Operation> {
self.cursor.next_op_with_len(Some(length))
}
pub fn seek<M: Metric>(&mut self, index: usize) -> Result<(), OTError> {
let _ = M::seek(&mut self.cursor, index)?;
Ok(())
pub fn seek<M: Metric>(&mut self, index: usize) {
match M::seek(&mut self.cursor, index) {
Ok(_) => {},
Err(e) => log::error!("Seek fail: {:?}", e),
}
}
pub fn has_next(&self) -> bool { self.cursor.has_next() }
pub fn is_next_insert(&self) -> bool { self.cursor.current_op().is_insert() }
pub fn is_next_insert(&self) -> bool {
match self.cursor.next_iter_op() {
None => false,
Some(op) => op.is_insert(),
}
}
pub fn is_next_retain(&self) -> bool { self.cursor.current_op().is_retain() }
pub fn is_next_retain(&self) -> bool {
match self.cursor.next_iter_op() {
None => false,
Some(op) => op.is_retain(),
}
}
pub fn is_next_delete(&self) -> bool { self.cursor.current_op().is_delete() }
pub fn is_next_delete(&self) -> bool {
match self.cursor.next_iter_op() {
None => false,
Some(op) => op.is_delete(),
}
}
}
impl<'a> Iterator for DeltaIter<'a> {
@ -52,7 +78,6 @@ impl<'a> Iterator for DeltaIter<'a> {
pub struct AttributesIter<'a> {
delta_iter: DeltaIter<'a>,
interval: Interval,
}
impl<'a> AttributesIter<'a> {
@ -63,10 +88,7 @@ impl<'a> AttributesIter<'a> {
pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self {
let delta_iter = DeltaIter::from_interval(delta, interval);
Self {
delta_iter,
interval,
}
Self { delta_iter }
}
pub fn next_or_empty(&mut self) -> Attributes {
@ -115,12 +137,3 @@ impl<'a> Iterator for AttributesIter<'a> {
Some((length, attributes))
}
}
pub(crate) fn attributes_with_length(delta: &Delta, index: usize) -> Attributes {
let mut iter = AttributesIter::new(delta);
iter.seek::<CharMetric>(index);
match iter.next() {
None => Attributes::new(),
Some((_, attributes)) => attributes,
}
}

View File

@ -62,6 +62,7 @@ impl Operation {
pub fn is_empty(&self) -> bool { self.length() == 0 }
#[allow(dead_code)]
pub fn split(&self, index: usize) -> (Option<Operation>, Option<Operation>) {
debug_assert!(index < self.length());
let mut left = None;
@ -100,10 +101,14 @@ impl Operation {
.attributes(retain.attributes.clone())
.build(),
Operation::Insert(insert) => {
if interval.start > insert.s.len() {
if interval.start > insert.num_chars() {
OpBuilder::insert("").build()
} else {
let s = &insert.s[interval.start..min(interval.end, insert.s.len())];
let chars = insert.chars().skip(interval.start);
let s = &chars
.take(min(interval.size(), insert.num_chars()))
.collect::<String>();
OpBuilder::insert(s)
.attributes(insert.attributes.clone())
.build()

View File

@ -28,6 +28,7 @@ pub enum OTErrorCode {
IncompatibleLength,
ApplyInsertFail,
ApplyFormatFail,
ComposeOperationFail,
UndoFail,
RedoFail,
}

View File

@ -43,11 +43,11 @@ fn delta_insert_text_with_attr() {
0,
r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"345"}]"#,
),
Insert(0, "abc", 1),
AssertOpsJson(
0,
r#"[{"insert":"1abc2","attributes":{"bold":"true"}},{"insert":"345"}]"#,
),
/* Insert(0, "abc", 1),
* AssertOpsJson(
* 0,
* r#"[{"insert":"1abc2","attributes":{"bold":"true"}},{"insert":"345"}]"#,
* ), */
];
OpTester::new().run_script(ops);
}
@ -147,7 +147,6 @@ fn delta_add_bold_consecutive() {
}
#[test]
#[should_panic]
fn delta_add_bold_empty_str() {
let ops = vec![Bold(0, Interval::new(0, 4), true)];
OpTester::new().run_script(ops);
@ -171,11 +170,7 @@ fn delta_add_bold_italic() {
Italic(0, Interval::new(4, 6), false),
AssertOpsJson(
0,
r#"[
{"insert":"1234","attributes":{"bold":"true","italic":"true"}},
{"insert":"56","attributes":{"bold":"true"}},
{"insert":"78","attributes":{"bold":"true","italic":"true"}}]
"#,
r#"[{"insert":"1234","attributes":{"bold":"true","italic":"true"}},{"insert":"56","attributes":{"bold":"true"}},{"insert":"78","attributes":{"bold":"true","italic":"true"}}]"#,
),
];
OpTester::new().run_script(ops);
@ -426,10 +421,7 @@ fn delta_replace_with_text() {
Replace(0, Interval::new(0, 3), "ab"),
AssertOpsJson(
0,
r#"[
{"insert":"ab"},
{"insert":"456","attributes":{"bold":"true"}}]
"#,
r#"[{"insert":"ab"},{"insert":"456","attributes":{"bold":"true"}}]"#,
),
];

View File

@ -72,7 +72,7 @@ impl OpTester {
match op {
TestOp::Insert(delta_i, s, index) => {
let document = &mut self.documents[*delta_i];
document.insert(*index, s).unwrap();
document.insert(*index, s, 0).unwrap();
},
TestOp::Delete(delta_i, interval) => {
let document = &mut self.documents[*delta_i];
@ -84,7 +84,7 @@ impl OpTester {
},
TestOp::InsertBold(delta_i, s, interval) => {
let document = &mut self.documents[*delta_i];
document.insert(interval.start, s).unwrap();
document.insert(interval.start, s, 0).unwrap();
document
.format(*interval, AttributeKey::Bold.with_value("true".to_owned()))
.unwrap();

View File

@ -148,7 +148,7 @@ fn delta_get_ops_in_interval_7() {
delta.add(retain_a.clone());
let mut iter_1 = DeltaIter::new(&delta);
iter_1.seek::<CharMetric>(2).unwrap();
iter_1.seek::<CharMetric>(2);
assert_eq!(iter_1.next_op().unwrap(), OpBuilder::insert("345").build());
assert_eq!(iter_1.next_op().unwrap(), OpBuilder::retain(3).build());
@ -170,7 +170,7 @@ fn delta_seek_1() {
delta.add(insert_a.clone());
delta.add(retain_a.clone());
let mut iter = DeltaIter::new(&delta);
iter.seek::<OpMetric>(1).unwrap();
iter.seek::<OpMetric>(1);
assert_eq!(iter.next_op().unwrap(), OpBuilder::retain(3).build());
}
@ -230,15 +230,29 @@ fn delta_next_op_len_test() {
let mut iter = DeltaIter::new(&delta);
iter.seek::<CharMetric>(3);
assert_eq!(iter.next_op_len(), 2);
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_len(), 1);
assert_eq!(iter.next_op_len().unwrap(), 1);
assert_eq!(iter.next_op().unwrap(), OpBuilder::insert("5").build());
}
#[test]
fn delta_next_op_len_test2() {
let mut delta = Delta::default();
delta.add(OpBuilder::insert("12345").build());
let mut iter = DeltaIter::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_len(), None);
}
#[test]
fn lengths() {
let mut delta = Delta::default();

View File

@ -178,6 +178,7 @@ fn delta_undo_delete2_with_lagging() {
fn delta_redo_delete() {
let ops = vec![
Insert(0, "123", 0),
Wait(RECORD_THRESHOLD),
Delete(0, Interval::new(0, 3)),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
Undo(0),