mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
preserve line format on merge && add merge test
This commit is contained in:
parent
4d3eabb761
commit
8272e2e8f6
@ -53,7 +53,7 @@ class Rules {
|
||||
const PreserveInlineStylesRule(),
|
||||
const CatchAllInsertRule(),
|
||||
// const EnsureEmbedLineRule(),
|
||||
// const PreserveLineStyleOnMergeRule(),
|
||||
const PreserveLineStyleOnMergeRule(),
|
||||
const CatchAllDeleteRule(),
|
||||
]);
|
||||
|
||||
|
@ -3,9 +3,9 @@ use crate::{
|
||||
core::{Delta, DeltaBuilder, Interval},
|
||||
};
|
||||
|
||||
pub struct DefaultDeleteExt {}
|
||||
impl DeleteExt for DefaultDeleteExt {
|
||||
fn ext_name(&self) -> &str { "DeleteExt" }
|
||||
pub struct DefaultDelete {}
|
||||
impl DeleteExt for DefaultDelete {
|
||||
fn ext_name(&self) -> &str { "DefaultDelete" }
|
||||
|
||||
fn apply(&self, _delta: &Delta, interval: Interval) -> Option<Delta> {
|
||||
Some(
|
||||
|
@ -1,3 +1,5 @@
|
||||
mod default_delete;
|
||||
mod preserve_line_format_merge;
|
||||
|
||||
pub use default_delete::*;
|
||||
pub use preserve_line_format_merge::*;
|
||||
|
@ -0,0 +1,59 @@
|
||||
use crate::{
|
||||
client::{extensions::DeleteExt, util::is_newline},
|
||||
core::{Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, Operation, NEW_LINE},
|
||||
};
|
||||
|
||||
pub struct PreserveLineFormatOnMerge {}
|
||||
impl DeleteExt for PreserveLineFormatOnMerge {
|
||||
fn ext_name(&self) -> &str { "PreserveLineFormatOnMerge" }
|
||||
|
||||
fn apply(&self, delta: &Delta, interval: Interval) -> Option<Delta> {
|
||||
if interval.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// seek to the interval start pos. e.g. You backspace enter pos
|
||||
let mut iter = DeltaIter::from_offset(delta, interval.start);
|
||||
|
||||
// op will be the "\n"
|
||||
let newline_op = iter.next_op_with_len(1)?;
|
||||
if !is_newline(newline_op.get_data()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
iter.seek::<CharMetric>(interval.size() - 1);
|
||||
let mut new_delta = DeltaBuilder::new()
|
||||
.retain(interval.start)
|
||||
.delete(interval.size())
|
||||
.build();
|
||||
|
||||
while iter.has_next() {
|
||||
match iter.next() {
|
||||
None => log::error!("op must be not None when has_next() return true"),
|
||||
Some(op) => {
|
||||
//
|
||||
match op.get_data().find(NEW_LINE) {
|
||||
None => {
|
||||
new_delta.retain(op.len(), Attributes::empty());
|
||||
continue;
|
||||
},
|
||||
Some(line_break) => {
|
||||
let mut attributes = op.get_attributes();
|
||||
attributes.mark_all_as_removed_except(None);
|
||||
|
||||
if newline_op.has_attribute() {
|
||||
attributes.extend(newline_op.get_attributes());
|
||||
}
|
||||
|
||||
new_delta.retain(line_break, Attributes::empty());
|
||||
new_delta.retain(1, attributes);
|
||||
break;
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Some(new_delta)
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
client::extensions::FormatExt,
|
||||
core::{Attribute, AttributeKey, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval},
|
||||
core::{Attribute, AttributeKey, Delta, DeltaBuilder, DeltaIter, Interval},
|
||||
};
|
||||
|
||||
pub struct FormatLinkAtCaretPositionExt {}
|
||||
@ -14,7 +14,7 @@ impl FormatExt for FormatLinkAtCaretPositionExt {
|
||||
}
|
||||
|
||||
let mut iter = DeltaIter::from_offset(delta, interval.start);
|
||||
let (before, after) = (iter.last_op_before_index(interval.size()), iter.next_op());
|
||||
let (before, after) = (iter.next_op_with_len(interval.size()), iter.next_op());
|
||||
let mut start = interval.end;
|
||||
let mut retain = 0;
|
||||
|
||||
|
@ -3,16 +3,7 @@ use crate::{
|
||||
extensions::{format::helper::line_break, FormatExt},
|
||||
util::find_newline,
|
||||
},
|
||||
core::{
|
||||
Attribute,
|
||||
AttributeScope,
|
||||
Attributes,
|
||||
CharMetric,
|
||||
Delta,
|
||||
DeltaBuilder,
|
||||
DeltaIter,
|
||||
Interval,
|
||||
},
|
||||
core::{Attribute, AttributeScope, Attributes, Delta, DeltaBuilder, DeltaIter, Interval},
|
||||
};
|
||||
|
||||
pub struct ResolveBlockFormat {}
|
||||
@ -29,7 +20,7 @@ impl FormatExt for ResolveBlockFormat {
|
||||
let mut start = 0;
|
||||
let end = interval.size();
|
||||
while start < end && iter.has_next() {
|
||||
let next_op = iter.last_op_before_index(end - start).unwrap();
|
||||
let next_op = iter.next_op_with_len(end - start).unwrap();
|
||||
match find_newline(next_op.get_data()) {
|
||||
None => new_delta.retain(next_op.len(), Attributes::empty()),
|
||||
Some(_) => {
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
extensions::{format::helper::line_break, FormatExt},
|
||||
util::find_newline,
|
||||
},
|
||||
core::{Attribute, AttributeScope, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval},
|
||||
core::{Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval},
|
||||
};
|
||||
|
||||
pub struct ResolveInlineFormat {}
|
||||
@ -20,7 +20,7 @@ impl FormatExt for ResolveInlineFormat {
|
||||
let end = interval.size();
|
||||
|
||||
while start < end && iter.has_next() {
|
||||
let next_op = iter.last_op_before_index(end - start).unwrap();
|
||||
let next_op = iter.next_op_with_len(end - start).unwrap();
|
||||
match find_newline(next_op.get_data()) {
|
||||
None => new_delta.retain(next_op.len(), attribute.clone().into()),
|
||||
Some(_) => {
|
||||
|
@ -1,15 +1,6 @@
|
||||
use crate::{
|
||||
client::{extensions::InsertExt, util::is_newline},
|
||||
core::{
|
||||
AttributeKey,
|
||||
AttributeValue,
|
||||
Attributes,
|
||||
CharMetric,
|
||||
Delta,
|
||||
DeltaBuilder,
|
||||
DeltaIter,
|
||||
Operation,
|
||||
},
|
||||
core::{AttributeKey, Delta, DeltaBuilder, DeltaIter, Operation},
|
||||
};
|
||||
|
||||
use crate::core::{attributes_except_header, is_empty_line_at_index};
|
||||
@ -42,7 +33,7 @@ impl InsertExt for AutoExitBlock {
|
||||
return None;
|
||||
}
|
||||
|
||||
match iter.first_newline_op() {
|
||||
match iter.next_op_with_newline() {
|
||||
None => {},
|
||||
Some((newline_op, _)) => {
|
||||
let newline_attributes = attributes_except_header(&newline_op);
|
||||
@ -52,7 +43,7 @@ impl InsertExt for AutoExitBlock {
|
||||
},
|
||||
}
|
||||
|
||||
attributes.mark_as_removed_except(&AttributeKey::Header);
|
||||
attributes.mark_all_as_removed_except(Some(AttributeKey::Header));
|
||||
|
||||
Some(
|
||||
DeltaBuilder::new()
|
||||
|
@ -13,7 +13,7 @@ impl InsertExt for AutoFormatExt {
|
||||
return None;
|
||||
}
|
||||
let mut iter = DeltaIter::new(delta);
|
||||
if let Some(prev) = iter.last_op_before_index(index) {
|
||||
if let Some(prev) = iter.next_op_with_len(index) {
|
||||
match AutoFormat::parse(prev.get_data()) {
|
||||
None => {},
|
||||
Some(formatter) => {
|
||||
@ -50,7 +50,7 @@ impl InsertExt for AutoFormatExt {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::core::{Attribute, AttributeBuilder, Attributes, DeltaBuilder, Operation};
|
||||
use crate::core::{AttributeBuilder, Attributes, DeltaBuilder, Operation};
|
||||
use bytecount::num_chars;
|
||||
use std::cmp::min;
|
||||
use url::Url;
|
||||
|
@ -2,7 +2,6 @@ use crate::{
|
||||
client::{extensions::InsertExt, util::is_newline},
|
||||
core::{
|
||||
attributes_except_header,
|
||||
AttributeBuilder,
|
||||
AttributeKey,
|
||||
Attributes,
|
||||
Delta,
|
||||
@ -23,7 +22,7 @@ impl InsertExt for PreserveBlockFormatOnInsert {
|
||||
}
|
||||
|
||||
let mut iter = DeltaIter::from_offset(delta, index);
|
||||
match iter.first_newline_op() {
|
||||
match iter.next_op_with_newline() {
|
||||
None => {},
|
||||
Some((newline_op, offset)) => {
|
||||
let newline_attributes = newline_op.get_attributes();
|
||||
|
@ -3,16 +3,7 @@ use crate::{
|
||||
extensions::InsertExt,
|
||||
util::{contain_newline, is_newline},
|
||||
},
|
||||
core::{
|
||||
AttributeKey,
|
||||
Attributes,
|
||||
Delta,
|
||||
DeltaBuilder,
|
||||
DeltaIter,
|
||||
OpNewline,
|
||||
Operation,
|
||||
NEW_LINE,
|
||||
},
|
||||
core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, OpNewline, NEW_LINE},
|
||||
};
|
||||
|
||||
pub struct PreserveInlineFormat {}
|
||||
@ -25,7 +16,7 @@ impl InsertExt for PreserveInlineFormat {
|
||||
}
|
||||
|
||||
let mut iter = DeltaIter::new(delta);
|
||||
let prev = iter.last_op_before_index(index)?;
|
||||
let prev = iter.next_op_with_len(index)?;
|
||||
if OpNewline::parse(&prev).is_contain() {
|
||||
return None;
|
||||
}
|
||||
@ -69,7 +60,7 @@ impl InsertExt for PreserveLineFormatOnSplit {
|
||||
}
|
||||
|
||||
let mut iter = DeltaIter::new(delta);
|
||||
let prev = iter.last_op_before_index(index)?;
|
||||
let prev = iter.next_op_with_len(index)?;
|
||||
if OpNewline::parse(&prev).is_end() {
|
||||
return None;
|
||||
}
|
||||
@ -89,7 +80,7 @@ impl InsertExt for PreserveLineFormatOnSplit {
|
||||
return Some(new_delta);
|
||||
}
|
||||
|
||||
match iter.first_newline_op() {
|
||||
match iter.next_op_with_newline() {
|
||||
None => {},
|
||||
Some((newline_op, _)) => {
|
||||
new_delta.insert(NEW_LINE, newline_op.get_attributes());
|
||||
|
@ -13,7 +13,8 @@ impl InsertExt for ResetLineFormatOnNewLine {
|
||||
}
|
||||
|
||||
let mut iter = DeltaIter::new(delta);
|
||||
let next_op = iter.first_op_after_index(index)?;
|
||||
iter.seek::<CharMetric>(index);
|
||||
let next_op = iter.next_op()?;
|
||||
if !next_op.get_data().starts_with(NEW_LINE) {
|
||||
return None;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::core::{Operation, NEW_LINE, WHITESPACE};
|
||||
use crate::core::{NEW_LINE, WHITESPACE};
|
||||
|
||||
#[inline]
|
||||
pub fn find_newline(s: &str) -> Option<usize> {
|
||||
@ -8,9 +8,6 @@ pub fn find_newline(s: &str) -> Option<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_op_contains_newline(op: &Operation) -> bool { contain_newline(op.get_data()) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_newline(s: &str) -> bool { s == NEW_LINE }
|
||||
|
||||
|
@ -102,7 +102,7 @@ fn construct_format_exts() -> Vec<FormatExtension> {
|
||||
|
||||
fn construct_delete_exts() -> Vec<DeleteExtension> {
|
||||
vec![
|
||||
//
|
||||
Box::new(DefaultDeleteExt {}),
|
||||
Box::new(PreserveLineFormatOnMerge {}),
|
||||
Box::new(DefaultDelete {}),
|
||||
]
|
||||
}
|
||||
|
@ -42,13 +42,21 @@ impl Attributes {
|
||||
self.inner.insert(key.clone(), value);
|
||||
}
|
||||
|
||||
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 mark_all_as_removed_except(&mut self, attribute: Option<AttributeKey>) {
|
||||
match attribute {
|
||||
None => {
|
||||
self.inner
|
||||
.iter_mut()
|
||||
.for_each(|(k, v)| v.0 = REMOVE_FLAG.into());
|
||||
},
|
||||
Some(attribute) => {
|
||||
self.inner.iter_mut().for_each(|(k, v)| {
|
||||
if k != &attribute {
|
||||
v.0 = REMOVE_FLAG.into();
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: AttributeKey) { self.inner.retain(|k, _| k != &key); }
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
use std::{cmp::min, iter::Enumerate, slice::Iter};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cursor<'a> {
|
||||
pub struct OpCursor<'a> {
|
||||
pub(crate) delta: &'a Delta,
|
||||
pub(crate) origin_iv: Interval,
|
||||
pub(crate) consume_iv: Interval,
|
||||
@ -15,8 +15,8 @@ pub struct Cursor<'a> {
|
||||
next_op: Option<Operation>,
|
||||
}
|
||||
|
||||
impl<'a> Cursor<'a> {
|
||||
pub fn new(delta: &'a Delta, interval: Interval) -> Cursor<'a> {
|
||||
impl<'a> OpCursor<'a> {
|
||||
pub fn new(delta: &'a Delta, interval: Interval) -> OpCursor<'a> {
|
||||
// debug_assert!(interval.start <= delta.target_len);
|
||||
let mut cursor = Self {
|
||||
delta,
|
||||
@ -32,51 +32,56 @@ impl<'a> Cursor<'a> {
|
||||
}
|
||||
|
||||
// get the next operation interval
|
||||
pub fn next_iv(&self) -> Interval { self.next_iv_before(None) }
|
||||
pub fn next_iv(&self) -> Interval { self.next_iv_before(None).unwrap_or(Interval::new(0, 0)) }
|
||||
|
||||
pub fn next_op(&mut self) -> Option<Operation> { self.last_op_before_index(None) }
|
||||
pub fn next(&mut self) -> Option<Operation> { self.next_with_len(None) }
|
||||
|
||||
// get the last operation before the index
|
||||
pub fn last_op_before_index(&mut self, index: Option<usize>) -> Option<Operation> {
|
||||
// get the last operation before the end.
|
||||
// checkout the delta_next_op_with_len_cross_op_return_last test for more detail
|
||||
pub fn next_with_len(&mut self, force_end: Option<usize>) -> Option<Operation> {
|
||||
let mut find_op = None;
|
||||
let holder = self.next_op.clone();
|
||||
let mut next_op = holder.as_ref();
|
||||
|
||||
if next_op.is_none() {
|
||||
next_op = find_next_op(self);
|
||||
next_op = find_next(self);
|
||||
}
|
||||
|
||||
let mut pos = 0;
|
||||
let mut consume_len = 0;
|
||||
while find_op.is_none() && next_op.is_some() {
|
||||
let op = next_op.take().unwrap();
|
||||
let interval = self.next_iv_before(index);
|
||||
let interval = self
|
||||
.next_iv_before(force_end)
|
||||
.unwrap_or(Interval::new(0, 0));
|
||||
|
||||
// cache the op if the interval is empty. e.g. last_op_before(Some(0))
|
||||
if interval.is_empty() {
|
||||
self.next_op = Some(op.clone());
|
||||
break;
|
||||
}
|
||||
|
||||
find_op = op.shrink(interval);
|
||||
self.next_op = None;
|
||||
|
||||
let suffix = Interval::new(0, op.len()).suffix(interval);
|
||||
if !suffix.is_empty() {
|
||||
if suffix.is_empty() {
|
||||
self.next_op = None;
|
||||
} else {
|
||||
self.next_op = op.shrink(suffix);
|
||||
}
|
||||
|
||||
pos += interval.end;
|
||||
consume_len += interval.end;
|
||||
self.consume_count += interval.end;
|
||||
self.consume_iv.start = self.consume_count;
|
||||
|
||||
// continue to find the op in next iteration
|
||||
if find_op.is_none() {
|
||||
next_op = find_next_op(self);
|
||||
next_op = find_next(self);
|
||||
}
|
||||
}
|
||||
|
||||
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 end = index.unwrap();
|
||||
if end > pos && self.has_next() {
|
||||
return self.last_op_before_index(Some(end - pos));
|
||||
if find_op.is_some() && force_end.is_some() {
|
||||
// try to find the next op before the index if consume_len less than index
|
||||
let end = force_end.unwrap();
|
||||
if end > consume_len && self.has_next() {
|
||||
return self.next_with_len(Some(end - consume_len));
|
||||
}
|
||||
}
|
||||
return find_op;
|
||||
@ -104,6 +109,19 @@ impl<'a> Cursor<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn next_iv_before(&self, force_end: Option<usize>) -> Option<Interval> {
|
||||
let op = self.next_iter_op()?;
|
||||
let start = self.consume_count;
|
||||
let end = match force_end {
|
||||
None => self.consume_count + op.len(),
|
||||
Some(index) => self.consume_count + min(index, op.len()),
|
||||
};
|
||||
|
||||
let intersect = Interval::new(start, end).intersect(self.consume_iv);
|
||||
let interval = intersect.translate_neg(start);
|
||||
Some(interval)
|
||||
}
|
||||
|
||||
pub fn next_iter_op(&self) -> Option<&Operation> {
|
||||
let mut next_op = self.next_op.as_ref();
|
||||
if next_op.is_none() {
|
||||
@ -118,27 +136,9 @@ impl<'a> Cursor<'a> {
|
||||
}
|
||||
next_op
|
||||
}
|
||||
|
||||
fn next_iv_before(&self, index: Option<usize>) -> Interval {
|
||||
let next_op = self.next_iter_op();
|
||||
if next_op.is_none() {
|
||||
return Interval::new(0, 0);
|
||||
}
|
||||
|
||||
let op = next_op.unwrap();
|
||||
let start = self.consume_count;
|
||||
let end = match index {
|
||||
None => self.consume_count + op.len(),
|
||||
Some(index) => self.consume_count + min(index, op.len()),
|
||||
};
|
||||
|
||||
let intersect = Interval::new(start, end).intersect(self.consume_iv);
|
||||
let interval = intersect.translate_neg(start);
|
||||
interval
|
||||
}
|
||||
}
|
||||
|
||||
fn find_next_op<'a>(cursor: &mut Cursor<'a>) -> Option<&'a Operation> {
|
||||
fn find_next<'a>(cursor: &mut OpCursor<'a>) -> Option<&'a Operation> {
|
||||
match cursor.iter.next() {
|
||||
None => None,
|
||||
Some((o_index, op)) => {
|
||||
@ -150,15 +150,15 @@ fn find_next_op<'a>(cursor: &mut Cursor<'a>) -> Option<&'a Operation> {
|
||||
|
||||
type SeekResult = Result<(), OTError>;
|
||||
pub trait Metric {
|
||||
fn seek(cursor: &mut Cursor, index: usize) -> SeekResult;
|
||||
fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult;
|
||||
}
|
||||
|
||||
pub struct OpMetric {}
|
||||
|
||||
impl Metric for OpMetric {
|
||||
fn seek(cursor: &mut Cursor, index: usize) -> SeekResult {
|
||||
fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult {
|
||||
let _ = check_bound(cursor.op_index, index)?;
|
||||
let mut seek_cursor = Cursor::new(cursor.delta, cursor.origin_iv);
|
||||
let mut seek_cursor = OpCursor::new(cursor.delta, cursor.origin_iv);
|
||||
let mut offset = 0;
|
||||
while let Some((_, op)) = seek_cursor.iter.next() {
|
||||
offset += op.len();
|
||||
@ -174,9 +174,9 @@ impl Metric for OpMetric {
|
||||
pub struct CharMetric {}
|
||||
|
||||
impl Metric for CharMetric {
|
||||
fn seek(cursor: &mut Cursor, index: usize) -> SeekResult {
|
||||
fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult {
|
||||
let _ = check_bound(cursor.consume_count, index)?;
|
||||
let _ = cursor.last_op_before_index(Some(index));
|
||||
let _ = cursor.next_with_len(Some(index));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -168,11 +168,11 @@ impl Delta {
|
||||
);
|
||||
|
||||
let op = iter
|
||||
.last_op_before_index(length)
|
||||
.next_op_with_len(length)
|
||||
.unwrap_or(OpBuilder::retain(length).build());
|
||||
|
||||
let other_op = other_iter
|
||||
.last_op_before_index(length)
|
||||
.next_op_with_len(length)
|
||||
.unwrap_or(OpBuilder::retain(length).build());
|
||||
|
||||
debug_assert_eq!(op.len(), other_op.len());
|
||||
|
@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut};
|
||||
pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize;
|
||||
|
||||
pub struct DeltaIter<'a> {
|
||||
cursor: Cursor<'a>,
|
||||
cursor: OpCursor<'a>,
|
||||
}
|
||||
|
||||
impl<'a> DeltaIter<'a> {
|
||||
@ -22,7 +22,7 @@ impl<'a> DeltaIter<'a> {
|
||||
}
|
||||
|
||||
pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self {
|
||||
let cursor = Cursor::new(delta, interval);
|
||||
let cursor = OpCursor::new(delta, interval);
|
||||
Self { cursor }
|
||||
}
|
||||
|
||||
@ -37,8 +37,14 @@ impl<'a> DeltaIter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_op(&mut self) -> Option<Operation> { self.cursor.next() }
|
||||
|
||||
pub fn next_op_with_len(&mut self, len: usize) -> Option<Operation> {
|
||||
self.cursor.next_with_len(Some(len))
|
||||
}
|
||||
|
||||
// find next op contains NEW_LINE
|
||||
pub fn first_newline_op(&mut self) -> Option<(Operation, usize)> {
|
||||
pub fn next_op_with_newline(&mut self) -> Option<(Operation, usize)> {
|
||||
let mut offset = 0;
|
||||
while self.has_next() {
|
||||
if let Some(op) = self.next_op() {
|
||||
@ -52,17 +58,6 @@ impl<'a> DeltaIter<'a> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn next_op(&mut self) -> Option<Operation> { self.cursor.next_op() }
|
||||
|
||||
pub fn last_op_before_index(&mut self, index: usize) -> Option<Operation> {
|
||||
self.cursor.last_op_before_index(Some(index))
|
||||
}
|
||||
|
||||
pub fn first_op_after_index(&mut self, index: usize) -> Option<Operation> {
|
||||
self.seek::<CharMetric>(index);
|
||||
self.next_op()
|
||||
}
|
||||
|
||||
pub fn seek<M: Metric>(&mut self, index: usize) {
|
||||
match M::seek(&mut self.cursor, index) {
|
||||
Ok(_) => {},
|
||||
@ -101,7 +96,7 @@ impl<'a> Iterator for DeltaIter<'a> {
|
||||
|
||||
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());
|
||||
let (prev, next) = (iter.next_op_with_len(index), iter.next_op());
|
||||
if prev.is_none() {
|
||||
return true;
|
||||
}
|
||||
@ -151,7 +146,7 @@ impl<'a> DerefMut for AttributesIter<'a> {
|
||||
impl<'a> Iterator for AttributesIter<'a> {
|
||||
type Item = (usize, Attributes);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next_op = self.delta_iter.next();
|
||||
let next_op = self.delta_iter.next_op();
|
||||
if next_op.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
@ -762,3 +762,23 @@ fn attributes_preserve_block_when_insert_newline_inside() {
|
||||
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attributes_preserve_line_format_on_merge() {
|
||||
let ops = vec![
|
||||
Insert(0, "123456", 0),
|
||||
Header(0, Interval::new(0, 6), 1, true),
|
||||
Insert(0, NEW_LINE, 3),
|
||||
AssertOpsJson(
|
||||
0,
|
||||
r#"[{"insert":"123"},{"insert":"\n","attributes":{"header":"1"}},{"insert":"456"},{"insert":"\n","attributes":{"header":"1"}}]"#,
|
||||
),
|
||||
Delete(0, Interval::new(3, 4)),
|
||||
AssertOpsJson(
|
||||
0,
|
||||
r#"[{"insert":"123456"},{"insert":"\n","attributes":{"header":"1"}}]"#,
|
||||
),
|
||||
];
|
||||
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ fn delta_get_ops_in_interval_7() {
|
||||
|
||||
let mut iter_2 = DeltaIter::new(&delta);
|
||||
assert_eq!(
|
||||
iter_2.last_op_before_index(2).unwrap(),
|
||||
iter_2.next_op_with_len(2).unwrap(),
|
||||
OpBuilder::insert("12").build()
|
||||
);
|
||||
assert_eq!(iter_2.next_op().unwrap(), OpBuilder::insert("345").build());
|
||||
@ -180,7 +180,7 @@ fn delta_seek_2() {
|
||||
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
assert_eq!(
|
||||
iter.last_op_before_index(1).unwrap(),
|
||||
iter.next_op_with_len(1).unwrap(),
|
||||
OpBuilder::insert("1").build()
|
||||
);
|
||||
}
|
||||
@ -192,21 +192,21 @@ fn delta_seek_3() {
|
||||
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
assert_eq!(
|
||||
iter.last_op_before_index(2).unwrap(),
|
||||
iter.next_op_with_len(2).unwrap(),
|
||||
OpBuilder::insert("12").build()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
iter.last_op_before_index(2).unwrap(),
|
||||
iter.next_op_with_len(2).unwrap(),
|
||||
OpBuilder::insert("34").build()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
iter.last_op_before_index(2).unwrap(),
|
||||
iter.next_op_with_len(2).unwrap(),
|
||||
OpBuilder::insert("5").build()
|
||||
);
|
||||
|
||||
assert_eq!(iter.last_op_before_index(1), None);
|
||||
assert_eq!(iter.next_op_with_len(1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -217,7 +217,7 @@ fn delta_seek_4() {
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
iter.seek::<CharMetric>(3);
|
||||
assert_eq!(
|
||||
iter.last_op_before_index(2).unwrap(),
|
||||
iter.next_op_with_len(2).unwrap(),
|
||||
OpBuilder::insert("45").build()
|
||||
);
|
||||
}
|
||||
@ -237,7 +237,7 @@ fn delta_seek_5() {
|
||||
iter.seek::<CharMetric>(0);
|
||||
|
||||
assert_eq!(
|
||||
iter.last_op_before_index(4).unwrap(),
|
||||
iter.next_op_with_len(4).unwrap(),
|
||||
OpBuilder::insert("1234").attributes(attributes).build(),
|
||||
);
|
||||
}
|
||||
@ -251,7 +251,7 @@ fn delta_next_op_len_test() {
|
||||
iter.seek::<CharMetric>(3);
|
||||
assert_eq!(iter.next_op_len().unwrap(), 2);
|
||||
assert_eq!(
|
||||
iter.last_op_before_index(1).unwrap(),
|
||||
iter.next_op_with_len(1).unwrap(),
|
||||
OpBuilder::insert("4").build()
|
||||
);
|
||||
assert_eq!(iter.next_op_len().unwrap(), 1);
|
||||
@ -266,22 +266,37 @@ fn delta_next_op_len_test2() {
|
||||
|
||||
assert_eq!(iter.next_op_len().unwrap(), 5);
|
||||
assert_eq!(
|
||||
iter.last_op_before_index(5).unwrap(),
|
||||
iter.next_op_with_len(5).unwrap(),
|
||||
OpBuilder::insert("12345").build()
|
||||
);
|
||||
assert_eq!(iter.next_op_len(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_next_op_len_test3() {
|
||||
fn delta_next_op_with_len_zero() {
|
||||
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_with_len(0), None,);
|
||||
assert_eq!(iter.next_op_len().unwrap(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_next_op_with_len_cross_op_return_last() {
|
||||
let mut delta = Delta::default();
|
||||
delta.add(OpBuilder::insert("12345").build());
|
||||
delta.add(OpBuilder::retain(1).build());
|
||||
delta.add(OpBuilder::insert("678").build());
|
||||
|
||||
let mut iter = DeltaIter::new(&delta);
|
||||
iter.seek::<CharMetric>(4);
|
||||
assert_eq!(iter.next_op_len().unwrap(), 1);
|
||||
assert_eq!(
|
||||
iter.next_op_with_len(2).unwrap(),
|
||||
OpBuilder::retain(1).build()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lengths() {
|
||||
let mut delta = Delta::default();
|
||||
|
Loading…
Reference in New Issue
Block a user