mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
extend cursor with generic metric
This commit is contained in:
parent
1ec4655d1b
commit
bc7da582a3
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user