mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: update lib ot tests
This commit is contained in:
parent
dda8c57823
commit
798e16d3aa
@ -10,7 +10,7 @@ use flowy_sync::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lib_infra::future::{BoxResultFuture, FutureResult};
|
use lib_infra::future::{BoxResultFuture, FutureResult};
|
||||||
use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta};
|
use lib_ot::core::{OperationTransform, PhantomAttributes, PlainTextDelta};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use flowy_sync::{
|
|||||||
};
|
};
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{Interval, OperationTransformable},
|
core::{Interval, OperationTransform},
|
||||||
rich_text::{RichTextAttribute, RichTextAttributes, RichTextDelta},
|
rich_text::{RichTextAttribute, RichTextAttributes, RichTextDelta},
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
#![cfg_attr(rustfmt, rustfmt::skip)]
|
||||||
use crate::editor::{TestBuilder, TestOp::*};
|
use crate::editor::{TestBuilder, TestOp::*};
|
||||||
use flowy_sync::client_document::{NewlineDoc, PlainDoc};
|
use flowy_sync::client_document::{NewlineDoc, PlainDoc};
|
||||||
use lib_ot::core::{Interval, OperationTransformable, NEW_LINE, WHITESPACE, FlowyStr};
|
use lib_ot::core::{Interval, OperationTransform, NEW_LINE, WHITESPACE, FlowyStr};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
use lib_ot::rich_text::RichTextDelta;
|
use lib_ot::rich_text::RichTextDelta;
|
||||||
|
|
||||||
@ -724,7 +724,7 @@ fn attributes_preserve_header_format_on_merge() {
|
|||||||
fn attributes_format_emoji() {
|
fn attributes_format_emoji() {
|
||||||
let emoji_s = "👋 ";
|
let emoji_s = "👋 ";
|
||||||
let s: FlowyStr = emoji_s.into();
|
let s: FlowyStr = emoji_s.into();
|
||||||
let len = s.utf16_size();
|
let len = s.utf16_len();
|
||||||
assert_eq!(3, len);
|
assert_eq!(3, len);
|
||||||
assert_eq!(2, s.graphemes(true).count());
|
assert_eq!(2, s.graphemes(true).count());
|
||||||
|
|
||||||
|
@ -302,7 +302,7 @@ impl Rng {
|
|||||||
let mut delta = RichTextDelta::default();
|
let mut delta = RichTextDelta::default();
|
||||||
let s = FlowyStr::from(s);
|
let s = FlowyStr::from(s);
|
||||||
loop {
|
loop {
|
||||||
let left = s.utf16_size() - delta.utf16_base_len;
|
let left = s.utf16_len() - delta.utf16_base_len;
|
||||||
if left == 0 {
|
if left == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#![allow(clippy::all)]
|
#![allow(clippy::all)]
|
||||||
use crate::editor::{Rng, TestBuilder, TestOp::*};
|
use crate::editor::{Rng, TestBuilder, TestOp::*};
|
||||||
use flowy_sync::client_document::{NewlineDoc, PlainDoc};
|
use flowy_sync::client_document::{NewlineDoc, PlainDoc};
|
||||||
|
use lib_ot::rich_text::RichTextDeltaBuilder;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::Interval,
|
core::Interval,
|
||||||
core::*,
|
core::*,
|
||||||
@ -39,12 +40,8 @@ fn attributes_insert_text_at_middle() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_get_ops_in_interval_1() {
|
fn delta_get_ops_in_interval_1() {
|
||||||
let mut delta = RichTextDelta::default();
|
let operations = OperationBuilder::new().insert("123", None).insert("4", None).build();
|
||||||
let insert_a = OperationBuilder::insert("123").build();
|
let delta = RichTextDeltaBuilder::from_operations(operations);
|
||||||
let insert_b = OperationBuilder::insert("4").build();
|
|
||||||
|
|
||||||
delta.add(insert_a.clone());
|
|
||||||
delta.add(insert_b.clone());
|
|
||||||
|
|
||||||
let mut iterator = DeltaIterator::from_interval(&delta, Interval::new(0, 4));
|
let mut iterator = DeltaIterator::from_interval(&delta, Interval::new(0, 4));
|
||||||
assert_eq!(iterator.ops(), delta.ops);
|
assert_eq!(iterator.ops(), delta.ops);
|
||||||
@ -365,7 +362,7 @@ fn apply_1000() {
|
|||||||
let mut rng = Rng::default();
|
let mut rng = Rng::default();
|
||||||
let s: FlowyStr = rng.gen_string(50).into();
|
let s: FlowyStr = rng.gen_string(50).into();
|
||||||
let delta = rng.gen_delta(&s);
|
let delta = rng.gen_delta(&s);
|
||||||
assert_eq!(s.utf16_size(), delta.utf16_base_len);
|
assert_eq!(s.utf16_len(), delta.utf16_base_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,11 +486,11 @@ fn compose() {
|
|||||||
let s = rng.gen_string(20);
|
let s = rng.gen_string(20);
|
||||||
let a = rng.gen_delta(&s);
|
let a = rng.gen_delta(&s);
|
||||||
let after_a: FlowyStr = a.apply(&s).unwrap().into();
|
let after_a: FlowyStr = a.apply(&s).unwrap().into();
|
||||||
assert_eq!(a.utf16_target_len, after_a.utf16_size());
|
assert_eq!(a.utf16_target_len, after_a.utf16_len());
|
||||||
|
|
||||||
let b = rng.gen_delta(&after_a);
|
let b = rng.gen_delta(&after_a);
|
||||||
let after_b: FlowyStr = b.apply(&after_a).unwrap().into();
|
let after_b: FlowyStr = b.apply(&after_a).unwrap().into();
|
||||||
assert_eq!(b.utf16_target_len, after_b.utf16_size());
|
assert_eq!(b.utf16_target_len, after_b.utf16_len());
|
||||||
|
|
||||||
let ab = a.compose(&b).unwrap();
|
let ab = a.compose(&b).unwrap();
|
||||||
assert_eq!(ab.utf16_target_len, b.utf16_target_len);
|
assert_eq!(ab.utf16_target_len, b.utf16_target_len);
|
||||||
|
@ -434,7 +434,7 @@ mod tests {
|
|||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
|
||||||
use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision, WorkspaceRevision};
|
use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision, WorkspaceRevision};
|
||||||
use lib_ot::core::{OperationTransformable, PlainTextDelta, PlainTextDeltaBuilder};
|
use lib_ot::core::{OperationTransform, PlainTextDelta, PlainTextDeltaBuilder};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_add_workspace() {
|
fn folder_add_workspace() {
|
||||||
|
@ -4,7 +4,7 @@ use crate::util::{cal_diff, make_delta_from_revisions};
|
|||||||
use flowy_grid_data_model::revision::{
|
use flowy_grid_data_model::revision::{
|
||||||
gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision,
|
gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision,
|
||||||
};
|
};
|
||||||
use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder};
|
use lib_ot::core::{OperationTransform, PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -9,7 +9,7 @@ use flowy_grid_data_model::revision::{
|
|||||||
GridLayoutRevision, GridRevision, GridSettingRevision, GridSortRevision,
|
GridLayoutRevision, GridRevision, GridSettingRevision, GridSortRevision,
|
||||||
};
|
};
|
||||||
use lib_infra::util::move_vec_element;
|
use lib_infra::util::move_vec_element;
|
||||||
use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder};
|
use lib_ot::core::{OperationTransform, PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{entities::folder::FolderDelta, errors::CollaborateError, synchronizer::RevisionSyncObject};
|
use crate::{entities::folder::FolderDelta, errors::CollaborateError, synchronizer::RevisionSyncObject};
|
||||||
use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta};
|
use lib_ot::core::{OperationTransform, PhantomAttributes, PlainTextDelta};
|
||||||
|
|
||||||
pub struct ServerFolder {
|
pub struct ServerFolder {
|
||||||
folder_id: String,
|
folder_id: String,
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
use dissimilar::Chunk;
|
use dissimilar::Chunk;
|
||||||
use lib_ot::core::{DeltaBuilder, FlowyStr};
|
use lib_ot::core::{DeltaBuilder, FlowyStr};
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{Attributes, Delta, OperationTransformable, NEW_LINE, WHITESPACE},
|
core::{Attributes, Delta, OperationTransform, NEW_LINE, WHITESPACE},
|
||||||
rich_text::RichTextDelta,
|
rich_text::RichTextDelta,
|
||||||
};
|
};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
@ -208,10 +208,10 @@ pub fn cal_diff<T: Attributes>(old: String, new: String) -> Option<Delta<T>> {
|
|||||||
for chunk in &chunks {
|
for chunk in &chunks {
|
||||||
match chunk {
|
match chunk {
|
||||||
Chunk::Equal(s) => {
|
Chunk::Equal(s) => {
|
||||||
delta_builder = delta_builder.retain(FlowyStr::from(*s).utf16_size());
|
delta_builder = delta_builder.retain(FlowyStr::from(*s).utf16_len());
|
||||||
}
|
}
|
||||||
Chunk::Delete(s) => {
|
Chunk::Delete(s) => {
|
||||||
delta_builder = delta_builder.delete(FlowyStr::from(*s).utf16_size());
|
delta_builder = delta_builder.delete(FlowyStr::from(*s).utf16_len());
|
||||||
}
|
}
|
||||||
Chunk::Insert(s) => {
|
Chunk::Insert(s) => {
|
||||||
delta_builder = delta_builder.insert(*s);
|
delta_builder = delta_builder.insert(*s);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::core::delta::{trim, Delta};
|
use crate::core::delta::{trim, Delta};
|
||||||
use crate::core::operation::{Attributes, PhantomAttributes};
|
use crate::core::operation::{Attributes, PhantomAttributes};
|
||||||
|
use crate::core::Operation;
|
||||||
|
|
||||||
pub type PlainTextDeltaBuilder = DeltaBuilder<PhantomAttributes>;
|
pub type PlainTextDeltaBuilder = DeltaBuilder<PhantomAttributes>;
|
||||||
|
|
||||||
@ -7,6 +8,16 @@ pub type PlainTextDeltaBuilder = DeltaBuilder<PhantomAttributes>;
|
|||||||
///
|
///
|
||||||
/// Note that all edit operations must be sorted; the start point of each
|
/// Note that all edit operations must be sorted; the start point of each
|
||||||
/// interval must be no less than the end point of the previous one.
|
/// interval must be no less than the end point of the previous one.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use lib_ot::core::PlainTextDeltaBuilder;
|
||||||
|
/// let delta = PlainTextDeltaBuilder::new()
|
||||||
|
/// .insert("AppFlowy")
|
||||||
|
/// .build();
|
||||||
|
/// assert_eq!(delta.content_str().unwrap(), "AppFlowy");
|
||||||
|
/// ```
|
||||||
pub struct DeltaBuilder<T: Attributes> {
|
pub struct DeltaBuilder<T: Attributes> {
|
||||||
delta: Delta<T>,
|
delta: Delta<T>,
|
||||||
}
|
}
|
||||||
@ -28,8 +39,26 @@ where
|
|||||||
DeltaBuilder::default()
|
DeltaBuilder::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_operations(operations: Vec<Operation<T>>) -> Delta<T> {
|
||||||
|
let mut delta = DeltaBuilder::default().build();
|
||||||
|
operations.into_iter().for_each(|operation| {
|
||||||
|
delta.add(operation);
|
||||||
|
});
|
||||||
|
delta
|
||||||
|
}
|
||||||
|
|
||||||
/// Retain the 'n' characters with the attributes. Use 'retain' instead if you don't
|
/// Retain the 'n' characters with the attributes. Use 'retain' instead if you don't
|
||||||
/// need any attributes.
|
/// need any attributes.
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use lib_ot::rich_text::{RichTextAttribute, RichTextDelta, RichTextDeltaBuilder};
|
||||||
|
///
|
||||||
|
/// let mut attribute = RichTextAttribute::Bold(true);
|
||||||
|
/// let delta = RichTextDeltaBuilder::new().retain_with_attributes(7, attribute.into()).build();
|
||||||
|
///
|
||||||
|
/// assert_eq!(delta.to_json_str(), r#"[{"retain":7,"attributes":{"bold":true}}]"#);
|
||||||
|
/// ```
|
||||||
pub fn retain_with_attributes(mut self, n: usize, attrs: T) -> Self {
|
pub fn retain_with_attributes(mut self, n: usize, attrs: T) -> Self {
|
||||||
self.delta.retain(n, attrs);
|
self.delta.retain(n, attrs);
|
||||||
self
|
self
|
||||||
@ -41,6 +70,24 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes the given interval. Panics if interval is not properly sorted.
|
/// Deletes the given interval. Panics if interval is not properly sorted.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use lib_ot::core::{OperationTransform, PlainTextDeltaBuilder};
|
||||||
|
///
|
||||||
|
/// let delta = PlainTextDeltaBuilder::new()
|
||||||
|
/// .insert("AppFlowy...")
|
||||||
|
/// .build();
|
||||||
|
///
|
||||||
|
/// let changeset = PlainTextDeltaBuilder::new()
|
||||||
|
/// .retain(8)
|
||||||
|
/// .delete(3)
|
||||||
|
/// .build();
|
||||||
|
///
|
||||||
|
/// let new_delta = delta.compose(&changeset).unwrap();
|
||||||
|
/// assert_eq!(new_delta.content_str().unwrap(), "AppFlowy");
|
||||||
|
/// ```
|
||||||
pub fn delete(mut self, n: usize) -> Self {
|
pub fn delete(mut self, n: usize) -> Self {
|
||||||
self.delta.delete(n);
|
self.delta.delete(n);
|
||||||
self
|
self
|
||||||
@ -58,6 +105,25 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes trailing retain operation with empty attributes
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use lib_ot::core::{OperationTransform, PlainTextDeltaBuilder};
|
||||||
|
/// use lib_ot::rich_text::{RichTextAttribute, RichTextDeltaBuilder};
|
||||||
|
/// let delta = PlainTextDeltaBuilder::new()
|
||||||
|
/// .retain(3)
|
||||||
|
/// .trim()
|
||||||
|
/// .build();
|
||||||
|
/// assert_eq!(delta.ops.len(), 0);
|
||||||
|
///
|
||||||
|
/// let delta = RichTextDeltaBuilder::new()
|
||||||
|
/// .retain_with_attributes(3, RichTextAttribute::Bold(true).into())
|
||||||
|
/// .trim()
|
||||||
|
/// .build();
|
||||||
|
/// assert_eq!(delta.ops.len(), 1);
|
||||||
|
/// ```
|
||||||
pub fn trim(mut self) -> Self {
|
pub fn trim(mut self) -> Self {
|
||||||
trim(&mut self.delta);
|
trim(&mut self.delta);
|
||||||
self
|
self
|
||||||
|
@ -3,7 +3,7 @@ use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
|
|||||||
use crate::core::delta::{DeltaIterator, MAX_IV_LEN};
|
use crate::core::delta::{DeltaIterator, MAX_IV_LEN};
|
||||||
use crate::core::flowy_str::FlowyStr;
|
use crate::core::flowy_str::FlowyStr;
|
||||||
use crate::core::interval::Interval;
|
use crate::core::interval::Interval;
|
||||||
use crate::core::operation::{Attributes, Operation, OperationBuilder, OperationTransformable, PhantomAttributes};
|
use crate::core::operation::{Attributes, Operation, OperationBuilder, OperationTransform, PhantomAttributes};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use std::{
|
use std::{
|
||||||
@ -123,7 +123,7 @@ where
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.utf16_target_len += s.utf16_size();
|
self.utf16_target_len += s.utf16_len();
|
||||||
let new_last = match self.ops.as_mut_slice() {
|
let new_last = match self.ops.as_mut_slice() {
|
||||||
[.., Operation::<T>::Insert(insert)] => {
|
[.., Operation::<T>::Insert(insert)] => {
|
||||||
//
|
//
|
||||||
@ -191,12 +191,12 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn apply(&self, applied_str: &str) -> Result<String, OTError> {
|
pub fn apply(&self, applied_str: &str) -> Result<String, OTError> {
|
||||||
let applied_str: FlowyStr = applied_str.into();
|
let applied_str: FlowyStr = applied_str.into();
|
||||||
if applied_str.utf16_size() != self.utf16_base_len {
|
if applied_str.utf16_len() != self.utf16_base_len {
|
||||||
return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength)
|
return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength)
|
||||||
.msg(format!(
|
.msg(format!(
|
||||||
"Expected: {}, but received: {}",
|
"Expected: {}, but received: {}",
|
||||||
self.utf16_base_len,
|
self.utf16_base_len,
|
||||||
applied_str.utf16_size()
|
applied_str.utf16_len()
|
||||||
))
|
))
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
@ -289,7 +289,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OperationTransformable for Delta<T>
|
impl<T> OperationTransform for Delta<T>
|
||||||
where
|
where
|
||||||
T: Attributes,
|
T: Attributes,
|
||||||
{
|
{
|
||||||
@ -568,6 +568,17 @@ impl<T> Delta<T>
|
|||||||
where
|
where
|
||||||
T: Attributes + DeserializeOwned,
|
T: Attributes + DeserializeOwned,
|
||||||
{
|
{
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use lib_ot::core::DeltaBuilder;
|
||||||
|
/// use lib_ot::rich_text::{RichTextDelta};
|
||||||
|
/// let json = r#"[
|
||||||
|
/// {"retain":7,"attributes":{"bold":null}}
|
||||||
|
/// ]"#;
|
||||||
|
/// let delta = RichTextDelta::from_json_str(json).unwrap();
|
||||||
|
/// assert_eq!(delta.to_json_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#);
|
||||||
|
/// ```
|
||||||
pub fn from_json_str(json: &str) -> Result<Self, OTError> {
|
pub fn from_json_str(json: &str) -> Result<Self, OTError> {
|
||||||
let delta = serde_json::from_str(json).map_err(|e| {
|
let delta = serde_json::from_str(json).map_err(|e| {
|
||||||
tracing::trace!("Deserialize failed: {:?}", e);
|
tracing::trace!("Deserialize failed: {:?}", e);
|
||||||
|
@ -7,6 +7,18 @@ 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;
|
||||||
|
|
||||||
|
/// Retain the 'n' characters with the attributes. Use 'retain' instead if you don't
|
||||||
|
/// need any attributes.
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use lib_ot::rich_text::{RichTextAttribute, RichTextDelta, RichTextDeltaBuilder};
|
||||||
|
///
|
||||||
|
/// let mut attribute = RichTextAttribute::Bold(true);
|
||||||
|
/// let delta = RichTextDeltaBuilder::new().retain_with_attributes(7, attribute.into()).build();
|
||||||
|
///
|
||||||
|
/// assert_eq!(delta.to_json_str(), r#"[{"retain":7,"attributes":{"bold":true}}]"#);
|
||||||
|
/// ```
|
||||||
pub struct DeltaIterator<'a, T: Attributes> {
|
pub struct DeltaIterator<'a, T: Attributes> {
|
||||||
cursor: DeltaCursor<'a, T>,
|
cursor: DeltaCursor<'a, T>,
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,24 @@ use std::{fmt, fmt::Formatter};
|
|||||||
pub struct FlowyStr(pub String);
|
pub struct FlowyStr(pub String);
|
||||||
|
|
||||||
impl FlowyStr {
|
impl FlowyStr {
|
||||||
// https://stackoverflow.com/questions/2241348/what-is-unicode-utf-8-utf-16
|
///
|
||||||
pub fn utf16_size(&self) -> usize {
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `delta`: The delta you want to iterate over.
|
||||||
|
/// * `interval`: The range for the cursor movement.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use lib_ot::core::FlowyStr;
|
||||||
|
/// let utf16_len = FlowyStr::from("👋").utf16_len();
|
||||||
|
/// assert_eq!(utf16_len, 2);
|
||||||
|
/// let bytes_len = String::from("👋").len();
|
||||||
|
/// assert_eq!(bytes_len, 4);
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// https://stackoverflow.com/questions/2241348/what-is-unicode-utf-8-utf-16
|
||||||
|
pub fn utf16_len(&self) -> usize {
|
||||||
count_utf16_code_units(&self.0)
|
count_utf16_code_units(&self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +247,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn flowy_str_code_unit() {
|
fn flowy_str_code_unit() {
|
||||||
let size = FlowyStr::from("👋").utf16_size();
|
let size = FlowyStr::from("👋").utf16_len();
|
||||||
assert_eq!(size, 2);
|
assert_eq!(size, 2);
|
||||||
|
|
||||||
let s: FlowyStr = "👋 \n👋".into();
|
let s: FlowyStr = "👋 \n👋".into();
|
||||||
@ -251,7 +267,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn flowy_str_sub_str_in_chinese() {
|
fn flowy_str_sub_str_in_chinese() {
|
||||||
let s: FlowyStr = "你好\n😁".into();
|
let s: FlowyStr = "你好\n😁".into();
|
||||||
let size = s.utf16_size();
|
let size = s.utf16_len();
|
||||||
assert_eq!(size, 5);
|
assert_eq!(size, 5);
|
||||||
|
|
||||||
let output1 = s.sub_str(Interval::new(0, 2)).unwrap();
|
let output1 = s.sub_str(Interval::new(0, 2)).unwrap();
|
||||||
@ -265,7 +281,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn flowy_str_sub_str_in_chinese2() {
|
fn flowy_str_sub_str_in_chinese2() {
|
||||||
let s: FlowyStr = "😁 \n".into();
|
let s: FlowyStr = "😁 \n".into();
|
||||||
let size = s.utf16_size();
|
let size = s.utf16_len();
|
||||||
assert_eq!(size, 4);
|
assert_eq!(size, 4);
|
||||||
|
|
||||||
let output1 = s.sub_str(Interval::new(0, 3)).unwrap();
|
let output1 = s.sub_str(Interval::new(0, 3)).unwrap();
|
||||||
@ -277,7 +293,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn flowy_str_sub_str_in_english() {
|
fn flowy_str_sub_str_in_english() {
|
||||||
let s: FlowyStr = "ab".into();
|
let s: FlowyStr = "ab".into();
|
||||||
let size = s.utf16_size();
|
let size = s.utf16_len();
|
||||||
assert_eq!(size, 2);
|
assert_eq!(size, 2);
|
||||||
|
|
||||||
let output = s.sub_str(Interval::new(0, 2)).unwrap();
|
let output = s.sub_str(Interval::new(0, 2)).unwrap();
|
||||||
|
@ -5,31 +5,43 @@ pub type RichTextOpBuilder = OperationBuilder<RichTextAttributes>;
|
|||||||
pub type PlainTextOpBuilder = OperationBuilder<PhantomAttributes>;
|
pub type PlainTextOpBuilder = OperationBuilder<PhantomAttributes>;
|
||||||
|
|
||||||
pub struct OperationBuilder<T: Attributes> {
|
pub struct OperationBuilder<T: Attributes> {
|
||||||
ty: Operation<T>,
|
operations: Vec<Operation<T>>,
|
||||||
attrs: T,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OperationBuilder<T>
|
impl<T> OperationBuilder<T>
|
||||||
where
|
where
|
||||||
T: Attributes,
|
T: Attributes,
|
||||||
{
|
{
|
||||||
pub fn new(ty: Operation<T>) -> OperationBuilder<T> {
|
pub fn new() -> OperationBuilder<T> {
|
||||||
OperationBuilder {
|
OperationBuilder { operations: vec![] }
|
||||||
ty,
|
}
|
||||||
attrs: T::default(),
|
|
||||||
|
pub fn retain(mut self, n: usize) -> OperationBuilder<T> {
|
||||||
|
let mut retain = Operation::Retain(n.into());
|
||||||
|
|
||||||
|
if let Some(attributes) = attributes {
|
||||||
|
if let Operation::Retain(r) = &mut retain {
|
||||||
|
r.attributes = attributes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
self.operations.push(retain);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn retain(n: usize) -> OperationBuilder<T> {
|
pub fn delete(mut self, n: usize) -> OperationBuilder<T> {
|
||||||
OperationBuilder::new(Operation::Retain(n.into()))
|
self.operations.push(Operation::Delete(n));
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(n: usize) -> OperationBuilder<T> {
|
pub fn insert(mut self, s: &str, attributes: Option<T>) -> OperationBuilder<T> {
|
||||||
OperationBuilder::new(Operation::Delete(n))
|
let mut insert = Operation::Insert(s.into());
|
||||||
}
|
if let Some(attributes) = attributes {
|
||||||
|
if let Operation::Retain(i) = &mut insert {
|
||||||
pub fn insert(s: &str) -> OperationBuilder<T> {
|
i.attributes = attributes;
|
||||||
OperationBuilder::new(Operation::Insert(s.into()))
|
}
|
||||||
|
}
|
||||||
|
self.operations.push(insert);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attributes(mut self, attrs: T) -> OperationBuilder<T> {
|
pub fn attributes(mut self, attrs: T) -> OperationBuilder<T> {
|
||||||
@ -37,13 +49,7 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Operation<T> {
|
pub fn build(self) -> Vec<Operation<T>> {
|
||||||
let mut operation = self.ty;
|
self.operations
|
||||||
match &mut operation {
|
|
||||||
Operation::Delete(_) => {}
|
|
||||||
Operation::Retain(retain) => retain.attributes = self.attrs,
|
|
||||||
Operation::Insert(insert) => insert.attributes = self.attrs,
|
|
||||||
}
|
|
||||||
operation
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use std::{
|
|||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait OperationTransformable {
|
pub trait OperationTransform {
|
||||||
/// Merges the operation with `other` into one operation while preserving
|
/// Merges the operation with `other` into one operation while preserving
|
||||||
/// the changes of both.
|
/// the changes of both.
|
||||||
///
|
///
|
||||||
@ -22,7 +22,7 @@ pub trait OperationTransformable {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use lib_ot::core::{OperationTransformable, PlainTextDeltaBuilder};
|
/// use lib_ot::core::{OperationTransform, PlainTextDeltaBuilder};
|
||||||
/// let document = PlainTextDeltaBuilder::new().build();
|
/// let document = PlainTextDeltaBuilder::new().build();
|
||||||
/// let delta = PlainTextDeltaBuilder::new().insert("abc").build();
|
/// let delta = PlainTextDeltaBuilder::new().insert("abc").build();
|
||||||
/// let new_document = document.compose(&delta).unwrap();
|
/// let new_document = document.compose(&delta).unwrap();
|
||||||
@ -51,7 +51,7 @@ pub trait OperationTransformable {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use lib_ot::core::{OperationTransformable, PlainTextDeltaBuilder};
|
/// use lib_ot::core::{OperationTransform, PlainTextDeltaBuilder};
|
||||||
/// let original_document = PlainTextDeltaBuilder::new().build();
|
/// let original_document = PlainTextDeltaBuilder::new().build();
|
||||||
/// let delta = PlainTextDeltaBuilder::new().insert("abc").build();
|
/// let delta = PlainTextDeltaBuilder::new().insert("abc").build();
|
||||||
///
|
///
|
||||||
@ -71,7 +71,7 @@ pub trait OperationTransformable {
|
|||||||
/// Because [Operation] is generic over the T, so you must specify the T. For example, the [PlainTextDelta]. It use
|
/// Because [Operation] is generic over the T, so you must specify the T. For example, the [PlainTextDelta]. It use
|
||||||
/// use [PhantomAttributes] as the T. [PhantomAttributes] does nothing, just a phantom.
|
/// use [PhantomAttributes] as the T. [PhantomAttributes] does nothing, just a phantom.
|
||||||
///
|
///
|
||||||
pub trait Attributes: Default + Display + Eq + PartialEq + Clone + Debug + OperationTransformable {
|
pub trait Attributes: Default + Display + Eq + PartialEq + Clone + Debug + OperationTransform {
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -360,7 +360,7 @@ where
|
|||||||
T: Attributes,
|
T: Attributes,
|
||||||
{
|
{
|
||||||
pub fn utf16_size(&self) -> usize {
|
pub fn utf16_size(&self) -> usize {
|
||||||
self.s.utf16_size()
|
self.s.utf16_len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merge_or_new_op(&mut self, s: &str, attributes: T) -> Option<Operation<T>> {
|
pub fn merge_or_new_op(&mut self, s: &str, attributes: T) -> Option<Operation<T>> {
|
||||||
@ -420,7 +420,7 @@ impl fmt::Display for PhantomAttributes {
|
|||||||
|
|
||||||
impl Attributes for PhantomAttributes {}
|
impl Attributes for PhantomAttributes {}
|
||||||
|
|
||||||
impl OperationTransformable for PhantomAttributes {
|
impl OperationTransform for PhantomAttributes {
|
||||||
fn compose(&self, _other: &Self) -> Result<Self, OTError> {
|
fn compose(&self, _other: &Self) -> Result<Self, OTError> {
|
||||||
Ok(self.clone())
|
Ok(self.clone())
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
use crate::core::{Attributes, Operation, OperationTransformable};
|
use crate::core::{Attributes, Operation, OperationTransform};
|
||||||
use crate::{block_attribute, errors::OTError, ignore_attribute, inline_attribute, list_attribute};
|
use crate::{block_attribute, errors::OTError, ignore_attribute, inline_attribute, list_attribute};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use std::{
|
use std::{
|
||||||
@ -122,7 +122,7 @@ impl Attributes for RichTextAttributes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OperationTransformable for RichTextAttributes {
|
impl OperationTransform for RichTextAttributes {
|
||||||
fn compose(&self, other: &Self) -> Result<Self, OTError>
|
fn compose(&self, other: &Self) -> Result<Self, OTError>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user