preserve line format on split

This commit is contained in:
appflowy 2021-08-17 22:04:39 +08:00
parent 7c4b2d74a5
commit 4d3eabb761
14 changed files with 164 additions and 126 deletions

View file

@ -47,7 +47,7 @@ class Rules {
// const ForceNewlineForInsertsAroundEmbedRule(), // const ForceNewlineForInsertsAroundEmbedRule(),
const AutoExitBlockRule(), const AutoExitBlockRule(),
const PreserveBlockStyleOnInsertRule(), const PreserveBlockStyleOnInsertRule(),
// const PreserveLineStyleOnSplitRule(), const PreserveLineStyleOnSplitRule(),
const ResetLineFormatOnNewLineRule(), const ResetLineFormatOnNewLineRule(),
const AutoFormatLinksRule(), const AutoFormatLinksRule(),
const PreserveInlineStylesRule(), const PreserveInlineStylesRule(),

View file

@ -6,7 +6,7 @@ use crate::{
pub struct FormatLinkAtCaretPositionExt {} pub struct FormatLinkAtCaretPositionExt {}
impl FormatExt for FormatLinkAtCaretPositionExt { impl FormatExt for FormatLinkAtCaretPositionExt {
fn ext_name(&self) -> &str { "FormatLinkAtCaretPositionExt" } fn ext_name(&self) -> &str { std::any::type_name::<FormatLinkAtCaretPositionExt>() }
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> { fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
if attribute.key != AttributeKey::Link || interval.size() != 0 { if attribute.key != AttributeKey::Link || interval.size() != 0 {

View file

@ -15,9 +15,9 @@ use crate::{
}, },
}; };
pub struct ResolveBlockFormatExt {} pub struct ResolveBlockFormat {}
impl FormatExt for ResolveBlockFormatExt { impl FormatExt for ResolveBlockFormat {
fn ext_name(&self) -> &str { "ResolveBlockFormatExt" } fn ext_name(&self) -> &str { std::any::type_name::<ResolveBlockFormat>() }
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> { fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
if attribute.scope != AttributeScope::Block { if attribute.scope != AttributeScope::Block {

View file

@ -6,9 +6,9 @@ use crate::{
core::{Attribute, AttributeScope, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval}, core::{Attribute, AttributeScope, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval},
}; };
pub struct ResolveInlineFormatExt {} pub struct ResolveInlineFormat {}
impl FormatExt for ResolveInlineFormatExt { impl FormatExt for ResolveInlineFormat {
fn ext_name(&self) -> &str { "ResolveInlineFormatExt" } fn ext_name(&self) -> &str { std::any::type_name::<ResolveInlineFormat>() }
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> { fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
if attribute.scope != AttributeScope::Inline { if attribute.scope != AttributeScope::Inline {

View file

@ -14,10 +14,10 @@ use crate::{
use crate::core::{attributes_except_header, is_empty_line_at_index}; use crate::core::{attributes_except_header, is_empty_line_at_index};
pub struct AutoExitBlockExt {} pub struct AutoExitBlock {}
impl InsertExt for AutoExitBlockExt { impl InsertExt for AutoExitBlock {
fn ext_name(&self) -> &str { "AutoExitBlockExt" } fn ext_name(&self) -> &str { std::any::type_name::<AutoExitBlock>() }
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> {
// Auto exit block will be triggered by enter two new lines // Auto exit block will be triggered by enter two new lines

View file

@ -5,7 +5,7 @@ use crate::{
pub struct AutoFormatExt {} pub struct AutoFormatExt {}
impl InsertExt for AutoFormatExt { impl InsertExt for AutoFormatExt {
fn ext_name(&self) -> &str { "AutoFormatExt" } fn ext_name(&self) -> &str { std::any::type_name::<AutoFormatExt>() }
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> {
// enter whitespace to trigger auto format // enter whitespace to trigger auto format

View file

@ -3,9 +3,9 @@ use crate::{
core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE}, core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE},
}; };
pub struct DefaultInsertExt {} pub struct DefaultInsertAttribute {}
impl InsertExt for DefaultInsertExt { impl InsertExt for DefaultInsertAttribute {
fn ext_name(&self) -> &str { "DefaultInsertExt" } fn ext_name(&self) -> &str { std::any::type_name::<DefaultInsertAttribute>() }
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> {
let iter = DeltaIter::new(delta); let iter = DeltaIter::new(delta);

View file

@ -1,34 +1,19 @@
pub use auto_exit_block::*; pub use auto_exit_block::*;
pub use auto_format::*; pub use auto_format::*;
pub use default_insert::*; pub use default_insert::*;
pub use preserve_block_style::*; pub use preserve_block_format::*;
pub use preserve_inline_style::*; pub use preserve_inline_format::*;
pub use reset_format_on_new_line::*; pub use reset_format_on_new_line::*;
mod auto_exit_block; mod auto_exit_block;
mod auto_format; mod auto_format;
mod default_insert; mod default_insert;
mod preserve_block_style; mod preserve_block_format;
mod preserve_inline_style; mod preserve_inline_format;
mod reset_format_on_new_line; mod reset_format_on_new_line;
use crate::{client::extensions::InsertExt, core::Delta}; 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<Delta> {
None
}
}
pub struct InsertEmbedsExt {} pub struct InsertEmbedsExt {}
impl InsertExt for InsertEmbedsExt { impl InsertExt for InsertEmbedsExt {
fn ext_name(&self) -> &str { "InsertEmbedsExt" } fn ext_name(&self) -> &str { "InsertEmbedsExt" }

View file

@ -13,9 +13,9 @@ use crate::{
}, },
}; };
pub struct PreserveBlockStyleOnInsertExt {} pub struct PreserveBlockFormatOnInsert {}
impl InsertExt for PreserveBlockStyleOnInsertExt { impl InsertExt for PreserveBlockFormatOnInsert {
fn ext_name(&self) -> &str { "PreserveBlockStyleOnInsertExt" } fn ext_name(&self) -> &str { std::any::type_name::<PreserveBlockFormatOnInsert>() }
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 !is_newline(text) { if !is_newline(text) {
@ -38,7 +38,6 @@ impl InsertExt for PreserveBlockStyleOnInsertExt {
} }
let lines: Vec<_> = text.split(NEW_LINE).collect(); let lines: Vec<_> = text.split(NEW_LINE).collect();
let line_count = lines.len();
let mut new_delta = DeltaBuilder::new().retain(index + replace_len).build(); let mut new_delta = DeltaBuilder::new().retain(index + replace_len).build();
lines.iter().enumerate().for_each(|(i, line)| { lines.iter().enumerate().for_each(|(i, line)| {
if !line.is_empty() { if !line.is_empty() {
@ -52,8 +51,6 @@ impl InsertExt for PreserveBlockStyleOnInsertExt {
} else { } else {
// do nothing // do nothing
} }
log::info!("{}", new_delta);
}); });
if !reset_attribute.is_empty() { if !reset_attribute.is_empty() {
new_delta.retain(offset, Attributes::empty()); new_delta.retain(offset, Attributes::empty());

View file

@ -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::<PreserveInlineFormat>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
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::<PreserveLineFormatOnSplit>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
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)
}
}

View file

@ -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<Delta> {
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);
}
}

View file

@ -3,9 +3,9 @@ use crate::{
core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE}, core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE},
}; };
pub struct ResetLineFormatOnNewLineExt {} pub struct ResetLineFormatOnNewLine {}
impl InsertExt for ResetLineFormatOnNewLineExt { impl InsertExt for ResetLineFormatOnNewLine {
fn ext_name(&self) -> &str { "ResetLineFormatOnNewLineExt" } fn ext_name(&self) -> &str { std::any::type_name::<ResetLineFormatOnNewLine>() }
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 !is_newline(text) { if !is_newline(text) {

View file

@ -82,21 +82,21 @@ fn construct_insert_exts() -> Vec<InsertExtension> {
vec![ vec![
Box::new(InsertEmbedsExt {}), Box::new(InsertEmbedsExt {}),
Box::new(ForceNewlineForInsertsAroundEmbedExt {}), Box::new(ForceNewlineForInsertsAroundEmbedExt {}),
Box::new(AutoExitBlockExt {}), Box::new(AutoExitBlock {}),
Box::new(PreserveBlockStyleOnInsertExt {}), Box::new(PreserveBlockFormatOnInsert {}),
Box::new(PreserveLineStyleOnSplitExt {}), Box::new(PreserveLineFormatOnSplit {}),
Box::new(ResetLineFormatOnNewLineExt {}), Box::new(ResetLineFormatOnNewLine {}),
Box::new(AutoFormatExt {}), Box::new(AutoFormatExt {}),
Box::new(PreserveInlineStylesExt {}), Box::new(PreserveInlineFormat {}),
Box::new(DefaultInsertExt {}), Box::new(DefaultInsertAttribute {}),
] ]
} }
fn construct_format_exts() -> Vec<FormatExtension> { fn construct_format_exts() -> Vec<FormatExtension> {
vec![ vec![
Box::new(FormatLinkAtCaretPositionExt {}), Box::new(FormatLinkAtCaretPositionExt {}),
Box::new(ResolveBlockFormatExt {}), Box::new(ResolveBlockFormat {}),
Box::new(ResolveInlineFormatExt {}), Box::new(ResolveInlineFormat {}),
] ]
} }

View file

@ -8,9 +8,9 @@ use std::{cmp::min, iter::Enumerate, slice::Iter};
pub struct Cursor<'a> { pub struct Cursor<'a> {
pub(crate) delta: &'a Delta, pub(crate) delta: &'a Delta,
pub(crate) origin_iv: Interval, pub(crate) origin_iv: Interval,
pub(crate) next_iv: Interval, pub(crate) consume_iv: Interval,
pub(crate) cur_char_count: usize, pub(crate) consume_count: usize,
pub(crate) cur_op_index: usize, pub(crate) op_index: usize,
iter: Enumerate<Iter<'a, Operation>>, iter: Enumerate<Iter<'a, Operation>>,
next_op: Option<Operation>, next_op: Option<Operation>,
} }
@ -21,9 +21,9 @@ impl<'a> Cursor<'a> {
let mut cursor = Self { let mut cursor = Self {
delta, delta,
origin_iv: interval, origin_iv: interval,
next_iv: interval, consume_iv: interval,
cur_char_count: 0, consume_count: 0,
cur_op_index: 0, op_index: 0,
iter: delta.ops.iter().enumerate(), iter: delta.ops.iter().enumerate(),
next_op: None, next_op: None,
}; };
@ -39,14 +39,14 @@ impl<'a> Cursor<'a> {
// get the last operation before the index // get the last operation before the index
pub fn last_op_before_index(&mut self, index: Option<usize>) -> Option<Operation> { pub fn last_op_before_index(&mut self, index: Option<usize>) -> Option<Operation> {
let mut find_op = None; let mut find_op = None;
let next_op = self.next_op.take(); let holder = self.next_op.clone();
let mut next_op = next_op.as_ref(); let mut next_op = holder.as_ref();
if next_op.is_none() { if next_op.is_none() {
next_op = find_next_op(self); 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() { while find_op.is_none() && next_op.is_some() {
let op = next_op.take().unwrap(); let op = next_op.take().unwrap();
let interval = self.next_iv_before(index); let interval = self.next_iv_before(index);
@ -56,13 +56,16 @@ impl<'a> Cursor<'a> {
} }
find_op = op.shrink(interval); find_op = op.shrink(interval);
self.next_op = None;
let suffix = Interval::new(0, op.len()).suffix(interval); let suffix = Interval::new(0, op.len()).suffix(interval);
if !suffix.is_empty() { if !suffix.is_empty() {
self.next_op = op.shrink(suffix); self.next_op = op.shrink(suffix);
} }
self.cur_char_count += interval.end; pos += interval.end;
self.next_iv.start = self.cur_char_count; self.consume_count += interval.end;
self.consume_iv.start = self.consume_count;
if find_op.is_none() { if find_op.is_none() {
next_op = find_next_op(self); next_op = find_next_op(self);
@ -71,9 +74,8 @@ impl<'a> Cursor<'a> {
if find_op.is_some() && index.is_some() { if find_op.is_some() && index.is_some() {
// try to find the next op before the index if iter_char_count less than index // 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(); let end = index.unwrap();
if end > pos { if end > pos && self.has_next() {
return self.last_op_before_index(Some(end - pos)); 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() } pub fn has_next(&self) -> bool { self.next_iter_op().is_some() }
fn descend(&mut self, index: usize) { 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; return;
} }
while let Some((o_index, op)) = self.iter.next() { while let Some((o_index, op)) = self.iter.next() {
self.cur_op_index = o_index; self.op_index = o_index;
let start = self.cur_char_count; let start = self.consume_count;
let end = start + op.len(); 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() { if intersect.is_empty() {
self.cur_char_count += op.len(); self.consume_count += op.len();
} else { } else {
self.next_op = Some(op.clone()); self.next_op = Some(op.clone());
break; break;
@ -108,7 +110,7 @@ impl<'a> Cursor<'a> {
let mut offset = 0; let mut offset = 0;
for op in &self.delta.ops { for op in &self.delta.ops {
offset += op.len(); offset += op.len();
if offset > self.cur_char_count { if offset > self.consume_count {
next_op = Some(op); next_op = Some(op);
break; break;
} }
@ -124,12 +126,13 @@ impl<'a> Cursor<'a> {
} }
let op = next_op.unwrap(); let op = next_op.unwrap();
let start = self.cur_char_count; let start = self.consume_count;
let end = match index { let end = match index {
None => self.cur_char_count + op.len(), None => self.consume_count + op.len(),
Some(index) => self.cur_char_count + min(index, 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); let interval = intersect.translate_neg(start);
interval interval
} }
@ -139,7 +142,7 @@ fn find_next_op<'a>(cursor: &mut Cursor<'a>) -> Option<&'a Operation> {
match cursor.iter.next() { match cursor.iter.next() {
None => None, None => None,
Some((o_index, op)) => { Some((o_index, op)) => {
cursor.cur_op_index = o_index; cursor.op_index = o_index;
Some(op) Some(op)
}, },
} }
@ -154,7 +157,7 @@ pub struct OpMetric {}
impl Metric for OpMetric { impl Metric for OpMetric {
fn seek(cursor: &mut Cursor, index: usize) -> SeekResult { 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 seek_cursor = Cursor::new(cursor.delta, cursor.origin_iv);
let mut offset = 0; let mut offset = 0;
while let Some((_, op)) = seek_cursor.iter.next() { while let Some((_, op)) = seek_cursor.iter.next() {
@ -172,7 +175,7 @@ pub struct CharMetric {}
impl Metric for CharMetric { impl Metric for CharMetric {
fn seek(cursor: &mut Cursor, index: usize) -> SeekResult { 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)); let _ = cursor.last_op_before_index(Some(index));
Ok(()) Ok(())