mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
cursor: next op with length
This commit is contained in:
parent
bc7da582a3
commit
93cf9712dc
@ -40,21 +40,21 @@ class Rules {
|
|||||||
final List<Rule> _rules;
|
final List<Rule> _rules;
|
||||||
|
|
||||||
static final Rules _instance = Rules([
|
static final Rules _instance = Rules([
|
||||||
const FormatLinkAtCaretPositionRule(),
|
// const FormatLinkAtCaretPositionRule(),
|
||||||
const ResolveLineFormatRule(),
|
// const ResolveLineFormatRule(),
|
||||||
const ResolveInlineFormatRule(),
|
const ResolveInlineFormatRule(),
|
||||||
const InsertEmbedsRule(),
|
// const InsertEmbedsRule(),
|
||||||
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(),
|
||||||
const CatchAllInsertRule(),
|
const CatchAllInsertRule(),
|
||||||
const EnsureEmbedLineRule(),
|
// const EnsureEmbedLineRule(),
|
||||||
const PreserveLineStyleOnMergeRule(),
|
// const PreserveLineStyleOnMergeRule(),
|
||||||
const CatchAllDeleteRule(),
|
// const CatchAllDeleteRule(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
static Rules getInstance() => _instance;
|
static Rules getInstance() => _instance;
|
||||||
|
@ -46,9 +46,12 @@ impl Document {
|
|||||||
|
|
||||||
pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> {
|
pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> {
|
||||||
log::debug!("format with {} at {}", attribute, interval);
|
log::debug!("format with {} at {}", attribute, interval);
|
||||||
|
// let format_delta = self
|
||||||
// let format_delta = self.view.format(&self.delta, attribute, interval)?;
|
// .view
|
||||||
|
// .format(&self.delta, attribute.clone(), interval)
|
||||||
|
// .unwrap();
|
||||||
|
// let a = self.delta.compose(&format_delta).unwrap();
|
||||||
|
// println!("{:?}", a);
|
||||||
self.update_with_attribute(attribute, interval)
|
self.update_with_attribute(attribute, interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +168,10 @@ impl Document {
|
|||||||
fn next_rev_id(&self) -> RevId { RevId(self.rev_id_counter) }
|
fn next_rev_id(&self) -> RevId { RevId(self.rev_id_counter) }
|
||||||
|
|
||||||
fn record_change(&mut self, delta: &Delta) -> Result<Delta, OTError> {
|
fn record_change(&mut self, delta: &Delta) -> Result<Delta, OTError> {
|
||||||
let (composed_delta, mut undo_delta) = self.invert_change(&delta)?;
|
log::debug!("👉invert change {}", delta);
|
||||||
|
let composed_delta = self.delta.compose(delta)?;
|
||||||
|
let mut undo_delta = delta.invert(&self.delta);
|
||||||
|
|
||||||
self.rev_id_counter += 1;
|
self.rev_id_counter += 1;
|
||||||
|
|
||||||
let now = chrono::Utc::now().timestamp_millis() as usize;
|
let now = chrono::Utc::now().timestamp_millis() as usize;
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
client::view::FormatExt,
|
client::view::{FormatExt, NEW_LINE},
|
||||||
core::{Attribute, AttributeScope, Attributes, Delta, DeltaBuilder, DeltaIter, Interval},
|
core::{
|
||||||
|
Attribute,
|
||||||
|
AttributeScope,
|
||||||
|
Attributes,
|
||||||
|
CharMetric,
|
||||||
|
Delta,
|
||||||
|
DeltaBuilder,
|
||||||
|
DeltaIter,
|
||||||
|
Interval,
|
||||||
|
Operation,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct FormatLinkAtCaretPositionExt {}
|
pub struct FormatLinkAtCaretPositionExt {}
|
||||||
@ -8,7 +18,7 @@ pub struct FormatLinkAtCaretPositionExt {}
|
|||||||
impl FormatExt for FormatLinkAtCaretPositionExt {
|
impl FormatExt for FormatLinkAtCaretPositionExt {
|
||||||
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
|
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
|
||||||
let mut iter = DeltaIter::new(delta);
|
let mut iter = DeltaIter::new(delta);
|
||||||
iter.seek(interval.start);
|
iter.seek::<CharMetric>(interval.start);
|
||||||
let (before, after) = (iter.next(), iter.next());
|
let (before, after) = (iter.next(), iter.next());
|
||||||
let mut start = interval.start;
|
let mut start = interval.start;
|
||||||
let mut retain = 0;
|
let mut retain = 0;
|
||||||
@ -42,7 +52,6 @@ impl FormatExt for FormatLinkAtCaretPositionExt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ResolveLineFormatExt {}
|
pub struct ResolveLineFormatExt {}
|
||||||
|
|
||||||
impl FormatExt for ResolveLineFormatExt {
|
impl FormatExt for ResolveLineFormatExt {
|
||||||
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 {
|
||||||
@ -53,7 +62,7 @@ impl FormatExt for ResolveLineFormatExt {
|
|||||||
new_delta.retain(interval.start, Attributes::default());
|
new_delta.retain(interval.start, Attributes::default());
|
||||||
|
|
||||||
let mut iter = DeltaIter::new(delta);
|
let mut iter = DeltaIter::new(delta);
|
||||||
iter.seek(interval.start);
|
iter.seek::<CharMetric>(interval.start);
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -63,6 +72,54 @@ pub struct ResolveInlineFormatExt {}
|
|||||||
|
|
||||||
impl FormatExt for ResolveInlineFormatExt {
|
impl FormatExt for ResolveInlineFormatExt {
|
||||||
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
|
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
|
||||||
unimplemented!()
|
if attribute.scope != AttributeScope::Inline {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut new_delta = DeltaBuilder::new()
|
||||||
|
.retain(interval.start, Attributes::default())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let mut iter = DeltaIter::new(delta);
|
||||||
|
iter.seek::<CharMetric>(interval.start);
|
||||||
|
|
||||||
|
let mut cur = 0;
|
||||||
|
let len = interval.size();
|
||||||
|
|
||||||
|
while cur < len && iter.has_next() {
|
||||||
|
let some_op = iter.next_op_with_length(len - cur);
|
||||||
|
if some_op.is_none() {
|
||||||
|
return Some(new_delta);
|
||||||
|
}
|
||||||
|
let op = some_op.unwrap();
|
||||||
|
if let Operation::Insert(insert) = &op {
|
||||||
|
let mut s = insert.s.as_str();
|
||||||
|
match s.find(NEW_LINE) {
|
||||||
|
None => {
|
||||||
|
new_delta.retain(op.length(), attribute.clone().into());
|
||||||
|
},
|
||||||
|
Some(mut line_break) => {
|
||||||
|
let mut pos = 0;
|
||||||
|
let mut some_line_break = Some(line_break);
|
||||||
|
while some_line_break.is_some() {
|
||||||
|
let line_break = some_line_break.unwrap();
|
||||||
|
new_delta.retain(line_break - pos, attribute.clone().into());
|
||||||
|
new_delta.retain(1, Attributes::default());
|
||||||
|
pos = line_break + 1;
|
||||||
|
|
||||||
|
s = &s[pos..s.len()];
|
||||||
|
some_line_break = s.find(NEW_LINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if pos < op.length() {
|
||||||
|
new_delta.retain(op.length() - pos, attribute.clone().into());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur += op.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(new_delta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
client::view::InsertExt,
|
client::view::InsertExt,
|
||||||
core::{
|
core::{
|
||||||
attributes_at_index,
|
attributes_with_length,
|
||||||
AttributeKey,
|
AttributeKey,
|
||||||
Attributes,
|
Attributes,
|
||||||
|
CharMetric,
|
||||||
Delta,
|
Delta,
|
||||||
DeltaBuilder,
|
DeltaBuilder,
|
||||||
DeltaIter,
|
DeltaIter,
|
||||||
@ -13,18 +14,57 @@ use crate::{
|
|||||||
|
|
||||||
pub const NEW_LINE: &'static str = "\n";
|
pub const NEW_LINE: &'static str = "\n";
|
||||||
|
|
||||||
pub struct PreserveInlineStyleExt {}
|
pub struct PreserveBlockStyleOnInsertExt {}
|
||||||
impl PreserveInlineStyleExt {
|
impl InsertExt for PreserveBlockStyleOnInsertExt {
|
||||||
pub fn new() -> Self { Self {} }
|
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InsertExt for PreserveInlineStyleExt {
|
pub struct PreserveLineStyleOnSplitExt {}
|
||||||
|
impl InsertExt for PreserveLineStyleOnSplitExt {
|
||||||
|
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AutoExitBlockExt {}
|
||||||
|
|
||||||
|
impl InsertExt for AutoExitBlockExt {
|
||||||
|
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InsertEmbedsExt {}
|
||||||
|
impl InsertExt for InsertEmbedsExt {
|
||||||
|
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ForceNewlineForInsertsAroundEmbedExt {}
|
||||||
|
impl InsertExt for ForceNewlineForInsertsAroundEmbedExt {
|
||||||
|
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AutoFormatLinksExt {}
|
||||||
|
impl InsertExt for AutoFormatLinksExt {
|
||||||
|
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PreserveInlineStylesExt {}
|
||||||
|
impl InsertExt for PreserveInlineStylesExt {
|
||||||
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 text.ends_with(NEW_LINE) {
|
if text.ends_with(NEW_LINE) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let probe_index = if index > 1 { index - 1 } else { index };
|
||||||
let attributes = attributes_at_index(delta, index);
|
let attributes = attributes_with_length(delta, probe_index);
|
||||||
let delta = DeltaBuilder::new().insert(text, attributes).build();
|
let delta = DeltaBuilder::new().insert(text, attributes).build();
|
||||||
|
|
||||||
Some(delta)
|
Some(delta)
|
||||||
@ -32,11 +72,6 @@ impl InsertExt for PreserveInlineStyleExt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ResetLineFormatOnNewLineExt {}
|
pub struct ResetLineFormatOnNewLineExt {}
|
||||||
|
|
||||||
impl ResetLineFormatOnNewLineExt {
|
|
||||||
pub fn new() -> Self { Self {} }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InsertExt for ResetLineFormatOnNewLineExt {
|
impl InsertExt for ResetLineFormatOnNewLineExt {
|
||||||
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 text != NEW_LINE {
|
if text != NEW_LINE {
|
||||||
@ -44,7 +79,7 @@ impl InsertExt for ResetLineFormatOnNewLineExt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut iter = DeltaIter::new(delta);
|
let mut iter = DeltaIter::new(delta);
|
||||||
iter.seek(index);
|
iter.seek::<CharMetric>(index);
|
||||||
let maybe_next_op = iter.next();
|
let maybe_next_op = iter.next();
|
||||||
if maybe_next_op.is_none() {
|
if maybe_next_op.is_none() {
|
||||||
return None;
|
return None;
|
||||||
@ -71,3 +106,15 @@ impl InsertExt for ResetLineFormatOnNewLineExt {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct DefaultInsertExt {}
|
||||||
|
impl InsertExt for DefaultInsertExt {
|
||||||
|
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
|
||||||
|
Some(
|
||||||
|
DeltaBuilder::new()
|
||||||
|
.retain(index + replace_len, Attributes::default())
|
||||||
|
.insert(text, Attributes::default())
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -73,8 +73,15 @@ impl View {
|
|||||||
|
|
||||||
fn construct_insert_exts() -> Vec<InsertExtension> {
|
fn construct_insert_exts() -> Vec<InsertExtension> {
|
||||||
vec![
|
vec![
|
||||||
Box::new(PreserveInlineStyleExt::new()),
|
Box::new(InsertEmbedsExt {}),
|
||||||
Box::new(ResetLineFormatOnNewLineExt::new()),
|
Box::new(ForceNewlineForInsertsAroundEmbedExt {}),
|
||||||
|
Box::new(AutoExitBlockExt {}),
|
||||||
|
Box::new(PreserveBlockStyleOnInsertExt {}),
|
||||||
|
Box::new(PreserveLineStyleOnSplitExt {}),
|
||||||
|
Box::new(ResetLineFormatOnNewLineExt {}),
|
||||||
|
Box::new(AutoFormatLinksExt {}),
|
||||||
|
Box::new(PreserveInlineStylesExt {}),
|
||||||
|
Box::new(DefaultInsertExt {}),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,153 +4,127 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use std::{cmp::min, slice::Iter};
|
use std::{cmp::min, slice::Iter};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Cursor<'a> {
|
pub struct Cursor<'a> {
|
||||||
delta: &'a Delta,
|
pub(crate) delta: &'a Delta,
|
||||||
interval: Interval,
|
pub(crate) origin_iv: Interval,
|
||||||
iterator: Iter<'a, Operation>,
|
pub(crate) next_iv: Interval,
|
||||||
char_index: usize,
|
pub(crate) c_index: usize,
|
||||||
op_index: usize,
|
pub(crate) o_index: usize,
|
||||||
current_op: Option<&'a Operation>,
|
iter: Iter<'a, Operation>,
|
||||||
|
next_op: Option<Operation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Cursor<'a> {
|
impl<'a> Cursor<'a> {
|
||||||
pub fn new(delta: &'a Delta, interval: Interval) -> Cursor<'a> {
|
pub fn new(delta: &'a Delta, interval: Interval) -> Cursor<'a> {
|
||||||
let cursor = Self {
|
// debug_assert!(interval.start <= delta.target_len);
|
||||||
|
let mut cursor = Self {
|
||||||
delta,
|
delta,
|
||||||
interval,
|
origin_iv: interval,
|
||||||
iterator: delta.ops.iter(),
|
next_iv: interval,
|
||||||
char_index: 0,
|
c_index: 0,
|
||||||
op_index: 0,
|
o_index: 0,
|
||||||
current_op: None,
|
iter: delta.ops.iter(),
|
||||||
|
next_op: None,
|
||||||
};
|
};
|
||||||
|
cursor.descend(0);
|
||||||
cursor
|
cursor
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_op(&mut self) -> Option<Operation> {
|
fn descend(&mut self, index: usize) {
|
||||||
let mut next_op = self.current_op.take();
|
self.next_iv.start += index;
|
||||||
if next_op.is_none() {
|
if self.c_index >= self.next_iv.start {
|
||||||
next_op = self.iterator.next();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut find_op = None;
|
while let Some(op) = self.iter.next() {
|
||||||
while find_op.is_none() && next_op.is_some() {
|
self.o_index += 1;
|
||||||
self.op_index += 1;
|
let start = self.c_index;
|
||||||
|
let end = start + op.length();
|
||||||
let op = next_op.unwrap();
|
let intersect = Interval::new(start, end).intersect(self.next_iv);
|
||||||
if self.char_index < self.interval.start {
|
if intersect.is_empty() {
|
||||||
let intersect = Interval::new(self.char_index, self.char_index + op.length())
|
self.c_index += op.length();
|
||||||
.intersect(self.interval);
|
|
||||||
if intersect.is_empty() {
|
|
||||||
self.char_index += op.length();
|
|
||||||
} else {
|
|
||||||
if let Some(new_op) = op.shrink(intersect.translate_neg(self.char_index)) {
|
|
||||||
// shrink the op to fit the intersect range
|
|
||||||
// ┌──────────────┐
|
|
||||||
// │ 1 2 3 4 5 6 │
|
|
||||||
// └───────▲───▲──┘
|
|
||||||
// │ │
|
|
||||||
// [3, 5)
|
|
||||||
// op = "45"
|
|
||||||
find_op = Some(new_op);
|
|
||||||
}
|
|
||||||
self.char_index = intersect.end;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// the interval passed in the shrink function is base on the op not the delta.
|
self.next_op = Some(op.clone());
|
||||||
if let Some(new_op) = op.shrink(self.interval.translate_neg(self.char_index)) {
|
break;
|
||||||
find_op = Some(new_op);
|
}
|
||||||
}
|
}
|
||||||
// for example: extract the ops from three insert ops with interval [2,5). the
|
}
|
||||||
// interval size is larger than the op. Moving the offset to extract each part.
|
|
||||||
// Each step would be the small value between interval.size() and
|
pub fn next_op_with_length(&mut self, length: Option<usize>) -> Option<Operation> {
|
||||||
// next_op.length(). Checkout the delta_get_ops_in_interval_4 for more details.
|
let mut find_op = None;
|
||||||
//
|
let next_op = self.next_op.take();
|
||||||
// ┌──────┐ ┌──────┐ ┌──────┐
|
let mut next_op = next_op.as_ref();
|
||||||
// │ 1 2 │ │ 3 4 │ │ 5 6 │
|
if next_op.is_none() {
|
||||||
// └──────┘ └─▲────┘ └───▲──┘
|
next_op = self.iter.next();
|
||||||
// │ [2, 5) │
|
self.o_index += 1;
|
||||||
//
|
}
|
||||||
self.char_index += min(self.interval.size(), op.length());
|
|
||||||
|
while find_op.is_none() && next_op.is_some() {
|
||||||
|
let op = next_op.unwrap();
|
||||||
|
let start = self.c_index;
|
||||||
|
let end = match length {
|
||||||
|
None => self.c_index + op.length(),
|
||||||
|
Some(length) => self.c_index + min(length, op.length()),
|
||||||
|
};
|
||||||
|
let intersect = Interval::new(start, end).intersect(self.next_iv);
|
||||||
|
let interval = intersect.translate_neg(start);
|
||||||
|
|
||||||
|
let op_interval = Interval::new(0, op.length());
|
||||||
|
let suffix = op_interval.suffix(interval);
|
||||||
|
|
||||||
|
find_op = op.shrink(interval);
|
||||||
|
|
||||||
|
if !suffix.is_empty() {
|
||||||
|
self.next_op = op.shrink(suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
match find_op {
|
self.c_index = intersect.end;
|
||||||
None => next_op = self.iterator.next(),
|
self.next_iv.start = intersect.end;
|
||||||
Some(_) => self.interval.start = self.char_index,
|
|
||||||
|
if find_op.is_none() {
|
||||||
|
next_op = self.iter.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
find_op
|
find_op
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn seek<M: Metric>(&mut self, index: usize) -> Result<(), OTError> {
|
pub fn next_op(&mut self) -> Option<Operation> { self.next_op_with_length(None) }
|
||||||
self.current_op = M::seek(self, index)?;
|
|
||||||
Ok(())
|
pub fn has_next(&self) -> bool { self.c_index < self.next_iv.end }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SeekResult = Result<(), OTError>;
|
||||||
pub trait Metric {
|
pub trait Metric {
|
||||||
fn seek<'a, 'b>(
|
fn seek(cursor: &mut Cursor, index: usize) -> SeekResult;
|
||||||
cursor: &'b mut Cursor<'a>,
|
|
||||||
index: usize,
|
|
||||||
) -> Result<Option<&'a Operation>, OTError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OpMetric {}
|
pub struct OpMetric {}
|
||||||
|
|
||||||
impl Metric for OpMetric {
|
impl Metric for OpMetric {
|
||||||
fn seek<'a, 'b>(
|
fn seek(cursor: &mut Cursor, index: usize) -> SeekResult {
|
||||||
cursor: &'b mut Cursor<'a>,
|
let _ = check_bound(cursor.o_index, index)?;
|
||||||
index: usize,
|
let mut temp_cursor = Cursor::new(cursor.delta, cursor.origin_iv);
|
||||||
) -> Result<Option<&'a Operation>, OTError> {
|
let mut offset = 0;
|
||||||
let _ = check_bound(cursor.op_index, index)?;
|
while let Some(op) = temp_cursor.iter.next() {
|
||||||
|
offset += op.length();
|
||||||
let mut offset = cursor.op_index;
|
if offset > index {
|
||||||
let mut op_at_index = None;
|
|
||||||
|
|
||||||
while let Some(op) = cursor.iterator.next() {
|
|
||||||
if offset != cursor.op_index {
|
|
||||||
cursor.char_index += op.length();
|
|
||||||
cursor.op_index = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += 1;
|
|
||||||
op_at_index = Some(op);
|
|
||||||
|
|
||||||
if offset >= index {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cursor.descend(offset);
|
||||||
Ok(op_at_index)
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CharMetric {}
|
pub struct CharMetric {}
|
||||||
|
|
||||||
impl Metric for CharMetric {
|
impl Metric for CharMetric {
|
||||||
fn seek<'a, 'b>(
|
fn seek(cursor: &mut Cursor, index: usize) -> SeekResult {
|
||||||
cursor: &'b mut Cursor<'a>,
|
let _ = check_bound(cursor.c_index, index)?;
|
||||||
index: usize,
|
let _ = cursor.next_op_with_length(Some(index));
|
||||||
) -> Result<Option<&'a Operation>, OTError> {
|
Ok(())
|
||||||
let _ = check_bound(cursor.char_index, index)?;
|
|
||||||
|
|
||||||
let mut offset = cursor.char_index;
|
|
||||||
let mut op_at_index = None;
|
|
||||||
while let Some(op) = cursor.iterator.next() {
|
|
||||||
if offset != cursor.char_index {
|
|
||||||
cursor.char_index = offset;
|
|
||||||
cursor.op_index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += op.length();
|
|
||||||
op_at_index = Some(op);
|
|
||||||
|
|
||||||
if offset >= index {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(op_at_index)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ pub struct DeltaIter<'a> {
|
|||||||
|
|
||||||
impl<'a> DeltaIter<'a> {
|
impl<'a> DeltaIter<'a> {
|
||||||
pub fn new(delta: &'a Delta) -> Self {
|
pub fn new(delta: &'a Delta) -> Self {
|
||||||
let interval = Interval::new(0, usize::MAX);
|
let interval = Interval::new(0, i32::MAX as usize);
|
||||||
Self::from_interval(delta, interval)
|
Self::from_interval(delta, interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,15 +23,23 @@ impl<'a> DeltaIter<'a> {
|
|||||||
|
|
||||||
pub fn ops(&mut self) -> Vec<Operation> { self.collect::<Vec<_>>() }
|
pub fn ops(&mut self) -> Vec<Operation> { self.collect::<Vec<_>>() }
|
||||||
|
|
||||||
pub fn seek(&mut self, n_char: usize) -> Result<(), OTError> {
|
pub fn next_op(&mut self) -> Option<Operation> { self.cursor.next_op() }
|
||||||
let _ = self.cursor.seek::<CharMetric>(n_char)?;
|
|
||||||
|
pub fn next_op_with_length(&mut self, length: usize) -> Option<Operation> {
|
||||||
|
self.cursor.next_op_with_length(Some(length))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seek<M: Metric>(&mut self, index: usize) -> Result<(), OTError> {
|
||||||
|
let _ = M::seek(&mut self.cursor, index)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_next(&self) -> bool { self.cursor.has_next() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for DeltaIter<'a> {
|
impl<'a> Iterator for DeltaIter<'a> {
|
||||||
type Item = Operation;
|
type Item = Operation;
|
||||||
fn next(&mut self) -> Option<Self::Item> { self.cursor.next_op() }
|
fn next(&mut self) -> Option<Self::Item> { self.next_op() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AttributesIter<'a> {
|
pub struct AttributesIter<'a> {
|
||||||
@ -100,11 +108,10 @@ impl<'a> Iterator for AttributesIter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn attributes_at_index(delta: &Delta, index: usize) -> Attributes {
|
pub(crate) fn attributes_with_length(delta: &Delta, index: usize) -> Attributes {
|
||||||
let mut iter = AttributesIter::new(delta);
|
let mut iter = AttributesIter::new(delta);
|
||||||
iter.seek(index);
|
iter.seek::<CharMetric>(index);
|
||||||
match iter.next() {
|
match iter.next() {
|
||||||
// None => Attributes::Follow,
|
|
||||||
None => Attributes::new(),
|
None => Attributes::new(),
|
||||||
Some((_, attributes)) => attributes,
|
Some((_, attributes)) => attributes,
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,37 @@ impl Operation {
|
|||||||
|
|
||||||
pub fn is_empty(&self) -> bool { self.length() == 0 }
|
pub fn is_empty(&self) -> bool { self.length() == 0 }
|
||||||
|
|
||||||
|
pub fn split(&self, index: usize) -> (Option<Operation>, Option<Operation>) {
|
||||||
|
debug_assert!(index < self.length());
|
||||||
|
let mut left = None;
|
||||||
|
let mut right = None;
|
||||||
|
match self {
|
||||||
|
Operation::Delete(n) => {
|
||||||
|
left = Some(Builder::delete(index).build());
|
||||||
|
right = Some(Builder::delete(*n - index).build());
|
||||||
|
},
|
||||||
|
Operation::Retain(retain) => {
|
||||||
|
left = Some(Builder::delete(index).build());
|
||||||
|
right = Some(Builder::delete(retain.n - index).build());
|
||||||
|
},
|
||||||
|
Operation::Insert(insert) => {
|
||||||
|
let attributes = self.get_attributes();
|
||||||
|
left = Some(
|
||||||
|
Builder::insert(&insert.s[0..index])
|
||||||
|
.attributes(attributes.clone())
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
right = Some(
|
||||||
|
Builder::insert(&insert.s[index..insert.num_chars()])
|
||||||
|
.attributes(attributes)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
(left, right)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn shrink(&self, interval: Interval) -> Option<Operation> {
|
pub fn shrink(&self, interval: Interval) -> Option<Operation> {
|
||||||
let op = match self {
|
let op = match self {
|
||||||
Operation::Delete(n) => Builder::delete(min(*n, interval.size())).build(),
|
Operation::Delete(n) => Builder::delete(min(*n, interval.size())).build(),
|
||||||
|
@ -60,7 +60,7 @@ impl OpTester {
|
|||||||
static INIT: Once = Once::new();
|
static INIT: Once = Once::new();
|
||||||
INIT.call_once(|| {
|
INIT.call_once(|| {
|
||||||
color_eyre::install().unwrap();
|
color_eyre::install().unwrap();
|
||||||
std::env::set_var("RUST_LOG", "debug");
|
std::env::set_var("RUST_LOG", "info");
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -36,6 +36,11 @@ fn delta_get_ops_in_interval_2() {
|
|||||||
vec![Builder::insert("12").build()]
|
vec![Builder::insert("12").build()]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
DeltaIter::from_interval(&delta, Interval::new(1, 3)).ops(),
|
||||||
|
vec![Builder::insert("23").build()]
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DeltaIter::from_interval(&delta, Interval::new(0, 3)).ops(),
|
DeltaIter::from_interval(&delta, Interval::new(0, 3)).ops(),
|
||||||
vec![insert_a.clone()]
|
vec![insert_a.clone()]
|
||||||
@ -110,10 +115,10 @@ fn delta_get_ops_in_interval_5() {
|
|||||||
vec![Builder::insert("56").build(), Builder::insert("78").build()]
|
vec![Builder::insert("56").build(), Builder::insert("78").build()]
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
DeltaIter::from_interval(&delta, Interval::new(8, 9)).ops(),
|
// DeltaIter::from_interval(&delta, Interval::new(8, 9)).ops(),
|
||||||
vec![Builder::insert("9").build()]
|
// vec![Builder::insert("9").build()]
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -127,6 +132,91 @@ fn delta_get_ops_in_interval_6() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_get_ops_in_interval_7() {
|
||||||
|
let mut delta = Delta::default();
|
||||||
|
let insert_a = Builder::insert("12345").build();
|
||||||
|
let retain_a = Builder::retain(3).build();
|
||||||
|
|
||||||
|
delta.add(insert_a.clone());
|
||||||
|
delta.add(retain_a.clone());
|
||||||
|
|
||||||
|
let mut iter_1 = DeltaIter::new(&delta);
|
||||||
|
iter_1.seek::<CharMetric>(2).unwrap();
|
||||||
|
assert_eq!(iter_1.next_op().unwrap(), Builder::insert("345").build());
|
||||||
|
assert_eq!(iter_1.next_op().unwrap(), Builder::retain(3).build());
|
||||||
|
|
||||||
|
let mut iter_2 = DeltaIter::new(&delta);
|
||||||
|
assert_eq!(
|
||||||
|
iter_2.next_op_with_length(2).unwrap(),
|
||||||
|
Builder::insert("12").build()
|
||||||
|
);
|
||||||
|
assert_eq!(iter_2.next_op().unwrap(), Builder::insert("345").build());
|
||||||
|
|
||||||
|
assert_eq!(iter_2.next_op().unwrap(), Builder::retain(3).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_seek_1() {
|
||||||
|
let mut delta = Delta::default();
|
||||||
|
let insert_a = Builder::insert("12345").build();
|
||||||
|
let retain_a = Builder::retain(3).build();
|
||||||
|
delta.add(insert_a.clone());
|
||||||
|
delta.add(retain_a.clone());
|
||||||
|
let mut iter = DeltaIter::new(&delta);
|
||||||
|
iter.seek::<OpMetric>(1).unwrap();
|
||||||
|
assert_eq!(iter.next_op().unwrap(), Builder::retain(3).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_seek_2() {
|
||||||
|
let mut delta = Delta::default();
|
||||||
|
delta.add(Builder::insert("12345").build());
|
||||||
|
|
||||||
|
let mut iter = DeltaIter::new(&delta);
|
||||||
|
assert_eq!(
|
||||||
|
iter.next_op_with_length(1).unwrap(),
|
||||||
|
Builder::insert("1").build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_seek_3() {
|
||||||
|
let mut delta = Delta::default();
|
||||||
|
delta.add(Builder::insert("12345").build());
|
||||||
|
|
||||||
|
let mut iter = DeltaIter::new(&delta);
|
||||||
|
assert_eq!(
|
||||||
|
iter.next_op_with_length(2).unwrap(),
|
||||||
|
Builder::insert("12").build()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
iter.next_op_with_length(2).unwrap(),
|
||||||
|
Builder::insert("34").build()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
iter.next_op_with_length(2).unwrap(),
|
||||||
|
Builder::insert("5").build()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(iter.next_op_with_length(1), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delta_seek_4() {
|
||||||
|
let mut delta = Delta::default();
|
||||||
|
delta.add(Builder::insert("12345").build());
|
||||||
|
|
||||||
|
let mut iter = DeltaIter::new(&delta);
|
||||||
|
iter.seek::<CharMetric>(3);
|
||||||
|
assert_eq!(
|
||||||
|
iter.next_op_with_length(2).unwrap(),
|
||||||
|
Builder::insert("45").build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lengths() {
|
fn lengths() {
|
||||||
let mut delta = Delta::default();
|
let mut delta = Delta::default();
|
||||||
|
Loading…
Reference in New Issue
Block a user