mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
refactor compose with iterator
This commit is contained in:
parent
b03dcde824
commit
f994155dfe
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ impl History {
|
||||
cur_undo: 1,
|
||||
undos: Vec::new(),
|
||||
redoes: Vec::new(),
|
||||
capacity: 20,
|
||||
capacity: MAX_UNDOS,
|
||||
}
|
||||
}
|
||||
|
||||
|
16
rust-lib/flowy-ot/src/client/view/delete_ext.rs
Normal file
16
rust-lib/flowy-ot/src/client/view/delete_ext.rs
Normal 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(),
|
||||
)
|
||||
}
|
||||
}
|
@ -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>;
|
||||
|
@ -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() {
|
||||
|
@ -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())
|
||||
|
@ -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::*;
|
||||
|
@ -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 {}),
|
||||
]
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -28,6 +28,7 @@ pub enum OTErrorCode {
|
||||
IncompatibleLength,
|
||||
ApplyInsertFail,
|
||||
ApplyFormatFail,
|
||||
ComposeOperationFail,
|
||||
UndoFail,
|
||||
RedoFail,
|
||||
}
|
||||
|
@ -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"}}]"#,
|
||||
),
|
||||
];
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user