extend cursor with generic metric

This commit is contained in:
appflowy 2021-08-12 13:57:41 +08:00
parent 1ec4655d1b
commit bc7da582a3
7 changed files with 152 additions and 42 deletions

View File

@ -47,6 +47,8 @@ impl Document {
pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> {
log::debug!("format with {} at {}", attribute, interval);
// let format_delta = self.view.format(&self.delta, attribute, interval)?;
self.update_with_attribute(attribute, interval)
}

View File

@ -1,13 +1,43 @@
use crate::{
client::view::FormatExt,
core::{Attribute, Delta, Interval},
core::{Attribute, AttributeScope, Attributes, Delta, DeltaBuilder, DeltaIter, Interval},
};
pub struct FormatLinkAtCaretPositionExt {}
impl FormatExt for FormatLinkAtCaretPositionExt {
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
unimplemented!()
let mut iter = DeltaIter::new(delta);
iter.seek(interval.start);
let (before, after) = (iter.next(), iter.next());
let mut start = interval.start;
let mut retain = 0;
if let Some(before) = before {
if before.contain_attribute(attribute) {
start -= before.length();
retain += before.length();
}
}
if let Some(after) = after {
if after.contain_attribute(attribute) {
if retain != 0 {
retain += after.length();
}
}
}
if retain == 0 {
return None;
}
Some(
DeltaBuilder::new()
.retain(start, Attributes::default())
.retain(retain, (attribute.clone()).into())
.build(),
)
}
}
@ -15,7 +45,17 @@ pub struct ResolveLineFormatExt {}
impl FormatExt for ResolveLineFormatExt {
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
unimplemented!()
if attribute.scope != AttributeScope::Block {
return None;
}
let mut new_delta = Delta::new();
new_delta.retain(interval.start, Attributes::default());
let mut iter = DeltaIter::new(delta);
iter.seek(interval.start);
None
}
}

View File

