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 AutoExitBlockRule(),
const PreserveBlockStyleOnInsertRule(),
// const PreserveLineStyleOnSplitRule(),
const PreserveLineStyleOnSplitRule(),
const ResetLineFormatOnNewLineRule(),
const AutoFormatLinksRule(),
const PreserveInlineStylesRule(),

View File

@ -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::<FormatLinkAtCaretPositionExt>() }
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
if attribute.key != AttributeKey::Link || interval.size() != 0 {

View File

@ -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::<ResolveBlockFormat>() }
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
if attribute.scope != AttributeScope::Block {

View File

@ -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::<ResolveInlineFormat>() }
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
if attribute.scope != AttributeScope::Inline {

View File

@ -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::<AutoExitBlock>() }
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

View File

@ -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::<AutoFormatExt>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
// enter whitespace to trigger auto format

View File

@ -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::<DefaultInsertAttribute>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
let iter = DeltaIter::new(delta);

View File

@ -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<Delta> {
None
}
}
pub struct InsertEmbedsExt {}
impl InsertExt for InsertEmbedsExt {
fn ext_name(&self) -> &str { "InsertEmbedsExt" }

View File

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

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},
};
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::<ResetLineFormatOnNewLine>() }
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
if !is_newline(text) {

View File

@ -82,21 +82,21 @@ fn construct_insert_exts() -> Vec<InsertExtension> {
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<FormatExtension> {
vec![
Box::new(FormatLinkAtCaretPositionExt {}),
Box::new(ResolveBlockFormatExt {}),
Box::new(ResolveInlineFormatExt {}),
Box::new(ResolveBlockFormat {}),
Box::new(ResolveInlineFormat {}),
]
}

View File

@ -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<Iter<'a, Operation>>,
next_op: Option<Operation>,
}
@ -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<usize>) -> Option<Operation> {
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(())