diff --git a/app_flowy/packages/flowy_editor/lib/src/model/document/document.dart b/app_flowy/packages/flowy_editor/lib/src/model/document/document.dart index 2619cd5885..35a85f6797 100644 --- a/app_flowy/packages/flowy_editor/lib/src/model/document/document.dart +++ b/app_flowy/packages/flowy_editor/lib/src/model/document/document.dart @@ -73,6 +73,7 @@ class Document { print('insert delta: $delta'); compose(delta, ChangeSource.LOCAL); print('compose insert, current document $_delta'); + print('compose end'); return 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 054b7f360f..4b27c301ed 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 @@ -13,10 +13,8 @@ impl FormatExt for FormatLinkAtCaretPositionExt { return None; } - let mut iter = DeltaIter::new(delta); - iter.seek::(interval.start); - - let (before, after) = (iter.next_op_before(interval.size()), iter.next()); + let mut iter = DeltaIter::from_offset(delta, interval.start); + let (before, after) = (iter.last_op_before_index(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 5d29bbad11..7c8a2724a4 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 @@ -25,12 +25,11 @@ impl FormatExt for ResolveBlockFormatExt { } let mut new_delta = DeltaBuilder::new().retain(interval.start).build(); - let mut iter = DeltaIter::new(delta); - iter.seek::(interval.start); + let mut iter = DeltaIter::from_offset(delta, interval.start); let mut start = 0; let end = interval.size(); while start < end && iter.has_next() { - let next_op = iter.next_op_before(end - start).unwrap(); + let next_op = iter.last_op_before_index(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 8b51dec78b..5edb7decb7 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 @@ -15,14 +15,12 @@ impl FormatExt for ResolveInlineFormatExt { return None; } let mut new_delta = DeltaBuilder::new().retain(interval.start).build(); - let mut iter = DeltaIter::new(delta); - iter.seek::(interval.start); - + let mut iter = DeltaIter::from_offset(delta, interval.start); let mut start = 0; let end = interval.size(); while start < end && iter.has_next() { - let next_op = iter.next_op_before(end - start).unwrap(); + let next_op = iter.last_op_before_index(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 a2cac4f023..b4a3550efd 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,23 +1,70 @@ use crate::{ client::{extensions::InsertExt, util::is_newline}, - core::{Delta, DeltaIter}, + core::{ + AttributeKey, + AttributeValue, + Attributes, + CharMetric, + Delta, + DeltaBuilder, + DeltaIter, + Operation, + }, }; +use crate::core::is_empty_line_at_index; + pub struct AutoExitBlockExt {} impl InsertExt for AutoExitBlockExt { fn ext_name(&self) -> &str { "AutoExitBlockExt" } - fn apply(&self, delta: &Delta, _replace_len: usize, text: &str, index: usize) -> Option { + fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { // Auto exit block will be triggered by enter two new lines if !is_newline(text) { return None; } - let mut iter = DeltaIter::new(delta); - let _prev = iter.next_op_before(index); - let _next = iter.next_op(); + if !is_empty_line_at_index(delta, index) { + return None; + } - None + let mut iter = DeltaIter::from_offset(delta, index); + let next = iter.next_op()?; + let mut attributes = next.get_attributes(); + + let block_attributes = attributes_except_header(&next); + if block_attributes.is_empty() { + return None; + } + + if next.len() > 1 { + return None; + } + + match iter.first_op_contains_newline() { + None => {}, + Some((newline_op, _)) => { + let newline_attributes = attributes_except_header(&newline_op); + if block_attributes == newline_attributes { + return None; + } + }, + } + + attributes.mark_as_removed_except(&AttributeKey::Header); + + Some( + DeltaBuilder::new() + .retain(index + replace_len) + .retain_with_attributes(1, attributes) + .build(), + ) } } + +fn attributes_except_header(op: &Operation) -> Attributes { + let mut attributes = op.get_attributes(); + attributes.remove(AttributeKey::Header); + attributes +} 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 ccc6fd66df..29b848fa32 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.next_op_before(index) { + if let Some(prev) = iter.last_op_before_index(index) { match AutoFormat::parse(prev.get_data()) { None => {}, Some(formatter) => { diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/default_insert.rs b/rust-lib/flowy-ot/src/client/extensions/insert/default_insert.rs index 813c173790..51f9498cfc 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/default_insert.rs +++ b/rust-lib/flowy-ot/src/client/extensions/insert/default_insert.rs @@ -1,6 +1,6 @@ use crate::{ - client::extensions::{InsertExt, NEW_LINE}, - core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter}, + client::extensions::InsertExt, + core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE}, }; pub struct DefaultInsertExt {} diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_style.rs b/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_style.rs index 4111218629..75a4e5ec0b 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_style.rs +++ b/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_style.rs @@ -1,9 +1,6 @@ use crate::{ - client::{ - extensions::InsertExt, - util::{contain_newline, OpNewline}, - }, - core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter}, + client::{extensions::InsertExt, util::contain_newline}, + core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, OpNewline}, }; pub struct PreserveInlineStylesExt {} @@ -16,7 +13,7 @@ impl InsertExt for PreserveInlineStylesExt { } let mut iter = DeltaIter::new(delta); - let prev = iter.next_op_before(index)?; + let prev = iter.last_op_before_index(index)?; if OpNewline::parse(&prev).is_contain() { return None; } 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 31d9a937b9..af47d57c4b 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 @@ -1,9 +1,6 @@ use crate::{ - client::{ - extensions::{InsertExt, NEW_LINE}, - util::is_newline, - }, - core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter}, + client::{extensions::InsertExt, util::is_newline}, + core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE}, }; pub struct ResetLineFormatOnNewLineExt {} @@ -16,8 +13,7 @@ impl InsertExt for ResetLineFormatOnNewLineExt { } let mut iter = DeltaIter::new(delta); - iter.seek::(index); - let next_op = iter.next()?; + let next_op = iter.first_op_after_index(index)?; if !next_op.get_data().starts_with(NEW_LINE) { return None; } diff --git a/rust-lib/flowy-ot/src/client/extensions/mod.rs b/rust-lib/flowy-ot/src/client/extensions/mod.rs index ea61a89e7d..ac1f61772b 100644 --- a/rust-lib/flowy-ot/src/client/extensions/mod.rs +++ b/rust-lib/flowy-ot/src/client/extensions/mod.rs @@ -8,9 +8,6 @@ mod delete; mod format; mod insert; -pub const NEW_LINE: &'static str = "\n"; -pub const WHITESPACE: &'static str = " "; - pub type InsertExtension = Box; pub type FormatExtension = Box; pub type DeleteExtension = Box; diff --git a/rust-lib/flowy-ot/src/client/util.rs b/rust-lib/flowy-ot/src/client/util.rs index 6a8a99f19b..462d2f0800 100644 --- a/rust-lib/flowy-ot/src/client/util.rs +++ b/rust-lib/flowy-ot/src/client/util.rs @@ -1,7 +1,4 @@ -use crate::{ - client::extensions::{NEW_LINE, WHITESPACE}, - core::Operation, -}; +use crate::core::{Operation, NEW_LINE, WHITESPACE}; #[inline] pub fn find_newline(s: &str) -> Option { @@ -11,51 +8,6 @@ pub fn find_newline(s: &str) -> Option { } } -#[derive(PartialEq, Eq)] -pub enum OpNewline { - Start, - End, - Contain, - Equal, - NotFound, -} - -impl OpNewline { - pub fn parse(op: &Operation) -> OpNewline { - let s = op.get_data(); - - if s == NEW_LINE { - return OpNewline::Equal; - } - - if s.starts_with(NEW_LINE) { - return OpNewline::Start; - } - - if s.ends_with(NEW_LINE) { - return OpNewline::End; - } - - if s.contains(NEW_LINE) { - return OpNewline::Contain; - } - - OpNewline::NotFound - } - - pub fn is_start(&self) -> bool { self == &OpNewline::Start } - - pub fn is_end(&self) -> bool { self == &OpNewline::End } - - pub fn is_not_found(&self) -> bool { self == &OpNewline::NotFound } - - pub fn is_contain(&self) -> bool { - self.is_start() || self.is_end() || self.is_equal() || self == &OpNewline::Contain - } - - pub fn is_equal(&self) -> bool { self == &OpNewline::Equal } -} - #[inline] pub fn is_op_contains_newline(op: &Operation) -> bool { contain_newline(op.get_data()) } diff --git a/rust-lib/flowy-ot/src/core/attributes/attribute.rs b/rust-lib/flowy-ot/src/core/attributes/attribute.rs index 4d0bce3782..24635cf9d9 100644 --- a/rust-lib/flowy-ot/src/core/attributes/attribute.rs +++ b/rust-lib/flowy-ot/src/core/attributes/attribute.rs @@ -6,11 +6,19 @@ use std::{collections::HashSet, fmt, fmt::Formatter, iter::FromIterator}; lazy_static! { static ref BLOCK_KEYS: HashSet = HashSet::from_iter(vec![ AttributeKey::Header, - AttributeKey::Align, - AttributeKey::List, - AttributeKey::CodeBlock, - AttributeKey::QuoteBlock, + AttributeKey::LeftAlignment, + AttributeKey::CenterAlignment, + AttributeKey::RightAlignment, + AttributeKey::JustifyAlignment, AttributeKey::Indent, + AttributeKey::Align, + AttributeKey::CodeBlock, + AttributeKey::List, + AttributeKey::Bullet, + AttributeKey::Ordered, + AttributeKey::Checked, + AttributeKey::UnChecked, + AttributeKey::QuoteBlock, ]); static ref INLINE_KEYS: HashSet = HashSet::from_iter(vec![ AttributeKey::Bold, @@ -19,8 +27,15 @@ lazy_static! { AttributeKey::StrikeThrough, AttributeKey::Link, AttributeKey::Color, + AttributeKey::Font, + AttributeKey::Size, AttributeKey::Background, ]); + static ref INGORE_KEYS: HashSet = HashSet::from_iter(vec![ + AttributeKey::Width, + AttributeKey::Height, + AttributeKey::Style, + ]); } #[derive(Debug, PartialEq, Eq, Clone)] @@ -116,45 +131,26 @@ impl AttributeKey { pub fn value>(&self, value: T) -> Attribute { let key = self.clone(); let value: AttributeValue = value.into(); - match self { - AttributeKey::Bold - | AttributeKey::Italic - | AttributeKey::Underline - | AttributeKey::StrikeThrough - | AttributeKey::Link - | AttributeKey::Color - | AttributeKey::Background - | AttributeKey::Font - | AttributeKey::Size => Attribute { + if INLINE_KEYS.contains(self) { + return Attribute { key, value, scope: AttributeScope::Inline, - }, + }; + } - AttributeKey::Header - | AttributeKey::LeftAlignment - | AttributeKey::CenterAlignment - | AttributeKey::RightAlignment - | AttributeKey::JustifyAlignment - | AttributeKey::Indent - | AttributeKey::Align - | AttributeKey::CodeBlock - | AttributeKey::List - | AttributeKey::Bullet - | AttributeKey::Ordered - | AttributeKey::Checked - | AttributeKey::UnChecked - | AttributeKey::QuoteBlock => Attribute { + if BLOCK_KEYS.contains(self) { + return Attribute { key, value, scope: AttributeScope::Block, - }, + }; + } - AttributeKey::Width | AttributeKey::Height | AttributeKey::Style => Attribute { - key, - value, - scope: AttributeScope::Ignore, - }, + Attribute { + key, + value, + scope: AttributeScope::Ignore, } } } @@ -183,3 +179,10 @@ impl std::convert::From for AttributeValue { AttributeValue(val.to_owned()) } } + +pub fn is_block_except_header(k: &AttributeKey) -> bool { + if k == &AttributeKey::Header { + return false; + } + BLOCK_KEYS.contains(k) +} diff --git a/rust-lib/flowy-ot/src/core/attributes/attributes.rs b/rust-lib/flowy-ot/src/core/attributes/attributes.rs index e25b6ec910..c59c19347c 100644 --- a/rust-lib/flowy-ot/src/core/attributes/attributes.rs +++ b/rust-lib/flowy-ot/src/core/attributes/attributes.rs @@ -37,17 +37,39 @@ impl Attributes { self.inner.insert(key, value); } - pub fn remove(&mut self, key: &AttributeKey) { + pub fn mark_as_removed(&mut self, key: &AttributeKey) { let value: AttributeValue = REMOVE_FLAG.into(); self.inner.insert(key.clone(), value); } - // Remove the key if its value is empty. e.g. { bold: "" } - pub fn remove_empty_value(&mut self) { self.inner.retain(|_, v| !should_remove(v)); } + 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 remove(&mut self, key: AttributeKey) { self.inner.retain(|k, _| k != &key); } + + // pub fn block_attributes_except_header(attributes: &Attributes) -> Attributes + // { let mut new_attributes = Attributes::new(); + // attributes.iter().for_each(|(k, v)| { + // if k != &AttributeKey::Header { + // new_attributes.insert(k.clone(), v.clone()); + // } + // }); + // + // new_attributes + // } + + // Remove the empty attribute which value is empty. e.g. {bold: ""}. + pub fn remove_empty(&mut self) { self.inner.retain(|_, v| !should_remove(v)); } pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); } - // Update self attributes by constructing new attributes from the other if it's + // Update inner by constructing new attributes from the other if it's // not None and replace the key/value with self key/value. pub fn merge(&mut self, other: Option) { if other.is_none() { @@ -144,7 +166,7 @@ pub fn invert_attributes(attr: Attributes, base: Attributes) -> Attributes { let inverted = attr.iter().fold(base_inverted, |mut attributes, (k, _)| { if base.get(k) != attr.get(k) && !base.contains_key(k) { - attributes.remove(k); + attributes.mark_as_removed(k); } attributes }); @@ -156,15 +178,3 @@ pub fn merge_attributes(mut attributes: Attributes, other: Attributes) -> Attrib attributes.extend(other); attributes } - -pub trait AttributesRule { - // Remove the empty attribute that its value is empty. e.g. {bold: ""}. - fn remove_empty(self) -> Attributes; -} - -impl AttributesRule for Attributes { - fn remove_empty(mut self) -> Attributes { - self.remove_empty_value(); - self.into() - } -} diff --git a/rust-lib/flowy-ot/src/core/delta/cursor.rs b/rust-lib/flowy-ot/src/core/delta/cursor.rs index 0086c491b8..eb8f180a15 100644 --- a/rust-lib/flowy-ot/src/core/delta/cursor.rs +++ b/rust-lib/flowy-ot/src/core/delta/cursor.rs @@ -34,10 +34,10 @@ impl<'a> Cursor<'a> { // get the next operation interval pub fn next_iv(&self) -> Interval { self.next_iv_before(None) } - pub fn next_op(&mut self) -> Option { self.next_op_before(None) } + pub fn next_op(&mut self) -> Option { self.last_op_before_index(None) } // get the last operation before the index - pub fn next_op_before(&mut self, index: Option) -> Option { + pub fn last_op_before_index(&mut self, index: Option) -> Option { let mut find_op = None; let next_op = self.next_op.take(); let mut next_op = next_op.as_ref(); @@ -74,7 +74,7 @@ impl<'a> Cursor<'a> { let pos = self.cur_char_count - pre_char_count; let end = index.unwrap(); if end > pos { - return self.next_op_before(Some(end - pos)); + return self.last_op_before_index(Some(end - pos)); } } return find_op; @@ -173,7 +173,7 @@ pub struct CharMetric {} impl Metric for CharMetric { fn seek(cursor: &mut Cursor, index: usize) -> SeekResult { let _ = check_bound(cursor.cur_char_count, index)?; - let _ = cursor.next_op_before(Some(index)); + let _ = cursor.last_op_before_index(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 45bee0e8bb..9a2d9ea8e7 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 - .next_op_before(length) + .last_op_before_index(length) .unwrap_or(OpBuilder::retain(length).build()); let other_op = other_iter - .next_op_before(length) + .last_op_before_index(length) .unwrap_or(OpBuilder::retain(length).build()); debug_assert_eq!(op.len(), other_op.len()); @@ -194,7 +194,7 @@ impl Delta { insert.attributes.clone(), other_retain.attributes.clone(), ); - composed_attrs = composed_attrs.remove_empty(); + composed_attrs.remove_empty(); new_delta.add( OpBuilder::insert(op.get_data()) .attributes(composed_attrs) @@ -300,7 +300,7 @@ impl Delta { }, (Some(Operation::Insert(insert)), Some(Operation::Retain(o_retain))) => { let mut composed_attrs = compose_operation(&next_op1, &next_op2); - composed_attrs = composed_attrs.remove_empty(); + composed_attrs.remove_empty(); log::debug!( "compose: [{} - {}], composed_attrs: {}", diff --git a/rust-lib/flowy-ot/src/core/delta/iterator.rs b/rust-lib/flowy-ot/src/core/delta/iterator.rs index 70a4f84963..f221c31253 100644 --- a/rust-lib/flowy-ot/src/core/delta/iterator.rs +++ b/rust-lib/flowy-ot/src/core/delta/iterator.rs @@ -1,5 +1,5 @@ use super::cursor::*; -use crate::core::{Attributes, Delta, Interval, Operation}; +use crate::core::{Attributes, Delta, Interval, Operation, NEW_LINE}; use std::ops::{Deref, DerefMut}; pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize; @@ -14,6 +14,13 @@ impl<'a> DeltaIter<'a> { Self::from_interval(delta, interval) } + pub fn from_offset(delta: &'a Delta, offset: usize) -> Self { + let interval = Interval::new(0, MAX_IV_LEN); + let mut iter = Self::from_interval(delta, interval); + iter.seek::(offset); + iter + } + pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self { let cursor = Cursor::new(delta, interval); Self { cursor } @@ -21,8 +28,6 @@ impl<'a> DeltaIter<'a> { pub fn ops(&mut self) -> Vec { self.collect::>() } - pub fn next_op(&mut self) -> Option { self.cursor.next_op() } - pub fn next_op_len(&self) -> Option { let interval = self.cursor.next_iv(); if interval.is_empty() { @@ -32,8 +37,30 @@ impl<'a> DeltaIter<'a> { } } - pub fn next_op_before(&mut self, index: usize) -> Option { - self.cursor.next_op_before(Some(index)) + // find next op contains NEW_LINE + pub fn first_op_contains_newline(&mut self) -> Option<(Operation, usize)> { + let mut offset = 0; + while self.has_next() { + if let Some(op) = self.next_op() { + if OpNewline::parse(&op).is_contain() { + return Some((op, offset)); + } + offset += op.len(); + } + } + + 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) { @@ -72,6 +99,22 @@ impl<'a> Iterator for DeltaIter<'a> { fn next(&mut self) -> Option { self.next_op() } } +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()); + if prev.is_none() { + return true; + } + + if next.is_none() { + return false; + } + + let prev = prev.unwrap(); + let next = next.unwrap(); + OpNewline::parse(&prev).is_end() && OpNewline::parse(&next).is_start() +} + pub struct AttributesIter<'a> { delta_iter: DeltaIter<'a>, } @@ -133,3 +176,48 @@ impl<'a> Iterator for AttributesIter<'a> { Some((length, attributes)) } } + +#[derive(PartialEq, Eq)] +pub enum OpNewline { + Start, + End, + Contain, + Equal, + NotFound, +} + +impl OpNewline { + pub fn parse(op: &Operation) -> OpNewline { + let s = op.get_data(); + + if s == NEW_LINE { + return OpNewline::Equal; + } + + if s.starts_with(NEW_LINE) { + return OpNewline::Start; + } + + if s.ends_with(NEW_LINE) { + return OpNewline::End; + } + + if s.contains(NEW_LINE) { + return OpNewline::Contain; + } + + OpNewline::NotFound + } + + pub fn is_start(&self) -> bool { self == &OpNewline::Start || self.is_equal() } + + pub fn is_end(&self) -> bool { self == &OpNewline::End || self.is_equal() } + + pub fn is_not_found(&self) -> bool { self == &OpNewline::NotFound } + + pub fn is_contain(&self) -> bool { + self.is_start() || self.is_end() || self.is_equal() || self == &OpNewline::Contain + } + + pub fn is_equal(&self) -> bool { self == &OpNewline::Equal } +} diff --git a/rust-lib/flowy-ot/src/core/delta/mod.rs b/rust-lib/flowy-ot/src/core/delta/mod.rs index 8cb9d71ee2..e8f8eb402f 100644 --- a/rust-lib/flowy-ot/src/core/delta/mod.rs +++ b/rust-lib/flowy-ot/src/core/delta/mod.rs @@ -8,3 +8,6 @@ pub use builder::*; pub use cursor::*; pub use delta::*; pub use iterator::*; + +pub const NEW_LINE: &'static str = "\n"; +pub const WHITESPACE: &'static str = " "; diff --git a/rust-lib/flowy-ot/tests/attribute_test.rs b/rust-lib/flowy-ot/tests/attribute_test.rs index 547bd5c2ba..b1ae58a50f 100644 --- a/rust-lib/flowy-ot/tests/attribute_test.rs +++ b/rust-lib/flowy-ot/tests/attribute_test.rs @@ -3,7 +3,7 @@ pub mod helper; use crate::helper::{TestOp::*, *}; use flowy_ot::core::Interval; -use flowy_ot::client::extensions::{NEW_LINE, WHITESPACE}; +use flowy_ot::core::{NEW_LINE, WHITESPACE}; #[test] fn attributes_insert_text() { @@ -646,6 +646,10 @@ fn attributes_add_bullet() { r#"[{"insert":"1"},{"insert":"\n","attributes":{"bullet":"true"}}]"#, ), Insert(0, NEW_LINE, 1), + AssertOpsJson( + 0, + r#"[{"insert":"1"},{"insert":"\n\n","attributes":{"bullet":"true"}}]"#, + ), Insert(0, "2", 2), AssertOpsJson( 0, @@ -672,3 +676,19 @@ fn attributes_un_bullet_one() { OpTester::new().run_script_with_newline(ops); } + +#[test] +fn attributes_auto_exit_block() { + let ops = vec![ + Insert(0, "1", 0), + Bullet(0, Interval::new(0, 1), true), + Insert(0, NEW_LINE, 1), + Insert(0, NEW_LINE, 2), + AssertOpsJson( + 0, + r#"[{"insert":"1"},{"insert":"\n","attributes":{"bullet":"true"}},{"insert":"\n"}]"#, + ), + ]; + + OpTester::new().run_script_with_newline(ops); +} diff --git a/rust-lib/flowy-ot/tests/helper/mod.rs b/rust-lib/flowy-ot/tests/helper/mod.rs index 045cb57c21..cc125636a9 100644 --- a/rust-lib/flowy-ot/tests/helper/mod.rs +++ b/rust-lib/flowy-ot/tests/helper/mod.rs @@ -3,7 +3,7 @@ use flowy_ot::{client::Document, core::*}; use rand::{prelude::*, Rng as WrappedRng}; use std::{sync::Once, time::Duration}; -const LEVEL: &'static str = "info"; +const LEVEL: &'static str = "debug"; #[derive(Clone, Debug, Display)] pub enum TestOp { diff --git a/rust-lib/flowy-ot/tests/op_test.rs b/rust-lib/flowy-ot/tests/op_test.rs index 01fae207e0..0bff6f80eb 100644 --- a/rust-lib/flowy-ot/tests/op_test.rs +++ b/rust-lib/flowy-ot/tests/op_test.rs @@ -147,14 +147,13 @@ fn delta_get_ops_in_interval_7() { delta.add(insert_a.clone()); delta.add(retain_a.clone()); - let mut iter_1 = DeltaIter::new(&delta); - iter_1.seek::(2); + let mut iter_1 = DeltaIter::from_offset(&delta, 2); assert_eq!(iter_1.next_op().unwrap(), OpBuilder::insert("345").build()); assert_eq!(iter_1.next_op().unwrap(), OpBuilder::retain(3).build()); let mut iter_2 = DeltaIter::new(&delta); assert_eq!( - iter_2.next_op_before(2).unwrap(), + iter_2.last_op_before_index(2).unwrap(), OpBuilder::insert("12").build() ); assert_eq!(iter_2.next_op().unwrap(), OpBuilder::insert("345").build()); @@ -181,7 +180,7 @@ fn delta_seek_2() { let mut iter = DeltaIter::new(&delta); assert_eq!( - iter.next_op_before(1).unwrap(), + iter.last_op_before_index(1).unwrap(), OpBuilder::insert("1").build() ); } @@ -193,21 +192,21 @@ fn delta_seek_3() { let mut iter = DeltaIter::new(&delta); assert_eq!( - iter.next_op_before(2).unwrap(), + iter.last_op_before_index(2).unwrap(), OpBuilder::insert("12").build() ); assert_eq!( - iter.next_op_before(2).unwrap(), + iter.last_op_before_index(2).unwrap(), OpBuilder::insert("34").build() ); assert_eq!( - iter.next_op_before(2).unwrap(), + iter.last_op_before_index(2).unwrap(), OpBuilder::insert("5").build() ); - assert_eq!(iter.next_op_before(1), None); + assert_eq!(iter.last_op_before_index(1), None); } #[test] @@ -218,7 +217,7 @@ fn delta_seek_4() { let mut iter = DeltaIter::new(&delta); iter.seek::(3); assert_eq!( - iter.next_op_before(2).unwrap(), + iter.last_op_before_index(2).unwrap(), OpBuilder::insert("45").build() ); } @@ -238,7 +237,7 @@ fn delta_seek_5() { iter.seek::(0); assert_eq!( - iter.next_op_before(4).unwrap(), + iter.last_op_before_index(4).unwrap(), OpBuilder::insert("1234").attributes(attributes).build(), ); } @@ -252,7 +251,7 @@ fn delta_next_op_len_test() { iter.seek::(3); assert_eq!(iter.next_op_len().unwrap(), 2); assert_eq!( - iter.next_op_before(1).unwrap(), + iter.last_op_before_index(1).unwrap(), OpBuilder::insert("4").build() ); assert_eq!(iter.next_op_len().unwrap(), 1); @@ -267,12 +266,22 @@ fn delta_next_op_len_test2() { assert_eq!(iter.next_op_len().unwrap(), 5); assert_eq!( - iter.next_op_before(5).unwrap(), + iter.last_op_before_index(5).unwrap(), OpBuilder::insert("12345").build() ); assert_eq!(iter.next_op_len(), None); } +#[test] +fn delta_next_op_len_test3() { + 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_len().unwrap(), 5); +} + #[test] fn lengths() { let mut delta = Delta::default();