mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
add rich_text crate
This commit is contained in:
parent
2f413a8e67
commit
8a9a23ddbe
@ -13,7 +13,7 @@ use flowy_document_infra::{
|
|||||||
entities::ws::{WsDataType, WsDocumentData},
|
entities::ws::{WsDataType, WsDocumentData},
|
||||||
protobuf::{Doc, RevId, RevType, Revision, RevisionRange, UpdateDocParams},
|
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 parking_lot::RwLock;
|
||||||
use protobuf::Message;
|
use protobuf::Message;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::document::helper::{DocScript, DocumentTest};
|
use crate::document::helper::{DocScript, DocumentTest};
|
||||||
use flowy_document_infra::core::{Document, FlowyDoc};
|
use flowy_document_infra::core::{Document, FlowyDoc};
|
||||||
use lib_ot::core::{Interval, RichTextAttribute};
|
use lib_ot::{core::Interval, rich_text::RichTextAttribute};
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// ┌─────────┐ ┌─────────┐
|
// ┌─────────┐ ┌─────────┐
|
||||||
|
@ -12,8 +12,9 @@ use tokio::time::{sleep, Duration};
|
|||||||
// use crate::helper::*;
|
// use crate::helper::*;
|
||||||
use crate::util::helper::{spawn_server, TestServer};
|
use crate::util::helper::{spawn_server, TestServer};
|
||||||
use flowy_document_infra::{entities::doc::DocIdentifier, protobuf::UpdateDocParams};
|
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 parking_lot::RwLock;
|
||||||
|
use lib_ot::core::Interval;
|
||||||
|
|
||||||
pub struct DocumentTest {
|
pub struct DocumentTest {
|
||||||
server: TestServer,
|
server: TestServer,
|
||||||
|
@ -17,7 +17,10 @@ use flowy_document_infra::{
|
|||||||
errors::DocumentResult,
|
errors::DocumentResult,
|
||||||
};
|
};
|
||||||
use lib_infra::retry::{ExponentialBackoff, Retry};
|
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 lib_ws::WsConnectState;
|
||||||
use std::{convert::TryFrom, sync::Arc};
|
use std::{convert::TryFrom, sync::Arc};
|
||||||
use tokio::sync::{mpsc, mpsc::UnboundedSender, oneshot};
|
use tokio::sync::{mpsc, mpsc::UnboundedSender, oneshot};
|
||||||
|
@ -6,7 +6,10 @@ use flowy_document_infra::{
|
|||||||
errors::DocumentError,
|
errors::DocumentError,
|
||||||
};
|
};
|
||||||
use futures::stream::StreamExt;
|
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 std::{convert::TryFrom, sync::Arc};
|
||||||
use tokio::sync::{mpsc, oneshot, RwLock};
|
use tokio::sync::{mpsc, oneshot, RwLock};
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use flowy_document_infra::{
|
|||||||
util::RevIdCounter,
|
util::RevIdCounter,
|
||||||
};
|
};
|
||||||
use lib_infra::future::ResultFuture;
|
use lib_infra::future::ResultFuture;
|
||||||
use lib_ot::core::{OperationTransformable, RichTextDelta};
|
use lib_ot::{core::OperationTransformable, rich_text::RichTextDelta};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
|
@ -9,7 +9,10 @@ use flowy_database::{ConnectionPool, SqliteConnection};
|
|||||||
use flowy_document_infra::entities::doc::{revision_from_doc, Doc, RevId, RevType, Revision, RevisionRange};
|
use flowy_document_infra::entities::doc::{revision_from_doc, Doc, RevId, RevType, Revision, RevisionRange};
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use lib_infra::future::ResultFuture;
|
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 std::{collections::VecDeque, sync::Arc, time::Duration};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::{broadcast, mpsc, RwLock},
|
sync::{broadcast, mpsc, RwLock},
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
#![cfg_attr(rustfmt, rustfmt::skip)]
|
||||||
use crate::editor::{TestBuilder, TestOp::*};
|
use crate::editor::{TestBuilder, TestOp::*};
|
||||||
use flowy_document_infra::core::{FlowyDoc, PlainDoc};
|
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 unicode_segmentation::UnicodeSegmentation;
|
||||||
|
use lib_ot::rich_text::RichTextDelta;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn attributes_bold_added() {
|
fn attributes_bold_added() {
|
||||||
|
@ -6,7 +6,10 @@ mod undo_redo_test;
|
|||||||
|
|
||||||
use derive_more::Display;
|
use derive_more::Display;
|
||||||
use flowy_document_infra::core::{CustomDocument, Document};
|
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 rand::{prelude::*, Rng as WrappedRng};
|
||||||
use std::{sync::Once, time::Duration};
|
use std::{sync::Once, time::Duration};
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
#![allow(clippy::all)]
|
#![allow(clippy::all)]
|
||||||
use crate::editor::{Rng, TestBuilder, TestOp::*};
|
use crate::editor::{Rng, TestBuilder, TestOp::*};
|
||||||
use flowy_document_infra::core::{FlowyDoc, PlainDoc};
|
use flowy_document_infra::core::{FlowyDoc, PlainDoc};
|
||||||
use lib_ot::core::*;
|
use lib_ot::{
|
||||||
|
core::*,
|
||||||
|
rich_text::{AttributeBuilder, RichTextAttribute, RichTextAttributes, RichTextDelta},
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn attributes_insert_text() {
|
fn attributes_insert_text() {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use flowy_document_infra::core::{Document, PlainDoc};
|
use flowy_document_infra::core::{Document, PlainDoc};
|
||||||
use lib_ot::core::*;
|
use lib_ot::{
|
||||||
|
core::*,
|
||||||
|
rich_text::{AttributeBuilder, RichTextAttribute, RichTextAttributeValue, RichTextDelta},
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn operation_insert_serialize_test() {
|
fn operation_insert_serialize_test() {
|
||||||
|
@ -6,7 +6,10 @@ use crate::{
|
|||||||
errors::DocumentError,
|
errors::DocumentError,
|
||||||
user_default::doc_initial_delta,
|
user_default::doc_initial_delta,
|
||||||
};
|
};
|
||||||
use lib_ot::core::*;
|
use lib_ot::{
|
||||||
|
core::*,
|
||||||
|
rich_text::{RichTextAttribute, RichTextDelta},
|
||||||
|
};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
pub trait CustomDocument {
|
pub trait CustomDocument {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use crate::core::extensions::DeleteExt;
|
use crate::core::extensions::DeleteExt;
|
||||||
use lib_ot::core::{DeltaBuilder, Interval, RichTextDelta};
|
use lib_ot::{
|
||||||
|
core::{DeltaBuilder, Interval},
|
||||||
|
rich_text::RichTextDelta,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct DefaultDelete {}
|
pub struct DefaultDelete {}
|
||||||
impl DeleteExt for DefaultDelete {
|
impl DeleteExt for DefaultDelete {
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
use crate::{core::extensions::DeleteExt, util::is_newline};
|
use crate::{core::extensions::DeleteExt, util::is_newline};
|
||||||
use lib_ot::core::{
|
use lib_ot::{
|
||||||
plain_attributes,
|
core::{Attributes, CharMetric, DeltaBuilder, DeltaIter, Interval, NEW_LINE},
|
||||||
Attributes,
|
rich_text::{plain_attributes, RichTextDelta},
|
||||||
CharMetric,
|
|
||||||
DeltaBuilder,
|
|
||||||
DeltaIter,
|
|
||||||
Interval,
|
|
||||||
RichTextDelta,
|
|
||||||
NEW_LINE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PreserveLineFormatOnMerge {}
|
pub struct PreserveLineFormatOnMerge {}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use crate::util::find_newline;
|
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(
|
pub(crate) fn line_break(
|
||||||
op: &RichTextOperation,
|
op: &RichTextOperation,
|
||||||
|
@ -2,14 +2,9 @@ use crate::{
|
|||||||
core::extensions::{format::helper::line_break, FormatExt},
|
core::extensions::{format::helper::line_break, FormatExt},
|
||||||
util::find_newline,
|
util::find_newline,
|
||||||
};
|
};
|
||||||
use lib_ot::core::{
|
use lib_ot::{
|
||||||
plain_attributes,
|
core::{DeltaBuilder, DeltaIter, Interval},
|
||||||
AttributeScope,
|
rich_text::{plain_attributes, AttributeScope, RichTextAttribute, RichTextDelta},
|
||||||
DeltaBuilder,
|
|
||||||
DeltaIter,
|
|
||||||
Interval,
|
|
||||||
RichTextAttribute,
|
|
||||||
RichTextDelta,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ResolveBlockFormat {}
|
pub struct ResolveBlockFormat {}
|
||||||
|
@ -2,7 +2,10 @@ use crate::{
|
|||||||
core::extensions::{format::helper::line_break, FormatExt},
|
core::extensions::{format::helper::line_break, FormatExt},
|
||||||
util::find_newline,
|
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 {}
|
pub struct ResolveInlineFormat {}
|
||||||
impl FormatExt for ResolveInlineFormat {
|
impl FormatExt for ResolveInlineFormat {
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
use crate::{core::extensions::InsertExt, util::is_newline};
|
use crate::{core::extensions::InsertExt, util::is_newline};
|
||||||
use lib_ot::core::{
|
use lib_ot::{
|
||||||
attributes_except_header,
|
core::{is_empty_line_at_index, DeltaBuilder, DeltaIter},
|
||||||
is_empty_line_at_index,
|
rich_text::{attributes_except_header, RichTextAttributeKey, RichTextDelta},
|
||||||
DeltaBuilder,
|
|
||||||
DeltaIter,
|
|
||||||
RichTextAttributeKey,
|
|
||||||
RichTextDelta,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct AutoExitBlock {}
|
pub struct AutoExitBlock {}
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
use crate::{core::extensions::InsertExt, util::is_whitespace};
|
use crate::{core::extensions::InsertExt, util::is_whitespace};
|
||||||
use lib_ot::core::{
|
use lib_ot::{
|
||||||
count_utf16_code_units,
|
core::{count_utf16_code_units, DeltaBuilder, DeltaIter},
|
||||||
plain_attributes,
|
rich_text::{plain_attributes, RichTextAttribute, RichTextAttributes, RichTextDelta},
|
||||||
DeltaBuilder,
|
|
||||||
DeltaIter,
|
|
||||||
RichTextAttribute,
|
|
||||||
RichTextAttributes,
|
|
||||||
RichTextDelta,
|
|
||||||
};
|
};
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
use crate::core::extensions::InsertExt;
|
use crate::core::extensions::InsertExt;
|
||||||
use lib_ot::core::{
|
use lib_ot::{
|
||||||
Attributes,
|
core::{Attributes, DeltaBuilder, DeltaIter, NEW_LINE},
|
||||||
DeltaBuilder,
|
rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta},
|
||||||
DeltaIter,
|
|
||||||
RichTextAttributeKey,
|
|
||||||
RichTextAttributes,
|
|
||||||
RichTextDelta,
|
|
||||||
NEW_LINE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct DefaultInsertAttribute {}
|
pub struct DefaultInsertAttribute {}
|
||||||
|
@ -2,7 +2,7 @@ use crate::core::extensions::InsertExt;
|
|||||||
pub use auto_exit_block::*;
|
pub use auto_exit_block::*;
|
||||||
pub use auto_format::*;
|
pub use auto_format::*;
|
||||||
pub use default_insert::*;
|
pub use default_insert::*;
|
||||||
use lib_ot::core::RichTextDelta;
|
use lib_ot::rich_text::RichTextDelta;
|
||||||
pub use preserve_block_format::*;
|
pub use preserve_block_format::*;
|
||||||
pub use preserve_inline_format::*;
|
pub use preserve_inline_format::*;
|
||||||
pub use reset_format_on_new_line::*;
|
pub use reset_format_on_new_line::*;
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use crate::{core::extensions::InsertExt, util::is_newline};
|
use crate::{core::extensions::InsertExt, util::is_newline};
|
||||||
use lib_ot::core::{
|
use lib_ot::{
|
||||||
attributes_except_header,
|
core::{DeltaBuilder, DeltaIter, NEW_LINE},
|
||||||
plain_attributes,
|
rich_text::{
|
||||||
DeltaBuilder,
|
attributes_except_header,
|
||||||
DeltaIter,
|
plain_attributes,
|
||||||
RichTextAttribute,
|
RichTextAttribute,
|
||||||
RichTextAttributeKey,
|
RichTextAttributeKey,
|
||||||
RichTextAttributes,
|
RichTextAttributes,
|
||||||
RichTextDelta,
|
RichTextDelta,
|
||||||
NEW_LINE,
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PreserveBlockFormatOnInsert {}
|
pub struct PreserveBlockFormatOnInsert {}
|
||||||
|
@ -2,14 +2,9 @@ use crate::{
|
|||||||
core::extensions::InsertExt,
|
core::extensions::InsertExt,
|
||||||
util::{contain_newline, is_newline},
|
util::{contain_newline, is_newline},
|
||||||
};
|
};
|
||||||
use lib_ot::core::{
|
use lib_ot::{
|
||||||
plain_attributes,
|
core::{DeltaBuilder, DeltaIter, OpNewline, NEW_LINE},
|
||||||
DeltaBuilder,
|
rich_text::{plain_attributes, RichTextAttributeKey, RichTextDelta},
|
||||||
DeltaIter,
|
|
||||||
OpNewline,
|
|
||||||
RichTextAttributeKey,
|
|
||||||
RichTextDelta,
|
|
||||||
NEW_LINE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PreserveInlineFormat {}
|
pub struct PreserveInlineFormat {}
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
use crate::{core::extensions::InsertExt, util::is_newline};
|
use crate::{core::extensions::InsertExt, util::is_newline};
|
||||||
use lib_ot::core::{
|
use lib_ot::{
|
||||||
CharMetric,
|
core::{CharMetric, DeltaBuilder, DeltaIter, NEW_LINE},
|
||||||
DeltaBuilder,
|
rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta},
|
||||||
DeltaIter,
|
|
||||||
RichTextAttributeKey,
|
|
||||||
RichTextAttributes,
|
|
||||||
RichTextDelta,
|
|
||||||
NEW_LINE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ResetLineFormatOnNewLine {}
|
pub struct ResetLineFormatOnNewLine {}
|
||||||
|
@ -2,7 +2,10 @@ pub use delete::*;
|
|||||||
pub use format::*;
|
pub use format::*;
|
||||||
pub use insert::*;
|
pub use insert::*;
|
||||||
|
|
||||||
use lib_ot::core::{Interval, RichTextAttribute, RichTextDelta};
|
use lib_ot::{
|
||||||
|
core::Interval,
|
||||||
|
rich_text::{RichTextAttribute, RichTextDelta},
|
||||||
|
};
|
||||||
|
|
||||||
mod delete;
|
mod delete;
|
||||||
mod format;
|
mod format;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use lib_ot::core::RichTextDelta;
|
use lib_ot::rich_text::RichTextDelta;
|
||||||
|
|
||||||
const MAX_UNDOS: usize = 20;
|
const MAX_UNDOS: usize = 20;
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::core::extensions::*;
|
use crate::core::extensions::*;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{trim, Interval, RichTextAttribute, RichTextDelta},
|
core::{trim, Interval},
|
||||||
errors::{ErrorBuilder, OTError, OTErrorCode},
|
errors::{ErrorBuilder, OTError, OTErrorCode},
|
||||||
|
rich_text::{RichTextAttribute, RichTextDelta},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const RECORD_THRESHOLD: usize = 400; // in milliseconds
|
pub const RECORD_THRESHOLD: usize = 400; // in milliseconds
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use flowy_derive::ProtoBuf;
|
use flowy_derive::ProtoBuf;
|
||||||
use lib_ot::{core::RichTextDelta, errors::OTError};
|
use lib_ot::{errors::OTError, rich_text::RichTextDelta};
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default, Debug, Clone)]
|
#[derive(ProtoBuf, Default, Debug, Clone)]
|
||||||
pub struct CreateDocParams {
|
pub struct CreateDocParams {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{entities::doc::Doc, util::md5};
|
use crate::{entities::doc::Doc, util::md5};
|
||||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||||
use lib_ot::core::RichTextDelta;
|
use lib_ot::rich_text::RichTextDelta;
|
||||||
use std::{fmt::Formatter, ops::RangeInclusive};
|
use std::{fmt::Formatter, ops::RangeInclusive};
|
||||||
|
|
||||||
#[derive(Debug, ProtoBuf_Enum, Clone, Eq, PartialEq)]
|
#[derive(Debug, ProtoBuf_Enum, Clone, Eq, PartialEq)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use lib_ot::core::{DeltaBuilder, RichTextDelta};
|
use lib_ot::{core::DeltaBuilder, rich_text::RichTextDelta};
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn doc_initial_delta() -> RichTextDelta { DeltaBuilder::new().insert("\n").build() }
|
pub fn doc_initial_delta() -> RichTextDelta { DeltaBuilder::new().insert("\n").build() }
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
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},
|
errors::{ErrorBuilder, OTError, OTErrorCode},
|
||||||
};
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
@ -469,8 +469,6 @@ fn transform_op_attribute<T: Attributes>(left: &Option<Operation<T>>, right: &Op
|
|||||||
left.transform(&right).unwrap().0
|
left.transform(&right).unwrap().0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RichTextDelta = Delta<RichTextAttributes>;
|
|
||||||
|
|
||||||
impl<T> Delta<T>
|
impl<T> Delta<T>
|
||||||
where
|
where
|
||||||
T: Attributes + DeserializeOwned,
|
T: Attributes + DeserializeOwned,
|
||||||
@ -503,22 +501,31 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for RichTextDelta {
|
impl<T> FromStr for Delta<T>
|
||||||
|
where
|
||||||
|
T: Attributes,
|
||||||
|
{
|
||||||
type Err = ();
|
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);
|
let mut delta = Delta::with_capacity(1);
|
||||||
delta.add(Operation::Insert(s.into()));
|
delta.add(Operation::Insert(s.into()));
|
||||||
Ok(delta)
|
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;
|
type Error = OTError;
|
||||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> { Delta::from_bytes(bytes) }
|
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;
|
type Error = OTError;
|
||||||
|
|
||||||
fn try_from(bytes: Bytes) -> Result<Self, Self::Error> { Delta::from_bytes(&bytes) }
|
fn try_from(bytes: Bytes) -> Result<Self, Self::Error> { Delta::from_bytes(&bytes) }
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use super::cursor::*;
|
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};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize;
|
pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize;
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
mod attributes;
|
|
||||||
mod delta;
|
mod delta;
|
||||||
mod flowy_str;
|
mod flowy_str;
|
||||||
mod interval;
|
mod interval;
|
||||||
mod operation;
|
mod operation;
|
||||||
|
|
||||||
use crate::errors::OTError;
|
use crate::errors::OTError;
|
||||||
pub use attributes::*;
|
|
||||||
pub use delta::*;
|
pub use delta::*;
|
||||||
pub use flowy_str::*;
|
pub use flowy_str::*;
|
||||||
pub use interval::*;
|
pub use interval::*;
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use crate::core::{Attributes, Operation, RichTextAttributes};
|
use crate::{
|
||||||
|
core::{Attributes, Operation},
|
||||||
|
rich_text::RichTextAttributes,
|
||||||
|
};
|
||||||
|
|
||||||
pub type RichTextOpBuilder = OpBuilder<RichTextAttributes>;
|
pub type RichTextOpBuilder = OpBuilder<RichTextAttributes>;
|
||||||
|
|
||||||
|
@ -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 serde::__private::Formatter;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::min,
|
cmp::min,
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
pub mod rich_text;
|
||||||
|
@ -1,11 +1,179 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
use crate::{
|
||||||
use crate::{block_attribute, core::RichTextAttributes, ignore_attribute, inline_attribute, list_attribute};
|
block_attribute,
|
||||||
|
core::{Attributes, OperationTransformable, RichTextOperation},
|
||||||
|
errors::OTError,
|
||||||
|
ignore_attribute,
|
||||||
|
inline_attribute,
|
||||||
|
list_attribute,
|
||||||
|
};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use std::{
|
||||||
use std::{collections::HashSet, fmt, fmt::Formatter, iter::FromIterator};
|
collections::{HashMap, HashSet},
|
||||||
|
fmt,
|
||||||
|
fmt::Formatter,
|
||||||
|
iter::FromIterator,
|
||||||
|
};
|
||||||
use strum_macros::Display;
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RichTextAttribute {
|
pub struct RichTextAttribute {
|
||||||
pub key: RichTextAttributeKey,
|
pub key: RichTextAttributeKey,
|
@ -1,6 +1,5 @@
|
|||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
use crate::core::RichTextAttributeValue;
|
use crate::rich_text::{RichTextAttribute, RichTextAttributeKey, RichTextAttributes, RichTextAttributeValue};
|
||||||
use crate::core::{RichTextAttribute, RichTextAttributeKey, RichTextAttributes};
|
|
||||||
use serde::{
|
use serde::{
|
||||||
de,
|
de,
|
||||||
de::{MapAccess, Visitor},
|
de::{MapAccess, Visitor},
|
@ -1,5 +1,6 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
use crate::core::{RichTextAttribute, RichTextAttributes};
|
use crate::rich_text::{RichTextAttribute, RichTextAttributes};
|
||||||
|
|
||||||
pub struct AttributeBuilder {
|
pub struct AttributeBuilder {
|
||||||
inner: RichTextAttributes,
|
inner: RichTextAttributes,
|
||||||
}
|
}
|
3
shared-lib/lib-ot/src/rich_text/delta.rs
Normal file
3
shared-lib/lib-ot/src/rich_text/delta.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
use crate::{core::Delta, rich_text::RichTextAttributes};
|
||||||
|
|
||||||
|
pub type RichTextDelta = Delta<RichTextAttributes>;
|
@ -1,12 +1,11 @@
|
|||||||
#![allow(clippy::module_inception)]
|
|
||||||
mod attribute;
|
|
||||||
mod attributes;
|
mod attributes;
|
||||||
mod attributes_serde;
|
mod attributes_serde;
|
||||||
mod builder;
|
mod builder;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
mod delta;
|
||||||
|
|
||||||
pub use attribute::*;
|
|
||||||
pub use attributes::*;
|
pub use attributes::*;
|
||||||
pub use builder::*;
|
pub use builder::*;
|
||||||
|
pub use delta::*;
|
0
shared-lib/lib-ot/tests/main.rs
Normal file
0
shared-lib/lib-ot/tests/main.rs
Normal file
Loading…
Reference in New Issue
Block a user