@ -44,7 +44,7 @@ impl InsertExt for ResetLineFormatOnNewLineExt {
}
let mut iter = DeltaIter::new(delta);
iter.seek_to(index);
iter.seek(index);
let maybe_next_op = iter.next();
if maybe_next_op.is_none() {
return None;

View File

@ -71,7 +71,7 @@ pub enum AttributeKey {
UnChecked,
}
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum AttributeScope {
Inline,
Block,
@ -79,7 +79,7 @@ pub enum AttributeScope {
Ignore,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Attribute {
pub key: AttributeKey,
pub value: String,
@ -93,6 +93,14 @@ impl fmt::Display for Attribute {
}
}
impl std::convert::Into<Attributes> for Attribute {
fn into(self) -> Attributes {
let mut attributes = Attributes::new();
attributes.add(self);
attributes
}
}
pub struct AttrsBuilder {
inner: Attributes,
}

View File

@ -8,8 +8,9 @@ pub struct Cursor<'a> {
delta: &'a Delta,
interval: Interval,
iterator: Iter<'a, Operation>,
offset: usize,
offset_op: Option<&'a Operation>,
char_index: usize,
op_index: usize,
current_op: Option<&'a Operation>,
}
impl<'a> Cursor<'a> {
@ -18,29 +19,31 @@ impl<'a> Cursor<'a> {
delta,
interval,
iterator: delta.ops.iter(),
offset: 0,
offset_op: None,
char_index: 0,
op_index: 0,
current_op: None,
};
cursor
}
pub fn next_op(&mut self) -> Option<Operation> {
let mut next_op = self.offset_op.take();
let mut next_op = self.current_op.take();
if next_op.is_none() {
next_op = self.iterator.next();
}
let mut find_op = None;
while find_op.is_none() && next_op.is_some() {
self.op_index += 1;
let op = next_op.unwrap();
if self.offset < self.interval.start {
let intersect =
Interval::new(self.offset, self.offset + op.length()).intersect(self.interval);
if self.char_index < self.interval.start {
let intersect = Interval::new(self.char_index, self.char_index + op.length())
.intersect(self.interval);
if intersect.is_empty() {
self.offset += op.length();
self.char_index += op.length();
} else {
if let Some(new_op) = op.shrink(intersect.translate_neg(self.offset)) {
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 │
@ -50,11 +53,11 @@ impl<'a> Cursor<'a> {
// op = "45"
find_op = Some(new_op);
}
self.offset = intersect.end;
self.char_index = intersect.end;
}
} else {
// the interval passed in the shrink function is base on the op not the delta.
if let Some(new_op) = op.shrink(self.interval.translate_neg(self.offset)) {
if let Some(new_op) = op.shrink(self.interval.translate_neg(self.char_index)) {
find_op = Some(new_op);
}
// for example: extract the ops from three insert ops with interval [2,5). the
@ -67,43 +70,96 @@ impl<'a> Cursor<'a> {
// └──────┘ └─▲────┘ └───▲──┘
// │ [2, 5) │
//
self.offset += min(self.interval.size(), op.length());
self.char_index += min(self.interval.size(), op.length());
}
match find_op {
None => next_op = self.iterator.next(),
Some(_) => self.interval.start = self.offset,
Some(_) => self.interval.start = self.char_index,
}
}
find_op
}
pub fn seek_to(&mut self, index: usize) -> Result<(), OTError> {
if self.offset > index {
let msg = format!(
"{} should be greater than current offset: {}",
index, self.offset
);
return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength)
.msg(&msg)
.build());
}
pub fn seek<M: Metric>(&mut self, index: usize) -> Result<(), OTError> {
self.current_op = M::seek(self, index)?;
Ok(())
}
}
let mut offset = 0;
while let Some(op) = self.iterator.next() {
if offset != 0 {
self.offset = offset;
pub trait Metric {
fn seek<'a, 'b>(
cursor: &'b mut Cursor<'a>,
index: usize,
) -> Result<Option<&'a Operation>, OTError>;
}
pub struct OpMetric {}
impl Metric for OpMetric {
fn seek<'a, 'b>(
cursor: &'b mut Cursor<'a>,
index: usize,
) -> Result<Option<&'a Operation>, OTError> {
let _ = check_bound(cursor.op_index, index)?;
let mut offset = cursor.op_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 += op.length();
self.offset_op = Some(op);
offset += 1;
op_at_index = Some(op);
if offset >= index {
break;
}
}
Ok(())
Ok(op_at_index)
}
}
pub struct CharMetric {}
impl Metric for CharMetric {
fn seek<'a, 'b>(
cursor: &'b mut Cursor<'a>,
index: usize,
) -> Result<Option<&'a Operation>, OTError> {
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)
}
}
fn check_bound(current: usize, target: usize) -> Result<(), OTError> {
if current > target {
let msg = format!("{} should be greater than current: {}", target, current);
return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength)
.msg(&msg)
.build());
}
Ok(())
}

View File

@ -23,8 +23,8 @@ impl<'a> DeltaIter<'a> {
pub fn ops(&mut self) -> Vec<Operation> { self.collect::<Vec<_>>() }
pub fn seek_to(&mut self, n_char: usize) -> Result<(), OTError> {
let _ = self.cursor.seek_to(n_char)?;
pub fn seek(&mut self, n_char: usize) -> Result<(), OTError> {
let _ = self.cursor.seek::<CharMetric>(n_char)?;
Ok(())
}
}
@ -102,7 +102,7 @@ impl<'a> Iterator for AttributesIter<'a> {
pub(crate) fn attributes_at_index(delta: &Delta, index: usize) -> Attributes {
let mut iter = AttributesIter::new(delta);
iter.seek_to(index);
iter.seek(index);
match iter.next() {
// None => Attributes::Follow,
None => Attributes::new(),

View File

@ -1,4 +1,4 @@
use crate::core::{Attributes, Builder, Interval};
use crate::core::{Attribute, Attributes, Builder, Interval};
use bytecount::num_chars;
use serde::__private::Formatter;
use std::{
@ -48,6 +48,10 @@ impl Operation {
pub fn has_attribute(&self) -> bool { !self.get_attributes().is_empty() }
pub fn contain_attribute(&self, attribute: &Attribute) -> bool {
self.get_attributes().contains_key(&attribute.key)
}
pub fn length(&self) -> usize {
match self {
Operation::Delete(n) => *n,