add rich_text crate

This commit is contained in:
appflowy 2021-12-07 19:59:08 +08:00
parent 2f413a8e67
commit 8a9a23ddbe
44 changed files with 296 additions and 280 deletions

View File

@ -13,7 +13,7 @@ use flowy_document_infra::{
entities::ws::{WsDataType, WsDocumentData},
protobuf::{Doc, RevId, RevType, Revision, RevisionRange, UpdateDocParams},
};
use lib_ot::core::{OperationTransformable, RichTextDelta};
use lib_ot::{core::OperationTransformable, rich_text::RichTextDelta};
use parking_lot::RwLock;
use protobuf::Message;
use sqlx::PgPool;

View File

@ -1,6 +1,6 @@
use crate::document::helper::{DocScript, DocumentTest};
use flowy_document_infra::core::{Document, FlowyDoc};
use lib_ot::core::{Interval, RichTextAttribute};
use lib_ot::{core::Interval, rich_text::RichTextAttribute};
#[rustfmt::skip]
// ┌─────────┐ ┌─────────┐

View File

@ -12,8 +12,9 @@ use tokio::time::{sleep, Duration};
// use crate::helper::*;
use crate::util::helper::{spawn_server, TestServer};
use flowy_document_infra::{entities::doc::DocIdentifier, protobuf::UpdateDocParams};
use lib_ot::core::{RichTextAttribute, RichTextDelta, Interval};
use lib_ot::rich_text::{RichTextAttribute, RichTextDelta};
use parking_lot::RwLock;
use lib_ot::core::Interval;
pub struct DocumentTest {
server: TestServer,

View File

@ -17,7 +17,10 @@ use flowy_document_infra::{
errors::DocumentResult,
};
use lib_infra::retry::{ExponentialBackoff, Retry};
use lib_ot::core::{Interval, RichTextAttribute, RichTextDelta};
use lib_ot::{
core::Interval,
rich_text::{RichTextAttribute, RichTextDelta},
};
use lib_ws::WsConnectState;
use std::{convert::TryFrom, sync::Arc};
use tokio::sync::{mpsc, mpsc::UnboundedSender, oneshot};

View File

@ -6,7 +6,10 @@ use flowy_document_infra::{
errors::DocumentError,
};
use futures::stream::StreamExt;
use lib_ot::core::{Interval, OperationTransformable, RichTextAttribute, RichTextDelta};
use lib_ot::{
core::{Interval, OperationTransformable},
rich_text::{RichTextAttribute, RichTextDelta},
};
use std::{convert::TryFrom, sync::Arc};
use tokio::sync::{mpsc, oneshot, RwLock};

View File

@ -8,7 +8,7 @@ use flowy_document_infra::{
util::RevIdCounter,
};
use lib_infra::future::ResultFuture;
use lib_ot::core::{OperationTransformable, RichTextDelta};
use lib_ot::{core::OperationTransformable, rich_text::RichTextDelta};
use std::sync::Arc;
use tokio::sync::mpsc;

View File

@ -9,7 +9,10 @@ use flowy_database::{ConnectionPool, SqliteConnection};
use flowy_document_infra::entities::doc::{revision_from_doc, Doc, RevId, RevType, Revision, RevisionRange};
use futures::stream::StreamExt;
use lib_infra::future::ResultFuture;
use lib_ot::core::{Operation, OperationTransformable, RichTextDelta};
use lib_ot::{
core::{Operation, OperationTransformable},
rich_text::RichTextDelta,
};
use std::{collections::VecDeque, sync::Arc, time::Duration};
use tokio::{
sync::{broadcast, mpsc, RwLock},

View File

@ -1,8 +1,9 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
use crate::editor::{TestBuilder, TestOp::*};
use flowy_document_infra::core::{FlowyDoc, PlainDoc};
use lib_ot::core::{Interval, OperationTransformable, NEW_LINE, WHITESPACE, FlowyStr, RichTextDelta};
use lib_ot::core::{Interval, OperationTransformable, NEW_LINE, WHITESPACE, FlowyStr};
use unicode_segmentation::UnicodeSegmentation;
use lib_ot::rich_text::RichTextDelta;
#[test]
fn attributes_bold_added() {

View File

@ -6,7 +6,10 @@ mod undo_redo_test;
use derive_more::Display;
use flowy_document_infra::core::{CustomDocument, Document};
use lib_ot::core::*;
use lib_ot::{
core::*,
rich_text::{RichTextAttribute, RichTextAttributes, RichTextDelta},
};
use rand::{prelude::*, Rng as WrappedRng};
use std::{sync::Once, time::Duration};

View File

@ -1,7 +1,10 @@
#![allow(clippy::all)]
use crate::editor::{Rng, TestBuilder, TestOp::*};
use flowy_document_infra::core::{FlowyDoc, PlainDoc};
use lib_ot::core::*;
use lib_ot::{
core::*,
rich_text::{AttributeBuilder, RichTextAttribute, RichTextAttributes, RichTextDelta},
};
#[test]
fn attributes_insert_text() {

View File

@ -1,5 +1,8 @@
use flowy_document_infra::core::{Document, PlainDoc};
use lib_ot::core::*;
use lib_ot::{
core::*,
rich_text::{AttributeBuilder, RichTextAttribute, RichTextAttributeValue, RichTextDelta},
};
#[test]
fn operation_insert_serialize_test() {

View File

@ -6,7 +6,10 @@ use crate::{
errors::DocumentError,
user_default::doc_initial_delta,
};
use lib_ot::core::*;
use lib_ot::{
core::*,
rich_text::{RichTextAttribute, RichTextDelta},
};
use tokio::sync::mpsc;
pub trait CustomDocument {

View File

@ -1,5 +1,8 @@
use crate::core::extensions::DeleteExt;
use lib_ot::core::{DeltaBuilder, Interval, RichTextDelta};
use lib_ot::{
core::{DeltaBuilder, Interval},
rich_text::RichTextDelta,
};
pub struct DefaultDelete {}
impl DeleteExt for DefaultDelete {

View File

@ -1,13 +1,7 @@
use crate::{core::extensions::DeleteExt, util::is_newline};
use lib_ot::core::{
plain_attributes,
Attributes,
CharMetric,
DeltaBuilder,
DeltaIter,
Interval,
RichTextDelta,
NEW_LINE,
use lib_ot::{
core::{Attributes, CharMetric, DeltaBuilder, DeltaIter, Interval, NEW_LINE},
rich_text::{plain_attributes, RichTextDelta},
};
pub struct PreserveLineFormatOnMerge {}

View File

@ -1,5 +1,8 @@
use crate::util::find_newline;
use lib_ot::core::{plain_attributes, AttributeScope, RichTextAttribute, RichTextDelta, RichTextOperation};
use lib_ot::{
core::RichTextOperation,
rich_text::{plain_attributes, AttributeScope, RichTextAttribute, RichTextDelta},
};
pub(crate) fn line_break(
op: &RichTextOperation,

View File

@ -2,14 +2,9 @@ use crate::{
core::extensions::{format::helper::line_break, FormatExt},
util::find_newline,
};
use lib_ot::core::{
plain_attributes,
AttributeScope,
DeltaBuilder,
DeltaIter,
Interval,
RichTextAttribute,
RichTextDelta,
use lib_ot::{
core::{DeltaBuilder, DeltaIter, Interval},
rich_text::{plain_attributes, AttributeScope, RichTextAttribute, RichTextDelta},
};
pub struct ResolveBlockFormat {}

View File

@ -2,7 +2,10 @@ use crate::{
core::extensions::{format::helper::line_break, FormatExt},
util::find_newline,
};
use lib_ot::core::{AttributeScope, DeltaBuilder, DeltaIter, Interval, RichTextAttribute, RichTextDelta};
use lib_ot::{
core::{DeltaBuilder, DeltaIter, Interval},
rich_text::{AttributeScope, RichTextAttribute, RichTextDelta},
};
pub struct ResolveInlineFormat {}
impl FormatExt for ResolveInlineFormat {

View File

@ -1,11 +1,7 @@
use crate::{core::extensions::InsertExt, util::is_newline};
use lib_ot::core::{
attributes_except_header,
is_empty_line_at_index,
DeltaBuilder,
DeltaIter,
RichTextAttributeKey,
RichTextDelta,
use lib_ot::{
core::{is_empty_line_at_index, DeltaBuilder, DeltaIter},
rich_text::{attributes_except_header, RichTextAttributeKey, RichTextDelta},
};
pub struct AutoExitBlock {}

View File

@ -1,12 +1,7 @@
use crate::{core::extensions::InsertExt, util::is_whitespace};
use lib_ot::core::{
count_utf16_code_units,
plain_attributes,
DeltaBuilder,
DeltaIter,
RichTextAttribute,
RichTextAttributes,
RichTextDelta,
use lib_ot::{
core::{count_utf16_code_units, DeltaBuilder, DeltaIter},
rich_text::{plain_attributes, RichTextAttribute, RichTextAttributes, RichTextDelta},
};
use std::cmp::min;
use url::Url;

View File

@ -1,12 +1,7 @@
use crate::core::extensions::InsertExt;
use lib_ot::core::{
Attributes,
DeltaBuilder,
DeltaIter,
RichTextAttributeKey,
RichTextAttributes,
RichTextDelta,
NEW_LINE,
use lib_ot::{
core::{Attributes, DeltaBuilder, DeltaIter, NEW_LINE},
rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta},
};
pub struct DefaultInsertAttribute {}

View File

@ -2,7 +2,7 @@ use crate::core::extensions::InsertExt;
pub use auto_exit_block::*;
pub use auto_format::*;
pub use default_insert::*;
use lib_ot::core::RichTextDelta;
use lib_ot::rich_text::RichTextDelta;
pub use preserve_block_format::*;
pub use preserve_inline_format::*;
pub use reset_format_on_new_line::*;

View File

@ -1,14 +1,14 @@
use crate::{core::extensions::InsertExt, util::is_newline};
use lib_ot::core::{
attributes_except_header,
plain_attributes,
DeltaBuilder,
DeltaIter,
RichTextAttribute,
RichTextAttributeKey,
RichTextAttributes,
RichTextDelta,
NEW_LINE,
use lib_ot::{
core::{DeltaBuilder, DeltaIter, NEW_LINE},
rich_text::{
attributes_except_header,
plain_attributes,
RichTextAttribute,
RichTextAttributeKey,
RichTextAttributes,
RichTextDelta,
},
};
pub struct PreserveBlockFormatOnInsert {}

View File

@ -2,14 +2,9 @@ use crate::{
core::extensions::InsertExt,
util::{contain_newline, is_newline},
};
use lib_ot::core::{
plain_attributes,
DeltaBuilder,
DeltaIter,
OpNewline,
RichTextAttributeKey,
RichTextDelta,
NEW_LINE,
use lib_ot::{
core::{DeltaBuilder, DeltaIter, OpNewline, NEW_LINE},
rich_text::{plain_attributes, RichTextAttributeKey, RichTextDelta},
};
pub struct PreserveInlineFormat {}

View File

@ -1,12 +1,7 @@
use crate::{core::extensions::InsertExt, util::is_newline};
use lib_ot::core::{
CharMetric,
DeltaBuilder,
DeltaIter,
RichTextAttributeKey,
RichTextAttributes,
RichTextDelta,
NEW_LINE,
use lib_ot::{
core::{CharMetric, DeltaBuilder, DeltaIter, NEW_LINE},
rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta},
};
pub struct ResetLineFormatOnNewLine {}

View File

@ -2,7 +2,10 @@ pub use delete::*;
pub use format::*;
pub use insert::*;
use lib_ot::core::{Interval, RichTextAttribute, RichTextDelta};
use lib_ot::{
core::Interval,
rich_text::{RichTextAttribute, RichTextDelta},
};
mod delete;
mod format;

View File

@ -1,4 +1,4 @@
use lib_ot::core::RichTextDelta;
use lib_ot::rich_text::RichTextDelta;
const MAX_UNDOS: usize = 20;

View File

@ -1,7 +1,8 @@
use crate::core::extensions::*;
use lib_ot::{
core::{trim, Interval, RichTextAttribute, RichTextDelta},
core::{trim, Interval},
errors::{ErrorBuilder, OTError, OTErrorCode},
rich_text::{RichTextAttribute, RichTextDelta},
};
pub const RECORD_THRESHOLD: usize = 400; // in milliseconds

View File

@ -1,5 +1,5 @@
use flowy_derive::ProtoBuf;
use lib_ot::{core::RichTextDelta, errors::OTError};
use lib_ot::{errors::OTError, rich_text::RichTextDelta};
#[derive(ProtoBuf, Default, Debug, Clone)]
pub struct CreateDocParams {

View File

@ -1,6 +1,6 @@
use crate::{entities::doc::Doc, util::md5};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use lib_ot::core::RichTextDelta;
use lib_ot::rich_text::RichTextDelta;
use std::{fmt::Formatter, ops::RangeInclusive};
#[derive(Debug, ProtoBuf_Enum, Clone, Eq, PartialEq)]

View File

@ -1,4 +1,4 @@
use lib_ot::core::{DeltaBuilder, RichTextDelta};
use lib_ot::{core::DeltaBuilder, rich_text::RichTextDelta};
#[inline]
pub fn doc_initial_delta() -> RichTextDelta { DeltaBuilder::new().insert("\n").build() }

View File

@ -1,170 +0,0 @@
use crate::{
core::{
Attributes,
OperationTransformable,
RichTextAttribute,
RichTextAttributeKey,
RichTextAttributeValue,
RichTextOperation,
},
errors::OTError,
};
use std::{collections::HashMap, fmt};
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RichTextAttributes {
pub(crate) inner: HashMap<RichTextAttributeKey, RichTextAttributeValue>,
}
impl std::default::Default for RichTextAttributes {
fn default() -> Self {
Self {
inner: HashMap::with_capacity(0),
}
}
}
impl fmt::Display for RichTextAttributes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("{:?}", self.inner)) }
}
pub fn plain_attributes() -> RichTextAttributes { RichTextAttributes::default() }
impl RichTextAttributes {
pub fn new() -> Self { RichTextAttributes { inner: HashMap::new() } }
pub fn is_empty(&self) -> bool { self.inner.is_empty() }
pub fn add(&mut self, attribute: RichTextAttribute) {
let RichTextAttribute { key, value, scope: _ } = attribute;
self.inner.insert(key, value);
}
pub fn add_kv(&mut self, key: RichTextAttributeKey, value: RichTextAttributeValue) {
self.inner.insert(key, value);
}
pub fn delete(&mut self, key: &RichTextAttributeKey) {
self.inner.insert(key.clone(), RichTextAttributeValue(None));
}
pub fn mark_all_as_removed_except(&mut self, attribute: Option<RichTextAttributeKey>) {
match attribute {
None => {
self.inner.iter_mut().for_each(|(_k, v)| v.0 = None);
},
Some(attribute) => {
self.inner.iter_mut().for_each(|(k, v)| {
if k != &attribute {
v.0 = None;
}
});
},
}
}
pub fn remove(&mut self, key: RichTextAttributeKey) { self.inner.retain(|k, _| k != &key); }
// pub fn block_attributes_except_header(attributes: &Attributes) -> Attributes
// { let mut new_attributes = Attributes::new();
// attributes.iter().for_each(|(k, v)| {
// if k != &AttributeKey::Header {
// new_attributes.insert(k.clone(), v.clone());
// }
// });
//
// new_attributes
// }
// Update inner 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<RichTextAttributes>) {
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 Attributes for RichTextAttributes {
fn is_empty(&self) -> bool { self.inner.is_empty() }
fn remove_empty(&mut self) { self.inner.retain(|_, v| v.0.is_some()); }
fn extend_other(&mut self, other: Self) { self.inner.extend(other.inner); }
}
impl OperationTransformable for RichTextAttributes {
fn compose(&self, other: &Self) -> Result<Self, OTError>
where
Self: Sized,
{
let mut attributes = self.clone();
attributes.extend_other(other.clone());
Ok(attributes)
}
fn transform(&self, other: &Self) -> Result<(Self, Self), OTError>
where
Self: Sized,
{
let a = self
.iter()
.fold(RichTextAttributes::new(), |mut new_attributes, (k, v)| {
if !other.contains_key(k) {
new_attributes.insert(k.clone(), v.clone());
}
new_attributes
});
let b = other
.iter()
.fold(RichTextAttributes::new(), |mut new_attributes, (k, v)| {
if !self.contains_key(k) {
new_attributes.insert(k.clone(), v.clone());
}
new_attributes
});
Ok((a, b))
}
fn invert(&self, other: &Self) -> Self {
let base_inverted = other.iter().fold(RichTextAttributes::new(), |mut attributes, (k, v)| {
if other.get(k) != self.get(k) && self.contains_key(k) {
attributes.insert(k.clone(), v.clone());
}
attributes
});
let inverted = self.iter().fold(base_inverted, |mut attributes, (k, _)| {
if other.get(k) != self.get(k) && !other.contains_key(k) {
attributes.delete(k);
}
attributes
});
inverted
}
}
impl std::ops::Deref for RichTextAttributes {
type Target = HashMap<RichTextAttributeKey, RichTextAttributeValue>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl std::ops::DerefMut for RichTextAttributes {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
pub fn attributes_except_header(op: &RichTextOperation) -> RichTextAttributes {
let mut attributes = op.get_attributes();
attributes.remove(RichTextAttributeKey::Header);
attributes
}

View File

@ -1,5 +1,5 @@
use crate::{
core::{attributes::*, operation::*, DeltaIter, FlowyStr, Interval, OperationTransformable, MAX_IV_LEN},
core::{operation::*, DeltaIter, FlowyStr, Interval, OperationTransformable, MAX_IV_LEN},
errors::{ErrorBuilder, OTError, OTErrorCode},
};
use bytes::Bytes;
@ -469,8 +469,6 @@ fn transform_op_attribute<T: Attributes>(left: &Option<Operation<T>>, right: &Op
left.transform(&right).unwrap().0
}
pub type RichTextDelta = Delta<RichTextAttributes>;
impl<T> Delta<T>
where
T: Attributes + DeserializeOwned,
@ -503,22 +501,31 @@ where
}
}
impl FromStr for RichTextDelta {
impl<T> FromStr for Delta<T>
where
T: Attributes,
{
type Err = ();
fn from_str(s: &str) -> Result<RichTextDelta, Self::Err> {
fn from_str(s: &str) -> Result<Delta<T>, Self::Err> {
let mut delta = Delta::with_capacity(1);
delta.add(Operation::Insert(s.into()));
Ok(delta)
}
}
impl std::convert::TryFrom<Vec<u8>> for RichTextDelta {
impl<T> std::convert::TryFrom<Vec<u8>> for Delta<T>
where
T: Attributes + DeserializeOwned,
{
type Error = OTError;
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> { Delta::from_bytes(bytes) }
}
impl std::convert::TryFrom<Bytes> for RichTextDelta {
impl<T> std::convert::TryFrom<Bytes> for Delta<T>
where
T: Attributes + DeserializeOwned,
{
type Error = OTError;
fn try_from(bytes: Bytes) -> Result<Self, Self::Error> { Delta::from_bytes(&bytes) }

View File

@ -1,5 +1,8 @@
use super::cursor::*;
use crate::core::{Attributes, Delta, Interval, Operation, RichTextAttributes, NEW_LINE};
use crate::{
core::{Attributes, Delta, Interval, Operation, NEW_LINE},
rich_text::RichTextAttributes,
};
use std::ops::{Deref, DerefMut};
pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize;

View File

@ -1,11 +1,9 @@
mod attributes;
mod delta;
mod flowy_str;
mod interval;
mod operation;
use crate::errors::OTError;
pub use attributes::*;
pub use delta::*;
pub use flowy_str::*;
pub use interval::*;

View File

@ -1,4 +1,7 @@
use crate::core::{Attributes, Operation, RichTextAttributes};
use crate::{
core::{Attributes, Operation},
rich_text::RichTextAttributes,
};
pub type RichTextOpBuilder = OpBuilder<RichTextAttributes>;

View File

@ -1,4 +1,7 @@
use crate::core::{FlowyStr, Interval, OpBuilder, OperationTransformable, RichTextAttribute, RichTextAttributes};
use crate::{
core::{FlowyStr, Interval, OpBuilder, OperationTransformable},
rich_text::{RichTextAttribute, RichTextAttributes},
};
use serde::__private::Formatter;
use std::{
cmp::min,

View File

@ -1,2 +1,3 @@
pub mod core;
pub mod errors;
pub mod rich_text;

View File

@ -1,11 +1,179 @@
#![allow(non_snake_case)]
use crate::{block_attribute, core::RichTextAttributes, ignore_attribute, inline_attribute, list_attribute};
use crate::{
block_attribute,
core::{Attributes, OperationTransformable, RichTextOperation},
errors::OTError,
ignore_attribute,
inline_attribute,
list_attribute,
};
use lazy_static::lazy_static;
use std::{collections::HashSet, fmt, fmt::Formatter, iter::FromIterator};
use std::{
collections::{HashMap, HashSet},
fmt,
fmt::Formatter,
iter::FromIterator,
};
use strum_macros::Display;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RichTextAttributes {
pub(crate) inner: HashMap<RichTextAttributeKey, RichTextAttributeValue>,
}
impl std::default::Default for RichTextAttributes {
fn default() -> Self {
Self {
inner: HashMap::with_capacity(0),
}
}
}
impl fmt::Display for RichTextAttributes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("{:?}", self.inner)) }
}
pub fn plain_attributes() -> RichTextAttributes { RichTextAttributes::default() }
impl RichTextAttributes {
pub fn new() -> Self { RichTextAttributes { inner: HashMap::new() } }
pub fn is_empty(&self) -> bool { self.inner.is_empty() }
pub fn add(&mut self, attribute: RichTextAttribute) {
let RichTextAttribute { key, value, scope: _ } = attribute;
self.inner.insert(key, value);
}
pub fn add_kv(&mut self, key: RichTextAttributeKey, value: RichTextAttributeValue) {
self.inner.insert(key, value);
}
pub fn delete(&mut self, key: &RichTextAttributeKey) {
self.inner.insert(key.clone(), RichTextAttributeValue(None));
}
pub fn mark_all_as_removed_except(&mut self, attribute: Option<RichTextAttributeKey>) {
match attribute {
None => {
self.inner.iter_mut().for_each(|(_k, v)| v.0 = None);
},
Some(attribute) => {
self.inner.iter_mut().for_each(|(k, v)| {
if k != &attribute {
v.0 = None;
}
});
},
}
}
pub fn remove(&mut self, key: RichTextAttributeKey) { self.inner.retain(|k, _| k != &key); }
// pub fn block_attributes_except_header(attributes: &Attributes) -> Attributes
// { let mut new_attributes = Attributes::new();
// attributes.iter().for_each(|(k, v)| {
// if k != &AttributeKey::Header {
// new_attributes.insert(k.clone(), v.clone());
// }
// });
//
// new_attributes
// }
// Update inner 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<RichTextAttributes>) {
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 Attributes for RichTextAttributes {
fn is_empty(&self) -> bool { self.inner.is_empty() }
fn remove_empty(&mut self) { self.inner.retain(|_, v| v.0.is_some()); }
fn extend_other(&mut self, other: Self) { self.inner.extend(other.inner); }
}
impl OperationTransformable for RichTextAttributes {
fn compose(&self, other: &Self) -> Result<Self, OTError>
where
Self: Sized,
{
let mut attributes = self.clone();
attributes.extend_other(other.clone());
Ok(attributes)
}
fn transform(&self, other: &Self) -> Result<(Self, Self), OTError>
where
Self: Sized,
{
let a = self
.iter()
.fold(RichTextAttributes::new(), |mut new_attributes, (k, v)| {
if !other.contains_key(k) {
new_attributes.insert(k.clone(), v.clone());
}
new_attributes
});
let b = other
.iter()
.fold(RichTextAttributes::new(), |mut new_attributes, (k, v)| {
if !self.contains_key(k) {
new_attributes.insert(k.clone(), v.clone());
}
new_attributes
});
Ok((a, b))
}
fn invert(&self, other: &Self) -> Self {
let base_inverted = other.iter().fold(RichTextAttributes::new(), |mut attributes, (k, v)| {
if other.get(k) != self.get(k) && self.contains_key(k) {
attributes.insert(k.clone(), v.clone());
}
attributes
});
let inverted = self.iter().fold(base_inverted, |mut attributes, (k, _)| {
if other.get(k) != self.get(k) && !other.contains_key(k) {
attributes.delete(k);
}
attributes
});
inverted
}
}
impl std::ops::Deref for RichTextAttributes {
type Target = HashMap<RichTextAttributeKey, RichTextAttributeValue>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl std::ops::DerefMut for RichTextAttributes {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
pub fn attributes_except_header(op: &RichTextOperation) -> RichTextAttributes {
let mut attributes = op.get_attributes();
attributes.remove(RichTextAttributeKey::Header);
attributes
}
#[derive(Debug, Clone)]
pub struct RichTextAttribute {
pub key: RichTextAttributeKey,

View File

@ -1,6 +1,5 @@
#[rustfmt::skip]
use crate::core::RichTextAttributeValue;
use crate::core::{RichTextAttribute, RichTextAttributeKey, RichTextAttributes};
use crate::rich_text::{RichTextAttribute, RichTextAttributeKey, RichTextAttributes, RichTextAttributeValue};
use serde::{
de,
de::{MapAccess, Visitor},

View File

@ -1,5 +1,6 @@
#![allow(non_snake_case)]
use crate::core::{RichTextAttribute, RichTextAttributes};
use crate::rich_text::{RichTextAttribute, RichTextAttributes};
pub struct AttributeBuilder {
inner: RichTextAttributes,
}

View File

@ -0,0 +1,3 @@
use crate::{core::Delta, rich_text::RichTextAttributes};
pub type RichTextDelta = Delta<RichTextAttributes>;

View File

@ -1,12 +1,11 @@
#![allow(clippy::module_inception)]
mod attribute;
mod attributes;
mod attributes_serde;
mod builder;
#[macro_use]
mod macros;
mod delta;
pub use attribute::*;
pub use attributes::*;
pub use builder::*;
pub use delta::*;

View File