remove attributes type follow

This commit is contained in:
appflowy 2021-08-11 17:18:10 +08:00
parent 039b4917ed
commit e4a64fd06a
13 changed files with 400 additions and 355 deletions

View File

@ -34,40 +34,22 @@ impl Document {
self.delta.target_len
);
}
let probe = Interval::new(index, index + 1);
let mut attributes = self.delta.get_attributes(probe);
if attributes.is_empty() {
attributes = Attributes::Follow;
}
// let delta = self.view.handle_insert(&self.delta, s, interval);
let mut delta = Delta::new();
let insert = Builder::insert(text).attributes(attributes).build();
let delta = self.view.handle_insert(&self.delta, text, index);
let interval = Interval::new(index, index);
delta.add(insert);
self.update_with_op(&delta, interval)
}
pub fn format(
&mut self,
interval: Interval,
attribute: Attribute,
enable: bool,
) -> Result<(), OTError> {
let attributes = match enable {
true => AttrsBuilder::new().add(attribute).build(),
false => AttrsBuilder::new().remove(&attribute).build(),
};
log::debug!("format with {} at {}", attributes, interval);
self.update_with_attribute(attributes, interval)
pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> {
log::debug!("format with {} at {}", attribute, interval);
self.update_with_attribute(attribute, interval)
}
pub fn replace(&mut self, interval: Interval, s: &str) -> Result<(), OTError> {
let mut delta = Delta::default();
if !s.is_empty() {
let insert = Builder::insert(s).attributes(Attributes::Follow).build();
let insert = Builder::insert(s).build();
delta.add(insert);
}
@ -128,12 +110,10 @@ impl Document {
// prefix
if prefix.is_empty() == false && prefix != interval {
DeltaAttributesIter::from_interval(&self.delta, prefix).for_each(
|(length, attributes)| {
log::debug!("prefix attribute: {:?}, len: {}", attributes, length);
new_delta.retain(length, attributes);
},
);
AttributesIter::from_interval(&self.delta, prefix).for_each(|(length, attributes)| {
log::debug!("prefix attribute: {:?}, len: {}", attributes, length);
new_delta.retain(length, attributes);
});
}
delta.ops.iter().for_each(|op| {
@ -142,12 +122,10 @@ impl Document {
// suffix
if suffix.is_empty() == false {
DeltaAttributesIter::from_interval(&self.delta, suffix).for_each(
|(length, attributes)| {
log::debug!("suffix attribute: {:?}, len: {}", attributes, length);
new_delta.retain(length, attributes);
},
);
AttributesIter::from_interval(&self.delta, suffix).for_each(|(length, attributes)| {
log::debug!("suffix attribute: {:?}, len: {}", attributes, length);
new_delta.retain(length, attributes);
});
}
self.delta = self.record_change(&new_delta)?;
@ -156,22 +134,18 @@ impl Document {
pub fn update_with_attribute(
&mut self,
mut attributes: Attributes,
attribute: Attribute,
interval: Interval,
) -> Result<(), OTError> {
log::debug!("Update document with attributes: {:?}", attributes,);
log::debug!("Update document with attribute: {}", attribute);
let mut attributes = AttrsBuilder::new().add(attribute).build();
let old_attributes = self.delta.get_attributes(interval);
log::debug!("combine with old: {:?}", old_attributes);
let new_attributes = match &mut attributes {
Attributes::Follow => old_attributes,
Attributes::Custom(attr_data) => {
attr_data.merge(old_attributes.data());
log::debug!("combine with old result : {:?}", attr_data);
attr_data.clone().into()
},
};
log::debug!("combine with old: {:?}", old_attributes);
attributes.merge(Some(old_attributes));
let new_attributes = attributes;
log::debug!("combine result: {:?}", new_attributes);
let retain = Builder::retain(interval.size())
.attributes(new_attributes)
.build();
@ -229,27 +203,6 @@ fn split_length_with_interval(length: usize, interval: Interval) -> (Interval, I
(prefix, interval, suffix)
}
fn split_interval_by_delta(delta: &Delta, interval: &Interval) -> Vec<Interval> {
let mut start = 0;
let mut new_intervals = vec![];
delta.ops.iter().for_each(|op| match op {
Operation::Delete(_) => {},
Operation::Retain(_) => {},
Operation::Insert(insert) => {
let len = insert.num_chars() as usize;
let end = start + len;
let insert_interval = Interval::new(start, end);
let new_interval = interval.intersect(insert_interval);
if !new_interval.is_empty() {
new_intervals.push(new_interval)
}
start += len;
},
});
new_intervals
}
pub fn trim(delta: &mut Delta) {
let remove_last = match delta.ops.last() {
None => false,

View File

@ -1,11 +1,10 @@
use crate::{
client::{view::insert_ext::*, Document},
client::Document,
core::{Attributes, Delta, Interval},
};
use lazy_static::lazy_static;
pub trait InsertExt {
fn apply(&self, delta: &Delta, s: &str, interval: Interval) -> Delta;
fn apply(&self, delta: &Delta, s: &str, index: usize) -> Delta;
}
pub trait FormatExt {

View File

@ -1,6 +1,6 @@
use crate::{
client::{view::InsertExt, Document},
core::{Builder, Delta, Interval},
client::view::InsertExt,
core::{attributes_at_index, Attributes, AttributesIter, Builder, Delta, Interval},
};
pub struct PreserveInlineStyleExt {}
@ -10,13 +10,12 @@ impl PreserveInlineStyleExt {
}
impl InsertExt for PreserveInlineStyleExt {
fn apply(&self, delta: &Delta, s: &str, interval: Interval) -> Delta {
// let mut delta = Delta::default();
// let insert = Builder::insert(text).attributes(attributes).build();
// let interval = Interval::new(index, index);
// delta.add(insert);
//
// delta
unimplemented!()
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);
delta
}
}

View File

@ -1,6 +1,6 @@
use crate::{
client::view::{InsertExt, PreserveInlineStyleExt},
core::{Delta, Interval},
core::Delta,
};
type InsertExtension = Box<dyn InsertExt>;
@ -15,10 +15,10 @@ impl View {
Self { insert_exts }
}
pub(crate) fn handle_insert(&self, delta: &Delta, s: &str, interval: Interval) -> Delta {
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, interval);
new_delta = ext.apply(delta, s, index);
});
new_delta
}

View File

@ -1,58 +1,72 @@
use crate::core::{AttributesData, Operation};
use std::{fmt, fmt::Formatter};
use crate::core::{Attribute, AttributeKey, Operation};
use std::{collections::HashMap, fmt, fmt::Formatter};
pub trait AttributesRule {
// Remove the empty attribute that its value is empty. e.g. {bold: ""}.
fn remove_empty(self) -> Attributes;
}
pub const REMOVE_FLAG: &'static str = "";
pub(crate) fn should_remove(s: &str) -> bool { s == REMOVE_FLAG }
impl AttributesRule for Attributes {
fn remove_empty(self) -> Attributes {
match self {
Attributes::Follow => self,
Attributes::Custom(mut data) => {
data.remove_empty_value();
data.into()
},
}
}
}
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum Attributes {
#[serde(skip)]
Follow,
Custom(AttributesData),
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Attributes {
#[serde(skip_serializing_if = "HashMap::is_empty")]
#[serde(flatten)]
pub(crate) inner: HashMap<AttributeKey, String>,
}
impl fmt::Display for Attributes {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Attributes::Follow => f.write_str("follow"),
Attributes::Custom(data) => f.write_fmt(format_args!("{}", data)),
}
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("{:?}", self.inner))
}
}
impl Attributes {
pub fn data(&self) -> Option<AttributesData> {
match self {
Attributes::Follow => None,
Attributes::Custom(data) => Some(data.clone()),
pub fn new() -> Self {
Attributes {
inner: HashMap::new(),
}
}
pub fn is_empty(&self) -> bool {
match self {
Attributes::Follow => false,
Attributes::Custom(data) => data.is_empty(),
pub fn is_empty(&self) -> bool { self.inner.is_empty() }
pub fn add(&mut self, attribute: Attribute) {
let Attribute {
key,
value,
scope: _,
} = attribute;
self.inner.insert(key, value);
}
pub fn remove(&mut self, key: &AttributeKey) {
self.inner.insert(key.clone(), REMOVE_FLAG.to_owned());
}
// Remove the key if its value is empty. e.g. { bold: "" }
pub fn remove_empty_value(&mut self) { self.inner.retain(|_, v| !should_remove(v)); }
pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); }
// Update self attributes by constructing new attributes from the other if it's
// not None and replace the key/value with self key/value.
pub fn merge(&mut self, other: Option<Attributes>) {
if other.is_none() {
return;
}
let mut new_attributes = other.unwrap().inner;
self.inner.iter().for_each(|(k, v)| {
new_attributes.insert(k.clone(), v.clone());
});
self.inner = new_attributes;
}
}
impl std::default::Default for Attributes {
fn default() -> Self { Attributes::Custom(AttributesData::new()) }
impl std::ops::Deref for Attributes {
type Target = HashMap<AttributeKey, String>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl std::ops::DerefMut for Attributes {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
pub(crate) fn attributes_from(operation: &Option<Operation>) -> Option<Attributes> {
@ -80,11 +94,7 @@ pub fn compose_operation(left: &Option<Operation>, right: &Option<Operation>) ->
let left = attr_left.unwrap();
let right = attr_right.unwrap();
log::trace!("compose attributes: a: {:?}, b: {:?}", left, right);
let attr = match (&left, &right) {
(_, Attributes::Custom(_)) => merge_attributes(left, right),
(Attributes::Custom(_), _) => merge_attributes(left, right),
_ => Attributes::Follow,
};
let attr = merge_attributes(left, right);
log::trace!("compose attributes result: {:?}", attr);
attr
}
@ -98,45 +108,24 @@ pub fn transform_operation(left: &Option<Operation>, right: &Option<Operation>)
return Attributes::default();
}
return match attr_r.as_ref().unwrap() {
Attributes::Follow => Attributes::Follow,
Attributes::Custom(_) => attr_r.unwrap(),
};
return attr_r.unwrap();
}
let left = attr_l.unwrap();
let right = attr_r.unwrap();
match (left, right) {
(Attributes::Custom(data_l), Attributes::Custom(data_r)) => {
let result = data_r
.iter()
.fold(AttributesData::new(), |mut new_attr_data, (k, v)| {
if !data_l.contains_key(k) {
new_attr_data.insert(k.clone(), v.clone());
}
new_attr_data
});
Attributes::Custom(result)
},
_ => Attributes::default(),
}
left.iter()
.fold(Attributes::new(), |mut new_attributes, (k, v)| {
if !right.contains_key(k) {
new_attributes.insert(k.clone(), v.clone());
}
new_attributes
})
}
pub fn invert_attributes(attr: Attributes, base: Attributes) -> Attributes {
let attr = attr.data();
let base = base.data();
if attr.is_none() && base.is_none() {
return Attributes::default();
}
let attr = attr.unwrap_or(AttributesData::new());
let base = base.unwrap_or(AttributesData::new());
let base_inverted = base
.iter()
.fold(AttributesData::new(), |mut attributes, (k, v)| {
.fold(Attributes::new(), |mut attributes, (k, v)| {
if base.get(k) != attr.get(k) && attr.contains_key(k) {
attributes.insert(k.clone(), v.clone());
}
@ -150,17 +139,22 @@ pub fn invert_attributes(attr: Attributes, base: Attributes) -> Attributes {
attributes
});
return Attributes::Custom(inverted);
return inverted;
}
pub fn merge_attributes(attributes: Attributes, other: Attributes) -> Attributes {
match (&attributes, &other) {
(Attributes::Custom(data), Attributes::Custom(o_data)) => {
let mut data = data.clone();
data.extend(Some(o_data.clone()));
Attributes::Custom(data)
},
(Attributes::Custom(data), _) => Attributes::Custom(data.clone()),
_ => other,
pub fn merge_attributes(mut attributes: Attributes, other: Attributes) -> Attributes {
attributes.extend(other);
attributes
}
pub trait AttributesRule {
// Remove the empty attribute that its value is empty. e.g. {bold: ""}.
fn remove_empty(self) -> Attributes;
}
impl AttributesRule for Attributes {
fn remove_empty(mut self) -> Attributes {
self.remove_empty_value();
self.into()
}
}

View File

@ -1,23 +1,118 @@
use crate::core::{Attributes, AttributesData};
use crate::core::{Attributes, REMOVE_FLAG};
use derive_more::Display;
use std::{fmt, fmt::Formatter};
#[derive(Clone, Debug, Display, Hash, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Attribute {
pub enum AttributeKey {
#[display(fmt = "bold")]
Bold,
#[display(fmt = "italic")]
Italic,
#[display(fmt = "underline")]
Underline,
#[display(fmt = "strike_through")]
StrikeThrough,
#[display(fmt = "font")]
Font,
#[display(fmt = "size")]
Size,
#[display(fmt = "link")]
Link,
#[display(fmt = "color")]
Color,
#[display(fmt = "background")]
Background,
#[display(fmt = "header")]
Header,
#[display(fmt = "ident")]
Ident,
#[display(fmt = "align")]
Align,
#[display(fmt = "code_block")]
CodeBlock,
#[display(fmt = "list")]
List,
#[display(fmt = "quote_block")]
QuoteBlock,
#[display(fmt = "width")]
Width,
#[display(fmt = "height")]
Height,
#[display(fmt = "style")]
Style,
#[display(fmt = "h1")]
H1,
#[display(fmt = "h2")]
H2,
#[display(fmt = "h3")]
H3,
#[display(fmt = "h4")]
H4,
#[display(fmt = "h5")]
H5,
#[display(fmt = "h6")]
H6,
#[display(fmt = "left")]
LeftAlignment,
#[display(fmt = "center")]
CenterAlignment,
#[display(fmt = "right")]
RightAlignment,
#[display(fmt = "justify")]
JustifyAlignment,
#[display(fmt = "bullet")]
Bullet,
#[display(fmt = "ordered")]
Ordered,
#[display(fmt = "checked")]
Checked,
#[display(fmt = "unchecked")]
UnChecked,
}
#[derive(Debug)]
pub enum AttributeScope {
Inline,
Block,
Embeds,
Ignore,
}
#[derive(Debug)]
pub struct Attribute {
pub key: AttributeKey,
pub value: String,
pub scope: AttributeScope,
}
impl fmt::Display for Attribute {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let s = format!("{:?}:{} {:?}", self.key, self.value, self.scope);
f.write_str(&s)
}
}
pub struct AttrsBuilder {
inner: AttributesData,
inner: Attributes,
}
macro_rules! impl_bool_attribute {
($name: ident,$key: expr) => {
pub fn $name(self, value: bool) -> Self {
let value = match value {
true => "true",
false => REMOVE_FLAG,
};
self.insert($key, value)
}
};
}
impl AttrsBuilder {
pub fn new() -> Self {
Self {
inner: AttributesData::default(),
inner: Attributes::default(),
}
}
@ -26,24 +121,73 @@ impl AttrsBuilder {
self
}
pub fn remove(mut self, attribute: &Attribute) -> Self {
self.inner.remove(attribute);
pub fn insert<T: Into<String>>(mut self, key: AttributeKey, value: T) -> Self {
self.inner.add(key.with_value(value));
self
}
pub fn bold(self, bold: bool) -> Self {
match bold {
true => self.add(Attribute::Bold),
false => self.remove(&Attribute::Bold),
}
pub fn remove<T: Into<String>>(mut self, key: AttributeKey) -> Self {
self.inner.add(key.with_value(REMOVE_FLAG));
self
}
pub fn italic(self, italic: bool) -> Self {
match italic {
true => self.add(Attribute::Italic),
false => self.remove(&Attribute::Italic),
}
}
impl_bool_attribute!(bold, AttributeKey::Bold);
impl_bool_attribute!(italic, AttributeKey::Italic);
impl_bool_attribute!(underline, AttributeKey::Underline);
impl_bool_attribute!(strike_through, AttributeKey::StrikeThrough);
pub fn build(self) -> Attributes { Attributes::Custom(self.inner) }
pub fn build(self) -> Attributes { self.inner }
}
impl AttributeKey {
pub fn with_value<T: Into<String>>(&self, value: T) -> Attribute {
let key = self.clone();
let value: String = value.into();
match self {
AttributeKey::Bold
| AttributeKey::Italic
| AttributeKey::Underline
| AttributeKey::StrikeThrough
| AttributeKey::Link
| AttributeKey::Color
| AttributeKey::Background
| AttributeKey::Font
| AttributeKey::Size => Attribute {
key,
value,
scope: AttributeScope::Inline,
},
AttributeKey::Header
| AttributeKey::H1
| AttributeKey::H2
| AttributeKey::H3
| AttributeKey::H4
| AttributeKey::H5
| AttributeKey::H6
| AttributeKey::LeftAlignment
| AttributeKey::CenterAlignment
| AttributeKey::RightAlignment
| AttributeKey::JustifyAlignment
| AttributeKey::Ident
| AttributeKey::Align
| AttributeKey::CodeBlock
| AttributeKey::List
| AttributeKey::Bullet
| AttributeKey::Ordered
| AttributeKey::Checked
| AttributeKey::UnChecked
| AttributeKey::QuoteBlock => Attribute {
key,
value,
scope: AttributeScope::Block,
},
AttributeKey::Width | AttributeKey::Height | AttributeKey::Style => Attribute {
key,
value,
scope: AttributeScope::Ignore,
},
}
}
}

View File

@ -1,72 +0,0 @@
use crate::core::{Attribute, Attributes};
use std::{collections::HashMap, fmt};
pub(crate) const REMOVE_FLAG: &'static str = "";
pub(crate) fn should_remove(s: &str) -> bool { s == REMOVE_FLAG }
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct AttributesData {
#[serde(skip_serializing_if = "HashMap::is_empty")]
#[serde(flatten)]
pub(crate) inner: HashMap<Attribute, String>,
}
impl fmt::Display for AttributesData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("{:?}", self.inner))
}
}
impl AttributesData {
pub fn new() -> Self {
AttributesData {
inner: HashMap::new(),
}
}
pub fn is_empty(&self) -> bool { self.inner.is_empty() }
pub fn add(&mut self, attribute: Attribute) { self.inner.insert(attribute, "true".to_owned()); }
pub fn remove(&mut self, attribute: &Attribute) {
self.inner.insert(attribute.clone(), REMOVE_FLAG.to_owned());
}
// Remove the key if its value is empty. e.g. { bold: "" }
pub fn remove_empty_value(&mut self) { self.inner.retain(|_, v| !should_remove(v)); }
pub fn extend(&mut self, other: Option<AttributesData>) {
if other.is_none() {
return;
}
self.inner.extend(other.unwrap().inner);
}
// Update self attributes by constructing new attributes from the other if it's
// not None and replace the key/value with self key/value.
pub fn merge(&mut self, other: Option<AttributesData>) {
if other.is_none() {
return;
}
let mut new_attributes = other.unwrap().inner;
self.inner.iter().for_each(|(k, v)| {
new_attributes.insert(k.clone(), v.clone());
});
self.inner = new_attributes;
}
}
impl std::ops::Deref for AttributesData {
type Target = HashMap<Attribute, String>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl std::ops::DerefMut for AttributesData {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
impl std::convert::Into<Attributes> for AttributesData {
fn into(self) -> Attributes { Attributes::Custom(self) }
}

View File

@ -1,7 +1,5 @@
mod attributes;
mod builder;
mod data;
pub use attributes::*;
pub use builder::*;
pub use data::*;

View File

@ -1,28 +1,41 @@
use crate::core::{Attributes, AttributesData, Delta, Interval, Operation};
use std::{cmp::min, slice::Iter};
use crate::{
core::{Attributes, Delta, Interval, Operation},
errors::{ErrorBuilder, OTError, OTErrorCode},
};
use std::{
cmp::min,
ops::{Deref, DerefMut},
slice::Iter,
};
pub struct Cursor<'a> {
delta: &'a Delta,
interval: Interval,
iterator: Iter<'a, Operation>,
offset: usize,
offset_op: Option<&'a Operation>,
}
impl<'a> Cursor<'a> {
pub fn new(delta: &'a Delta, interval: Interval) -> Cursor<'a> {
let mut cursor = Self {
let cursor = Self {
delta,
interval,
iterator: delta.ops.iter(),
offset: 0,
offset_op: None,
};
cursor
}
pub fn next_op(&mut self) -> Option<Operation> {
let mut next_op = self.iterator.next();
let mut find_op = None;
let mut next_op = self.offset_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() {
let op = next_op.unwrap();
if self.offset < self.interval.start {
@ -69,6 +82,34 @@ impl<'a> Cursor<'a> {
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());
}
let mut offset = 0;
while let Some(op) = self.iterator.next() {
if offset != 0 {
self.offset = offset;
}
offset += op.length();
self.offset_op = Some(op);
if offset >= index {
break;
}
}
Ok(())
}
}
pub struct DeltaIter<'a> {
@ -88,6 +129,11 @@ 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)?;
Ok(())
}
}
impl<'a> Iterator for DeltaIter<'a> {
@ -95,12 +141,12 @@ impl<'a> Iterator for DeltaIter<'a> {
fn next(&mut self) -> Option<Self::Item> { self.cursor.next_op() }
}
pub struct DeltaAttributesIter<'a> {
pub struct AttributesIter<'a> {
delta_iter: DeltaIter<'a>,
interval: Interval,
}
impl<'a> DeltaAttributesIter<'a> {
impl<'a> AttributesIter<'a> {
pub fn new(delta: &'a Delta) -> Self {
let interval = Interval::new(0, usize::MAX);
Self::from_interval(delta, interval)
@ -115,7 +161,17 @@ impl<'a> DeltaAttributesIter<'a> {
}
}
impl<'a> Iterator for DeltaAttributesIter<'a> {
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();
@ -123,28 +179,34 @@ impl<'a> Iterator for DeltaAttributesIter<'a> {
return None;
}
let mut length: usize = 0;
let mut attributes_data = AttributesData::new();
let mut attributes = Attributes::new();
match next_op.unwrap() {
Operation::Delete(_n) => {},
Operation::Retain(retain) => {
if let Attributes::Custom(data) = &retain.attributes {
log::debug!("extend retain attributes with {} ", &data);
attributes_data.extend(Some(data.clone()));
}
log::debug!("extend retain attributes with {} ", &retain.attributes);
attributes.extend(retain.attributes.clone());
length = retain.n;
},
Operation::Insert(insert) => {
if let Attributes::Custom(data) = &insert.attributes {
log::debug!("extend insert attributes with {} ", &data);
attributes_data.extend(Some(data.clone()));
}
log::debug!("extend insert attributes with {} ", &insert.attributes);
attributes.extend(insert.attributes.clone());
length = insert.num_chars();
},
}
let attribute: Attributes = attributes_data.into();
Some((length, attribute))
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,
}
}

View File

@ -3,12 +3,7 @@ use crate::{
errors::{ErrorBuilder, OTError, OTErrorCode},
};
use bytecount::num_chars;
use std::{
cmp::{min, Ordering},
fmt,
iter::FromIterator,
str::FromStr,
};
use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr};
#[derive(Clone, Debug, PartialEq)]
pub struct Delta {
@ -454,7 +449,7 @@ impl Delta {
for op in &self.ops {
match &op {
Operation::Retain(retain) => {
inverted.retain(retain.n, Attributes::Follow);
inverted.retain(retain.n, Attributes::default());
// TODO: use advance_by instead, but it's unstable now
// chars.advance_by(retain.num)
@ -531,35 +526,31 @@ impl Delta {
pub fn is_empty(&self) -> bool { self.ops.is_empty() }
pub fn get_attributes(&self, interval: Interval) -> Attributes {
let mut attributes_data = AttributesData::new();
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 let Attributes::Custom(data) = &retain.attributes {
if interval.contains_range(offset, offset + retain.n) {
log::debug!("extend retain attributes with {} ", &data);
attributes_data.extend(Some(data.clone()));
}
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 let Attributes::Custom(data) = &insert.attributes {
if interval.contains_range(offset, offset + end) {
log::debug!("extend insert attributes with {} ", &data);
attributes_data.extend(Some(data.clone()));
}
if interval.contains_range(offset, offset + end) {
log::debug!("extend insert attributes with {} ", &insert.attributes);
attributes.extend(insert.attributes.clone());
}
offset += end;
},
});
let attribute: Attributes = attributes_data.into();
log::debug!("Get attributes result: {} ", &attribute);
attribute
log::debug!("Get attributes result: {} ", &attributes);
attributes
}
pub fn to_json(&self) -> String { serde_json::to_string(self).unwrap_or("".to_owned()) }

View File

@ -52,12 +52,7 @@ impl Operation {
}
}
pub fn has_attribute(&self) -> bool {
match self.get_attributes() {
Attributes::Follow => false,
Attributes::Custom(data) => !data.is_empty(),
}
}
pub fn has_attribute(&self) -> bool { !self.get_attributes().is_empty() }
pub fn length(&self) -> usize {
match self {
@ -139,29 +134,15 @@ impl Retain {
attributes
);
match &attributes {
Attributes::Follow => {
log::debug!("Follow attribute: {:?}", self.attributes);
self.n += n;
None
},
Attributes::Custom(_) => {
if self.attributes == attributes {
self.n += n;
None
} else {
Some(Builder::retain(n).attributes(attributes).build())
}
},
if self.attributes == attributes {
self.n += n;
None
} else {
Some(Builder::retain(n).attributes(attributes).build())
}
}
pub fn is_plain(&self) -> bool {
match &self.attributes {
Attributes::Follow => true,
Attributes::Custom(data) => data.is_empty(),
}
}
pub fn is_plain(&self) -> bool { self.attributes.is_empty() }
}
impl std::convert::From<usize> for Retain {
@ -217,19 +198,11 @@ impl Insert {
pub fn num_chars(&self) -> usize { num_chars(self.s.as_bytes()) as _ }
pub fn merge_or_new_op(&mut self, s: &str, attributes: Attributes) -> Option<Operation> {
match &attributes {
Attributes::Follow => {
self.s += s;
return None;
},
Attributes::Custom(_) => {
if self.attributes == attributes {
self.s += s;
None
} else {
Some(Builder::insert(s).attributes(attributes).build())
}
},
if self.attributes == attributes {
self.s += s;
None
} else {
Some(Builder::insert(s).attributes(attributes).build())
}
}
}
@ -247,9 +220,4 @@ impl std::convert::From<&str> for Insert {
fn from(s: &str) -> Self { Insert::from(s.to_owned()) }
}
fn is_empty(attributes: &Attributes) -> bool {
match attributes {
Attributes::Follow => true,
Attributes::Custom(data) => data.is_empty(),
}
}
fn is_empty(attributes: &Attributes) -> bool { attributes.is_empty() }

View File

@ -337,7 +337,7 @@ fn delta_compose_attr_delta_with_attr_delta_test2() {
#[test]
fn delta_compose_attr_delta_with_no_attr_delta_test() {
let expected = r#"[{"insert":"1234567","attributes":{"bold":"true"}}]"#;
let expected = r#"[{"insert":"123456","attributes":{"bold":"true"}},{"insert":"7"}]"#;
let ops = vec![
InsertBold(0, "123456", Interval::new(0, 6)),

View File

@ -1,5 +1,8 @@
use derive_more::Display;
use flowy_ot::{client::Document, core::*};
use flowy_ot::{
client::Document,
core::{REMOVE_FLAG, *},
};
use rand::{prelude::*, Rng as WrappedRng};
use std::{sync::Once, time::Duration};
@ -86,19 +89,25 @@ impl OpTester {
TestOp::InsertBold(delta_i, s, interval) => {
let document = &mut self.documents[*delta_i];
document.insert(interval.start, s).unwrap();
document.format(*interval, Attribute::Bold, true).unwrap();
document
.format(*interval, AttributeKey::Bold.with_value("true".to_owned()))
.unwrap();
},
TestOp::Bold(delta_i, interval, enable) => {
let document = &mut self.documents[*delta_i];
document
.format(*interval, Attribute::Bold, *enable)
.unwrap();
let attribute = match *enable {
true => AttributeKey::Bold.with_value("true".to_owned()),
false => AttributeKey::Bold.with_value("".to_owned()),
};
document.format(*interval, attribute).unwrap();
},
TestOp::Italic(delta_i, interval, enable) => {
let document = &mut self.documents[*delta_i];
document
.format(*interval, Attribute::Italic, *enable)
.unwrap();
let attribute = match *enable {
true => AttributeKey::Italic.with_value("true"),
false => AttributeKey::Italic.with_value(REMOVE_FLAG),
};
document.format(*interval, attribute).unwrap();
},
TestOp::Transform(delta_a_i, delta_b_i) => {
let (a_prime, b_prime) = self.documents[*delta_a_i]
@ -207,7 +216,7 @@ impl Rng {
delta.delete(i);
},
_ => {
delta.retain(i, Attributes::Follow);
delta.retain(i, Attributes::default());
},
}
}