diff --git a/app_flowy/packages/flowy_editor/lib/src/model/heuristic/rule.dart b/app_flowy/packages/flowy_editor/lib/src/model/heuristic/rule.dart index bcc123cb3c..8eb52e6441 100644 --- a/app_flowy/packages/flowy_editor/lib/src/model/heuristic/rule.dart +++ b/app_flowy/packages/flowy_editor/lib/src/model/heuristic/rule.dart @@ -53,7 +53,7 @@ class Rules { const PreserveInlineStylesRule(), const CatchAllInsertRule(), // const EnsureEmbedLineRule(), - // const PreserveLineStyleOnMergeRule(), + const PreserveLineStyleOnMergeRule(), const CatchAllDeleteRule(), ]); diff --git a/rust-lib/flowy-ot/src/client/extensions/delete/default_delete.rs b/rust-lib/flowy-ot/src/client/extensions/delete/default_delete.rs index 2ddb980efb..fcfa8fd222 100644 --- a/rust-lib/flowy-ot/src/client/extensions/delete/default_delete.rs +++ b/rust-lib/flowy-ot/src/client/extensions/delete/default_delete.rs @@ -3,9 +3,9 @@ use crate::{ core::{Delta, DeltaBuilder, Interval}, }; -pub struct DefaultDeleteExt {} -impl DeleteExt for DefaultDeleteExt { - fn ext_name(&self) -> &str { "DeleteExt" } +pub struct DefaultDelete {} +impl DeleteExt for DefaultDelete { + fn ext_name(&self) -> &str { "DefaultDelete" } fn apply(&self, _delta: &Delta, interval: Interval) -> Option { Some( diff --git a/rust-lib/flowy-ot/src/client/extensions/delete/mod.rs b/rust-lib/flowy-ot/src/client/extensions/delete/mod.rs index b3cf97ef95..061369bb13 100644 --- a/rust-lib/flowy-ot/src/client/extensions/delete/mod.rs +++ b/rust-lib/flowy-ot/src/client/extensions/delete/mod.rs @@ -1,3 +1,5 @@ mod default_delete; +mod preserve_line_format_merge; pub use default_delete::*; +pub use preserve_line_format_merge::*; diff --git a/rust-lib/flowy-ot/src/client/extensions/delete/preserve_line_format_merge.rs b/rust-lib/flowy-ot/src/client/extensions/delete/preserve_line_format_merge.rs new file mode 100644 index 0000000000..74c39066a9 --- /dev/null +++ b/rust-lib/flowy-ot/src/client/extensions/delete/preserve_line_format_merge.rs @@ -0,0 +1,59 @@ +use crate::{ + client::{extensions::DeleteExt, util::is_newline}, + core::{Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, Operation, NEW_LINE}, +}; + +pub struct PreserveLineFormatOnMerge {} +impl DeleteExt for PreserveLineFormatOnMerge { + fn ext_name(&self) -> &str { "PreserveLineFormatOnMerge" } + + fn apply(&self, delta: &Delta, interval: Interval) -> Option { + if interval.is_empty() { + return None; + } + + // seek to the interval start pos. e.g. You backspace enter pos + let mut iter = DeltaIter::from_offset(delta, interval.start); + + // op will be the "\n" + let newline_op = iter.next_op_with_len(1)?; + if !is_newline(newline_op.get_data()) { + return None; + } + + iter.seek::(interval.size() - 1); + let mut new_delta = DeltaBuilder::new() + .retain(interval.start) + .delete(interval.size()) + .build(); + + while iter.has_next() { + match iter.next() { + None => log::error!("op must be not None when has_next() return true"), + Some(op) => { + // + match op.get_data().find(NEW_LINE) { + None => { + new_delta.retain(op.len(), Attributes::empty()); + continue; + }, + Some(line_break) => { + let mut attributes = op.get_attributes(); + attributes.mark_all_as_removed_except(None); + + if newline_op.has_attribute() { + attributes.extend(newline_op.get_attributes()); + } + + new_delta.retain(line_break, Attributes::empty()); + new_delta.retain(1, attributes); + break; + }, + } + }, + } + } + + Some(new_delta) + } +} diff --git a/rust-lib/flowy-ot/src/client/extensions/format/format_at_position.rs b/rust-lib/flowy-ot/src/client/extensions/format/format_at_position.rs index df057bd6a8..5e0886070f 100644 --- a/rust-lib/flowy-ot/src/client/extensions/format/format_at_position.rs +++ b/rust-lib/flowy-ot/src/client/extensions/format/format_at_position.rs @@ -1,6 +1,6 @@ use crate::{ client::extensions::FormatExt, - core::{Attribute, AttributeKey, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval}, + core::{Attribute, AttributeKey, Delta, DeltaBuilder, DeltaIter, Interval}, }; pub struct FormatLinkAtCaretPositionExt {} @@ -14,7 +14,7 @@ impl FormatExt for FormatLinkAtCaretPositionExt { } let mut iter = DeltaIter::from_offset(delta, interval.start); - let (before, after) = (iter.last_op_before_index(interval.size()), iter.next_op()); + let (before, after) = (iter.next_op_with_len(interval.size()), iter.next_op()); let mut start = interval.end; let mut retain = 0; diff --git a/rust-lib/flowy-ot/src/client/extensions/format/resolve_block_format.rs b/rust-lib/flowy-ot/src/client/extensions/format/resolve_block_format.rs index ee8751bdba..84f023f71f 100644 --- a/rust-lib/flowy-ot/src/client/extensions/format/resolve_block_format.rs +++ b/rust-lib/flowy-ot/src/client/extensions/format/resolve_block_format.rs @@ -3,16 +3,7 @@ use crate::{ extensions::{format::helper::line_break, FormatExt}, util::find_newline, }, - core::{ - Attribute, - AttributeScope, - Attributes, - CharMetric, - Delta, - DeltaBuilder, - DeltaIter, - Interval, - }, + core::{Attribute, AttributeScope, Attributes, Delta, DeltaBuilder, DeltaIter, Interval}, }; pub struct ResolveBlockFormat {} @@ -29,7 +20,7 @@ impl FormatExt for ResolveBlockFormat { let mut start = 0; let end = interval.size(); while start < end && iter.has_next() { - let next_op = iter.last_op_before_index(end - start).unwrap(); + let next_op = iter.next_op_with_len(end - start).unwrap(); match find_newline(next_op.get_data()) { None => new_delta.retain(next_op.len(), Attributes::empty()), Some(_) => { diff --git a/rust-lib/flowy-ot/src/client/extensions/format/resolve_inline_format.rs b/rust-lib/flowy-ot/src/client/extensions/format/resolve_inline_format.rs index 208a1f372f..046e7d116e 100644 --- a/rust-lib/flowy-ot/src/client/extensions/format/resolve_inline_format.rs +++ b/rust-lib/flowy-ot/src/client/extensions/format/resolve_inline_format.rs @@ -3,7 +3,7 @@ use crate::{ extensions::{format::helper::line_break, FormatExt}, util::find_newline, }, - core::{Attribute, AttributeScope, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval}, + core::{Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval}, }; pub struct ResolveInlineFormat {} @@ -20,7 +20,7 @@ impl FormatExt for ResolveInlineFormat { let end = interval.size(); while start < end && iter.has_next() { - let next_op = iter.last_op_before_index(end - start).unwrap(); + let next_op = iter.next_op_with_len(end - start).unwrap(); match find_newline(next_op.get_data()) { None => new_delta.retain(next_op.len(), attribute.clone().into()), Some(_) => { diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/auto_exit_block.rs b/rust-lib/flowy-ot/src/client/extensions/insert/auto_exit_block.rs index cd60e301b9..80e5e1b2b3 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/auto_exit_block.rs +++ b/rust-lib/flowy-ot/src/client/extensions/insert/auto_exit_block.rs @@ -1,15 +1,6 @@ use crate::{ client::{extensions::InsertExt, util::is_newline}, - core::{ - AttributeKey, - AttributeValue, - Attributes, - CharMetric, - Delta, - DeltaBuilder, - DeltaIter, - Operation, - }, + core::{AttributeKey, Delta, DeltaBuilder, DeltaIter, Operation}, }; use crate::core::{attributes_except_header, is_empty_line_at_index}; @@ -42,7 +33,7 @@ impl InsertExt for AutoExitBlock { return None; } - match iter.first_newline_op() { + match iter.next_op_with_newline() { None => {}, Some((newline_op, _)) => { let newline_attributes = attributes_except_header(&newline_op); @@ -52,7 +43,7 @@ impl InsertExt for AutoExitBlock { }, } - attributes.mark_as_removed_except(&AttributeKey::Header); + attributes.mark_all_as_removed_except(Some(AttributeKey::Header)); Some( DeltaBuilder::new() diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/auto_format.rs b/rust-lib/flowy-ot/src/client/extensions/insert/auto_format.rs index 92762a4390..994bf27dad 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/auto_format.rs +++ b/rust-lib/flowy-ot/src/client/extensions/insert/auto_format.rs @@ -13,7 +13,7 @@ impl InsertExt for AutoFormatExt { return None; } let mut iter = DeltaIter::new(delta); - if let Some(prev) = iter.last_op_before_index(index) { + if let Some(prev) = iter.next_op_with_len(index) { match AutoFormat::parse(prev.get_data()) { None => {}, Some(formatter) => { @@ -50,7 +50,7 @@ impl InsertExt for AutoFormatExt { } } -use crate::core::{Attribute, AttributeBuilder, Attributes, DeltaBuilder, Operation}; +use crate::core::{AttributeBuilder, Attributes, DeltaBuilder, Operation}; use bytecount::num_chars; use std::cmp::min; use url::Url; diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_format.rs b/rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_format.rs index 1312f3c27d..9ca070a9bf 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_format.rs +++ b/rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_format.rs @@ -2,7 +2,6 @@ use crate::{ client::{extensions::InsertExt, util::is_newline}, core::{ attributes_except_header, - AttributeBuilder, AttributeKey, Attributes, Delta, @@ -23,7 +22,7 @@ impl InsertExt for PreserveBlockFormatOnInsert { } let mut iter = DeltaIter::from_offset(delta, index); - match iter.first_newline_op() { + match iter.next_op_with_newline() { None => {}, Some((newline_op, offset)) => { let newline_attributes = newline_op.get_attributes(); diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_format.rs b/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_format.rs index a9a777bd6f..3166a510bd 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_format.rs +++ b/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_format.rs @@ -3,16 +3,7 @@ use crate::{ extensions::InsertExt, util::{contain_newline, is_newline}, }, - core::{ - AttributeKey, - Attributes, - Delta, - DeltaBuilder, - DeltaIter, - OpNewline, - Operation, - NEW_LINE, - }, + core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, OpNewline, NEW_LINE}, }; pub struct PreserveInlineFormat {} @@ -25,7 +16,7 @@ impl InsertExt for PreserveInlineFormat { } let mut iter = DeltaIter::new(delta); - let prev = iter.last_op_before_index(index)?; + let prev = iter.next_op_with_len(index)?; if OpNewline::parse(&prev).is_contain() { return None; } @@ -69,7 +60,7 @@ impl InsertExt for PreserveLineFormatOnSplit { } let mut iter = DeltaIter::new(delta); - let prev = iter.last_op_before_index(index)?; + let prev = iter.next_op_with_len(index)?; if OpNewline::parse(&prev).is_end() { return None; } @@ -89,7 +80,7 @@ impl InsertExt for PreserveLineFormatOnSplit { return Some(new_delta); } - match iter.first_newline_op() { + match iter.next_op_with_newline() { None => {}, Some((newline_op, _)) => { new_delta.insert(NEW_LINE, newline_op.get_attributes()); diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/reset_format_on_new_line.rs b/rust-lib/flowy-ot/src/client/extensions/insert/reset_format_on_new_line.rs index 79befd8320..369b375ab6 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/reset_format_on_new_line.rs +++ b/rust-lib/flowy-ot/src/client/extensions/insert/reset_format_on_new_line.rs @@ -13,7 +13,8 @@ impl InsertExt for ResetLineFormatOnNewLine { } let mut iter = DeltaIter::new(delta); - let next_op = iter.first_op_after_index(index)?; + iter.seek::(index); + let next_op = iter.next_op()?; if !next_op.get_data().starts_with(NEW_LINE) { return None; } diff --git a/rust-lib/flowy-ot/src/client/util.rs b/rust-lib/flowy-ot/src/client/util.rs index 462d2f0800..3c014b07fe 100644 --- a/rust-lib/flowy-ot/src/client/util.rs +++ b/rust-lib/flowy-ot/src/client/util.rs @@ -1,4 +1,4 @@ -use crate::core::{Operation, NEW_LINE, WHITESPACE}; +use crate::core::{NEW_LINE, WHITESPACE}; #[inline] pub fn find_newline(s: &str) -> Option { @@ -8,9 +8,6 @@ pub fn find_newline(s: &str) -> Option { } } -#[inline] -pub fn is_op_contains_newline(op: &Operation) -> bool { contain_newline(op.get_data()) } - #[inline] pub fn is_newline(s: &str) -> bool { s == NEW_LINE } diff --git a/rust-lib/flowy-ot/src/client/view.rs b/rust-lib/flowy-ot/src/client/view.rs index 6c6df62ca8..48b677276d 100644 --- a/rust-lib/flowy-ot/src/client/view.rs +++ b/rust-lib/flowy-ot/src/client/view.rs @@ -102,7 +102,7 @@ fn construct_format_exts() -> Vec { fn construct_delete_exts() -> Vec { vec![ - // - Box::new(DefaultDeleteExt {}), + Box::new(PreserveLineFormatOnMerge {}), + Box::new(DefaultDelete {}), ] } diff --git a/rust-lib/flowy-ot/src/core/attributes/attributes.rs b/rust-lib/flowy-ot/src/core/attributes/attributes.rs index 08cade6cb3..90a2f86816 100644 --- a/rust-lib/flowy-ot/src/core/attributes/attributes.rs +++ b/rust-lib/flowy-ot/src/core/attributes/attributes.rs @@ -42,13 +42,21 @@ impl Attributes { self.inner.insert(key.clone(), value); } - pub fn mark_as_removed_except(&mut self, attribute: &AttributeKey) { - self.inner.iter_mut().for_each(|(k, v)| { - if k != attribute { - v.0 = REMOVE_FLAG.into(); - } - v.0 = REMOVE_FLAG.into(); - }); + pub fn mark_all_as_removed_except(&mut self, attribute: Option) { + match attribute { + None => { + self.inner + .iter_mut() + .for_each(|(k, v)| v.0 = REMOVE_FLAG.into()); + }, + Some(attribute) => { + self.inner.iter_mut().for_each(|(k, v)| { + if k != &attribute { + v.0 = REMOVE_FLAG.into(); + } + }); + }, + } } pub fn remove(&mut self, key: AttributeKey) { self.inner.retain(|k, _| k != &key); } diff --git a/rust-lib/flowy-ot/src/core/delta/cursor.rs b/rust-lib/flowy-ot/src/core/delta/cursor.rs index 0a70e1e513..d4128c6109 100644 --- a/rust-lib/flowy-ot/src/core/delta/cursor.rs +++ b/rust-lib/flowy-ot/src/core/delta/cursor.rs @@ -5,7 +5,7 @@ use crate::{ use std::{cmp::min, iter::Enumerate, slice::Iter}; #[derive(Debug)] -pub struct Cursor<'a> { +pub struct OpCursor<'a> { pub(crate) delta: &'a Delta, pub(crate) origin_iv: Interval, pub(crate) consume_iv: Interval, @@ -15,8 +15,8 @@ pub struct Cursor<'a> { next_op: Option, } -impl<'a> Cursor<'a> { - pub fn new(delta: &'a Delta, interval: Interval) -> Cursor<'a> { +impl<'a> OpCursor<'a> { + pub fn new(delta: &'a Delta, interval: Interval) -> OpCursor<'a> { // debug_assert!(interval.start <= delta.target_len); let mut cursor = Self { delta, @@ -32,51 +32,56 @@ impl<'a> Cursor<'a> { } // get the next operation interval - pub fn next_iv(&self) -> Interval { self.next_iv_before(None) } + pub fn next_iv(&self) -> Interval { self.next_iv_before(None).unwrap_or(Interval::new(0, 0)) } - pub fn next_op(&mut self) -> Option { self.last_op_before_index(None) } + pub fn next(&mut self) -> Option { self.next_with_len(None) } - // get the last operation before the index - pub fn last_op_before_index(&mut self, index: Option) -> Option { + // get the last operation before the end. + // checkout the delta_next_op_with_len_cross_op_return_last test for more detail + pub fn next_with_len(&mut self, force_end: Option) -> Option { let mut find_op = None; let holder = self.next_op.clone(); let mut next_op = holder.as_ref(); if next_op.is_none() { - next_op = find_next_op(self); + next_op = find_next(self); } - let mut pos = 0; + let mut consume_len = 0; while find_op.is_none() && next_op.is_some() { let op = next_op.take().unwrap(); - let interval = self.next_iv_before(index); + let interval = self + .next_iv_before(force_end) + .unwrap_or(Interval::new(0, 0)); + + // cache the op if the interval is empty. e.g. last_op_before(Some(0)) if interval.is_empty() { self.next_op = Some(op.clone()); break; } - find_op = op.shrink(interval); - self.next_op = None; - let suffix = Interval::new(0, op.len()).suffix(interval); - if !suffix.is_empty() { + if suffix.is_empty() { + self.next_op = None; + } else { self.next_op = op.shrink(suffix); } - pos += interval.end; + consume_len += interval.end; self.consume_count += interval.end; self.consume_iv.start = self.consume_count; + // continue to find the op in next iteration if find_op.is_none() { - next_op = find_next_op(self); + next_op = find_next(self); } } - if find_op.is_some() && index.is_some() { - // try to find the next op before the index if iter_char_count less than index - let end = index.unwrap(); - if end > pos && self.has_next() { - return self.last_op_before_index(Some(end - pos)); + if find_op.is_some() && force_end.is_some() { + // try to find the next op before the index if consume_len less than index + let end = force_end.unwrap(); + if end > consume_len && self.has_next() { + return self.next_with_len(Some(end - consume_len)); } } return find_op; @@ -104,6 +109,19 @@ impl<'a> Cursor<'a> { } } + fn next_iv_before(&self, force_end: Option) -> Option { + let op = self.next_iter_op()?; + let start = self.consume_count; + let end = match force_end { + None => self.consume_count + op.len(), + Some(index) => self.consume_count + min(index, op.len()), + }; + + let intersect = Interval::new(start, end).intersect(self.consume_iv); + let interval = intersect.translate_neg(start); + Some(interval) + } + pub fn next_iter_op(&self) -> Option<&Operation> { let mut next_op = self.next_op.as_ref(); if next_op.is_none() { @@ -118,27 +136,9 @@ impl<'a> Cursor<'a> { } next_op } - - fn next_iv_before(&self, index: Option) -> 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.consume_count; - let end = match index { - None => self.consume_count + op.len(), - Some(index) => self.consume_count + min(index, op.len()), - }; - - let intersect = Interval::new(start, end).intersect(self.consume_iv); - let interval = intersect.translate_neg(start); - interval - } } -fn find_next_op<'a>(cursor: &mut Cursor<'a>) -> Option<&'a Operation> { +fn find_next<'a>(cursor: &mut OpCursor<'a>) -> Option<&'a Operation> { match cursor.iter.next() { None => None, Some((o_index, op)) => { @@ -150,15 +150,15 @@ fn find_next_op<'a>(cursor: &mut Cursor<'a>) -> Option<&'a Operation> { type SeekResult = Result<(), OTError>; pub trait Metric { - fn seek(cursor: &mut Cursor, index: usize) -> SeekResult; + fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult; } pub struct OpMetric {} impl Metric for OpMetric { - fn seek(cursor: &mut Cursor, index: usize) -> SeekResult { + fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult { let _ = check_bound(cursor.op_index, index)?; - let mut seek_cursor = Cursor::new(cursor.delta, cursor.origin_iv); + let mut seek_cursor = OpCursor::new(cursor.delta, cursor.origin_iv); let mut offset = 0; while let Some((_, op)) = seek_cursor.iter.next() { offset += op.len(); @@ -174,9 +174,9 @@ impl Metric for OpMetric { pub struct CharMetric {} impl Metric for CharMetric { - fn seek(cursor: &mut Cursor, index: usize) -> SeekResult { + fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult { let _ = check_bound(cursor.consume_count, index)?; - let _ = cursor.last_op_before_index(Some(index)); + let _ = cursor.next_with_len(Some(index)); Ok(()) } diff --git a/rust-lib/flowy-ot/src/core/delta/delta.rs b/rust-lib/flowy-ot/src/core/delta/delta.rs index 9a2d9ea8e7..20c29616a8 100644 --- a/rust-lib/flowy-ot/src/core/delta/delta.rs +++ b/rust-lib/flowy-ot/src/core/delta/delta.rs @@ -168,11 +168,11 @@ impl Delta { ); let op = iter - .last_op_before_index(length) + .next_op_with_len(length) .unwrap_or(OpBuilder::retain(length).build()); let other_op = other_iter - .last_op_before_index(length) + .next_op_with_len(length) .unwrap_or(OpBuilder::retain(length).build()); debug_assert_eq!(op.len(), other_op.len()); diff --git a/rust-lib/flowy-ot/src/core/delta/iterator.rs b/rust-lib/flowy-ot/src/core/delta/iterator.rs index 823d13f68e..62ac4473b2 100644 --- a/rust-lib/flowy-ot/src/core/delta/iterator.rs +++ b/rust-lib/flowy-ot/src/core/delta/iterator.rs @@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut}; pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize; pub struct DeltaIter<'a> { - cursor: Cursor<'a>, + cursor: OpCursor<'a>, } impl<'a> DeltaIter<'a> { @@ -22,7 +22,7 @@ impl<'a> DeltaIter<'a> { } pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self { - let cursor = Cursor::new(delta, interval); + let cursor = OpCursor::new(delta, interval); Self { cursor } } @@ -37,8 +37,14 @@ impl<'a> DeltaIter<'a> { } } + pub fn next_op(&mut self) -> Option { self.cursor.next() } + + pub fn next_op_with_len(&mut self, len: usize) -> Option { + self.cursor.next_with_len(Some(len)) + } + // find next op contains NEW_LINE - pub fn first_newline_op(&mut self) -> Option<(Operation, usize)> { + pub fn next_op_with_newline(&mut self) -> Option<(Operation, usize)> { let mut offset = 0; while self.has_next() { if let Some(op) = self.next_op() { @@ -52,17 +58,6 @@ impl<'a> DeltaIter<'a> { None } - pub fn next_op(&mut self) -> Option { self.cursor.next_op() } - - pub fn last_op_before_index(&mut self, index: usize) -> Option { - self.cursor.last_op_before_index(Some(index)) - } - - pub fn first_op_after_index(&mut self, index: usize) -> Option { - self.seek::(index); - self.next_op() - } - pub fn seek(&mut self, index: usize) { match M::seek(&mut self.cursor, index) { Ok(_) => {}, @@ -101,7 +96,7 @@ impl<'a> Iterator for DeltaIter<'a> { pub fn is_empty_line_at_index(delta: &Delta, index: usize) -> bool { let mut iter = DeltaIter::new(delta); - let (prev, next) = (iter.last_op_before_index(index), iter.next_op()); + let (prev, next) = (iter.next_op_with_len(index), iter.next_op()); if prev.is_none() { return true; } @@ -151,7 +146,7 @@ impl<'a> DerefMut for AttributesIter<'a> { impl<'a> Iterator for AttributesIter<'a> { type Item = (usize, Attributes); fn next(&mut self) -> Option { - let next_op = self.delta_iter.next(); + let next_op = self.delta_iter.next_op(); if next_op.is_none() { return None; } diff --git a/rust-lib/flowy-ot/tests/attribute_test.rs b/rust-lib/flowy-ot/tests/attribute_test.rs index 32d5002137..aaf1f5a280 100644 --- a/rust-lib/flowy-ot/tests/attribute_test.rs +++ b/rust-lib/flowy-ot/tests/attribute_test.rs @@ -762,3 +762,23 @@ fn attributes_preserve_block_when_insert_newline_inside() { OpTester::new().run_script_with_newline(ops); } + +#[test] +fn attributes_preserve_line_format_on_merge() { + let ops = vec![ + Insert(0, "123456", 0), + Header(0, Interval::new(0, 6), 1, true), + Insert(0, NEW_LINE, 3), + AssertOpsJson( + 0, + r#"[{"insert":"123"},{"insert":"\n","attributes":{"header":"1"}},{"insert":"456"},{"insert":"\n","attributes":{"header":"1"}}]"#, + ), + Delete(0, Interval::new(3, 4)), + AssertOpsJson( + 0, + r#"[{"insert":"123456"},{"insert":"\n","attributes":{"header":"1"}}]"#, + ), + ]; + + OpTester::new().run_script_with_newline(ops); +} diff --git a/rust-lib/flowy-ot/tests/op_test.rs b/rust-lib/flowy-ot/tests/op_test.rs index 0bff6f80eb..1266a54ed5 100644 --- a/rust-lib/flowy-ot/tests/op_test.rs +++ b/rust-lib/flowy-ot/tests/op_test.rs @@ -153,7 +153,7 @@ fn delta_get_ops_in_interval_7() { let mut iter_2 = DeltaIter::new(&delta); assert_eq!( - iter_2.last_op_before_index(2).unwrap(), + iter_2.next_op_with_len(2).unwrap(), OpBuilder::insert("12").build() ); assert_eq!(iter_2.next_op().unwrap(), OpBuilder::insert("345").build()); @@ -180,7 +180,7 @@ fn delta_seek_2() { let mut iter = DeltaIter::new(&delta); assert_eq!( - iter.last_op_before_index(1).unwrap(), + iter.next_op_with_len(1).unwrap(), OpBuilder::insert("1").build() ); } @@ -192,21 +192,21 @@ fn delta_seek_3() { let mut iter = DeltaIter::new(&delta); assert_eq!( - iter.last_op_before_index(2).unwrap(), + iter.next_op_with_len(2).unwrap(), OpBuilder::insert("12").build() ); assert_eq!( - iter.last_op_before_index(2).unwrap(), + iter.next_op_with_len(2).unwrap(), OpBuilder::insert("34").build() ); assert_eq!( - iter.last_op_before_index(2).unwrap(), + iter.next_op_with_len(2).unwrap(), OpBuilder::insert("5").build() ); - assert_eq!(iter.last_op_before_index(1), None); + assert_eq!(iter.next_op_with_len(1), None); } #[test] @@ -217,7 +217,7 @@ fn delta_seek_4() { let mut iter = DeltaIter::new(&delta); iter.seek::(3); assert_eq!( - iter.last_op_before_index(2).unwrap(), + iter.next_op_with_len(2).unwrap(), OpBuilder::insert("45").build() ); } @@ -237,7 +237,7 @@ fn delta_seek_5() { iter.seek::(0); assert_eq!( - iter.last_op_before_index(4).unwrap(), + iter.next_op_with_len(4).unwrap(), OpBuilder::insert("1234").attributes(attributes).build(), ); } @@ -251,7 +251,7 @@ fn delta_next_op_len_test() { iter.seek::(3); assert_eq!(iter.next_op_len().unwrap(), 2); assert_eq!( - iter.last_op_before_index(1).unwrap(), + iter.next_op_with_len(1).unwrap(), OpBuilder::insert("4").build() ); assert_eq!(iter.next_op_len().unwrap(), 1); @@ -266,22 +266,37 @@ fn delta_next_op_len_test2() { assert_eq!(iter.next_op_len().unwrap(), 5); assert_eq!( - iter.last_op_before_index(5).unwrap(), + iter.next_op_with_len(5).unwrap(), OpBuilder::insert("12345").build() ); assert_eq!(iter.next_op_len(), None); } #[test] -fn delta_next_op_len_test3() { +fn delta_next_op_with_len_zero() { let mut delta = Delta::default(); delta.add(OpBuilder::insert("12345").build()); let mut iter = DeltaIter::new(&delta); - - assert_eq!(iter.last_op_before_index(0), None,); + assert_eq!(iter.next_op_with_len(0), None,); assert_eq!(iter.next_op_len().unwrap(), 5); } +#[test] +fn delta_next_op_with_len_cross_op_return_last() { + let mut delta = Delta::default(); + delta.add(OpBuilder::insert("12345").build()); + delta.add(OpBuilder::retain(1).build()); + delta.add(OpBuilder::insert("678").build()); + + let mut iter = DeltaIter::new(&delta); + iter.seek::(4); + assert_eq!(iter.next_op_len().unwrap(), 1); + assert_eq!( + iter.next_op_with_len(2).unwrap(), + OpBuilder::retain(1).build() + ); +} + #[test] fn lengths() { let mut delta = Delta::default();