mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
preserve line format on split
This commit is contained in:
parent
7c4b2d74a5
commit
4d3eabb761
@ -47,7 +47,7 @@ class Rules {
|
||||
// const ForceNewlineForInsertsAroundEmbedRule(),
|
||||
const AutoExitBlockRule(),
|
||||
const PreserveBlockStyleOnInsertRule(),
|
||||
// const PreserveLineStyleOnSplitRule(),
|
||||
const PreserveLineStyleOnSplitRule(),
|
||||
const ResetLineFormatOnNewLineRule(),
|
||||
const AutoFormatLinksRule(),
|
||||
const PreserveInlineStylesRule(),
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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" }
|
||||
|
@ -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());
|
@ -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)
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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 {}),
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
|
Loading…
Reference in New Issue
Block a user