From 4d3eabb76128d3e75c483fd854fe08f54d1a7e0a Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 17 Aug 2021 22:04:39 +0800 Subject: [PATCH] preserve line format on split --- .../lib/src/model/heuristic/rule.dart | 2 +- .../extensions/format/format_at_position.rs | 2 +- .../extensions/format/resolve_block_format.rs | 6 +- .../format/resolve_inline_format.rs | 6 +- .../extensions/insert/auto_exit_block.rs | 6 +- .../client/extensions/insert/auto_format.rs | 2 +- .../extensions/insert/default_insert.rs | 6 +- .../src/client/extensions/insert/mod.rs | 23 +--- ...lock_style.rs => preserve_block_format.rs} | 9 +- .../insert/preserve_inline_format.rs | 101 ++++++++++++++++++ .../insert/preserve_inline_style.rs | 48 --------- .../insert/reset_format_on_new_line.rs | 6 +- rust-lib/flowy-ot/src/client/view.rs | 16 +-- rust-lib/flowy-ot/src/core/delta/cursor.rs | 57 +++++----- 14 files changed, 164 insertions(+), 126 deletions(-) rename rust-lib/flowy-ot/src/client/extensions/insert/{preserve_block_style.rs => preserve_block_format.rs} (89%) create mode 100644 rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_format.rs delete mode 100644 rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_style.rs 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 0478843ef8..bcc123cb3c 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 @@ -47,7 +47,7 @@ class Rules { // const ForceNewlineForInsertsAroundEmbedRule(), const AutoExitBlockRule(), const PreserveBlockStyleOnInsertRule(), - // const PreserveLineStyleOnSplitRule(), + const PreserveLineStyleOnSplitRule(), const ResetLineFormatOnNewLineRule(), const AutoFormatLinksRule(), const PreserveInlineStylesRule(), 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 4b27c301ed..df057bd6a8 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 @@ -6,7 +6,7 @@ use crate::{ pub struct FormatLinkAtCaretPositionExt {} impl FormatExt for FormatLinkAtCaretPositionExt { - fn ext_name(&self) -> &str { "FormatLinkAtCaretPositionExt" } + fn ext_name(&self) -> &str { std::any::type_name::() } fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option { if attribute.key != AttributeKey::Link || interval.size() != 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 f3b4fd430c..ee8751bdba 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 @@ -15,9 +15,9 @@ use crate::{ }, }; -pub struct ResolveBlockFormatExt {} -impl FormatExt for ResolveBlockFormatExt { - fn ext_name(&self) -> &str { "ResolveBlockFormatExt" } +pub struct ResolveBlockFormat {} +impl FormatExt for ResolveBlockFormat { + fn ext_name(&self) -> &str { std::any::type_name::() } fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option { if attribute.scope != AttributeScope::Block { 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 5edb7decb7..208a1f372f 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 @@ -6,9 +6,9 @@ use crate::{ core::{Attribute, AttributeScope, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval}, }; -pub struct ResolveInlineFormatExt {} -impl FormatExt for ResolveInlineFormatExt { - fn ext_name(&self) -> &str { "ResolveInlineFormatExt" } +pub struct ResolveInlineFormat {} +impl FormatExt for ResolveInlineFormat { + fn ext_name(&self) -> &str { std::any::type_name::() } fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option { if attribute.scope != AttributeScope::Inline { 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 9b64d12148..cd60e301b9 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 @@ -14,10 +14,10 @@ use crate::{ use crate::core::{attributes_except_header, is_empty_line_at_index}; -pub struct AutoExitBlockExt {} +pub struct AutoExitBlock {} -impl InsertExt for AutoExitBlockExt { - fn ext_name(&self) -> &str { "AutoExitBlockExt" } +impl InsertExt for AutoExitBlock { + fn ext_name(&self) -> &str { std::any::type_name::() } fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { // Auto exit block will be triggered by enter two new lines 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 29b848fa32..92762a4390 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 @@ -5,7 +5,7 @@ use crate::{ pub struct AutoFormatExt {} impl InsertExt for AutoFormatExt { - fn ext_name(&self) -> &str { "AutoFormatExt" } + fn ext_name(&self) -> &str { std::any::type_name::() } fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { // enter whitespace to trigger auto format 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 51f9498cfc..d367254e5d 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 @@ -3,9 +3,9 @@ use crate::{ core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE}, }; -pub struct DefaultInsertExt {} -impl InsertExt for DefaultInsertExt { - fn ext_name(&self) -> &str { "DefaultInsertExt" } +pub struct DefaultInsertAttribute {} +impl InsertExt for DefaultInsertAttribute { + fn ext_name(&self) -> &str { std::any::type_name::() } fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { let iter = DeltaIter::new(delta); diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/mod.rs b/rust-lib/flowy-ot/src/client/extensions/insert/mod.rs index ba073908ff..6ee9c3bc6a 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/mod.rs +++ b/rust-lib/flowy-ot/src/client/extensions/insert/mod.rs @@ -1,34 +1,19 @@ pub use auto_exit_block::*; pub use auto_format::*; pub use default_insert::*; -pub use preserve_block_style::*; -pub use preserve_inline_style::*; +pub use preserve_block_format::*; +pub use preserve_inline_format::*; pub use reset_format_on_new_line::*; mod auto_exit_block; mod auto_format; mod default_insert; -mod preserve_block_style; -mod preserve_inline_style; +mod preserve_block_format; +mod preserve_inline_format; mod reset_format_on_new_line; use crate::{client::extensions::InsertExt, core::Delta}; -pub struct PreserveLineStyleOnSplitExt {} -impl InsertExt for PreserveLineStyleOnSplitExt { - fn ext_name(&self) -> &str { "PreserveLineStyleOnSplitExt" } - - fn apply( - &self, - _delta: &Delta, - _replace_len: usize, - _text: &str, - _index: usize, - ) -> Option { - None - } -} - pub struct InsertEmbedsExt {} impl InsertExt for InsertEmbedsExt { fn ext_name(&self) -> &str { "InsertEmbedsExt" } diff --git a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_style.rs b/rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_format.rs similarity index 89% rename from rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_style.rs rename to rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_format.rs index b6f97d69b8..1312f3c27d 100644 --- a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_style.rs +++ b/rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_format.rs @@ -13,9 +13,9 @@ use crate::{ }, }; -pub struct PreserveBlockStyleOnInsertExt {} -impl InsertExt for PreserveBlockStyleOnInsertExt { - fn ext_name(&self) -> &str { "PreserveBlockStyleOnInsertExt" } +pub struct PreserveBlockFormatOnInsert {} +impl InsertExt for PreserveBlockFormatOnInsert { + fn ext_name(&self) -> &str { std::any::type_name::() } fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { if !is_newline(text) { @@ -38,7 +38,6 @@ impl InsertExt for PreserveBlockStyleOnInsertExt { } let lines: Vec<_> = text.split(NEW_LINE).collect(); - let line_count = lines.len(); let mut new_delta = DeltaBuilder::new().retain(index + replace_len).build(); lines.iter().enumerate().for_each(|(i, line)| { if !line.is_empty() { @@ -52,8 +51,6 @@ impl InsertExt for PreserveBlockStyleOnInsertExt { } else { // do nothing } - - log::info!("{}", new_delta); }); if !reset_attribute.is_empty() { new_delta.retain(offset, Attributes::empty()); 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 new file mode 100644 index 0000000000..a9a777bd6f --- /dev/null +++ b/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_format.rs @@ -0,0 +1,101 @@ +use crate::{ + client::{ + extensions::InsertExt, + util::{contain_newline, is_newline}, + }, + core::{ + AttributeKey, + Attributes, + Delta, + DeltaBuilder, + DeltaIter, + OpNewline, + Operation, + NEW_LINE, + }, +}; + +pub struct PreserveInlineFormat {} +impl InsertExt for PreserveInlineFormat { + fn ext_name(&self) -> &str { std::any::type_name::() } + + fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { + if contain_newline(text) { + return None; + } + + let mut iter = DeltaIter::new(delta); + let prev = iter.last_op_before_index(index)?; + if OpNewline::parse(&prev).is_contain() { + 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) + .insert_with_attributes(text, attributes) + .build(), + ); + } + + let next = iter.next_op(); + match &next { + None => attributes = Attributes::empty(), + Some(next) => { + if OpNewline::parse(&next).is_equal() { + attributes = Attributes::empty(); + } + }, + } + + let new_delta = DeltaBuilder::new() + .retain(index + replace_len) + .insert_with_attributes(text, attributes) + .build(); + + return Some(new_delta); + } +} + +pub struct PreserveLineFormatOnSplit {} +impl InsertExt for PreserveLineFormatOnSplit { + fn ext_name(&self) -> &str { std::any::type_name::() } + + fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { + if !is_newline(text) { + return None; + } + + let mut iter = DeltaIter::new(delta); + let prev = iter.last_op_before_index(index)?; + if OpNewline::parse(&prev).is_end() { + return None; + } + + let next = iter.next_op()?; + let newline_status = OpNewline::parse(&next); + if newline_status.is_end() { + return None; + } + + let mut new_delta = Delta::new(); + new_delta.retain(index + replace_len, Attributes::empty()); + + if newline_status.is_contain() { + debug_assert!(next.has_attribute() == false); + new_delta.insert(NEW_LINE, Attributes::empty()); + return Some(new_delta); + } + + match iter.first_newline_op() { + None => {}, + Some((newline_op, _)) => { + new_delta.insert(NEW_LINE, newline_op.get_attributes()); + }, + } + + Some(new_delta) + } +} 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 deleted file mode 100644 index 75a4e5ec0b..0000000000 --- a/rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_style.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::{ - client::{extensions::InsertExt, util::contain_newline}, - core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, OpNewline}, -}; - -pub struct PreserveInlineStylesExt {} -impl InsertExt for PreserveInlineStylesExt { - fn ext_name(&self) -> &str { "PreserveInlineStylesExt" } - - fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { - if contain_newline(text) { - return None; - } - - let mut iter = DeltaIter::new(delta); - let prev = iter.last_op_before_index(index)?; - if OpNewline::parse(&prev).is_contain() { - 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) - .insert_with_attributes(text, attributes) - .build(), - ); - } - - let next = iter.next_op(); - match &next { - None => attributes = Attributes::empty(), - Some(next) => { - if OpNewline::parse(&next).is_equal() { - attributes = Attributes::empty(); - } - }, - } - - let new_delta = DeltaBuilder::new() - .retain(index + replace_len) - .insert_with_attributes(text, attributes) - .build(); - - return Some(new_delta); - } -} 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 af47d57c4b..79befd8320 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 @@ -3,9 +3,9 @@ use crate::{ core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE}, }; -pub struct ResetLineFormatOnNewLineExt {} -impl InsertExt for ResetLineFormatOnNewLineExt { - fn ext_name(&self) -> &str { "ResetLineFormatOnNewLineExt" } +pub struct ResetLineFormatOnNewLine {} +impl InsertExt for ResetLineFormatOnNewLine { + fn ext_name(&self) -> &str { std::any::type_name::() } fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option { if !is_newline(text) { diff --git a/rust-lib/flowy-ot/src/client/view.rs b/rust-lib/flowy-ot/src/client/view.rs index 7c89ac83ab..6c6df62ca8 100644 --- a/rust-lib/flowy-ot/src/client/view.rs +++ b/rust-lib/flowy-ot/src/client/view.rs @@ -82,21 +82,21 @@ fn construct_insert_exts() -> Vec { vec![ Box::new(InsertEmbedsExt {}), Box::new(ForceNewlineForInsertsAroundEmbedExt {}), - Box::new(AutoExitBlockExt {}), - Box::new(PreserveBlockStyleOnInsertExt {}), - Box::new(PreserveLineStyleOnSplitExt {}), - Box::new(ResetLineFormatOnNewLineExt {}), + Box::new(AutoExitBlock {}), + Box::new(PreserveBlockFormatOnInsert {}), + Box::new(PreserveLineFormatOnSplit {}), + Box::new(ResetLineFormatOnNewLine {}), Box::new(AutoFormatExt {}), - Box::new(PreserveInlineStylesExt {}), - Box::new(DefaultInsertExt {}), + Box::new(PreserveInlineFormat {}), + Box::new(DefaultInsertAttribute {}), ] } fn construct_format_exts() -> Vec { vec![ Box::new(FormatLinkAtCaretPositionExt {}), - Box::new(ResolveBlockFormatExt {}), - Box::new(ResolveInlineFormatExt {}), + Box::new(ResolveBlockFormat {}), + Box::new(ResolveInlineFormat {}), ] } diff --git a/rust-lib/flowy-ot/src/core/delta/cursor.rs b/rust-lib/flowy-ot/src/core/delta/cursor.rs index eb8f180a15..0a70e1e513 100644 --- a/rust-lib/flowy-ot/src/core/delta/cursor.rs +++ b/rust-lib/flowy-ot/src/core/delta/cursor.rs @@ -8,9 +8,9 @@ use std::{cmp::min, iter::Enumerate, slice::Iter}; pub struct Cursor<'a> { pub(crate) delta: &'a Delta, pub(crate) origin_iv: Interval, - pub(crate) next_iv: Interval, - pub(crate) cur_char_count: usize, - pub(crate) cur_op_index: usize, + pub(crate) consume_iv: Interval, + pub(crate) consume_count: usize, + pub(crate) op_index: usize, iter: Enumerate>, next_op: Option, } @@ -21,9 +21,9 @@ impl<'a> Cursor<'a> { let mut cursor = Self { delta, origin_iv: interval, - next_iv: interval, - cur_char_count: 0, - cur_op_index: 0, + consume_iv: interval, + consume_count: 0, + op_index: 0, iter: delta.ops.iter().enumerate(), next_op: None, }; @@ -39,14 +39,14 @@ impl<'a> Cursor<'a> { // get the last operation before the index 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(); + let holder = self.next_op.clone(); + let mut next_op = holder.as_ref(); if next_op.is_none() { next_op = find_next_op(self); } - let pre_char_count = self.cur_char_count; + let mut pos = 0; while find_op.is_none() && next_op.is_some() { let op = next_op.take().unwrap(); let interval = self.next_iv_before(index); @@ -56,13 +56,16 @@ impl<'a> Cursor<'a> { } find_op = op.shrink(interval); + self.next_op = None; + let suffix = Interval::new(0, op.len()).suffix(interval); if !suffix.is_empty() { self.next_op = op.shrink(suffix); } - self.cur_char_count += interval.end; - self.next_iv.start = self.cur_char_count; + pos += interval.end; + self.consume_count += interval.end; + self.consume_iv.start = self.consume_count; if find_op.is_none() { next_op = find_next_op(self); @@ -71,9 +74,8 @@ impl<'a> Cursor<'a> { 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 pos = self.cur_char_count - pre_char_count; let end = index.unwrap(); - if end > pos { + if end > pos && self.has_next() { return self.last_op_before_index(Some(end - pos)); } } @@ -83,18 +85,18 @@ impl<'a> Cursor<'a> { pub fn has_next(&self) -> bool { self.next_iter_op().is_some() } fn descend(&mut self, index: usize) { - self.next_iv.start += index; + self.consume_iv.start += index; - if self.cur_char_count >= self.next_iv.start { + if self.consume_count >= self.consume_iv.start { return; } while let Some((o_index, op)) = self.iter.next() { - self.cur_op_index = o_index; - let start = self.cur_char_count; + self.op_index = o_index; + let start = self.consume_count; let end = start + op.len(); - let intersect = Interval::new(start, end).intersect(self.next_iv); + let intersect = Interval::new(start, end).intersect(self.consume_iv); if intersect.is_empty() { - self.cur_char_count += op.len(); + self.consume_count += op.len(); } else { self.next_op = Some(op.clone()); break; @@ -108,7 +110,7 @@ impl<'a> Cursor<'a> { let mut offset = 0; for op in &self.delta.ops { offset += op.len(); - if offset > self.cur_char_count { + if offset > self.consume_count { next_op = Some(op); break; } @@ -124,12 +126,13 @@ impl<'a> Cursor<'a> { } let op = next_op.unwrap(); - let start = self.cur_char_count; + let start = self.consume_count; let end = match index { - None => self.cur_char_count + op.len(), - Some(index) => self.cur_char_count + min(index, op.len()), + None => self.consume_count + op.len(), + Some(index) => self.consume_count + min(index, op.len()), }; - let intersect = Interval::new(start, end).intersect(self.next_iv); + + let intersect = Interval::new(start, end).intersect(self.consume_iv); let interval = intersect.translate_neg(start); interval } @@ -139,7 +142,7 @@ fn find_next_op<'a>(cursor: &mut Cursor<'a>) -> Option<&'a Operation> { match cursor.iter.next() { None => None, Some((o_index, op)) => { - cursor.cur_op_index = o_index; + cursor.op_index = o_index; Some(op) }, } @@ -154,7 +157,7 @@ pub struct OpMetric {} impl Metric for OpMetric { fn seek(cursor: &mut Cursor, index: usize) -> SeekResult { - let _ = check_bound(cursor.cur_op_index, index)?; + let _ = check_bound(cursor.op_index, index)?; let mut seek_cursor = Cursor::new(cursor.delta, cursor.origin_iv); let mut offset = 0; while let Some((_, op)) = seek_cursor.iter.next() { @@ -172,7 +175,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 _ = check_bound(cursor.consume_count, index)?; let _ = cursor.last_op_before_index(Some(index)); Ok(())