mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
remove attributes type follow
This commit is contained in:
parent
039b4917ed
commit
e4a64fd06a
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) }
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
mod attributes;
|
||||
mod builder;
|
||||
mod data;
|
||||
|
||||
pub use attributes::*;
|
||||
pub use builder::*;
|
||||
pub use data::*;
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()) }
|
||||
|
@ -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() }
|
||||
|
@ -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)),
|
||||
|
@ -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());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user