mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
add format and insert extension
This commit is contained in:
parent
e4a64fd06a
commit
1ec4655d1b
@ -40,21 +40,21 @@ class Rules {
|
||||
final List<Rule> _rules;
|
||||
|
||||
static final Rules _instance = Rules([
|
||||
// const FormatLinkAtCaretPositionRule(),
|
||||
// const ResolveLineFormatRule(),
|
||||
// const ResolveInlineFormatRule(),
|
||||
// const InsertEmbedsRule(),
|
||||
// const ForceNewlineForInsertsAroundEmbedRule(),
|
||||
// const AutoExitBlockRule(),
|
||||
// const PreserveBlockStyleOnInsertRule(),
|
||||
// const PreserveLineStyleOnSplitRule(),
|
||||
// const ResetLineFormatOnNewLineRule(),
|
||||
// const AutoFormatLinksRule(),
|
||||
// const PreserveInlineStylesRule(),
|
||||
// const CatchAllInsertRule(),
|
||||
// const EnsureEmbedLineRule(),
|
||||
// const PreserveLineStyleOnMergeRule(),
|
||||
// const CatchAllDeleteRule(),
|
||||
const FormatLinkAtCaretPositionRule(),
|
||||
const ResolveLineFormatRule(),
|
||||
const ResolveInlineFormatRule(),
|
||||
const InsertEmbedsRule(),
|
||||
const ForceNewlineForInsertsAroundEmbedRule(),
|
||||
const AutoExitBlockRule(),
|
||||
const PreserveBlockStyleOnInsertRule(),
|
||||
const PreserveLineStyleOnSplitRule(),
|
||||
const ResetLineFormatOnNewLineRule(),
|
||||
const AutoFormatLinksRule(),
|
||||
const PreserveInlineStylesRule(),
|
||||
const CatchAllInsertRule(),
|
||||
const EnsureEmbedLineRule(),
|
||||
const PreserveLineStyleOnMergeRule(),
|
||||
const CatchAllDeleteRule(),
|
||||
]);
|
||||
|
||||
static Rules getInstance() => _instance;
|
||||
|
@ -17,6 +17,10 @@ pub struct Document {
|
||||
impl Document {
|
||||
pub fn new() -> Self {
|
||||
let delta = Delta::new();
|
||||
Self::from_delta(delta)
|
||||
}
|
||||
|
||||
pub fn from_delta(delta: Delta) -> Self {
|
||||
Document {
|
||||
delta,
|
||||
history: History::new(),
|
||||
@ -35,7 +39,7 @@ impl Document {
|
||||
);
|
||||
}
|
||||
|
||||
let delta = self.view.handle_insert(&self.delta, text, index);
|
||||
let delta = self.view.insert(&self.delta, text, index)?;
|
||||
let interval = Interval::new(index, index);
|
||||
self.update_with_op(&delta, interval)
|
||||
}
|
||||
@ -139,7 +143,7 @@ impl Document {
|
||||
) -> Result<(), OTError> {
|
||||
log::debug!("Update document with attribute: {}", attribute);
|
||||
let mut attributes = AttrsBuilder::new().add(attribute).build();
|
||||
let old_attributes = self.delta.get_attributes(interval);
|
||||
let old_attributes = AttributesIter::from_interval(&self.delta, interval).next_or_empty();
|
||||
|
||||
log::debug!("combine with old: {:?}", old_attributes);
|
||||
attributes.merge(Some(old_attributes));
|
||||
@ -202,17 +206,3 @@ fn split_length_with_interval(length: usize, interval: Interval) -> (Interval, I
|
||||
let suffix = original_interval.suffix(interval);
|
||||
(prefix, interval, suffix)
|
||||
}
|
||||
|
||||
pub fn trim(delta: &mut Delta) {
|
||||
let remove_last = match delta.ops.last() {
|
||||
None => false,
|
||||
Some(op) => match op {
|
||||
Operation::Delete(_) => false,
|
||||
Operation::Retain(retain) => retain.is_plain(),
|
||||
Operation::Insert(_) => false,
|
||||
},
|
||||
};
|
||||
if remove_last {
|
||||
delta.ops.pop();
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
use crate::{
|
||||
client::Document,
|
||||
core::{Attributes, Delta, Interval},
|
||||
core::{Attribute, Delta, Interval},
|
||||
};
|
||||
|
||||
pub trait InsertExt {
|
||||
fn apply(&self, delta: &Delta, s: &str, index: usize) -> Delta;
|
||||
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta>;
|
||||
}
|
||||
|
||||
pub trait FormatExt {
|
||||
fn apply(&self, document: &Document, interval: Interval, attributes: Attributes);
|
||||
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta>;
|
||||
}
|
||||
|
||||
pub trait DeleteExt {
|
||||
fn apply(&self, document: &Document, interval: Interval);
|
||||
fn apply(&self, delta: &Delta, interval: Interval) -> Option<Delta>;
|
||||
}
|
||||
|
28
rust-lib/flowy-ot/src/client/view/format_ext.rs
Normal file
28
rust-lib/flowy-ot/src/client/view/format_ext.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use crate::{
|
||||
client::view::FormatExt,
|
||||
core::{Attribute, Delta, Interval},
|
||||
};
|
||||
|
||||
pub struct FormatLinkAtCaretPositionExt {}
|
||||
|
||||
impl FormatExt for FormatLinkAtCaretPositionExt {
|
||||
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResolveLineFormatExt {}
|
||||
|
||||
impl FormatExt for ResolveLineFormatExt {
|
||||
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResolveInlineFormatExt {}
|
||||
|
||||
impl FormatExt for ResolveInlineFormatExt {
|
||||
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
@ -1,21 +1,73 @@
|
||||
use crate::{
|
||||
client::view::InsertExt,
|
||||
core::{attributes_at_index, Attributes, AttributesIter, Builder, Delta, Interval},
|
||||
core::{
|
||||
attributes_at_index,
|
||||
AttributeKey,
|
||||
Attributes,
|
||||
Delta,
|
||||
DeltaBuilder,
|
||||
DeltaIter,
|
||||
Operation,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct PreserveInlineStyleExt {}
|
||||
pub const NEW_LINE: &'static str = "\n";
|
||||
|
||||
pub struct PreserveInlineStyleExt {}
|
||||
impl PreserveInlineStyleExt {
|
||||
pub fn new() -> Self { Self {} }
|
||||
}
|
||||
|
||||
impl InsertExt for PreserveInlineStyleExt {
|
||||
fn apply(&self, delta: &Delta, text: &str, index: usize) -> Delta {
|
||||
let attributes = attributes_at_index(delta, index);
|
||||
let mut delta = Delta::new();
|
||||
let insert = Builder::insert(text).attributes(attributes).build();
|
||||
delta.add(insert);
|
||||
fn apply(&self, delta: &Delta, _replace_len: usize, text: &str, index: usize) -> Option<Delta> {
|
||||
if text.ends_with(NEW_LINE) {
|
||||
return None;
|
||||
}
|
||||
|
||||
delta
|
||||
let attributes = attributes_at_index(delta, index);
|
||||
let delta = DeltaBuilder::new().insert(text, attributes).build();
|
||||
|
||||
Some(delta)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResetLineFormatOnNewLineExt {}
|
||||
|
||||
impl ResetLineFormatOnNewLineExt {
|
||||
pub fn new() -> Self { Self {} }
|
||||
}
|
||||
|
||||
impl InsertExt for ResetLineFormatOnNewLineExt {
|
||||
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
|
||||
if text != NEW_LINE {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut iter = DeltaIter::new(delta);
|
||||
iter.seek_to(index);
|
||||
let maybe_next_op = iter.next();
|
||||
if maybe_next_op.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let op = maybe_next_op.unwrap();
|
||||
if !op.get_data().starts_with(NEW_LINE) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut reset_attribute = Attributes::new();
|
||||
if op.get_attributes().contains_key(&AttributeKey::Header) {
|
||||
reset_attribute.add(AttributeKey::Header.with_value(""));
|
||||
}
|
||||
|
||||
let len = index + replace_len;
|
||||
Some(
|
||||
DeltaBuilder::new()
|
||||
.retain(len, Attributes::default())
|
||||
.insert(NEW_LINE, op.get_attributes())
|
||||
.retain(1, reset_attribute)
|
||||
.trim()
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
mod extension;
|
||||
mod format_ext;
|
||||
mod insert_ext;
|
||||
mod view;
|
||||
|
||||
pub use extension::*;
|
||||
pub use format_ext::*;
|
||||
pub use insert_ext::*;
|
||||
pub use view::*;
|
||||
|
@ -1,32 +1,94 @@
|
||||
use crate::{
|
||||
client::view::{InsertExt, PreserveInlineStyleExt},
|
||||
core::Delta,
|
||||
client::view::{DeleteExt, FormatExt, InsertExt, *},
|
||||
core::{Attribute, Delta, Interval},
|
||||
errors::{ErrorBuilder, OTError, OTErrorCode},
|
||||
};
|
||||
|
||||
type InsertExtension = Box<dyn InsertExt>;
|
||||
type FormatExtension = Box<dyn FormatExt>;
|
||||
type DeleteExtension = Box<dyn DeleteExt>;
|
||||
|
||||
pub struct View {
|
||||
insert_exts: Vec<InsertExtension>,
|
||||
format_exts: Vec<FormatExtension>,
|
||||
delete_exts: Vec<DeleteExtension>,
|
||||
}
|
||||
|
||||
impl View {
|
||||
pub(crate) fn new() -> Self {
|
||||
let insert_exts = construct_insert_exts();
|
||||
Self { insert_exts }
|
||||
let format_exts = construct_format_exts();
|
||||
let delete_exts = construct_delete_exts();
|
||||
Self {
|
||||
insert_exts,
|
||||
format_exts,
|
||||
delete_exts,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_insert(&self, delta: &Delta, s: &str, index: usize) -> Delta {
|
||||
let mut new_delta = Delta::new();
|
||||
self.insert_exts.iter().for_each(|ext| {
|
||||
new_delta = ext.apply(delta, s, index);
|
||||
});
|
||||
new_delta
|
||||
pub(crate) fn insert(&self, delta: &Delta, text: &str, index: usize) -> Result<Delta, OTError> {
|
||||
let mut new_delta = None;
|
||||
for ext in &self.insert_exts {
|
||||
if let Some(delta) = ext.apply(delta, 0, text, index) {
|
||||
new_delta = Some(delta);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
match new_delta {
|
||||
None => Err(ErrorBuilder::new(OTErrorCode::ApplyInsertFail).build()),
|
||||
Some(new_delta) => Ok(new_delta),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn replace(
|
||||
&self,
|
||||
delta: &Delta,
|
||||
text: &str,
|
||||
interval: Interval,
|
||||
) -> Result<Delta, OTError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub(crate) fn format(
|
||||
&self,
|
||||
delta: &Delta,
|
||||
attribute: Attribute,
|
||||
interval: Interval,
|
||||
) -> Result<Delta, OTError> {
|
||||
let mut new_delta = None;
|
||||
for ext in &self.format_exts {
|
||||
if let Some(delta) = ext.apply(delta, interval, &attribute) {
|
||||
new_delta = Some(delta);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
match new_delta {
|
||||
None => Err(ErrorBuilder::new(OTErrorCode::ApplyFormatFail).build()),
|
||||
Some(new_delta) => Ok(new_delta),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_insert_exts() -> Vec<InsertExtension> {
|
||||
vec![
|
||||
//
|
||||
Box::new(PreserveInlineStyleExt::new()),
|
||||
Box::new(ResetLineFormatOnNewLineExt::new()),
|
||||
]
|
||||
}
|
||||
|
||||
fn construct_format_exts() -> Vec<FormatExtension> {
|
||||
vec![
|
||||
Box::new(FormatLinkAtCaretPositionExt {}),
|
||||
Box::new(ResolveLineFormatExt {}),
|
||||
Box::new(ResolveInlineFormatExt {}),
|
||||
]
|
||||
}
|
||||
|
||||
fn construct_delete_exts() -> Vec<DeleteExtension> {
|
||||
vec![
|
||||
//
|
||||
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::core::{Attribute, AttributeKey, Operation};
|
||||
use std::{collections::HashMap, fmt, fmt::Formatter};
|
||||
use std::{collections::HashMap, fmt};
|
||||
|
||||
pub const REMOVE_FLAG: &'static str = "";
|
||||
pub(crate) fn should_remove(s: &str) -> bool { s == REMOVE_FLAG }
|
||||
|
44
rust-lib/flowy-ot/src/core/delta/builder.rs
Normal file
44
rust-lib/flowy-ot/src/core/delta/builder.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use crate::core::{Attributes, Delta, Operation};
|
||||
|
||||
pub struct DeltaBuilder {
|
||||
delta: Delta,
|
||||
}
|
||||
|
||||
impl DeltaBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
delta: Delta::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retain(mut self, n: usize, attrs: Attributes) -> Self {
|
||||
self.delta.retain(n, attrs);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn insert(mut self, s: &str, attrs: Attributes) -> Self {
|
||||
self.delta.insert(s, attrs);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn trim(mut self) -> Self {
|
||||
trim(&mut self.delta);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Delta { self.delta }
|
||||
}
|
||||
|
||||
pub fn trim(delta: &mut Delta) {
|
||||
let remove_last = match delta.ops.last() {
|
||||
None => false,
|
||||
Some(op) => match op {
|
||||
Operation::Delete(_) => false,
|
||||
Operation::Retain(retain) => retain.is_plain(),
|
||||
Operation::Insert(_) => false,
|
||||
},
|
||||
};
|
||||
if remove_last {
|
||||
delta.ops.pop();
|
||||
}
|
||||
}
|
@ -1,12 +1,8 @@
|
||||
use crate::{
|
||||
core::{Attributes, Delta, Interval, Operation},
|
||||
core::{Delta, Interval, Operation},
|
||||
errors::{ErrorBuilder, OTError, OTErrorCode},
|
||||
};
|
||||
use std::{
|
||||
cmp::min,
|
||||
ops::{Deref, DerefMut},
|
||||
slice::Iter,
|
||||
};
|
||||
use std::{cmp::min, slice::Iter};
|
||||
|
||||
pub struct Cursor<'a> {
|
||||
delta: &'a Delta,
|
||||
@ -111,108 +107,3 @@ impl<'a> Cursor<'a> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeltaIter<'a> {
|
||||
cursor: Cursor<'a>,
|
||||
interval: Interval,
|
||||
}
|
||||
|
||||
impl<'a> DeltaIter<'a> {
|
||||
pub fn new(delta: &'a Delta) -> Self {
|
||||
let interval = Interval::new(0, usize::MAX);
|
||||
Self::from_interval(delta, interval)
|
||||
}
|
||||
|
||||
pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self {
|
||||
let cursor = Cursor::new(delta, interval);
|
||||
Self { cursor, interval }
|
||||
}
|
||||
|
||||
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)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DeltaIter<'a> {
|
||||
type Item = Operation;
|
||||
fn next(&mut self) -> Option<Self::Item> { self.cursor.next_op() }
|
||||
}
|
||||
|
||||
pub struct AttributesIter<'a> {
|
||||
delta_iter: DeltaIter<'a>,
|
||||
interval: Interval,
|
||||
}
|
||||
|
||||
impl<'a> AttributesIter<'a> {
|
||||
pub fn new(delta: &'a Delta) -> Self {
|
||||
let interval = Interval::new(0, usize::MAX);
|
||||
Self::from_interval(delta, interval)
|
||||
}
|
||||
|
||||
pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self {
|
||||
let delta_iter = DeltaIter::from_interval(delta, interval);
|
||||
Self {
|
||||
delta_iter,
|
||||
interval,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for AttributesIter<'a> {
|
||||
type Target = DeltaIter<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.delta_iter }
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for AttributesIter<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.delta_iter }
|
||||
}
|
||||
|
||||
impl<'a> Iterator for AttributesIter<'a> {
|
||||
type Item = (usize, Attributes);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next_op = self.delta_iter.next();
|
||||
if next_op.is_none() {
|
||||
return None;
|
||||
}
|
||||
let mut length: usize = 0;
|
||||
let mut attributes = Attributes::new();
|
||||
|
||||
match next_op.unwrap() {
|
||||
Operation::Delete(_n) => {},
|
||||
Operation::Retain(retain) => {
|
||||
log::debug!("extend retain attributes with {} ", &retain.attributes);
|
||||
attributes.extend(retain.attributes.clone());
|
||||
|
||||
length = retain.n;
|
||||
},
|
||||
Operation::Insert(insert) => {
|
||||
log::debug!("extend insert attributes with {} ", &insert.attributes);
|
||||
attributes.extend(insert.attributes.clone());
|
||||
length = insert.num_chars();
|
||||
},
|
||||
}
|
||||
|
||||
Some((length, attributes))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn attributes_at_index(delta: &Delta, index: usize) -> Attributes {
|
||||
let mut iter = AttributesIter::new(delta);
|
||||
iter.seek_to(index);
|
||||
match iter.next() {
|
||||
// None => Attributes::Follow,
|
||||
None => Attributes::new(),
|
||||
Some((_, attributes)) => attributes,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[test]
|
||||
fn test() {}
|
||||
}
|
||||
|
@ -525,34 +525,6 @@ impl Delta {
|
||||
|
||||
pub fn is_empty(&self) -> bool { self.ops.is_empty() }
|
||||
|
||||
pub fn get_attributes(&self, interval: Interval) -> Attributes {
|
||||
let mut attributes = Attributes::new();
|
||||
let mut offset: usize = 0;
|
||||
log::debug!("Get attributes at {:?}", interval);
|
||||
self.ops.iter().for_each(|op| match op {
|
||||
Operation::Delete(_n) => {},
|
||||
Operation::Retain(retain) => {
|
||||
if interval.contains_range(offset, offset + retain.n) {
|
||||
log::debug!("extend retain attributes with {} ", &retain.attributes);
|
||||
attributes.extend(retain.attributes.clone());
|
||||
}
|
||||
|
||||
offset += retain.n;
|
||||
},
|
||||
Operation::Insert(insert) => {
|
||||
let end = insert.num_chars() as usize;
|
||||
if interval.contains_range(offset, offset + end) {
|
||||
log::debug!("extend insert attributes with {} ", &insert.attributes);
|
||||
attributes.extend(insert.attributes.clone());
|
||||
}
|
||||
offset += end;
|
||||
},
|
||||
});
|
||||
|
||||
log::debug!("Get attributes result: {} ", &attributes);
|
||||
attributes
|
||||
}
|
||||
|
||||
pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap_or("".to_owned()) }
|
||||
}
|
||||
|
||||
|
111
rust-lib/flowy-ot/src/core/delta/iterator.rs
Normal file
111
rust-lib/flowy-ot/src/core/delta/iterator.rs
Normal file
@ -0,0 +1,111 @@
|
||||
use super::cursor::*;
|
||||
use crate::{
|
||||
core::{Attributes, Delta, Interval, Operation},
|
||||
errors::OTError,
|
||||
};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub struct DeltaIter<'a> {
|
||||
cursor: Cursor<'a>,
|
||||
interval: Interval,
|
||||
}
|
||||
|
||||
impl<'a> DeltaIter<'a> {
|
||||
pub fn new(delta: &'a Delta) -> Self {
|
||||
let interval = Interval::new(0, usize::MAX);
|
||||
Self::from_interval(delta, interval)
|
||||
}
|
||||
|
||||
pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self {
|
||||
let cursor = Cursor::new(delta, interval);
|
||||
Self { cursor, interval }
|
||||
}
|
||||
|
||||
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)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DeltaIter<'a> {
|
||||
type Item = Operation;
|
||||
fn next(&mut self) -> Option<Self::Item> { self.cursor.next_op() }
|
||||
}
|
||||
|
||||
pub struct AttributesIter<'a> {
|
||||
delta_iter: DeltaIter<'a>,
|
||||
interval: Interval,
|
||||
}
|
||||
|
||||
impl<'a> AttributesIter<'a> {
|
||||
pub fn new(delta: &'a Delta) -> Self {
|
||||
let interval = Interval::new(0, usize::MAX);
|
||||
Self::from_interval(delta, interval)
|
||||
}
|
||||
|
||||
pub fn from_interval(delta: &'a Delta, interval: Interval) -> Self {
|
||||
let delta_iter = DeltaIter::from_interval(delta, interval);
|
||||
Self {
|
||||
delta_iter,
|
||||
interval,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_or_empty(&mut self) -> Attributes {
|
||||
match self.next() {
|
||||
None => Attributes::default(),
|
||||
Some((_, attributes)) => attributes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for AttributesIter<'a> {
|
||||
type Target = DeltaIter<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.delta_iter }
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for AttributesIter<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.delta_iter }
|
||||
}
|
||||
|
||||
impl<'a> Iterator for AttributesIter<'a> {
|
||||
type Item = (usize, Attributes);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next_op = self.delta_iter.next();
|
||||
if next_op.is_none() {
|
||||
return None;
|
||||
}
|
||||
let mut length: usize = 0;
|
||||
let mut attributes = Attributes::new();
|
||||
|
||||
match next_op.unwrap() {
|
||||
Operation::Delete(_n) => {},
|
||||
Operation::Retain(retain) => {
|
||||
log::debug!("extend retain attributes with {} ", &retain.attributes);
|
||||
attributes.extend(retain.attributes.clone());
|
||||
|
||||
length = retain.n;
|
||||
},
|
||||
Operation::Insert(insert) => {
|
||||
log::debug!("extend insert attributes with {} ", &insert.attributes);
|
||||
attributes.extend(insert.attributes.clone());
|
||||
length = insert.num_chars();
|
||||
},
|
||||
}
|
||||
|
||||
Some((length, attributes))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn attributes_at_index(delta: &Delta, index: usize) -> Attributes {
|
||||
let mut iter = AttributesIter::new(delta);
|
||||
iter.seek_to(index);
|
||||
match iter.next() {
|
||||
// None => Attributes::Follow,
|
||||
None => Attributes::new(),
|
||||
Some((_, attributes)) => attributes,
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
mod builder;
|
||||
mod cursor;
|
||||
mod delta;
|
||||
mod iterator;
|
||||
|
||||
pub use builder::*;
|
||||
pub use cursor::*;
|
||||
pub use delta::*;
|
||||
pub use iterator::*;
|
||||
|
@ -16,17 +16,11 @@ pub enum Operation {
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
pub fn is_delete(&self) -> bool {
|
||||
pub fn get_data(&self) -> &str {
|
||||
match self {
|
||||
Operation::Delete(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_noop(&self) -> bool {
|
||||
match self {
|
||||
Operation::Retain(_) => true,
|
||||
_ => false,
|
||||
Operation::Delete(_) => "",
|
||||
Operation::Retain(_) => "",
|
||||
Operation::Insert(insert) => &insert.s,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,8 @@ impl Error for OTError {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OTErrorCode {
|
||||
IncompatibleLength,
|
||||
ApplyInsertFail,
|
||||
ApplyFormatFail,
|
||||
UndoFail,
|
||||
RedoFail,
|
||||
}
|
||||
|
@ -64,11 +64,7 @@ impl OpTester {
|
||||
env_logger::init();
|
||||
});
|
||||
|
||||
let mut documents = Vec::with_capacity(2);
|
||||
for _ in 0..2 {
|
||||
documents.push(Document::new());
|
||||
}
|
||||
Self { documents }
|
||||
Self { documents: vec![] }
|
||||
}
|
||||
|
||||
pub fn run_op(&mut self, op: &TestOp) {
|
||||
@ -173,6 +169,22 @@ impl OpTester {
|
||||
}
|
||||
|
||||
pub fn run_script(&mut self, script: Vec<TestOp>) {
|
||||
let delta = Delta::new();
|
||||
self.run(script, delta);
|
||||
}
|
||||
|
||||
pub fn run_script_with_newline(&mut self, script: Vec<TestOp>) {
|
||||
let mut delta = Delta::new();
|
||||
delta.insert("\n", Attributes::default());
|
||||
self.run(script, delta);
|
||||
}
|
||||
|
||||
fn run(&mut self, script: Vec<TestOp>, delta: Delta) {
|
||||
let mut documents = Vec::with_capacity(2);
|
||||
for _ in 0..2 {
|
||||
documents.push(Document::from_delta(delta.clone()));
|
||||
}
|
||||
self.documents = documents;
|
||||
for (_i, op) in script.iter().enumerate() {
|
||||
self.run_op(op);
|
||||
}
|
||||
|
@ -6,18 +6,16 @@ use flowy_ot::{client::RECORD_THRESHOLD, core::Interval};
|
||||
#[test]
|
||||
fn delta_undo_insert() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Undo(0),
|
||||
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_undo_insert2() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Wait(RECORD_THRESHOLD),
|
||||
Insert(0, "456", 0),
|
||||
@ -26,13 +24,12 @@ fn delta_undo_insert2() {
|
||||
Undo(0),
|
||||
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_redo_insert() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
AssertOpsJson(0, r#"[{"insert":"123\n"}]"#),
|
||||
Undo(0),
|
||||
@ -40,13 +37,12 @@ fn delta_redo_insert() {
|
||||
Redo(0),
|
||||
AssertOpsJson(0, r#"[{"insert":"123\n"}]"#),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_redo_insert_with_lagging() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Wait(RECORD_THRESHOLD),
|
||||
Insert(0, "456", 3),
|
||||
@ -60,38 +56,35 @@ fn delta_redo_insert_with_lagging() {
|
||||
Undo(0),
|
||||
AssertOpsJson(0, r#"[{"insert":"123\n"}]"#),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_undo_attributes() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Bold(0, Interval::new(0, 3), true),
|
||||
Undo(0),
|
||||
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_undo_attributes_with_lagging() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Wait(RECORD_THRESHOLD),
|
||||
Bold(0, Interval::new(0, 3), true),
|
||||
Undo(0),
|
||||
AssertOpsJson(0, r#"[{"insert":"123\n"}]"#),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_redo_attributes() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Bold(0, Interval::new(0, 3), true),
|
||||
Undo(0),
|
||||
@ -102,13 +95,12 @@ fn delta_redo_attributes() {
|
||||
r#" [{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"}]"#,
|
||||
),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_redo_attributes_with_lagging() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Wait(RECORD_THRESHOLD),
|
||||
Bold(0, Interval::new(0, 3), true),
|
||||
@ -120,7 +112,7 @@ fn delta_redo_attributes_with_lagging() {
|
||||
r#"[{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"}]"#,
|
||||
),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -139,7 +131,6 @@ fn delta_undo_delete() {
|
||||
#[test]
|
||||
fn delta_undo_delete2() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Bold(0, Interval::new(0, 3), true),
|
||||
Delete(0, Interval::new(0, 1)),
|
||||
@ -153,13 +144,12 @@ fn delta_undo_delete2() {
|
||||
Undo(0),
|
||||
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_undo_delete2_with_lagging() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Wait(RECORD_THRESHOLD),
|
||||
Bold(0, Interval::new(0, 3), true),
|
||||
@ -181,13 +171,12 @@ fn delta_undo_delete2_with_lagging() {
|
||||
"#,
|
||||
),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_redo_delete() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Delete(0, Interval::new(0, 3)),
|
||||
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
|
||||
@ -195,13 +184,12 @@ fn delta_redo_delete() {
|
||||
Redo(0),
|
||||
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_undo_replace() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Bold(0, Interval::new(0, 3), true),
|
||||
Replace(0, Interval::new(0, 2), "ab"),
|
||||
@ -215,13 +203,12 @@ fn delta_undo_replace() {
|
||||
Undo(0),
|
||||
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_undo_replace_with_lagging() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Wait(RECORD_THRESHOLD),
|
||||
Bold(0, Interval::new(0, 3), true),
|
||||
@ -240,13 +227,12 @@ fn delta_undo_replace_with_lagging() {
|
||||
r#"[{"insert":"123","attributes":{"bold":"true"}},{"insert":"\n"}]"#,
|
||||
),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delta_redo_replace() {
|
||||
let ops = vec![
|
||||
Insert(0, "\n", 0),
|
||||
Insert(0, "123", 0),
|
||||
Bold(0, Interval::new(0, 3), true),
|
||||
Replace(0, Interval::new(0, 2), "ab"),
|
||||
@ -260,5 +246,5 @@ fn delta_redo_replace() {
|
||||
"#,
|
||||
),
|
||||
];
|
||||
OpTester::new().run_script(ops);
|
||||
OpTester::new().run_script_with_newline(ops);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user