mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Refactor/rename delta (#1256)
* chore: rename delta * chore: renanme delta
This commit is contained in:
parent
9758fe51f8
commit
4549ed20e7
@ -16,7 +16,7 @@ use flowy_error::FlowyError;
|
|||||||
use flowy_folder_data_model::user_default;
|
use flowy_folder_data_model::user_default;
|
||||||
use flowy_revision::disk::SQLiteTextBlockRevisionPersistence;
|
use flowy_revision::disk::SQLiteTextBlockRevisionPersistence;
|
||||||
use flowy_revision::{RevisionManager, RevisionPersistence, RevisionWebSocket, SQLiteRevisionSnapshotPersistence};
|
use flowy_revision::{RevisionManager, RevisionPersistence, RevisionWebSocket, SQLiteRevisionSnapshotPersistence};
|
||||||
use flowy_sync::client_document::default::{initial_quill_delta_string, initial_read_me};
|
use flowy_sync::client_document::default::{initial_document_str, initial_read_me};
|
||||||
use flowy_sync::{client_folder::FolderPad, entities::ws_data::ServerRevisionWSData};
|
use flowy_sync::{client_folder::FolderPad, entities::ws_data::ServerRevisionWSData};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
@ -218,7 +218,7 @@ impl DefaultFolderBuilder {
|
|||||||
let view_data = if index == 0 {
|
let view_data = if index == 0 {
|
||||||
initial_read_me().json_str()
|
initial_read_me().json_str()
|
||||||
} else {
|
} else {
|
||||||
initial_quill_delta_string()
|
initial_document_str()
|
||||||
};
|
};
|
||||||
let _ = view_controller.set_latest_view(&view.id);
|
let _ = view_controller.set_latest_view(&view.id);
|
||||||
let layout_type = ViewLayoutTypePB::from(view.layout.clone());
|
let layout_type = ViewLayoutTypePB::from(view.layout.clone());
|
||||||
|
@ -4,13 +4,14 @@ use flowy_error::{FlowyError, FlowyResult};
|
|||||||
use flowy_revision::{
|
use flowy_revision::{
|
||||||
RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder, RevisionWebSocket,
|
RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder, RevisionWebSocket,
|
||||||
};
|
};
|
||||||
use flowy_sync::util::make_text_delta_from_revisions;
|
use flowy_sync::util::make_operations_from_revisions;
|
||||||
use flowy_sync::{
|
use flowy_sync::{
|
||||||
client_folder::{FolderChangeset, FolderPad},
|
client_folder::{FolderChangeset, FolderPad},
|
||||||
entities::{revision::Revision, ws_data::ServerRevisionWSData},
|
entities::{revision::Revision, ws_data::ServerRevisionWSData},
|
||||||
};
|
};
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
|
|
||||||
|
use lib_ot::core::EmptyAttributes;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -77,7 +78,7 @@ impl FolderEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn apply_change(&self, change: FolderChangeset) -> FlowyResult<()> {
|
pub(crate) fn apply_change(&self, change: FolderChangeset) -> FlowyResult<()> {
|
||||||
let FolderChangeset { delta, md5 } = change;
|
let FolderChangeset { operations: delta, md5 } = change;
|
||||||
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
|
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
|
||||||
let delta_data = delta.json_bytes();
|
let delta_data = delta.json_bytes();
|
||||||
let revision = Revision::new(
|
let revision = Revision::new(
|
||||||
@ -131,7 +132,7 @@ impl FolderEditor {
|
|||||||
pub struct FolderRevisionCompactor();
|
pub struct FolderRevisionCompactor();
|
||||||
impl RevisionCompactor for FolderRevisionCompactor {
|
impl RevisionCompactor for FolderRevisionCompactor {
|
||||||
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
|
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
|
||||||
let delta = make_text_delta_from_revisions(revisions)?;
|
let operations = make_operations_from_revisions::<EmptyAttributes>(revisions)?;
|
||||||
Ok(delta.json_bytes())
|
Ok(operations.json_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,10 +52,10 @@ struct FolderConflictResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ConflictResolver<EmptyAttributes> for FolderConflictResolver {
|
impl ConflictResolver<EmptyAttributes> for FolderConflictResolver {
|
||||||
fn compose_delta(&self, delta: Delta) -> BoxResultFuture<DeltaMD5, FlowyError> {
|
fn compose_delta(&self, delta: Delta) -> BoxResultFuture<OperationsMD5, FlowyError> {
|
||||||
let folder_pad = self.folder_pad.clone();
|
let folder_pad = self.folder_pad.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let md5 = folder_pad.write().compose_remote_delta(delta)?;
|
let md5 = folder_pad.write().compose_remote_operations(delta)?;
|
||||||
Ok(md5)
|
Ok(md5)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ impl ConflictResolver<EmptyAttributes> for FolderConflictResolver {
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
client_prime = delta;
|
client_prime = delta;
|
||||||
} else {
|
} else {
|
||||||
let (s_prime, c_prime) = read_guard.delta().transform(&delta)?;
|
let (s_prime, c_prime) = read_guard.get_operations().transform(&delta)?;
|
||||||
client_prime = c_prime;
|
client_prime = c_prime;
|
||||||
server_prime = Some(s_prime);
|
server_prime = Some(s_prime);
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ impl ConflictResolver<EmptyAttributes> for FolderConflictResolver {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_delta(&self, delta: Delta) -> BoxResultFuture<DeltaMD5, FlowyError> {
|
fn reset_delta(&self, delta: Delta) -> BoxResultFuture<OperationsMD5, FlowyError> {
|
||||||
let folder_pad = self.folder_pad.clone();
|
let folder_pad = self.folder_pad.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let md5 = folder_pad.write().reset_folder(delta)?;
|
let md5 = folder_pad.write().reset_folder(delta)?;
|
||||||
|
@ -14,7 +14,7 @@ use flowy_error::{FlowyError, FlowyResult};
|
|||||||
use flowy_grid_data_model::revision::{BuildGridContext, GridRevision, GridViewRevision};
|
use flowy_grid_data_model::revision::{BuildGridContext, GridRevision, GridViewRevision};
|
||||||
use flowy_revision::disk::{SQLiteGridBlockRevisionPersistence, SQLiteGridRevisionPersistence};
|
use flowy_revision::disk::{SQLiteGridBlockRevisionPersistence, SQLiteGridRevisionPersistence};
|
||||||
use flowy_revision::{RevisionManager, RevisionPersistence, RevisionWebSocket, SQLiteRevisionSnapshotPersistence};
|
use flowy_revision::{RevisionManager, RevisionPersistence, RevisionWebSocket, SQLiteRevisionSnapshotPersistence};
|
||||||
use flowy_sync::client_grid::{make_grid_block_delta, make_grid_delta, make_grid_view_delta};
|
use flowy_sync::client_grid::{make_grid_block_operations, make_grid_operations, make_grid_view_operations};
|
||||||
use flowy_sync::entities::revision::{RepeatedRevision, Revision};
|
use flowy_sync::entities::revision::{RepeatedRevision, Revision};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
@ -198,7 +198,7 @@ pub async fn make_grid_view_data(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Create grid's block
|
// Create grid's block
|
||||||
let grid_block_delta = make_grid_block_delta(block_meta_data);
|
let grid_block_delta = make_grid_block_operations(block_meta_data);
|
||||||
let block_delta_data = grid_block_delta.json_bytes();
|
let block_delta_data = grid_block_delta.json_bytes();
|
||||||
let repeated_revision: RepeatedRevision =
|
let repeated_revision: RepeatedRevision =
|
||||||
Revision::initial_revision(user_id, block_id, block_delta_data).into();
|
Revision::initial_revision(user_id, block_id, block_delta_data).into();
|
||||||
@ -210,7 +210,7 @@ pub async fn make_grid_view_data(
|
|||||||
let grid_rev = GridRevision::from_build_context(&grid_id, field_revs, block_metas);
|
let grid_rev = GridRevision::from_build_context(&grid_id, field_revs, block_metas);
|
||||||
|
|
||||||
// Create grid
|
// Create grid
|
||||||
let grid_rev_delta = make_grid_delta(&grid_rev);
|
let grid_rev_delta = make_grid_operations(&grid_rev);
|
||||||
let grid_rev_delta_bytes = grid_rev_delta.json_bytes();
|
let grid_rev_delta_bytes = grid_rev_delta.json_bytes();
|
||||||
let repeated_revision: RepeatedRevision =
|
let repeated_revision: RepeatedRevision =
|
||||||
Revision::initial_revision(user_id, &grid_id, grid_rev_delta_bytes.clone()).into();
|
Revision::initial_revision(user_id, &grid_id, grid_rev_delta_bytes.clone()).into();
|
||||||
@ -222,7 +222,7 @@ pub async fn make_grid_view_data(
|
|||||||
} else {
|
} else {
|
||||||
GridViewRevision::from_json(grid_view_revision_data)?
|
GridViewRevision::from_json(grid_view_revision_data)?
|
||||||
};
|
};
|
||||||
let grid_view_delta = make_grid_view_delta(&grid_view);
|
let grid_view_delta = make_grid_view_operations(&grid_view);
|
||||||
let grid_view_delta_bytes = grid_view_delta.json_bytes();
|
let grid_view_delta_bytes = grid_view_delta.json_bytes();
|
||||||
let repeated_revision: RepeatedRevision =
|
let repeated_revision: RepeatedRevision =
|
||||||
Revision::initial_revision(user_id, view_id, grid_view_delta_bytes).into();
|
Revision::initial_revision(user_id, view_id, grid_view_delta_bytes).into();
|
||||||
|
@ -5,9 +5,10 @@ use flowy_grid_data_model::revision::{CellRevision, GridBlockRevision, RowChange
|
|||||||
use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
|
use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
|
||||||
use flowy_sync::client_grid::{GridBlockRevisionChangeset, GridBlockRevisionPad};
|
use flowy_sync::client_grid::{GridBlockRevisionChangeset, GridBlockRevisionPad};
|
||||||
use flowy_sync::entities::revision::Revision;
|
use flowy_sync::entities::revision::Revision;
|
||||||
use flowy_sync::util::make_text_delta_from_revisions;
|
use flowy_sync::util::make_operations_from_revisions;
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
|
|
||||||
|
use lib_ot::core::EmptyAttributes;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
@ -162,7 +163,7 @@ impl GridBlockRevisionEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn apply_change(&self, change: GridBlockRevisionChangeset) -> FlowyResult<()> {
|
async fn apply_change(&self, change: GridBlockRevisionChangeset) -> FlowyResult<()> {
|
||||||
let GridBlockRevisionChangeset { delta, md5 } = change;
|
let GridBlockRevisionChangeset { operations: delta, md5 } = change;
|
||||||
let user_id = self.user_id.clone();
|
let user_id = self.user_id.clone();
|
||||||
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
|
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
|
||||||
let delta_data = delta.json_bytes();
|
let delta_data = delta.json_bytes();
|
||||||
@ -204,7 +205,7 @@ impl RevisionObjectBuilder for GridBlockRevisionPadBuilder {
|
|||||||
pub struct GridBlockRevisionCompactor();
|
pub struct GridBlockRevisionCompactor();
|
||||||
impl RevisionCompactor for GridBlockRevisionCompactor {
|
impl RevisionCompactor for GridBlockRevisionCompactor {
|
||||||
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
|
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
|
||||||
let delta = make_text_delta_from_revisions(revisions)?;
|
let operations = make_operations_from_revisions::<EmptyAttributes>(revisions)?;
|
||||||
Ok(delta.json_bytes())
|
Ok(operations.json_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,10 @@ use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, R
|
|||||||
use flowy_sync::client_grid::{GridRevisionChangeset, GridRevisionPad, JsonDeserializer};
|
use flowy_sync::client_grid::{GridRevisionChangeset, GridRevisionPad, JsonDeserializer};
|
||||||
use flowy_sync::entities::revision::Revision;
|
use flowy_sync::entities::revision::Revision;
|
||||||
use flowy_sync::errors::CollaborateResult;
|
use flowy_sync::errors::CollaborateResult;
|
||||||
use flowy_sync::util::make_text_delta_from_revisions;
|
use flowy_sync::util::make_operations_from_revisions;
|
||||||
use lib_infra::future::{wrap_future, FutureResult};
|
use lib_infra::future::{wrap_future, FutureResult};
|
||||||
|
|
||||||
|
use lib_ot::core::EmptyAttributes;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
@ -147,7 +148,7 @@ impl GridRevisionEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn next_field_rev(&self, field_type: &FieldType) -> FlowyResult<FieldRevision> {
|
pub async fn next_field_rev(&self, field_type: &FieldType) -> FlowyResult<FieldRevision> {
|
||||||
let name = format!("Property {}", self.grid_pad.read().await.fields().len() + 1);
|
let name = format!("Property {}", self.grid_pad.read().await.get_fields().len() + 1);
|
||||||
let field_rev = FieldBuilder::from_field_type(field_type).name(&name).build();
|
let field_rev = FieldBuilder::from_field_type(field_type).name(&name).build();
|
||||||
Ok(field_rev)
|
Ok(field_rev)
|
||||||
}
|
}
|
||||||
@ -675,10 +676,6 @@ impl GridRevisionEditor {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delta_bytes(&self) -> Bytes {
|
|
||||||
self.grid_pad.read().await.delta_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn duplicate_grid(&self) -> FlowyResult<BuildGridContext> {
|
pub async fn duplicate_grid(&self) -> FlowyResult<BuildGridContext> {
|
||||||
let grid_pad = self.grid_pad.read().await;
|
let grid_pad = self.grid_pad.read().await;
|
||||||
let grid_view_revision_data = self.view_manager.duplicate_grid_view().await?;
|
let grid_view_revision_data = self.view_manager.duplicate_grid_view().await?;
|
||||||
@ -750,7 +747,7 @@ impl GridRevisionEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn apply_change(&self, change: GridRevisionChangeset) -> FlowyResult<()> {
|
async fn apply_change(&self, change: GridRevisionChangeset) -> FlowyResult<()> {
|
||||||
let GridRevisionChangeset { delta, md5 } = change;
|
let GridRevisionChangeset { operations: delta, md5 } = change;
|
||||||
let user_id = self.user.user_id()?;
|
let user_id = self.user.user_id()?;
|
||||||
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
|
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
|
||||||
let delta_data = delta.json_bytes();
|
let delta_data = delta.json_bytes();
|
||||||
@ -844,8 +841,8 @@ impl RevisionCloudService for GridRevisionCloudService {
|
|||||||
pub struct GridRevisionCompactor();
|
pub struct GridRevisionCompactor();
|
||||||
impl RevisionCompactor for GridRevisionCompactor {
|
impl RevisionCompactor for GridRevisionCompactor {
|
||||||
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
|
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
|
||||||
let delta = make_text_delta_from_revisions(revisions)?;
|
let operations = make_operations_from_revisions::<EmptyAttributes>(revisions)?;
|
||||||
Ok(delta.json_bytes())
|
Ok(operations.json_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +435,7 @@ async fn apply_change(
|
|||||||
rev_manager: Arc<RevisionManager>,
|
rev_manager: Arc<RevisionManager>,
|
||||||
change: GridViewRevisionChangeset,
|
change: GridViewRevisionChangeset,
|
||||||
) -> FlowyResult<()> {
|
) -> FlowyResult<()> {
|
||||||
let GridViewRevisionChangeset { delta, md5 } = change;
|
let GridViewRevisionChangeset { operations: delta, md5 } = change;
|
||||||
let (base_rev_id, rev_id) = rev_manager.next_rev_id_pair();
|
let (base_rev_id, rev_id) = rev_manager.next_rev_id_pair();
|
||||||
let delta_data = delta.json_bytes();
|
let delta_data = delta.json_bytes();
|
||||||
let revision = Revision::new(&rev_manager.object_id, base_rev_id, rev_id, delta_data, user_id, md5);
|
let revision = Revision::new(&rev_manager.object_id, base_rev_id, rev_id, delta_data, user_id, md5);
|
||||||
|
@ -12,8 +12,9 @@ use flowy_grid_data_model::revision::{FieldRevision, RowChangeset, RowRevision};
|
|||||||
use flowy_revision::disk::SQLiteGridViewRevisionPersistence;
|
use flowy_revision::disk::SQLiteGridViewRevisionPersistence;
|
||||||
use flowy_revision::{RevisionCompactor, RevisionManager, RevisionPersistence, SQLiteRevisionSnapshotPersistence};
|
use flowy_revision::{RevisionCompactor, RevisionManager, RevisionPersistence, SQLiteRevisionSnapshotPersistence};
|
||||||
use flowy_sync::entities::revision::Revision;
|
use flowy_sync::entities::revision::Revision;
|
||||||
use flowy_sync::util::make_text_delta_from_revisions;
|
use flowy_sync::util::make_operations_from_revisions;
|
||||||
use lib_infra::future::AFFuture;
|
use lib_infra::future::AFFuture;
|
||||||
|
use lib_ot::core::EmptyAttributes;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
type ViewId = String;
|
type ViewId = String;
|
||||||
@ -262,7 +263,7 @@ pub async fn make_grid_view_rev_manager(user: &Arc<dyn GridUser>, view_id: &str)
|
|||||||
pub struct GridViewRevisionCompactor();
|
pub struct GridViewRevisionCompactor();
|
||||||
impl RevisionCompactor for GridViewRevisionCompactor {
|
impl RevisionCompactor for GridViewRevisionCompactor {
|
||||||
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
|
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
|
||||||
let delta = make_text_delta_from_revisions(revisions)?;
|
let operations = make_operations_from_revisions::<EmptyAttributes>(revisions)?;
|
||||||
Ok(delta.json_bytes())
|
Ok(operations.json_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use bytes::Bytes;
|
|||||||
use flowy_error::{internal_error, FlowyError};
|
use flowy_error::{internal_error, FlowyError};
|
||||||
use flowy_folder::event_map::FolderCouldServiceV1;
|
use flowy_folder::event_map::FolderCouldServiceV1;
|
||||||
use flowy_sync::{
|
use flowy_sync::{
|
||||||
client_document::default::initial_quill_delta_string,
|
client_document::default::initial_document_str,
|
||||||
entities::{
|
entities::{
|
||||||
text_block::{CreateTextBlockParams, DocumentPB, ResetTextBlockParams, TextBlockIdPB},
|
text_block::{CreateTextBlockParams, DocumentPB, ResetTextBlockParams, TextBlockIdPB},
|
||||||
ws_data::{ClientRevisionWSData, ClientRevisionWSDataType},
|
ws_data::{ClientRevisionWSData, ClientRevisionWSDataType},
|
||||||
@ -422,7 +422,7 @@ impl TextEditorCloudService for LocalServer {
|
|||||||
fn read_text_block(&self, _token: &str, params: TextBlockIdPB) -> FutureResult<Option<DocumentPB>, FlowyError> {
|
fn read_text_block(&self, _token: &str, params: TextBlockIdPB) -> FutureResult<Option<DocumentPB>, FlowyError> {
|
||||||
let doc = DocumentPB {
|
let doc = DocumentPB {
|
||||||
block_id: params.value,
|
block_id: params.value,
|
||||||
text: initial_quill_delta_string(),
|
text: initial_document_str(),
|
||||||
rev_id: 0,
|
rev_id: 0,
|
||||||
base_rev_id: 0,
|
base_rev_id: 0,
|
||||||
};
|
};
|
||||||
|
@ -109,7 +109,7 @@ impl TextRevisionSql {
|
|||||||
dsl::doc_id.eq(record.revision.object_id),
|
dsl::doc_id.eq(record.revision.object_id),
|
||||||
dsl::base_rev_id.eq(record.revision.base_rev_id),
|
dsl::base_rev_id.eq(record.revision.base_rev_id),
|
||||||
dsl::rev_id.eq(record.revision.rev_id),
|
dsl::rev_id.eq(record.revision.rev_id),
|
||||||
dsl::data.eq(record.revision.delta_data),
|
dsl::data.eq(record.revision.bytes),
|
||||||
dsl::state.eq(rev_state),
|
dsl::state.eq(rev_state),
|
||||||
dsl::ty.eq(RevTableType::Local),
|
dsl::ty.eq(RevTableType::Local),
|
||||||
)
|
)
|
||||||
|
@ -108,7 +108,7 @@ impl GridMetaRevisionSql {
|
|||||||
dsl::object_id.eq(record.revision.object_id),
|
dsl::object_id.eq(record.revision.object_id),
|
||||||
dsl::base_rev_id.eq(record.revision.base_rev_id),
|
dsl::base_rev_id.eq(record.revision.base_rev_id),
|
||||||
dsl::rev_id.eq(record.revision.rev_id),
|
dsl::rev_id.eq(record.revision.rev_id),
|
||||||
dsl::data.eq(record.revision.delta_data),
|
dsl::data.eq(record.revision.bytes),
|
||||||
dsl::state.eq(rev_state),
|
dsl::state.eq(rev_state),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -107,7 +107,7 @@ impl GridRevisionSql {
|
|||||||
dsl::object_id.eq(record.revision.object_id),
|
dsl::object_id.eq(record.revision.object_id),
|
||||||
dsl::base_rev_id.eq(record.revision.base_rev_id),
|
dsl::base_rev_id.eq(record.revision.base_rev_id),
|
||||||
dsl::rev_id.eq(record.revision.rev_id),
|
dsl::rev_id.eq(record.revision.rev_id),
|
||||||
dsl::data.eq(record.revision.delta_data),
|
dsl::data.eq(record.revision.bytes),
|
||||||
dsl::state.eq(rev_state),
|
dsl::state.eq(rev_state),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -106,7 +106,7 @@ impl GridViewRevisionSql {
|
|||||||
dsl::object_id.eq(record.revision.object_id),
|
dsl::object_id.eq(record.revision.object_id),
|
||||||
dsl::base_rev_id.eq(record.revision.base_rev_id),
|
dsl::base_rev_id.eq(record.revision.base_rev_id),
|
||||||
dsl::rev_id.eq(record.revision.rev_id),
|
dsl::rev_id.eq(record.revision.rev_id),
|
||||||
dsl::data.eq(record.revision.delta_data),
|
dsl::data.eq(record.revision.bytes),
|
||||||
dsl::state.eq(rev_state),
|
dsl::state.eq(rev_state),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -6,23 +6,23 @@ use flowy_sync::{
|
|||||||
revision::{RepeatedRevision, Revision, RevisionRange},
|
revision::{RepeatedRevision, Revision, RevisionRange},
|
||||||
ws_data::ServerRevisionWSDataType,
|
ws_data::ServerRevisionWSDataType,
|
||||||
},
|
},
|
||||||
util::make_delta_from_revisions,
|
util::make_operations_from_revisions,
|
||||||
};
|
};
|
||||||
use lib_infra::future::BoxResultFuture;
|
use lib_infra::future::BoxResultFuture;
|
||||||
use lib_ot::core::{Attributes, EmptyAttributes, OperationAttributes, Operations};
|
use lib_ot::core::{AttributeHashMap, DeltaOperations, EmptyAttributes, OperationAttributes};
|
||||||
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use std::{convert::TryFrom, sync::Arc};
|
use std::{convert::TryFrom, sync::Arc};
|
||||||
|
|
||||||
pub type DeltaMD5 = String;
|
pub type OperationsMD5 = String;
|
||||||
|
|
||||||
pub trait ConflictResolver<T>
|
pub trait ConflictResolver<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes + Send + Sync,
|
T: OperationAttributes + Send + Sync,
|
||||||
{
|
{
|
||||||
fn compose_delta(&self, delta: Operations<T>) -> BoxResultFuture<DeltaMD5, FlowyError>;
|
fn compose_delta(&self, delta: DeltaOperations<T>) -> BoxResultFuture<OperationsMD5, FlowyError>;
|
||||||
fn transform_delta(&self, delta: Operations<T>) -> BoxResultFuture<TransformDeltas<T>, FlowyError>;
|
fn transform_delta(&self, delta: DeltaOperations<T>) -> BoxResultFuture<TransformDeltas<T>, FlowyError>;
|
||||||
fn reset_delta(&self, delta: Operations<T>) -> BoxResultFuture<DeltaMD5, FlowyError>;
|
fn reset_delta(&self, delta: DeltaOperations<T>) -> BoxResultFuture<OperationsMD5, FlowyError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ConflictRevisionSink: Send + Sync + 'static {
|
pub trait ConflictRevisionSink: Send + Sync + 'static {
|
||||||
@ -30,7 +30,7 @@ pub trait ConflictRevisionSink: Send + Sync + 'static {
|
|||||||
fn ack(&self, rev_id: String, ty: ServerRevisionWSDataType) -> BoxResultFuture<(), FlowyError>;
|
fn ack(&self, rev_id: String, ty: ServerRevisionWSDataType) -> BoxResultFuture<(), FlowyError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RichTextConflictController = ConflictController<Attributes>;
|
pub type RichTextConflictController = ConflictController<AttributeHashMap>;
|
||||||
pub type PlainTextConflictController = ConflictController<EmptyAttributes>;
|
pub type PlainTextConflictController = ConflictController<EmptyAttributes>;
|
||||||
|
|
||||||
pub struct ConflictController<T>
|
pub struct ConflictController<T>
|
||||||
@ -103,7 +103,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_delta = make_delta_from_revisions(revisions.clone())?;
|
let new_delta = make_operations_from_revisions(revisions.clone())?;
|
||||||
|
|
||||||
let TransformDeltas {
|
let TransformDeltas {
|
||||||
client_prime,
|
client_prime,
|
||||||
@ -142,8 +142,8 @@ where
|
|||||||
fn make_client_and_server_revision<T>(
|
fn make_client_and_server_revision<T>(
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
rev_manager: &Arc<RevisionManager>,
|
rev_manager: &Arc<RevisionManager>,
|
||||||
client_delta: Operations<T>,
|
client_delta: DeltaOperations<T>,
|
||||||
server_delta: Option<Operations<T>>,
|
server_delta: Option<DeltaOperations<T>>,
|
||||||
md5: String,
|
md5: String,
|
||||||
) -> (Revision, Option<Revision>)
|
) -> (Revision, Option<Revision>)
|
||||||
where
|
where
|
||||||
@ -175,12 +175,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RichTextTransformDeltas = TransformDeltas<Attributes>;
|
pub type RichTextTransformDeltas = TransformDeltas<AttributeHashMap>;
|
||||||
|
|
||||||
pub struct TransformDeltas<T>
|
pub struct TransformDeltas<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
pub client_prime: Operations<T>,
|
pub client_prime: DeltaOperations<T>,
|
||||||
pub server_prime: Option<Operations<T>>,
|
pub server_prime: Option<DeltaOperations<T>>,
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ use flowy_sync::{
|
|||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub type SyncObject = lib_ot::text_delta::TextOperations;
|
||||||
|
|
||||||
pub trait RevisionCloudService: Send + Sync {
|
pub trait RevisionCloudService: Send + Sync {
|
||||||
fn fetch_object(&self, user_id: &str, object_id: &str) -> FutureResult<Vec<Revision>, FlowyError>;
|
fn fetch_object(&self, user_id: &str, object_id: &str) -> FutureResult<Vec<Revision>, FlowyError>;
|
||||||
}
|
}
|
||||||
@ -33,8 +35,8 @@ pub trait RevisionCompactor: Send + Sync {
|
|||||||
|
|
||||||
let (base_rev_id, rev_id) = first_revision.pair_rev_id();
|
let (base_rev_id, rev_id) = first_revision.pair_rev_id();
|
||||||
let md5 = last_revision.md5.clone();
|
let md5 = last_revision.md5.clone();
|
||||||
let delta_data = self.bytes_from_revisions(revisions)?;
|
let bytes = self.bytes_from_revisions(revisions)?;
|
||||||
Ok(Revision::new(object_id, base_rev_id, rev_id, delta_data, user_id, md5))
|
Ok(Revision::new(object_id, base_rev_id, rev_id, bytes, user_id, md5))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes>;
|
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes>;
|
||||||
@ -113,7 +115,7 @@ impl RevisionManager {
|
|||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self, revision), err)]
|
#[tracing::instrument(level = "debug", skip(self, revision), err)]
|
||||||
pub async fn add_remote_revision(&self, revision: &Revision) -> Result<(), FlowyError> {
|
pub async fn add_remote_revision(&self, revision: &Revision) -> Result<(), FlowyError> {
|
||||||
if revision.delta_data.is_empty() {
|
if revision.bytes.is_empty() {
|
||||||
return Err(FlowyError::internal().context("Delta data should be empty"));
|
return Err(FlowyError::internal().context("Delta data should be empty"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +127,7 @@ impl RevisionManager {
|
|||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn add_local_revision(&self, revision: &Revision) -> Result<(), FlowyError> {
|
pub async fn add_local_revision(&self, revision: &Revision) -> Result<(), FlowyError> {
|
||||||
if revision.delta_data.is_empty() {
|
if revision.bytes.is_empty() {
|
||||||
return Err(FlowyError::internal().context("Delta data should be empty"));
|
return Err(FlowyError::internal().context("Delta data should be empty"));
|
||||||
}
|
}
|
||||||
let rev_id = self
|
let rev_id = self
|
||||||
|
@ -16,7 +16,7 @@ use flowy_net::{
|
|||||||
http_server::folder::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect,
|
http_server::folder::FolderHttpCloudService, local_server::LocalServer, ws::connection::FlowyWebSocketConnect,
|
||||||
};
|
};
|
||||||
use flowy_revision::{RevisionWebSocket, WSStateReceiver};
|
use flowy_revision::{RevisionWebSocket, WSStateReceiver};
|
||||||
use flowy_sync::client_document::default::initial_quill_delta_string;
|
use flowy_sync::client_document::default::initial_document_str;
|
||||||
use flowy_sync::entities::revision::{RepeatedRevision, Revision};
|
use flowy_sync::entities::revision::{RepeatedRevision, Revision};
|
||||||
use flowy_sync::entities::ws_data::ClientRevisionWSData;
|
use flowy_sync::entities::ws_data::ClientRevisionWSData;
|
||||||
use flowy_text_block::TextEditorManager;
|
use flowy_text_block::TextEditorManager;
|
||||||
@ -191,7 +191,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
|
|||||||
let view_id = view_id.to_string();
|
let view_id = view_id.to_string();
|
||||||
let manager = self.0.clone();
|
let manager = self.0.clone();
|
||||||
FutureResult::new(async move {
|
FutureResult::new(async move {
|
||||||
let view_data = initial_quill_delta_string();
|
let view_data = initial_document_str();
|
||||||
let delta_data = Bytes::from(view_data);
|
let delta_data = Bytes::from(view_data);
|
||||||
let repeated_revision: RepeatedRevision =
|
let repeated_revision: RepeatedRevision =
|
||||||
Revision::initial_revision(&user_id, &view_id, delta_data.clone()).into();
|
Revision::initial_revision(&user_id, &view_id, delta_data.clone()).into();
|
||||||
|
@ -11,12 +11,12 @@ use flowy_sync::entities::ws_data::ServerRevisionWSData;
|
|||||||
use flowy_sync::{
|
use flowy_sync::{
|
||||||
entities::{revision::Revision, text_block::DocumentPB},
|
entities::{revision::Revision, text_block::DocumentPB},
|
||||||
errors::CollaborateResult,
|
errors::CollaborateResult,
|
||||||
util::make_delta_from_revisions,
|
util::make_operations_from_revisions,
|
||||||
};
|
};
|
||||||
use lib_ot::core::AttributeEntry;
|
use lib_ot::core::AttributeEntry;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{Interval, Operation},
|
core::{DeltaOperation, Interval},
|
||||||
text_delta::TextDelta,
|
text_delta::TextOperations,
|
||||||
};
|
};
|
||||||
use lib_ws::WSConnectState;
|
use lib_ws::WSConnectState;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -150,7 +150,7 @@ impl TextBlockEditor {
|
|||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self, data), err)]
|
#[tracing::instrument(level = "trace", skip(self, data), err)]
|
||||||
pub(crate) async fn compose_local_delta(&self, data: Bytes) -> Result<(), FlowyError> {
|
pub(crate) async fn compose_local_delta(&self, data: Bytes) -> Result<(), FlowyError> {
|
||||||
let delta = TextDelta::from_bytes(&data)?;
|
let delta = TextOperations::from_bytes(&data)?;
|
||||||
let (ret, rx) = oneshot::channel::<CollaborateResult<()>>();
|
let (ret, rx) = oneshot::channel::<CollaborateResult<()>>();
|
||||||
let msg = EditorCommand::ComposeLocalDelta {
|
let msg = EditorCommand::ComposeLocalDelta {
|
||||||
delta: delta.clone(),
|
delta: delta.clone(),
|
||||||
@ -196,7 +196,7 @@ impl std::ops::Drop for TextBlockEditor {
|
|||||||
fn spawn_edit_queue(
|
fn spawn_edit_queue(
|
||||||
user: Arc<dyn TextEditorUser>,
|
user: Arc<dyn TextEditorUser>,
|
||||||
rev_manager: Arc<RevisionManager>,
|
rev_manager: Arc<RevisionManager>,
|
||||||
delta: TextDelta,
|
delta: TextOperations,
|
||||||
) -> EditorCommandSender {
|
) -> EditorCommandSender {
|
||||||
let (sender, receiver) = mpsc::channel(1000);
|
let (sender, receiver) = mpsc::channel(1000);
|
||||||
let edit_queue = EditBlockQueue::new(user, rev_manager, delta, receiver);
|
let edit_queue = EditBlockQueue::new(user, rev_manager, delta, receiver);
|
||||||
@ -215,8 +215,8 @@ fn spawn_edit_queue(
|
|||||||
|
|
||||||
#[cfg(feature = "flowy_unit_test")]
|
#[cfg(feature = "flowy_unit_test")]
|
||||||
impl TextBlockEditor {
|
impl TextBlockEditor {
|
||||||
pub async fn text_block_delta(&self) -> FlowyResult<TextDelta> {
|
pub async fn text_block_delta(&self) -> FlowyResult<TextOperations> {
|
||||||
let (ret, rx) = oneshot::channel::<CollaborateResult<TextDelta>>();
|
let (ret, rx) = oneshot::channel::<CollaborateResult<TextOperations>>();
|
||||||
let msg = EditorCommand::ReadDelta { ret };
|
let msg = EditorCommand::ReadDelta { ret };
|
||||||
let _ = self.edit_cmd_tx.send(msg).await;
|
let _ = self.edit_cmd_tx.send(msg).await;
|
||||||
let delta = rx.await.map_err(internal_error)??;
|
let delta = rx.await.map_err(internal_error)??;
|
||||||
@ -234,7 +234,7 @@ impl RevisionObjectBuilder for TextBlockInfoBuilder {
|
|||||||
|
|
||||||
fn build_object(object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
|
fn build_object(object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
|
||||||
let (base_rev_id, rev_id) = revisions.last().unwrap().pair_rev_id();
|
let (base_rev_id, rev_id) = revisions.last().unwrap().pair_rev_id();
|
||||||
let mut delta = make_delta_from_revisions(revisions)?;
|
let mut delta = make_operations_from_revisions(revisions)?;
|
||||||
correct_delta(&mut delta);
|
correct_delta(&mut delta);
|
||||||
|
|
||||||
Result::<DocumentPB, FlowyError>::Ok(DocumentPB {
|
Result::<DocumentPB, FlowyError>::Ok(DocumentPB {
|
||||||
@ -248,12 +248,12 @@ impl RevisionObjectBuilder for TextBlockInfoBuilder {
|
|||||||
|
|
||||||
// quill-editor requires the delta should end with '\n' and only contains the
|
// quill-editor requires the delta should end with '\n' and only contains the
|
||||||
// insert operation. The function, correct_delta maybe be removed in the future.
|
// insert operation. The function, correct_delta maybe be removed in the future.
|
||||||
fn correct_delta(delta: &mut TextDelta) {
|
fn correct_delta(delta: &mut TextOperations) {
|
||||||
if let Some(op) = delta.ops.last() {
|
if let Some(op) = delta.ops.last() {
|
||||||
let op_data = op.get_data();
|
let op_data = op.get_data();
|
||||||
if !op_data.ends_with('\n') {
|
if !op_data.ends_with('\n') {
|
||||||
tracing::warn!("The document must end with newline. Correcting it by inserting newline op");
|
tracing::warn!("The document must end with newline. Correcting it by inserting newline op");
|
||||||
delta.ops.push(Operation::Insert("\n".into()));
|
delta.ops.push(DeltaOperation::Insert("\n".into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,18 +3,18 @@ use crate::TextEditorUser;
|
|||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use flowy_revision::{DeltaMD5, RevisionCompactor, RevisionManager, RichTextTransformDeltas, TransformDeltas};
|
use flowy_revision::{OperationsMD5, RevisionCompactor, RevisionManager, RichTextTransformDeltas, TransformDeltas};
|
||||||
use flowy_sync::util::make_delta_from_revisions;
|
use flowy_sync::util::make_operations_from_revisions;
|
||||||
use flowy_sync::{
|
use flowy_sync::{
|
||||||
client_document::{history::UndoResult, ClientDocument},
|
client_document::{history::UndoResult, ClientDocument},
|
||||||
entities::revision::{RevId, Revision},
|
entities::revision::{RevId, Revision},
|
||||||
errors::CollaborateError,
|
errors::CollaborateError,
|
||||||
};
|
};
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use lib_ot::core::{AttributeEntry, Attributes};
|
use lib_ot::core::{AttributeEntry, AttributeHashMap};
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{Interval, OperationTransform},
|
core::{Interval, OperationTransform},
|
||||||
text_delta::TextDelta,
|
text_delta::TextOperations,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::{oneshot, RwLock};
|
use tokio::sync::{oneshot, RwLock};
|
||||||
@ -32,10 +32,10 @@ impl EditBlockQueue {
|
|||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
user: Arc<dyn TextEditorUser>,
|
user: Arc<dyn TextEditorUser>,
|
||||||
rev_manager: Arc<RevisionManager>,
|
rev_manager: Arc<RevisionManager>,
|
||||||
delta: TextDelta,
|
delta: TextOperations,
|
||||||
receiver: EditorCommandReceiver,
|
receiver: EditorCommandReceiver,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let document = Arc::new(RwLock::new(ClientDocument::from_delta(delta)));
|
let document = Arc::new(RwLock::new(ClientDocument::from_operations(delta)));
|
||||||
Self {
|
Self {
|
||||||
document,
|
document,
|
||||||
user,
|
user,
|
||||||
@ -69,7 +69,7 @@ impl EditBlockQueue {
|
|||||||
match command {
|
match command {
|
||||||
EditorCommand::ComposeLocalDelta { delta, ret } => {
|
EditorCommand::ComposeLocalDelta { delta, ret } => {
|
||||||
let mut document = self.document.write().await;
|
let mut document = self.document.write().await;
|
||||||
let _ = document.compose_delta(delta.clone())?;
|
let _ = document.compose_operations(delta.clone())?;
|
||||||
let md5 = document.md5();
|
let md5 = document.md5();
|
||||||
drop(document);
|
drop(document);
|
||||||
let _ = self.save_local_delta(delta, md5).await?;
|
let _ = self.save_local_delta(delta, md5).await?;
|
||||||
@ -77,14 +77,14 @@ impl EditBlockQueue {
|
|||||||
}
|
}
|
||||||
EditorCommand::ComposeRemoteDelta { client_delta, ret } => {
|
EditorCommand::ComposeRemoteDelta { client_delta, ret } => {
|
||||||
let mut document = self.document.write().await;
|
let mut document = self.document.write().await;
|
||||||
let _ = document.compose_delta(client_delta.clone())?;
|
let _ = document.compose_operations(client_delta.clone())?;
|
||||||
let md5 = document.md5();
|
let md5 = document.md5();
|
||||||
drop(document);
|
drop(document);
|
||||||
let _ = ret.send(Ok(md5));
|
let _ = ret.send(Ok(md5));
|
||||||
}
|
}
|
||||||
EditorCommand::ResetDelta { delta, ret } => {
|
EditorCommand::ResetDelta { delta, ret } => {
|
||||||
let mut document = self.document.write().await;
|
let mut document = self.document.write().await;
|
||||||
let _ = document.set_delta(delta);
|
let _ = document.set_operations(delta);
|
||||||
let md5 = document.md5();
|
let md5 = document.md5();
|
||||||
drop(document);
|
drop(document);
|
||||||
let _ = ret.send(Ok(md5));
|
let _ = ret.send(Ok(md5));
|
||||||
@ -92,14 +92,14 @@ impl EditBlockQueue {
|
|||||||
EditorCommand::TransformDelta { delta, ret } => {
|
EditorCommand::TransformDelta { delta, ret } => {
|
||||||
let f = || async {
|
let f = || async {
|
||||||
let read_guard = self.document.read().await;
|
let read_guard = self.document.read().await;
|
||||||
let mut server_prime: Option<TextDelta> = None;
|
let mut server_prime: Option<TextOperations> = None;
|
||||||
let client_prime: TextDelta;
|
let client_prime: TextOperations;
|
||||||
|
|
||||||
if read_guard.is_empty() {
|
if read_guard.is_empty() {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
client_prime = delta;
|
client_prime = delta;
|
||||||
} else {
|
} else {
|
||||||
let (s_prime, c_prime) = read_guard.delta().transform(&delta)?;
|
let (s_prime, c_prime) = read_guard.get_operations().transform(&delta)?;
|
||||||
client_prime = c_prime;
|
client_prime = c_prime;
|
||||||
server_prime = Some(s_prime);
|
server_prime = Some(s_prime);
|
||||||
}
|
}
|
||||||
@ -151,31 +151,31 @@ impl EditBlockQueue {
|
|||||||
}
|
}
|
||||||
EditorCommand::Undo { ret } => {
|
EditorCommand::Undo { ret } => {
|
||||||
let mut write_guard = self.document.write().await;
|
let mut write_guard = self.document.write().await;
|
||||||
let UndoResult { delta } = write_guard.undo()?;
|
let UndoResult { operations: delta } = write_guard.undo()?;
|
||||||
let md5 = write_guard.md5();
|
let md5 = write_guard.md5();
|
||||||
let _ = self.save_local_delta(delta, md5).await?;
|
let _ = self.save_local_delta(delta, md5).await?;
|
||||||
let _ = ret.send(Ok(()));
|
let _ = ret.send(Ok(()));
|
||||||
}
|
}
|
||||||
EditorCommand::Redo { ret } => {
|
EditorCommand::Redo { ret } => {
|
||||||
let mut write_guard = self.document.write().await;
|
let mut write_guard = self.document.write().await;
|
||||||
let UndoResult { delta } = write_guard.redo()?;
|
let UndoResult { operations: delta } = write_guard.redo()?;
|
||||||
let md5 = write_guard.md5();
|
let md5 = write_guard.md5();
|
||||||
let _ = self.save_local_delta(delta, md5).await?;
|
let _ = self.save_local_delta(delta, md5).await?;
|
||||||
let _ = ret.send(Ok(()));
|
let _ = ret.send(Ok(()));
|
||||||
}
|
}
|
||||||
EditorCommand::ReadDeltaStr { ret } => {
|
EditorCommand::ReadDeltaStr { ret } => {
|
||||||
let data = self.document.read().await.delta_str();
|
let data = self.document.read().await.get_operations_json();
|
||||||
let _ = ret.send(Ok(data));
|
let _ = ret.send(Ok(data));
|
||||||
}
|
}
|
||||||
EditorCommand::ReadDelta { ret } => {
|
EditorCommand::ReadDelta { ret } => {
|
||||||
let delta = self.document.read().await.delta().clone();
|
let delta = self.document.read().await.get_operations().clone();
|
||||||
let _ = ret.send(Ok(delta));
|
let _ = ret.send(Ok(delta));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save_local_delta(&self, delta: TextDelta, md5: String) -> Result<RevId, FlowyError> {
|
async fn save_local_delta(&self, delta: TextOperations, md5: String) -> Result<RevId, FlowyError> {
|
||||||
let delta_data = delta.json_bytes();
|
let delta_data = delta.json_bytes();
|
||||||
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
|
let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
|
||||||
let user_id = self.user.user_id()?;
|
let user_id = self.user.user_id()?;
|
||||||
@ -195,7 +195,7 @@ impl EditBlockQueue {
|
|||||||
pub(crate) struct TextBlockRevisionCompactor();
|
pub(crate) struct TextBlockRevisionCompactor();
|
||||||
impl RevisionCompactor for TextBlockRevisionCompactor {
|
impl RevisionCompactor for TextBlockRevisionCompactor {
|
||||||
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
|
fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
|
||||||
let delta = make_delta_from_revisions::<Attributes>(revisions)?;
|
let delta = make_operations_from_revisions::<AttributeHashMap>(revisions)?;
|
||||||
Ok(delta.json_bytes())
|
Ok(delta.json_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,19 +204,19 @@ pub(crate) type Ret<T> = oneshot::Sender<Result<T, CollaborateError>>;
|
|||||||
|
|
||||||
pub(crate) enum EditorCommand {
|
pub(crate) enum EditorCommand {
|
||||||
ComposeLocalDelta {
|
ComposeLocalDelta {
|
||||||
delta: TextDelta,
|
delta: TextOperations,
|
||||||
ret: Ret<()>,
|
ret: Ret<()>,
|
||||||
},
|
},
|
||||||
ComposeRemoteDelta {
|
ComposeRemoteDelta {
|
||||||
client_delta: TextDelta,
|
client_delta: TextOperations,
|
||||||
ret: Ret<DeltaMD5>,
|
ret: Ret<OperationsMD5>,
|
||||||
},
|
},
|
||||||
ResetDelta {
|
ResetDelta {
|
||||||
delta: TextDelta,
|
delta: TextOperations,
|
||||||
ret: Ret<DeltaMD5>,
|
ret: Ret<OperationsMD5>,
|
||||||
},
|
},
|
||||||
TransformDelta {
|
TransformDelta {
|
||||||
delta: TextDelta,
|
delta: TextOperations,
|
||||||
ret: Ret<RichTextTransformDeltas>,
|
ret: Ret<RichTextTransformDeltas>,
|
||||||
},
|
},
|
||||||
Insert {
|
Insert {
|
||||||
@ -255,7 +255,7 @@ pub(crate) enum EditorCommand {
|
|||||||
},
|
},
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
ReadDelta {
|
ReadDelta {
|
||||||
ret: Ret<TextDelta>,
|
ret: Ret<TextOperations>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,9 +10,9 @@ use flowy_sync::{
|
|||||||
errors::CollaborateResult,
|
errors::CollaborateResult,
|
||||||
};
|
};
|
||||||
use lib_infra::future::{BoxResultFuture, FutureResult};
|
use lib_infra::future::{BoxResultFuture, FutureResult};
|
||||||
use lib_ot::core::Attributes;
|
use lib_ot::core::AttributeHashMap;
|
||||||
|
|
||||||
use lib_ot::text_delta::TextDelta;
|
use lib_ot::text_delta::TextOperations;
|
||||||
use lib_ws::WSConnectState;
|
use lib_ws::WSConnectState;
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
use tokio::sync::{
|
use tokio::sync::{
|
||||||
@ -112,8 +112,8 @@ struct TextBlockConflictResolver {
|
|||||||
edit_cmd_tx: EditorCommandSender,
|
edit_cmd_tx: EditorCommandSender,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConflictResolver<Attributes> for TextBlockConflictResolver {
|
impl ConflictResolver<AttributeHashMap> for TextBlockConflictResolver {
|
||||||
fn compose_delta(&self, delta: TextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> {
|
fn compose_delta(&self, delta: TextOperations) -> BoxResultFuture<OperationsMD5, FlowyError> {
|
||||||
let tx = self.edit_cmd_tx.clone();
|
let tx = self.edit_cmd_tx.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let (ret, rx) = oneshot::channel();
|
let (ret, rx) = oneshot::channel();
|
||||||
@ -132,7 +132,7 @@ impl ConflictResolver<Attributes> for TextBlockConflictResolver {
|
|||||||
|
|
||||||
fn transform_delta(
|
fn transform_delta(
|
||||||
&self,
|
&self,
|
||||||
delta: TextDelta,
|
delta: TextOperations,
|
||||||
) -> BoxResultFuture<flowy_revision::RichTextTransformDeltas, FlowyError> {
|
) -> BoxResultFuture<flowy_revision::RichTextTransformDeltas, FlowyError> {
|
||||||
let tx = self.edit_cmd_tx.clone();
|
let tx = self.edit_cmd_tx.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@ -147,7 +147,7 @@ impl ConflictResolver<Attributes> for TextBlockConflictResolver {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_delta(&self, delta: TextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> {
|
fn reset_delta(&self, delta: TextOperations) -> BoxResultFuture<OperationsMD5, FlowyError> {
|
||||||
let tx = self.edit_cmd_tx.clone();
|
let tx = self.edit_cmd_tx.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let (ret, rx) = oneshot::channel();
|
let (ret, rx) = oneshot::channel();
|
||||||
|
@ -2,7 +2,7 @@ use flowy_revision::disk::RevisionState;
|
|||||||
use flowy_test::{helper::ViewTest, FlowySDKTest};
|
use flowy_test::{helper::ViewTest, FlowySDKTest};
|
||||||
use flowy_text_block::editor::TextBlockEditor;
|
use flowy_text_block::editor::TextBlockEditor;
|
||||||
use flowy_text_block::TEXT_BLOCK_SYNC_INTERVAL_IN_MILLIS;
|
use flowy_text_block::TEXT_BLOCK_SYNC_INTERVAL_IN_MILLIS;
|
||||||
use lib_ot::{core::Interval, text_delta::TextDelta};
|
use lib_ot::{core::Interval, text_delta::TextOperations};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::time::{sleep, Duration};
|
use tokio::time::{sleep, Duration};
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ impl TextBlockEditorTest {
|
|||||||
assert_eq!(next_revision.rev_id, rev_id.unwrap());
|
assert_eq!(next_revision.rev_id, rev_id.unwrap());
|
||||||
}
|
}
|
||||||
EditorScript::AssertJson(expected) => {
|
EditorScript::AssertJson(expected) => {
|
||||||
let expected_delta: TextDelta = serde_json::from_str(expected).unwrap();
|
let expected_delta: TextOperations = serde_json::from_str(expected).unwrap();
|
||||||
let delta = self.editor.text_block_delta().await.unwrap();
|
let delta = self.editor.text_block_delta().await.unwrap();
|
||||||
if expected_delta != delta {
|
if expected_delta != delta {
|
||||||
eprintln!("✅ expect: {}", expected,);
|
eprintln!("✅ expect: {}", expected,);
|
||||||
|
@ -3,7 +3,7 @@ use crate::editor::{TestBuilder, TestOp::*};
|
|||||||
use flowy_sync::client_document::{NewlineDoc, EmptyDoc};
|
use flowy_sync::client_document::{NewlineDoc, EmptyDoc};
|
||||||
use lib_ot::core::{Interval, OperationTransform, NEW_LINE, WHITESPACE, OTString};
|
use lib_ot::core::{Interval, OperationTransform, NEW_LINE, WHITESPACE, OTString};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
use lib_ot::text_delta::TextDelta;
|
use lib_ot::text_delta::TextOperations;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn attributes_bold_added() {
|
fn attributes_bold_added() {
|
||||||
@ -762,12 +762,12 @@ fn attributes_preserve_list_format_on_merge() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_compose() {
|
fn delta_compose() {
|
||||||
let mut delta = TextDelta::from_json(r#"[{"insert":"\n"}]"#).unwrap();
|
let mut delta = TextOperations::from_json(r#"[{"insert":"\n"}]"#).unwrap();
|
||||||
let deltas = vec![
|
let deltas = vec![
|
||||||
TextDelta::from_json(r#"[{"retain":1,"attributes":{"list":"unchecked"}}]"#).unwrap(),
|
TextOperations::from_json(r#"[{"retain":1,"attributes":{"list":"unchecked"}}]"#).unwrap(),
|
||||||
TextDelta::from_json(r#"[{"insert":"a"}]"#).unwrap(),
|
TextOperations::from_json(r#"[{"insert":"a"}]"#).unwrap(),
|
||||||
TextDelta::from_json(r#"[{"retain":1},{"insert":"\n","attributes":{"list":"unchecked"}}]"#).unwrap(),
|
TextOperations::from_json(r#"[{"retain":1},{"insert":"\n","attributes":{"list":"unchecked"}}]"#).unwrap(),
|
||||||
TextDelta::from_json(r#"[{"retain":2},{"retain":1,"attributes":{"list":""}}]"#).unwrap(),
|
TextOperations::from_json(r#"[{"retain":2},{"retain":1,"attributes":{"list":""}}]"#).unwrap(),
|
||||||
];
|
];
|
||||||
|
|
||||||
for d in deltas {
|
for d in deltas {
|
||||||
|
@ -5,10 +5,10 @@ mod serde_test;
|
|||||||
mod undo_redo_test;
|
mod undo_redo_test;
|
||||||
|
|
||||||
use derive_more::Display;
|
use derive_more::Display;
|
||||||
use flowy_sync::client_document::{ClientDocument, InitialDocumentText};
|
use flowy_sync::client_document::{ClientDocument, InitialDocumentContent};
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::*,
|
core::*,
|
||||||
text_delta::{BuildInTextAttribute, TextDelta},
|
text_delta::{BuildInTextAttribute, TextOperations},
|
||||||
};
|
};
|
||||||
use rand::{prelude::*, Rng as WrappedRng};
|
use rand::{prelude::*, Rng as WrappedRng};
|
||||||
use std::{sync::Once, time::Duration};
|
use std::{sync::Once, time::Duration};
|
||||||
@ -81,8 +81,8 @@ pub enum TestOp {
|
|||||||
|
|
||||||
pub struct TestBuilder {
|
pub struct TestBuilder {
|
||||||
documents: Vec<ClientDocument>,
|
documents: Vec<ClientDocument>,
|
||||||
deltas: Vec<Option<TextDelta>>,
|
deltas: Vec<Option<TextOperations>>,
|
||||||
primes: Vec<Option<TextDelta>>,
|
primes: Vec<Option<TextOperations>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestBuilder {
|
impl TestBuilder {
|
||||||
@ -169,29 +169,29 @@ impl TestBuilder {
|
|||||||
}
|
}
|
||||||
TestOp::Transform(delta_a_i, delta_b_i) => {
|
TestOp::Transform(delta_a_i, delta_b_i) => {
|
||||||
let (a_prime, b_prime) = self.documents[*delta_a_i]
|
let (a_prime, b_prime) = self.documents[*delta_a_i]
|
||||||
.delta()
|
.get_operations()
|
||||||
.transform(self.documents[*delta_b_i].delta())
|
.transform(self.documents[*delta_b_i].get_operations())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
tracing::trace!("a:{:?},b:{:?}", a_prime, b_prime);
|
tracing::trace!("a:{:?},b:{:?}", a_prime, b_prime);
|
||||||
|
|
||||||
let data_left = self.documents[*delta_a_i].delta().compose(&b_prime).unwrap();
|
let data_left = self.documents[*delta_a_i].get_operations().compose(&b_prime).unwrap();
|
||||||
let data_right = self.documents[*delta_b_i].delta().compose(&a_prime).unwrap();
|
let data_right = self.documents[*delta_b_i].get_operations().compose(&a_prime).unwrap();
|
||||||
|
|
||||||
self.documents[*delta_a_i].set_delta(data_left);
|
self.documents[*delta_a_i].set_operations(data_left);
|
||||||
self.documents[*delta_b_i].set_delta(data_right);
|
self.documents[*delta_b_i].set_operations(data_right);
|
||||||
}
|
}
|
||||||
TestOp::TransformPrime(a_doc_index, b_doc_index) => {
|
TestOp::TransformPrime(a_doc_index, b_doc_index) => {
|
||||||
let (prime_left, prime_right) = self.documents[*a_doc_index]
|
let (prime_left, prime_right) = self.documents[*a_doc_index]
|
||||||
.delta()
|
.get_operations()
|
||||||
.transform(self.documents[*b_doc_index].delta())
|
.transform(self.documents[*b_doc_index].get_operations())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.primes.insert(*a_doc_index, Some(prime_left));
|
self.primes.insert(*a_doc_index, Some(prime_left));
|
||||||
self.primes.insert(*b_doc_index, Some(prime_right));
|
self.primes.insert(*b_doc_index, Some(prime_right));
|
||||||
}
|
}
|
||||||
TestOp::Invert(delta_a_i, delta_b_i) => {
|
TestOp::Invert(delta_a_i, delta_b_i) => {
|
||||||
let delta_a = &self.documents[*delta_a_i].delta();
|
let delta_a = &self.documents[*delta_a_i].get_operations();
|
||||||
let delta_b = &self.documents[*delta_b_i].delta();
|
let delta_b = &self.documents[*delta_b_i].get_operations();
|
||||||
tracing::debug!("Invert: ");
|
tracing::debug!("Invert: ");
|
||||||
tracing::debug!("a: {}", delta_a.json_str());
|
tracing::debug!("a: {}", delta_a.json_str());
|
||||||
tracing::debug!("b: {}", delta_b.json_str());
|
tracing::debug!("b: {}", delta_b.json_str());
|
||||||
@ -209,7 +209,7 @@ impl TestBuilder {
|
|||||||
|
|
||||||
assert_eq!(delta_a, &&new_delta_after_undo);
|
assert_eq!(delta_a, &&new_delta_after_undo);
|
||||||
|
|
||||||
self.documents[*delta_a_i].set_delta(new_delta_after_undo);
|
self.documents[*delta_a_i].set_operations(new_delta_after_undo);
|
||||||
}
|
}
|
||||||
TestOp::Undo(delta_i) => {
|
TestOp::Undo(delta_i) => {
|
||||||
self.documents[*delta_i].undo().unwrap();
|
self.documents[*delta_i].undo().unwrap();
|
||||||
@ -221,13 +221,13 @@ impl TestBuilder {
|
|||||||
std::thread::sleep(Duration::from_millis(*mills_sec as u64));
|
std::thread::sleep(Duration::from_millis(*mills_sec as u64));
|
||||||
}
|
}
|
||||||
TestOp::AssertStr(delta_i, expected) => {
|
TestOp::AssertStr(delta_i, expected) => {
|
||||||
assert_eq!(&self.documents[*delta_i].to_plain_string(), expected);
|
assert_eq!(&self.documents[*delta_i].to_content(), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
TestOp::AssertDocJson(delta_i, expected) => {
|
TestOp::AssertDocJson(delta_i, expected) => {
|
||||||
let delta_json = self.documents[*delta_i].delta_str();
|
let delta_json = self.documents[*delta_i].get_operations_json();
|
||||||
let expected_delta: TextDelta = serde_json::from_str(expected).unwrap();
|
let expected_delta: TextOperations = serde_json::from_str(expected).unwrap();
|
||||||
let target_delta: TextDelta = serde_json::from_str(&delta_json).unwrap();
|
let target_delta: TextOperations = serde_json::from_str(&delta_json).unwrap();
|
||||||
|
|
||||||
if expected_delta != target_delta {
|
if expected_delta != target_delta {
|
||||||
log::error!("✅ expect: {}", expected,);
|
log::error!("✅ expect: {}", expected,);
|
||||||
@ -238,8 +238,8 @@ impl TestBuilder {
|
|||||||
|
|
||||||
TestOp::AssertPrimeJson(doc_i, expected) => {
|
TestOp::AssertPrimeJson(doc_i, expected) => {
|
||||||
let prime_json = self.primes[*doc_i].as_ref().unwrap().json_str();
|
let prime_json = self.primes[*doc_i].as_ref().unwrap().json_str();
|
||||||
let expected_prime: TextDelta = serde_json::from_str(expected).unwrap();
|
let expected_prime: TextOperations = serde_json::from_str(expected).unwrap();
|
||||||
let target_prime: TextDelta = serde_json::from_str(&prime_json).unwrap();
|
let target_prime: TextOperations = serde_json::from_str(&prime_json).unwrap();
|
||||||
|
|
||||||
if expected_prime != target_prime {
|
if expected_prime != target_prime {
|
||||||
log::error!("✅ expect prime: {}", expected,);
|
log::error!("✅ expect prime: {}", expected,);
|
||||||
@ -249,7 +249,7 @@ impl TestBuilder {
|
|||||||
}
|
}
|
||||||
TestOp::DocComposeDelta(doc_index, delta_i) => {
|
TestOp::DocComposeDelta(doc_index, delta_i) => {
|
||||||
let delta = self.deltas.get(*delta_i).unwrap().as_ref().unwrap();
|
let delta = self.deltas.get(*delta_i).unwrap().as_ref().unwrap();
|
||||||
self.documents[*doc_index].compose_delta(delta.clone()).unwrap();
|
self.documents[*doc_index].compose_operations(delta.clone()).unwrap();
|
||||||
}
|
}
|
||||||
TestOp::DocComposePrime(doc_index, prime_i) => {
|
TestOp::DocComposePrime(doc_index, prime_i) => {
|
||||||
let delta = self
|
let delta = self
|
||||||
@ -258,13 +258,13 @@ impl TestBuilder {
|
|||||||
.expect("Must call TransformPrime first")
|
.expect("Must call TransformPrime first")
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let new_delta = self.documents[*doc_index].delta().compose(delta).unwrap();
|
let new_delta = self.documents[*doc_index].get_operations().compose(delta).unwrap();
|
||||||
self.documents[*doc_index].set_delta(new_delta);
|
self.documents[*doc_index].set_operations(new_delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_scripts<C: InitialDocumentText>(mut self, scripts: Vec<TestOp>) {
|
pub fn run_scripts<C: InitialDocumentContent>(mut self, scripts: Vec<TestOp>) {
|
||||||
self.documents = vec![ClientDocument::new::<C>(), ClientDocument::new::<C>()];
|
self.documents = vec![ClientDocument::new::<C>(), ClientDocument::new::<C>()];
|
||||||
self.primes = vec![None, None];
|
self.primes = vec![None, None];
|
||||||
self.deltas = vec![None, None];
|
self.deltas = vec![None, None];
|
||||||
@ -297,8 +297,8 @@ impl Rng {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_delta(&mut self, s: &str) -> TextDelta {
|
pub fn gen_delta(&mut self, s: &str) -> TextOperations {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
let s = OTString::from(s);
|
let s = OTString::from(s);
|
||||||
loop {
|
loop {
|
||||||
let left = s.utf16_len() - delta.utf16_base_len;
|
let left = s.utf16_len() - delta.utf16_base_len;
|
||||||
@ -312,18 +312,18 @@ impl Rng {
|
|||||||
};
|
};
|
||||||
match self.0.gen_range(0.0..1.0) {
|
match self.0.gen_range(0.0..1.0) {
|
||||||
f if f < 0.2 => {
|
f if f < 0.2 => {
|
||||||
delta.insert(&self.gen_string(i), Attributes::default());
|
delta.insert(&self.gen_string(i), AttributeHashMap::default());
|
||||||
}
|
}
|
||||||
f if f < 0.4 => {
|
f if f < 0.4 => {
|
||||||
delta.delete(i);
|
delta.delete(i);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
delta.retain(i, Attributes::default());
|
delta.retain(i, AttributeHashMap::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.0.gen_range(0.0..1.0) < 0.3 {
|
if self.0.gen_range(0.0..1.0) < 0.3 {
|
||||||
delta.insert(&("1".to_owned() + &self.gen_string(10)), Attributes::default());
|
delta.insert(&("1".to_owned() + &self.gen_string(10)), AttributeHashMap::default());
|
||||||
}
|
}
|
||||||
delta
|
delta
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#![allow(clippy::all)]
|
#![allow(clippy::all)]
|
||||||
use crate::editor::{Rng, TestBuilder, TestOp::*};
|
use crate::editor::{Rng, TestBuilder, TestOp::*};
|
||||||
use flowy_sync::client_document::{EmptyDoc, NewlineDoc};
|
use flowy_sync::client_document::{EmptyDoc, NewlineDoc};
|
||||||
use lib_ot::text_delta::TextDeltaBuilder;
|
use lib_ot::text_delta::TextOperationBuilder;
|
||||||
use lib_ot::{core::Interval, core::*, text_delta::TextDelta};
|
use lib_ot::{core::Interval, core::*, text_delta::TextOperations};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn attributes_insert_text() {
|
fn attributes_insert_text() {
|
||||||
@ -37,7 +37,7 @@ fn attributes_insert_text_at_middle() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn delta_get_ops_in_interval_1() {
|
fn delta_get_ops_in_interval_1() {
|
||||||
let operations = OperationsBuilder::new().insert("123").insert("4").build();
|
let operations = OperationsBuilder::new().insert("123").insert("4").build();
|
||||||
let delta = TextDeltaBuilder::from_operations(operations);
|
let delta = TextOperationBuilder::from_operations(operations);
|
||||||
|
|
||||||
let mut iterator = OperationIterator::from_interval(&delta, Interval::new(0, 4));
|
let mut iterator = OperationIterator::from_interval(&delta, Interval::new(0, 4));
|
||||||
assert_eq!(iterator.ops(), delta.ops);
|
assert_eq!(iterator.ops(), delta.ops);
|
||||||
@ -45,11 +45,11 @@ fn delta_get_ops_in_interval_1() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_get_ops_in_interval_2() {
|
fn delta_get_ops_in_interval_2() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
let insert_a = Operation::insert("123");
|
let insert_a = DeltaOperation::insert("123");
|
||||||
let insert_b = Operation::insert("4");
|
let insert_b = DeltaOperation::insert("4");
|
||||||
let insert_c = Operation::insert("5");
|
let insert_c = DeltaOperation::insert("5");
|
||||||
let retain_a = Operation::retain(3);
|
let retain_a = DeltaOperation::retain(3);
|
||||||
|
|
||||||
delta.add(insert_a.clone());
|
delta.add(insert_a.clone());
|
||||||
delta.add(retain_a.clone());
|
delta.add(retain_a.clone());
|
||||||
@ -58,12 +58,12 @@ fn delta_get_ops_in_interval_2() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
OperationIterator::from_interval(&delta, Interval::new(0, 2)).ops(),
|
OperationIterator::from_interval(&delta, Interval::new(0, 2)).ops(),
|
||||||
vec![Operation::insert("12")]
|
vec![DeltaOperation::insert("12")]
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
OperationIterator::from_interval(&delta, Interval::new(1, 3)).ops(),
|
OperationIterator::from_interval(&delta, Interval::new(1, 3)).ops(),
|
||||||
vec![Operation::insert("23")]
|
vec![DeltaOperation::insert("23")]
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -73,7 +73,7 @@ fn delta_get_ops_in_interval_2() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
OperationIterator::from_interval(&delta, Interval::new(0, 4)).ops(),
|
OperationIterator::from_interval(&delta, Interval::new(0, 4)).ops(),
|
||||||
vec![insert_a.clone(), Operation::retain(1)]
|
vec![insert_a.clone(), DeltaOperation::retain(1)]
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -89,21 +89,21 @@ fn delta_get_ops_in_interval_2() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_get_ops_in_interval_3() {
|
fn delta_get_ops_in_interval_3() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
let insert_a = Operation::insert("123456");
|
let insert_a = DeltaOperation::insert("123456");
|
||||||
delta.add(insert_a.clone());
|
delta.add(insert_a.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
OperationIterator::from_interval(&delta, Interval::new(3, 5)).ops(),
|
OperationIterator::from_interval(&delta, Interval::new(3, 5)).ops(),
|
||||||
vec![Operation::insert("45")]
|
vec![DeltaOperation::insert("45")]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_get_ops_in_interval_4() {
|
fn delta_get_ops_in_interval_4() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
let insert_a = Operation::insert("12");
|
let insert_a = DeltaOperation::insert("12");
|
||||||
let insert_b = Operation::insert("34");
|
let insert_b = DeltaOperation::insert("34");
|
||||||
let insert_c = Operation::insert("56");
|
let insert_c = DeltaOperation::insert("56");
|
||||||
|
|
||||||
delta.ops.push(insert_a.clone());
|
delta.ops.push(insert_a.clone());
|
||||||
delta.ops.push(insert_b.clone());
|
delta.ops.push(insert_b.clone());
|
||||||
@ -124,20 +124,20 @@ fn delta_get_ops_in_interval_4() {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
OperationIterator::from_interval(&delta, Interval::new(2, 5)).ops(),
|
OperationIterator::from_interval(&delta, Interval::new(2, 5)).ops(),
|
||||||
vec![Operation::insert("34"), Operation::insert("5")]
|
vec![DeltaOperation::insert("34"), DeltaOperation::insert("5")]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_get_ops_in_interval_5() {
|
fn delta_get_ops_in_interval_5() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
let insert_a = Operation::insert("123456");
|
let insert_a = DeltaOperation::insert("123456");
|
||||||
let insert_b = Operation::insert("789");
|
let insert_b = DeltaOperation::insert("789");
|
||||||
delta.ops.push(insert_a.clone());
|
delta.ops.push(insert_a.clone());
|
||||||
delta.ops.push(insert_b.clone());
|
delta.ops.push(insert_b.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
OperationIterator::from_interval(&delta, Interval::new(4, 8)).ops(),
|
OperationIterator::from_interval(&delta, Interval::new(4, 8)).ops(),
|
||||||
vec![Operation::insert("56"), Operation::insert("78")]
|
vec![DeltaOperation::insert("56"), DeltaOperation::insert("78")]
|
||||||
);
|
);
|
||||||
|
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
@ -148,40 +148,40 @@ fn delta_get_ops_in_interval_5() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_get_ops_in_interval_6() {
|
fn delta_get_ops_in_interval_6() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
let insert_a = Operation::insert("12345678");
|
let insert_a = DeltaOperation::insert("12345678");
|
||||||
delta.add(insert_a.clone());
|
delta.add(insert_a.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
OperationIterator::from_interval(&delta, Interval::new(4, 6)).ops(),
|
OperationIterator::from_interval(&delta, Interval::new(4, 6)).ops(),
|
||||||
vec![Operation::insert("56")]
|
vec![DeltaOperation::insert("56")]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_get_ops_in_interval_7() {
|
fn delta_get_ops_in_interval_7() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
let insert_a = Operation::insert("12345");
|
let insert_a = DeltaOperation::insert("12345");
|
||||||
let retain_a = Operation::retain(3);
|
let retain_a = DeltaOperation::retain(3);
|
||||||
|
|
||||||
delta.add(insert_a.clone());
|
delta.add(insert_a.clone());
|
||||||
delta.add(retain_a.clone());
|
delta.add(retain_a.clone());
|
||||||
|
|
||||||
let mut iter_1 = OperationIterator::from_offset(&delta, 2);
|
let mut iter_1 = OperationIterator::from_offset(&delta, 2);
|
||||||
assert_eq!(iter_1.next_op().unwrap(), Operation::insert("345"));
|
assert_eq!(iter_1.next_op().unwrap(), DeltaOperation::insert("345"));
|
||||||
assert_eq!(iter_1.next_op().unwrap(), Operation::retain(3));
|
assert_eq!(iter_1.next_op().unwrap(), DeltaOperation::retain(3));
|
||||||
|
|
||||||
let mut iter_2 = OperationIterator::new(&delta);
|
let mut iter_2 = OperationIterator::new(&delta);
|
||||||
assert_eq!(iter_2.next_op_with_len(2).unwrap(), Operation::insert("12"));
|
assert_eq!(iter_2.next_op_with_len(2).unwrap(), DeltaOperation::insert("12"));
|
||||||
assert_eq!(iter_2.next_op().unwrap(), Operation::insert("345"));
|
assert_eq!(iter_2.next_op().unwrap(), DeltaOperation::insert("345"));
|
||||||
|
|
||||||
assert_eq!(iter_2.next_op().unwrap(), Operation::retain(3));
|
assert_eq!(iter_2.next_op().unwrap(), DeltaOperation::retain(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_op_seek() {
|
fn delta_op_seek() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
let insert_a = Operation::insert("12345");
|
let insert_a = DeltaOperation::insert("12345");
|
||||||
let retain_a = Operation::retain(3);
|
let retain_a = DeltaOperation::retain(3);
|
||||||
delta.add(insert_a.clone());
|
delta.add(insert_a.clone());
|
||||||
delta.add(retain_a.clone());
|
delta.add(retain_a.clone());
|
||||||
let mut iter = OperationIterator::new(&delta);
|
let mut iter = OperationIterator::new(&delta);
|
||||||
@ -191,92 +191,92 @@ fn delta_op_seek() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_utf16_code_unit_seek() {
|
fn delta_utf16_code_unit_seek() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
delta.add(Operation::insert("12345"));
|
delta.add(DeltaOperation::insert("12345"));
|
||||||
|
|
||||||
let mut iter = OperationIterator::new(&delta);
|
let mut iter = OperationIterator::new(&delta);
|
||||||
iter.seek::<Utf16CodeUnitMetric>(3);
|
iter.seek::<Utf16CodeUnitMetric>(3);
|
||||||
assert_eq!(iter.next_op_with_len(2).unwrap(), Operation::insert("45"));
|
assert_eq!(iter.next_op_with_len(2).unwrap(), DeltaOperation::insert("45"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_utf16_code_unit_seek_with_attributes() {
|
fn delta_utf16_code_unit_seek_with_attributes() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
let attributes = AttributeBuilder::new()
|
let attributes = AttributeBuilder::new()
|
||||||
.insert("bold", true)
|
.insert("bold", true)
|
||||||
.insert("italic", true)
|
.insert("italic", true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
delta.add(Operation::insert_with_attributes("1234", attributes.clone()));
|
delta.add(DeltaOperation::insert_with_attributes("1234", attributes.clone()));
|
||||||
delta.add(Operation::insert("\n"));
|
delta.add(DeltaOperation::insert("\n"));
|
||||||
|
|
||||||
let mut iter = OperationIterator::new(&delta);
|
let mut iter = OperationIterator::new(&delta);
|
||||||
iter.seek::<Utf16CodeUnitMetric>(0);
|
iter.seek::<Utf16CodeUnitMetric>(0);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
iter.next_op_with_len(4).unwrap(),
|
iter.next_op_with_len(4).unwrap(),
|
||||||
Operation::insert_with_attributes("1234", attributes),
|
DeltaOperation::insert_with_attributes("1234", attributes),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_next_op_len() {
|
fn delta_next_op_len() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
delta.add(Operation::insert("12345"));
|
delta.add(DeltaOperation::insert("12345"));
|
||||||
let mut iter = OperationIterator::new(&delta);
|
let mut iter = OperationIterator::new(&delta);
|
||||||
assert_eq!(iter.next_op_with_len(2).unwrap(), Operation::insert("12"));
|
assert_eq!(iter.next_op_with_len(2).unwrap(), DeltaOperation::insert("12"));
|
||||||
assert_eq!(iter.next_op_with_len(2).unwrap(), Operation::insert("34"));
|
assert_eq!(iter.next_op_with_len(2).unwrap(), DeltaOperation::insert("34"));
|
||||||
assert_eq!(iter.next_op_with_len(2).unwrap(), Operation::insert("5"));
|
assert_eq!(iter.next_op_with_len(2).unwrap(), DeltaOperation::insert("5"));
|
||||||
assert_eq!(iter.next_op_with_len(1), None);
|
assert_eq!(iter.next_op_with_len(1), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_next_op_len_with_chinese() {
|
fn delta_next_op_len_with_chinese() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
delta.add(Operation::insert("你好"));
|
delta.add(DeltaOperation::insert("你好"));
|
||||||
|
|
||||||
let mut iter = OperationIterator::new(&delta);
|
let mut iter = OperationIterator::new(&delta);
|
||||||
assert_eq!(iter.next_op_len().unwrap(), 2);
|
assert_eq!(iter.next_op_len().unwrap(), 2);
|
||||||
assert_eq!(iter.next_op_with_len(2).unwrap(), Operation::insert("你好"));
|
assert_eq!(iter.next_op_with_len(2).unwrap(), DeltaOperation::insert("你好"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_next_op_len_with_english() {
|
fn delta_next_op_len_with_english() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
delta.add(Operation::insert("ab"));
|
delta.add(DeltaOperation::insert("ab"));
|
||||||
let mut iter = OperationIterator::new(&delta);
|
let mut iter = OperationIterator::new(&delta);
|
||||||
assert_eq!(iter.next_op_len().unwrap(), 2);
|
assert_eq!(iter.next_op_len().unwrap(), 2);
|
||||||
assert_eq!(iter.next_op_with_len(2).unwrap(), Operation::insert("ab"));
|
assert_eq!(iter.next_op_with_len(2).unwrap(), DeltaOperation::insert("ab"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_next_op_len_after_seek() {
|
fn delta_next_op_len_after_seek() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
delta.add(Operation::insert("12345"));
|
delta.add(DeltaOperation::insert("12345"));
|
||||||
let mut iter = OperationIterator::new(&delta);
|
let mut iter = OperationIterator::new(&delta);
|
||||||
assert_eq!(iter.next_op_len().unwrap(), 5);
|
assert_eq!(iter.next_op_len().unwrap(), 5);
|
||||||
iter.seek::<Utf16CodeUnitMetric>(3);
|
iter.seek::<Utf16CodeUnitMetric>(3);
|
||||||
assert_eq!(iter.next_op_len().unwrap(), 2);
|
assert_eq!(iter.next_op_len().unwrap(), 2);
|
||||||
assert_eq!(iter.next_op_with_len(1).unwrap(), Operation::insert("4"));
|
assert_eq!(iter.next_op_with_len(1).unwrap(), DeltaOperation::insert("4"));
|
||||||
assert_eq!(iter.next_op_len().unwrap(), 1);
|
assert_eq!(iter.next_op_len().unwrap(), 1);
|
||||||
assert_eq!(iter.next_op().unwrap(), Operation::insert("5"));
|
assert_eq!(iter.next_op().unwrap(), DeltaOperation::insert("5"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_next_op_len_none() {
|
fn delta_next_op_len_none() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
delta.add(Operation::insert("12345"));
|
delta.add(DeltaOperation::insert("12345"));
|
||||||
let mut iter = OperationIterator::new(&delta);
|
let mut iter = OperationIterator::new(&delta);
|
||||||
|
|
||||||
assert_eq!(iter.next_op_len().unwrap(), 5);
|
assert_eq!(iter.next_op_len().unwrap(), 5);
|
||||||
assert_eq!(iter.next_op_with_len(5).unwrap(), Operation::insert("12345"));
|
assert_eq!(iter.next_op_with_len(5).unwrap(), DeltaOperation::insert("12345"));
|
||||||
assert_eq!(iter.next_op_len(), None);
|
assert_eq!(iter.next_op_len(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_next_op_with_len_zero() {
|
fn delta_next_op_with_len_zero() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
delta.add(Operation::insert("12345"));
|
delta.add(DeltaOperation::insert("12345"));
|
||||||
let mut iter = OperationIterator::new(&delta);
|
let mut iter = OperationIterator::new(&delta);
|
||||||
assert_eq!(iter.next_op_with_len(0), None,);
|
assert_eq!(iter.next_op_with_len(0), None,);
|
||||||
assert_eq!(iter.next_op_len().unwrap(), 5);
|
assert_eq!(iter.next_op_len().unwrap(), 5);
|
||||||
@ -284,29 +284,29 @@ fn delta_next_op_with_len_zero() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_next_op_with_len_cross_op_return_last() {
|
fn delta_next_op_with_len_cross_op_return_last() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
delta.add(Operation::insert("12345"));
|
delta.add(DeltaOperation::insert("12345"));
|
||||||
delta.add(Operation::retain(1));
|
delta.add(DeltaOperation::retain(1));
|
||||||
delta.add(Operation::insert("678"));
|
delta.add(DeltaOperation::insert("678"));
|
||||||
|
|
||||||
let mut iter = OperationIterator::new(&delta);
|
let mut iter = OperationIterator::new(&delta);
|
||||||
iter.seek::<Utf16CodeUnitMetric>(4);
|
iter.seek::<Utf16CodeUnitMetric>(4);
|
||||||
assert_eq!(iter.next_op_len().unwrap(), 1);
|
assert_eq!(iter.next_op_len().unwrap(), 1);
|
||||||
assert_eq!(iter.next_op_with_len(2).unwrap(), Operation::retain(1));
|
assert_eq!(iter.next_op_with_len(2).unwrap(), DeltaOperation::retain(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lengths() {
|
fn lengths() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
assert_eq!(delta.utf16_base_len, 0);
|
assert_eq!(delta.utf16_base_len, 0);
|
||||||
assert_eq!(delta.utf16_target_len, 0);
|
assert_eq!(delta.utf16_target_len, 0);
|
||||||
delta.retain(5, Attributes::default());
|
delta.retain(5, AttributeHashMap::default());
|
||||||
assert_eq!(delta.utf16_base_len, 5);
|
assert_eq!(delta.utf16_base_len, 5);
|
||||||
assert_eq!(delta.utf16_target_len, 5);
|
assert_eq!(delta.utf16_target_len, 5);
|
||||||
delta.insert("abc", Attributes::default());
|
delta.insert("abc", AttributeHashMap::default());
|
||||||
assert_eq!(delta.utf16_base_len, 5);
|
assert_eq!(delta.utf16_base_len, 5);
|
||||||
assert_eq!(delta.utf16_target_len, 8);
|
assert_eq!(delta.utf16_target_len, 8);
|
||||||
delta.retain(2, Attributes::default());
|
delta.retain(2, AttributeHashMap::default());
|
||||||
assert_eq!(delta.utf16_base_len, 7);
|
assert_eq!(delta.utf16_base_len, 7);
|
||||||
assert_eq!(delta.utf16_target_len, 10);
|
assert_eq!(delta.utf16_target_len, 10);
|
||||||
delta.delete(2);
|
delta.delete(2);
|
||||||
@ -315,11 +315,11 @@ fn lengths() {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn sequence() {
|
fn sequence() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
delta.retain(5, Attributes::default());
|
delta.retain(5, AttributeHashMap::default());
|
||||||
delta.retain(0, Attributes::default());
|
delta.retain(0, AttributeHashMap::default());
|
||||||
delta.insert("appflowy", Attributes::default());
|
delta.insert("appflowy", AttributeHashMap::default());
|
||||||
delta.insert("", Attributes::default());
|
delta.insert("", AttributeHashMap::default());
|
||||||
delta.delete(3);
|
delta.delete(3);
|
||||||
delta.delete(0);
|
delta.delete(0);
|
||||||
assert_eq!(delta.ops.len(), 3);
|
assert_eq!(delta.ops.len(), 3);
|
||||||
@ -348,16 +348,16 @@ fn apply_test() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn base_len_test() {
|
fn base_len_test() {
|
||||||
let mut delta_a = TextDelta::default();
|
let mut delta_a = TextOperations::default();
|
||||||
delta_a.insert("a", Attributes::default());
|
delta_a.insert("a", AttributeHashMap::default());
|
||||||
delta_a.insert("b", Attributes::default());
|
delta_a.insert("b", AttributeHashMap::default());
|
||||||
delta_a.insert("c", Attributes::default());
|
delta_a.insert("c", AttributeHashMap::default());
|
||||||
|
|
||||||
let s = "hello world,".to_owned();
|
let s = "hello world,".to_owned();
|
||||||
delta_a.delete(s.len());
|
delta_a.delete(s.len());
|
||||||
let after_a = delta_a.apply(&s).unwrap();
|
let after_a = delta_a.apply(&s).unwrap();
|
||||||
|
|
||||||
delta_a.insert("d", Attributes::default());
|
delta_a.insert("d", AttributeHashMap::default());
|
||||||
assert_eq!("abc", &after_a);
|
assert_eq!("abc", &after_a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,62 +387,62 @@ fn invert_test() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_ops() {
|
fn empty_ops() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
delta.retain(0, Attributes::default());
|
delta.retain(0, AttributeHashMap::default());
|
||||||
delta.insert("", Attributes::default());
|
delta.insert("", AttributeHashMap::default());
|
||||||
delta.delete(0);
|
delta.delete(0);
|
||||||
assert_eq!(delta.ops.len(), 0);
|
assert_eq!(delta.ops.len(), 0);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn eq() {
|
fn eq() {
|
||||||
let mut delta_a = TextDelta::default();
|
let mut delta_a = TextOperations::default();
|
||||||
delta_a.delete(1);
|
delta_a.delete(1);
|
||||||
delta_a.insert("lo", Attributes::default());
|
delta_a.insert("lo", AttributeHashMap::default());
|
||||||
delta_a.retain(2, Attributes::default());
|
delta_a.retain(2, AttributeHashMap::default());
|
||||||
delta_a.retain(3, Attributes::default());
|
delta_a.retain(3, AttributeHashMap::default());
|
||||||
let mut delta_b = TextDelta::default();
|
let mut delta_b = TextOperations::default();
|
||||||
delta_b.delete(1);
|
delta_b.delete(1);
|
||||||
delta_b.insert("l", Attributes::default());
|
delta_b.insert("l", AttributeHashMap::default());
|
||||||
delta_b.insert("o", Attributes::default());
|
delta_b.insert("o", AttributeHashMap::default());
|
||||||
delta_b.retain(5, Attributes::default());
|
delta_b.retain(5, AttributeHashMap::default());
|
||||||
assert_eq!(delta_a, delta_b);
|
assert_eq!(delta_a, delta_b);
|
||||||
delta_a.delete(1);
|
delta_a.delete(1);
|
||||||
delta_b.retain(1, Attributes::default());
|
delta_b.retain(1, AttributeHashMap::default());
|
||||||
assert_ne!(delta_a, delta_b);
|
assert_ne!(delta_a, delta_b);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn ops_merging() {
|
fn ops_merging() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
assert_eq!(delta.ops.len(), 0);
|
assert_eq!(delta.ops.len(), 0);
|
||||||
delta.retain(2, Attributes::default());
|
delta.retain(2, AttributeHashMap::default());
|
||||||
assert_eq!(delta.ops.len(), 1);
|
assert_eq!(delta.ops.len(), 1);
|
||||||
assert_eq!(delta.ops.last(), Some(&Operation::retain(2)));
|
assert_eq!(delta.ops.last(), Some(&DeltaOperation::retain(2)));
|
||||||
delta.retain(3, Attributes::default());
|
delta.retain(3, AttributeHashMap::default());
|
||||||
assert_eq!(delta.ops.len(), 1);
|
assert_eq!(delta.ops.len(), 1);
|
||||||
assert_eq!(delta.ops.last(), Some(&Operation::retain(5)));
|
assert_eq!(delta.ops.last(), Some(&DeltaOperation::retain(5)));
|
||||||
delta.insert("abc", Attributes::default());
|
delta.insert("abc", AttributeHashMap::default());
|
||||||
assert_eq!(delta.ops.len(), 2);
|
assert_eq!(delta.ops.len(), 2);
|
||||||
assert_eq!(delta.ops.last(), Some(&Operation::insert("abc")));
|
assert_eq!(delta.ops.last(), Some(&DeltaOperation::insert("abc")));
|
||||||
delta.insert("xyz", Attributes::default());
|
delta.insert("xyz", AttributeHashMap::default());
|
||||||
assert_eq!(delta.ops.len(), 2);
|
assert_eq!(delta.ops.len(), 2);
|
||||||
assert_eq!(delta.ops.last(), Some(&Operation::insert("abcxyz")));
|
assert_eq!(delta.ops.last(), Some(&DeltaOperation::insert("abcxyz")));
|
||||||
delta.delete(1);
|
delta.delete(1);
|
||||||
assert_eq!(delta.ops.len(), 3);
|
assert_eq!(delta.ops.len(), 3);
|
||||||
assert_eq!(delta.ops.last(), Some(&Operation::delete(1)));
|
assert_eq!(delta.ops.last(), Some(&DeltaOperation::delete(1)));
|
||||||
delta.delete(1);
|
delta.delete(1);
|
||||||
assert_eq!(delta.ops.len(), 3);
|
assert_eq!(delta.ops.len(), 3);
|
||||||
assert_eq!(delta.ops.last(), Some(&Operation::delete(2)));
|
assert_eq!(delta.ops.last(), Some(&DeltaOperation::delete(2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_noop() {
|
fn is_noop() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
assert!(delta.is_noop());
|
assert!(delta.is_noop());
|
||||||
delta.retain(5, Attributes::default());
|
delta.retain(5, AttributeHashMap::default());
|
||||||
assert!(delta.is_noop());
|
assert!(delta.is_noop());
|
||||||
delta.retain(3, Attributes::default());
|
delta.retain(3, AttributeHashMap::default());
|
||||||
assert!(delta.is_noop());
|
assert!(delta.is_noop());
|
||||||
delta.insert("lorem", Attributes::default());
|
delta.insert("lorem", AttributeHashMap::default());
|
||||||
assert!(!delta.is_noop());
|
assert!(!delta.is_noop());
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@ -484,15 +484,15 @@ fn transform_random_delta() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn transform_with_two_delta() {
|
fn transform_with_two_delta() {
|
||||||
let mut a = TextDelta::default();
|
let mut a = TextOperations::default();
|
||||||
let mut a_s = String::new();
|
let mut a_s = String::new();
|
||||||
a.insert("123", AttributeBuilder::new().insert("bold", true).build());
|
a.insert("123", AttributeBuilder::new().insert("bold", true).build());
|
||||||
a_s = a.apply(&a_s).unwrap();
|
a_s = a.apply(&a_s).unwrap();
|
||||||
assert_eq!(&a_s, "123");
|
assert_eq!(&a_s, "123");
|
||||||
|
|
||||||
let mut b = TextDelta::default();
|
let mut b = TextOperations::default();
|
||||||
let mut b_s = String::new();
|
let mut b_s = String::new();
|
||||||
b.insert("456", Attributes::default());
|
b.insert("456", AttributeHashMap::default());
|
||||||
b_s = b.apply(&b_s).unwrap();
|
b_s = b.apply(&b_s).unwrap();
|
||||||
assert_eq!(&b_s, "456");
|
assert_eq!(&b_s, "456");
|
||||||
|
|
||||||
@ -580,12 +580,12 @@ fn transform_two_conflict_non_seq_delta() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_invert_no_attribute_delta() {
|
fn delta_invert_no_attribute_delta() {
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
delta.add(Operation::insert("123"));
|
delta.add(DeltaOperation::insert("123"));
|
||||||
|
|
||||||
let mut change = TextDelta::default();
|
let mut change = TextOperations::default();
|
||||||
change.add(Operation::retain(3));
|
change.add(DeltaOperation::retain(3));
|
||||||
change.add(Operation::insert("456"));
|
change.add(DeltaOperation::insert("456"));
|
||||||
let undo = change.invert(&delta);
|
let undo = change.invert(&delta);
|
||||||
|
|
||||||
let new_delta = delta.compose(&change).unwrap();
|
let new_delta = delta.compose(&change).unwrap();
|
||||||
|
@ -2,7 +2,7 @@ use flowy_sync::client_document::{ClientDocument, EmptyDoc};
|
|||||||
use lib_ot::text_delta::TextOperation;
|
use lib_ot::text_delta::TextOperation;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::*,
|
core::*,
|
||||||
text_delta::{BuildInTextAttribute, TextDelta},
|
text_delta::{BuildInTextAttribute, TextOperations},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -11,7 +11,7 @@ fn operation_insert_serialize_test() {
|
|||||||
.insert("bold", true)
|
.insert("bold", true)
|
||||||
.insert("italic", true)
|
.insert("italic", true)
|
||||||
.build();
|
.build();
|
||||||
let operation = Operation::insert_with_attributes("123", attributes);
|
let operation = DeltaOperation::insert_with_attributes("123", attributes);
|
||||||
let json = serde_json::to_string(&operation).unwrap();
|
let json = serde_json::to_string(&operation).unwrap();
|
||||||
eprintln!("{}", json);
|
eprintln!("{}", json);
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ fn operation_insert_serialize_test() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn operation_retain_serialize_test() {
|
fn operation_retain_serialize_test() {
|
||||||
let operation = Operation::Retain(12.into());
|
let operation = DeltaOperation::Retain(12.into());
|
||||||
let json = serde_json::to_string(&operation).unwrap();
|
let json = serde_json::to_string(&operation).unwrap();
|
||||||
eprintln!("{}", json);
|
eprintln!("{}", json);
|
||||||
let insert_op: TextOperation = serde_json::from_str(&json).unwrap();
|
let insert_op: TextOperation = serde_json::from_str(&json).unwrap();
|
||||||
@ -42,7 +42,7 @@ fn attributes_serialize_test() {
|
|||||||
.insert_entry(BuildInTextAttribute::Bold(true))
|
.insert_entry(BuildInTextAttribute::Bold(true))
|
||||||
.insert_entry(BuildInTextAttribute::Italic(true))
|
.insert_entry(BuildInTextAttribute::Italic(true))
|
||||||
.build();
|
.build();
|
||||||
let retain = Operation::insert_with_attributes("123", attributes);
|
let retain = DeltaOperation::insert_with_attributes("123", attributes);
|
||||||
|
|
||||||
let json = serde_json::to_string(&retain).unwrap();
|
let json = serde_json::to_string(&retain).unwrap();
|
||||||
eprintln!("{}", json);
|
eprintln!("{}", json);
|
||||||
@ -50,22 +50,22 @@ fn attributes_serialize_test() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delta_serialize_multi_attribute_test() {
|
fn delta_serialize_multi_attribute_test() {
|
||||||
let mut delta = Operations::default();
|
let mut delta = DeltaOperations::default();
|
||||||
|
|
||||||
let attributes = AttributeBuilder::new()
|
let attributes = AttributeBuilder::new()
|
||||||
.insert_entry(BuildInTextAttribute::Bold(true))
|
.insert_entry(BuildInTextAttribute::Bold(true))
|
||||||
.insert_entry(BuildInTextAttribute::Italic(true))
|
.insert_entry(BuildInTextAttribute::Italic(true))
|
||||||
.build();
|
.build();
|
||||||
let retain = Operation::insert_with_attributes("123", attributes);
|
let retain = DeltaOperation::insert_with_attributes("123", attributes);
|
||||||
|
|
||||||
delta.add(retain);
|
delta.add(retain);
|
||||||
delta.add(Operation::Retain(5.into()));
|
delta.add(DeltaOperation::Retain(5.into()));
|
||||||
delta.add(Operation::Delete(3));
|
delta.add(DeltaOperation::Delete(3));
|
||||||
|
|
||||||
let json = serde_json::to_string(&delta).unwrap();
|
let json = serde_json::to_string(&delta).unwrap();
|
||||||
eprintln!("{}", json);
|
eprintln!("{}", json);
|
||||||
|
|
||||||
let delta_from_json = Operations::from_json(&json).unwrap();
|
let delta_from_json = DeltaOperations::from_json(&json).unwrap();
|
||||||
assert_eq!(delta_from_json, delta);
|
assert_eq!(delta_from_json, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ fn delta_deserialize_test() {
|
|||||||
{"retain":2,"attributes":{"italic":true,"bold":true}},
|
{"retain":2,"attributes":{"italic":true,"bold":true}},
|
||||||
{"retain":2,"attributes":{"italic":true,"bold":true}}
|
{"retain":2,"attributes":{"italic":true,"bold":true}}
|
||||||
]"#;
|
]"#;
|
||||||
let delta = TextDelta::from_json(json).unwrap();
|
let delta = TextOperations::from_json(json).unwrap();
|
||||||
eprintln!("{}", delta);
|
eprintln!("{}", delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ fn delta_deserialize_null_test() {
|
|||||||
let json = r#"[
|
let json = r#"[
|
||||||
{"retain":7,"attributes":{"bold":null}}
|
{"retain":7,"attributes":{"bold":null}}
|
||||||
]"#;
|
]"#;
|
||||||
let delta1 = TextDelta::from_json(json).unwrap();
|
let delta1 = TextOperations::from_json(json).unwrap();
|
||||||
|
|
||||||
let mut attribute = BuildInTextAttribute::Bold(true);
|
let mut attribute = BuildInTextAttribute::Bold(true);
|
||||||
attribute.remove_value();
|
attribute.remove_value();
|
||||||
@ -104,10 +104,10 @@ fn document_insert_serde_test() {
|
|||||||
let mut document = ClientDocument::new::<EmptyDoc>();
|
let mut document = ClientDocument::new::<EmptyDoc>();
|
||||||
document.insert(0, "\n").unwrap();
|
document.insert(0, "\n").unwrap();
|
||||||
document.insert(0, "123").unwrap();
|
document.insert(0, "123").unwrap();
|
||||||
let json = document.delta_str();
|
let json = document.get_operations_json();
|
||||||
assert_eq!(r#"[{"insert":"123\n"}]"#, json);
|
assert_eq!(r#"[{"insert":"123\n"}]"#, json);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
r#"[{"insert":"123\n"}]"#,
|
r#"[{"insert":"123\n"}]"#,
|
||||||
ClientDocument::from_json(&json).unwrap().delta_str()
|
ClientDocument::from_json(&json).unwrap().get_operations_json()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct ImageData {
|
|
||||||
image: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for ImageData {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
self.image.clone()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +1,19 @@
|
|||||||
use lib_ot::{core::OperationBuilder, text_delta::TextDelta};
|
use lib_ot::{core::OperationBuilder, text_delta::TextOperations};
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn initial_quill_delta() -> TextDelta {
|
pub fn initial_document_operations() -> TextOperations {
|
||||||
OperationBuilder::new().insert("\n").build()
|
OperationBuilder::new().insert("\n").build()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn initial_quill_delta_string() -> String {
|
pub fn initial_document_str() -> String {
|
||||||
initial_quill_delta().json_str()
|
initial_document_operations().json_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn initial_read_me() -> TextDelta {
|
pub fn initial_read_me() -> TextOperations {
|
||||||
let json = include_str!("READ_ME.json");
|
let json = include_str!("READ_ME.json");
|
||||||
TextDelta::from_json(json).unwrap()
|
TextOperations::from_json(json).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
|
use crate::client_document::default::initial_document_str;
|
||||||
use crate::{
|
use crate::{
|
||||||
client_document::{
|
client_document::{
|
||||||
default::initial_quill_delta,
|
|
||||||
history::{History, UndoResult},
|
history::{History, UndoResult},
|
||||||
view::{ViewExtensions, RECORD_THRESHOLD},
|
view::{ViewExtensions, RECORD_THRESHOLD},
|
||||||
},
|
},
|
||||||
errors::CollaborateError,
|
errors::CollaborateError,
|
||||||
};
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use lib_ot::{core::*, text_delta::TextDelta};
|
use lib_ot::{core::*, text_delta::TextOperations};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
pub trait InitialDocumentText {
|
pub trait InitialDocumentContent {
|
||||||
fn initial_delta() -> TextDelta;
|
fn json_str() -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EmptyDoc();
|
pub struct EmptyDoc();
|
||||||
impl InitialDocumentText for EmptyDoc {
|
impl InitialDocumentContent for EmptyDoc {
|
||||||
fn initial_delta() -> TextDelta {
|
fn json_str() -> String {
|
||||||
TextDelta::new()
|
TextOperations::default().json_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NewlineDoc();
|
pub struct NewlineDoc();
|
||||||
impl InitialDocumentText for NewlineDoc {
|
impl InitialDocumentContent for NewlineDoc {
|
||||||
fn initial_delta() -> TextDelta {
|
fn json_str() -> String {
|
||||||
initial_quill_delta()
|
initial_document_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClientDocument {
|
pub struct ClientDocument {
|
||||||
delta: TextDelta,
|
operations: TextOperations,
|
||||||
history: History,
|
history: History,
|
||||||
view: ViewExtensions,
|
view: ViewExtensions,
|
||||||
last_edit_time: usize,
|
last_edit_time: usize,
|
||||||
@ -37,13 +37,14 @@ pub struct ClientDocument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClientDocument {
|
impl ClientDocument {
|
||||||
pub fn new<C: InitialDocumentText>() -> Self {
|
pub fn new<C: InitialDocumentContent>() -> Self {
|
||||||
Self::from_delta(C::initial_delta())
|
let content = C::json_str();
|
||||||
|
Self::from_json(&content).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_delta(delta: TextDelta) -> Self {
|
pub fn from_operations(operations: TextOperations) -> Self {
|
||||||
ClientDocument {
|
ClientDocument {
|
||||||
delta,
|
operations,
|
||||||
history: History::new(),
|
history: History::new(),
|
||||||
view: ViewExtensions::new(),
|
view: ViewExtensions::new(),
|
||||||
last_edit_time: 0,
|
last_edit_time: 0,
|
||||||
@ -52,24 +53,24 @@ impl ClientDocument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_json(json: &str) -> Result<Self, CollaborateError> {
|
pub fn from_json(json: &str) -> Result<Self, CollaborateError> {
|
||||||
let delta = TextDelta::from_json(json)?;
|
let operations = TextOperations::from_json(json)?;
|
||||||
Ok(Self::from_delta(delta))
|
Ok(Self::from_operations(operations))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delta_str(&self) -> String {
|
pub fn get_operations_json(&self) -> String {
|
||||||
self.delta.json_str()
|
self.operations.json_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> Bytes {
|
pub fn to_bytes(&self) -> Bytes {
|
||||||
self.delta.json_bytes()
|
self.operations.json_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_plain_string(&self) -> String {
|
pub fn to_content(&self) -> String {
|
||||||
self.delta.apply("").unwrap()
|
self.operations.content().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delta(&self) -> &TextDelta {
|
pub fn get_operations(&self) -> &TextOperations {
|
||||||
&self.delta
|
&self.operations
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn md5(&self) -> String {
|
pub fn md5(&self) -> String {
|
||||||
@ -81,9 +82,9 @@ impl ClientDocument {
|
|||||||
self.notify = Some(notify);
|
self.notify = Some(notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_delta(&mut self, data: TextDelta) {
|
pub fn set_operations(&mut self, operations: TextOperations) {
|
||||||
tracing::trace!("document: {}", data.json_str());
|
tracing::trace!("document: {}", operations.json_str());
|
||||||
self.delta = data;
|
self.operations = operations;
|
||||||
|
|
||||||
match &self.notify {
|
match &self.notify {
|
||||||
None => {}
|
None => {}
|
||||||
@ -93,74 +94,78 @@ impl ClientDocument {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compose_delta(&mut self, delta: TextDelta) -> Result<(), CollaborateError> {
|
pub fn compose_operations(&mut self, operations: TextOperations) -> Result<(), CollaborateError> {
|
||||||
tracing::trace!("{} compose {}", &self.delta.json_str(), delta.json_str());
|
tracing::trace!("{} compose {}", &self.operations.json_str(), operations.json_str());
|
||||||
let composed_delta = self.delta.compose(&delta)?;
|
let composed_operations = self.operations.compose(&operations)?;
|
||||||
let mut undo_delta = delta.invert(&self.delta);
|
let mut undo_operations = operations.invert(&self.operations);
|
||||||
|
|
||||||
let now = chrono::Utc::now().timestamp_millis() as usize;
|
let now = chrono::Utc::now().timestamp_millis() as usize;
|
||||||
if now - self.last_edit_time < RECORD_THRESHOLD {
|
if now - self.last_edit_time < RECORD_THRESHOLD {
|
||||||
if let Some(last_delta) = self.history.undo() {
|
if let Some(last_operation) = self.history.undo() {
|
||||||
tracing::trace!("compose previous change");
|
tracing::trace!("compose previous change");
|
||||||
tracing::trace!("current = {}", undo_delta);
|
tracing::trace!("current = {}", undo_operations);
|
||||||
tracing::trace!("previous = {}", last_delta);
|
tracing::trace!("previous = {}", last_operation);
|
||||||
undo_delta = undo_delta.compose(&last_delta)?;
|
undo_operations = undo_operations.compose(&last_operation)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.last_edit_time = now;
|
self.last_edit_time = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !undo_delta.is_empty() {
|
if !undo_operations.is_empty() {
|
||||||
tracing::trace!("add history delta: {}", undo_delta);
|
tracing::trace!("add history operations: {}", undo_operations);
|
||||||
self.history.record(undo_delta);
|
self.history.record(undo_operations);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_delta(composed_delta);
|
self.set_operations(composed_operations);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert<T: ToString>(&mut self, index: usize, data: T) -> Result<TextDelta, CollaborateError> {
|
pub fn insert<T: ToString>(&mut self, index: usize, data: T) -> Result<TextOperations, CollaborateError> {
|
||||||
let text = data.to_string();
|
let text = data.to_string();
|
||||||
let interval = Interval::new(index, index);
|
let interval = Interval::new(index, index);
|
||||||
let _ = validate_interval(&self.delta, &interval)?;
|
let _ = validate_interval(&self.operations, &interval)?;
|
||||||
let delta = self.view.insert(&self.delta, &text, interval)?;
|
let operations = self.view.insert(&self.operations, &text, interval)?;
|
||||||
self.compose_delta(delta.clone())?;
|
self.compose_operations(operations.clone())?;
|
||||||
Ok(delta)
|
Ok(operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&mut self, interval: Interval) -> Result<TextDelta, CollaborateError> {
|
pub fn delete(&mut self, interval: Interval) -> Result<TextOperations, CollaborateError> {
|
||||||
let _ = validate_interval(&self.delta, &interval)?;
|
let _ = validate_interval(&self.operations, &interval)?;
|
||||||
debug_assert!(!interval.is_empty());
|
debug_assert!(!interval.is_empty());
|
||||||
let delete = self.view.delete(&self.delta, interval)?;
|
let operations = self.view.delete(&self.operations, interval)?;
|
||||||
if !delete.is_empty() {
|
if !operations.is_empty() {
|
||||||
let _ = self.compose_delta(delete.clone())?;
|
let _ = self.compose_operations(operations.clone())?;
|
||||||
}
|
}
|
||||||
Ok(delete)
|
Ok(operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format(&mut self, interval: Interval, attribute: AttributeEntry) -> Result<TextDelta, CollaborateError> {
|
pub fn format(
|
||||||
let _ = validate_interval(&self.delta, &interval)?;
|
&mut self,
|
||||||
|
interval: Interval,
|
||||||
|
attribute: AttributeEntry,
|
||||||
|
) -> Result<TextOperations, CollaborateError> {
|
||||||
|
let _ = validate_interval(&self.operations, &interval)?;
|
||||||
tracing::trace!("format {} with {:?}", interval, attribute);
|
tracing::trace!("format {} with {:?}", interval, attribute);
|
||||||
let format_delta = self.view.format(&self.delta, attribute, interval).unwrap();
|
let operations = self.view.format(&self.operations, attribute, interval).unwrap();
|
||||||
self.compose_delta(format_delta.clone())?;
|
self.compose_operations(operations.clone())?;
|
||||||
Ok(format_delta)
|
Ok(operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace<T: ToString>(&mut self, interval: Interval, data: T) -> Result<TextDelta, CollaborateError> {
|
pub fn replace<T: ToString>(&mut self, interval: Interval, data: T) -> Result<TextOperations, CollaborateError> {
|
||||||
let _ = validate_interval(&self.delta, &interval)?;
|
let _ = validate_interval(&self.operations, &interval)?;
|
||||||
let mut delta = TextDelta::default();
|
let mut operations = TextOperations::default();
|
||||||
let text = data.to_string();
|
let text = data.to_string();
|
||||||
if !text.is_empty() {
|
if !text.is_empty() {
|
||||||
delta = self.view.insert(&self.delta, &text, interval)?;
|
operations = self.view.insert(&self.operations, &text, interval)?;
|
||||||
self.compose_delta(delta.clone())?;
|
self.compose_operations(operations.clone())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !interval.is_empty() {
|
if !interval.is_empty() {
|
||||||
let delete = self.delete(interval)?;
|
let delete = self.delete(interval)?;
|
||||||
delta = delta.compose(&delete)?;
|
operations = operations.compose(&delete)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(delta)
|
Ok(operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_undo(&self) -> bool {
|
pub fn can_undo(&self) -> bool {
|
||||||
@ -174,11 +179,13 @@ impl ClientDocument {
|
|||||||
pub fn undo(&mut self) -> Result<UndoResult, CollaborateError> {
|
pub fn undo(&mut self) -> Result<UndoResult, CollaborateError> {
|
||||||
match self.history.undo() {
|
match self.history.undo() {
|
||||||
None => Err(CollaborateError::undo().context("Undo stack is empty")),
|
None => Err(CollaborateError::undo().context("Undo stack is empty")),
|
||||||
Some(undo_delta) => {
|
Some(undo_operations) => {
|
||||||
let (new_delta, inverted_delta) = self.invert(&undo_delta)?;
|
let (new_operations, inverted_operations) = self.invert(&undo_operations)?;
|
||||||
self.set_delta(new_delta);
|
self.set_operations(new_operations);
|
||||||
self.history.add_redo(inverted_delta);
|
self.history.add_redo(inverted_operations);
|
||||||
Ok(UndoResult { delta: undo_delta })
|
Ok(UndoResult {
|
||||||
|
operations: undo_operations,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,35 +193,41 @@ impl ClientDocument {
|
|||||||
pub fn redo(&mut self) -> Result<UndoResult, CollaborateError> {
|
pub fn redo(&mut self) -> Result<UndoResult, CollaborateError> {
|
||||||
match self.history.redo() {
|
match self.history.redo() {
|
||||||
None => Err(CollaborateError::redo()),
|
None => Err(CollaborateError::redo()),
|
||||||
Some(redo_delta) => {
|
Some(redo_operations) => {
|
||||||
let (new_delta, inverted_delta) = self.invert(&redo_delta)?;
|
let (new_operations, inverted_operations) = self.invert(&redo_operations)?;
|
||||||
self.set_delta(new_delta);
|
self.set_operations(new_operations);
|
||||||
self.history.add_undo(inverted_delta);
|
self.history.add_undo(inverted_operations);
|
||||||
Ok(UndoResult { delta: redo_delta })
|
Ok(UndoResult {
|
||||||
|
operations: redo_operations,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
// The document is empty if its text is equal to the initial text.
|
// The document is empty if its text is equal to the initial text.
|
||||||
self.delta == NewlineDoc::initial_delta()
|
self.operations.json_str() == NewlineDoc::json_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientDocument {
|
impl ClientDocument {
|
||||||
fn invert(&self, delta: &TextDelta) -> Result<(TextDelta, TextDelta), CollaborateError> {
|
fn invert(&self, operations: &TextOperations) -> Result<(TextOperations, TextOperations), CollaborateError> {
|
||||||
// c = a.compose(b)
|
// c = a.compose(b)
|
||||||
// d = b.invert(a)
|
// d = b.invert(a)
|
||||||
// a = c.compose(d)
|
// a = c.compose(d)
|
||||||
let new_delta = self.delta.compose(delta)?;
|
let new_operations = self.operations.compose(operations)?;
|
||||||
let inverted_delta = delta.invert(&self.delta);
|
let inverted_operations = operations.invert(&self.operations);
|
||||||
Ok((new_delta, inverted_delta))
|
Ok((new_operations, inverted_operations))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_interval(delta: &TextDelta, interval: &Interval) -> Result<(), CollaborateError> {
|
fn validate_interval(operations: &TextOperations, interval: &Interval) -> Result<(), CollaborateError> {
|
||||||
if delta.utf16_target_len < interval.end {
|
if operations.utf16_target_len < interval.end {
|
||||||
log::error!("{:?} out of bounds. should 0..{}", interval, delta.utf16_target_len);
|
log::error!(
|
||||||
|
"{:?} out of bounds. should 0..{}",
|
||||||
|
interval,
|
||||||
|
operations.utf16_target_len
|
||||||
|
);
|
||||||
return Err(CollaborateError::out_of_bound());
|
return Err(CollaborateError::out_of_bound());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::client_document::DeleteExt;
|
use crate::client_document::DeleteExt;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{Interval, OperationBuilder},
|
core::{Interval, OperationBuilder},
|
||||||
text_delta::TextDelta,
|
text_delta::TextOperations,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct DefaultDelete {}
|
pub struct DefaultDelete {}
|
||||||
@ -10,7 +10,7 @@ impl DeleteExt for DefaultDelete {
|
|||||||
"DefaultDelete"
|
"DefaultDelete"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, _delta: &TextDelta, interval: Interval) -> Option<TextDelta> {
|
fn apply(&self, _delta: &TextOperations, interval: Interval) -> Option<TextOperations> {
|
||||||
Some(
|
Some(
|
||||||
OperationBuilder::new()
|
OperationBuilder::new()
|
||||||
.retain(interval.start)
|
.retain(interval.start)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{client_document::DeleteExt, util::is_newline};
|
use crate::{client_document::DeleteExt, util::is_newline};
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{Interval, OperationAttributes, OperationBuilder, OperationIterator, Utf16CodeUnitMetric, NEW_LINE},
|
core::{Interval, OperationAttributes, OperationBuilder, OperationIterator, Utf16CodeUnitMetric, NEW_LINE},
|
||||||
text_delta::{empty_attributes, TextDelta},
|
text_delta::{empty_attributes, TextOperations},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PreserveLineFormatOnMerge {}
|
pub struct PreserveLineFormatOnMerge {}
|
||||||
@ -10,7 +10,7 @@ impl DeleteExt for PreserveLineFormatOnMerge {
|
|||||||
"PreserveLineFormatOnMerge"
|
"PreserveLineFormatOnMerge"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, delta: &TextDelta, interval: Interval) -> Option<TextDelta> {
|
fn apply(&self, delta: &TextOperations, interval: Interval) -> Option<TextOperations> {
|
||||||
if interval.is_empty() {
|
if interval.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ impl DeleteExt for PreserveLineFormatOnMerge {
|
|||||||
attributes.remove_all_value();
|
attributes.remove_all_value();
|
||||||
|
|
||||||
if newline_op.has_attribute() {
|
if newline_op.has_attribute() {
|
||||||
attributes.extend_other(newline_op.get_attributes());
|
attributes.extend(newline_op.get_attributes());
|
||||||
}
|
}
|
||||||
|
|
||||||
new_delta.retain(line_break, empty_attributes());
|
new_delta.retain(line_break, empty_attributes());
|
||||||
|
@ -2,7 +2,7 @@ use lib_ot::core::AttributeEntry;
|
|||||||
use lib_ot::text_delta::is_block;
|
use lib_ot::text_delta::is_block;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{Interval, OperationBuilder, OperationIterator},
|
core::{Interval, OperationBuilder, OperationIterator},
|
||||||
text_delta::{empty_attributes, AttributeScope, TextDelta},
|
text_delta::{empty_attributes, AttributeScope, TextOperations},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -16,7 +16,7 @@ impl FormatExt for ResolveBlockFormat {
|
|||||||
"ResolveBlockFormat"
|
"ResolveBlockFormat"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, delta: &TextDelta, interval: Interval, attribute: &AttributeEntry) -> Option<TextDelta> {
|
fn apply(&self, delta: &TextOperations, interval: Interval, attribute: &AttributeEntry) -> Option<TextOperations> {
|
||||||
if !is_block(&attribute.key) {
|
if !is_block(&attribute.key) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use lib_ot::core::AttributeEntry;
|
|||||||
use lib_ot::text_delta::is_inline;
|
use lib_ot::text_delta::is_inline;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{Interval, OperationBuilder, OperationIterator},
|
core::{Interval, OperationBuilder, OperationIterator},
|
||||||
text_delta::{AttributeScope, TextDelta},
|
text_delta::{AttributeScope, TextOperations},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -16,7 +16,7 @@ impl FormatExt for ResolveInlineFormat {
|
|||||||
"ResolveInlineFormat"
|
"ResolveInlineFormat"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, delta: &TextDelta, interval: Interval, attribute: &AttributeEntry) -> Option<TextDelta> {
|
fn apply(&self, delta: &TextOperations, interval: Interval, attribute: &AttributeEntry) -> Option<TextOperations> {
|
||||||
if !is_inline(&attribute.key) {
|
if !is_inline(&attribute.key) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::util::find_newline;
|
use crate::util::find_newline;
|
||||||
use lib_ot::core::AttributeEntry;
|
use lib_ot::core::AttributeEntry;
|
||||||
use lib_ot::text_delta::{empty_attributes, AttributeScope, TextDelta, TextOperation};
|
use lib_ot::text_delta::{empty_attributes, AttributeScope, TextOperation, TextOperations};
|
||||||
|
|
||||||
pub(crate) fn line_break(op: &TextOperation, attribute: &AttributeEntry, scope: AttributeScope) -> TextDelta {
|
pub(crate) fn line_break(op: &TextOperation, attribute: &AttributeEntry, scope: AttributeScope) -> TextOperations {
|
||||||
let mut new_delta = TextDelta::new();
|
let mut new_delta = TextOperations::new();
|
||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
let end = op.len();
|
let end = op.len();
|
||||||
let mut s = op.get_data();
|
let mut s = op.get_data();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{client_document::InsertExt, util::is_newline};
|
use crate::{client_document::InsertExt, util::is_newline};
|
||||||
use lib_ot::core::{is_empty_line_at_index, OperationBuilder, OperationIterator};
|
use lib_ot::core::{is_empty_line_at_index, OperationBuilder, OperationIterator};
|
||||||
use lib_ot::text_delta::{attributes_except_header, BuildInTextAttributeKey, TextDelta};
|
use lib_ot::text_delta::{attributes_except_header, BuildInTextAttributeKey, TextOperations};
|
||||||
|
|
||||||
pub struct AutoExitBlock {}
|
pub struct AutoExitBlock {}
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ impl InsertExt for AutoExitBlock {
|
|||||||
"AutoExitBlock"
|
"AutoExitBlock"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, delta: &TextDelta, replace_len: usize, text: &str, index: usize) -> Option<TextDelta> {
|
fn apply(&self, delta: &TextOperations, replace_len: usize, text: &str, index: usize) -> Option<TextOperations> {
|
||||||
// Auto exit block will be triggered by enter two new lines
|
// Auto exit block will be triggered by enter two new lines
|
||||||
if !is_newline(text) {
|
if !is_newline(text) {
|
||||||
return None;
|
return None;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::{client_document::InsertExt, util::is_whitespace};
|
use crate::{client_document::InsertExt, util::is_whitespace};
|
||||||
use lib_ot::core::Attributes;
|
use lib_ot::core::AttributeHashMap;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{count_utf16_code_units, OperationBuilder, OperationIterator},
|
core::{count_utf16_code_units, OperationBuilder, OperationIterator},
|
||||||
text_delta::{empty_attributes, BuildInTextAttribute, TextDelta},
|
text_delta::{empty_attributes, BuildInTextAttribute, TextOperations},
|
||||||
};
|
};
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@ -13,7 +13,7 @@ impl InsertExt for AutoFormatExt {
|
|||||||
"AutoFormatExt"
|
"AutoFormatExt"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, delta: &TextDelta, replace_len: usize, text: &str, index: usize) -> Option<TextDelta> {
|
fn apply(&self, delta: &TextOperations, replace_len: usize, text: &str, index: usize) -> Option<TextOperations> {
|
||||||
// enter whitespace to trigger auto format
|
// enter whitespace to trigger auto format
|
||||||
if !is_whitespace(text) {
|
if !is_whitespace(text) {
|
||||||
return None;
|
return None;
|
||||||
@ -61,7 +61,7 @@ pub enum AutoFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AutoFormatter {
|
impl AutoFormatter {
|
||||||
pub fn to_attributes(&self) -> Attributes {
|
pub fn to_attributes(&self) -> AttributeHashMap {
|
||||||
match self {
|
match self {
|
||||||
AutoFormatter::Url(url) => BuildInTextAttribute::Link(url.as_str()).into(),
|
AutoFormatter::Url(url) => BuildInTextAttribute::Link(url.as_str()).into(),
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::client_document::InsertExt;
|
use crate::client_document::InsertExt;
|
||||||
use lib_ot::core::Attributes;
|
use lib_ot::core::AttributeHashMap;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{OperationAttributes, OperationBuilder, OperationIterator, NEW_LINE},
|
core::{OperationAttributes, OperationBuilder, OperationIterator, NEW_LINE},
|
||||||
text_delta::{BuildInTextAttributeKey, TextDelta},
|
text_delta::{BuildInTextAttributeKey, TextOperations},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct DefaultInsertAttribute {}
|
pub struct DefaultInsertAttribute {}
|
||||||
@ -11,9 +11,9 @@ impl InsertExt for DefaultInsertAttribute {
|
|||||||
"DefaultInsertAttribute"
|
"DefaultInsertAttribute"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, delta: &TextDelta, replace_len: usize, text: &str, index: usize) -> Option<TextDelta> {
|
fn apply(&self, delta: &TextOperations, replace_len: usize, text: &str, index: usize) -> Option<TextOperations> {
|
||||||
let iter = OperationIterator::new(delta);
|
let iter = OperationIterator::new(delta);
|
||||||
let mut attributes = Attributes::new();
|
let mut attributes = AttributeHashMap::new();
|
||||||
|
|
||||||
// Enable each line split by "\n" remains the block attributes. for example:
|
// Enable each line split by "\n" remains the block attributes. for example:
|
||||||
// insert "\n" to "123456" at index 3
|
// insert "\n" to "123456" at index 3
|
||||||
@ -28,7 +28,7 @@ impl InsertExt for DefaultInsertAttribute {
|
|||||||
.get_attributes()
|
.get_attributes()
|
||||||
.contains_key(BuildInTextAttributeKey::Header.as_ref())
|
.contains_key(BuildInTextAttributeKey::Header.as_ref())
|
||||||
{
|
{
|
||||||
attributes.extend_other(op.get_attributes());
|
attributes.extend(op.get_attributes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use crate::client_document::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::text_delta::TextDelta;
|
use lib_ot::text_delta::TextOperations;
|
||||||
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::*;
|
||||||
@ -20,7 +20,13 @@ impl InsertExt for InsertEmbedsExt {
|
|||||||
"InsertEmbedsExt"
|
"InsertEmbedsExt"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, _delta: &TextDelta, _replace_len: usize, _text: &str, _index: usize) -> Option<TextDelta> {
|
fn apply(
|
||||||
|
&self,
|
||||||
|
_delta: &TextOperations,
|
||||||
|
_replace_len: usize,
|
||||||
|
_text: &str,
|
||||||
|
_index: usize,
|
||||||
|
) -> Option<TextOperations> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,7 +37,13 @@ impl InsertExt for ForceNewlineForInsertsAroundEmbedExt {
|
|||||||
"ForceNewlineForInsertsAroundEmbedExt"
|
"ForceNewlineForInsertsAroundEmbedExt"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, _delta: &TextDelta, _replace_len: usize, _text: &str, _index: usize) -> Option<TextDelta> {
|
fn apply(
|
||||||
|
&self,
|
||||||
|
_delta: &TextOperations,
|
||||||
|
_replace_len: usize,
|
||||||
|
_text: &str,
|
||||||
|
_index: usize,
|
||||||
|
) -> Option<TextOperations> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::{client_document::InsertExt, util::is_newline};
|
use crate::{client_document::InsertExt, util::is_newline};
|
||||||
use lib_ot::core::Attributes;
|
use lib_ot::core::AttributeHashMap;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{OperationBuilder, OperationIterator, NEW_LINE},
|
core::{OperationBuilder, OperationIterator, NEW_LINE},
|
||||||
text_delta::{attributes_except_header, empty_attributes, BuildInTextAttributeKey, TextDelta},
|
text_delta::{attributes_except_header, empty_attributes, BuildInTextAttributeKey, TextOperations},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PreserveBlockFormatOnInsert {}
|
pub struct PreserveBlockFormatOnInsert {}
|
||||||
@ -11,7 +11,7 @@ impl InsertExt for PreserveBlockFormatOnInsert {
|
|||||||
"PreserveBlockFormatOnInsert"
|
"PreserveBlockFormatOnInsert"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, delta: &TextDelta, replace_len: usize, text: &str, index: usize) -> Option<TextDelta> {
|
fn apply(&self, delta: &TextOperations, replace_len: usize, text: &str, index: usize) -> Option<TextOperations> {
|
||||||
if !is_newline(text) {
|
if !is_newline(text) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -26,7 +26,7 @@ impl InsertExt for PreserveBlockFormatOnInsert {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut reset_attribute = Attributes::new();
|
let mut reset_attribute = AttributeHashMap::new();
|
||||||
if newline_attributes.contains_key(BuildInTextAttributeKey::Header.as_ref()) {
|
if newline_attributes.contains_key(BuildInTextAttributeKey::Header.as_ref()) {
|
||||||
reset_attribute.insert(BuildInTextAttributeKey::Header, 1);
|
reset_attribute.insert(BuildInTextAttributeKey::Header, 1);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{OpNewline, OperationBuilder, OperationIterator, NEW_LINE},
|
core::{OpNewline, OperationBuilder, OperationIterator, NEW_LINE},
|
||||||
text_delta::{empty_attributes, BuildInTextAttributeKey, TextDelta},
|
text_delta::{empty_attributes, BuildInTextAttributeKey, TextOperations},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PreserveInlineFormat {}
|
pub struct PreserveInlineFormat {}
|
||||||
@ -13,7 +13,7 @@ impl InsertExt for PreserveInlineFormat {
|
|||||||
"PreserveInlineFormat"
|
"PreserveInlineFormat"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, delta: &TextDelta, replace_len: usize, text: &str, index: usize) -> Option<TextDelta> {
|
fn apply(&self, delta: &TextOperations, replace_len: usize, text: &str, index: usize) -> Option<TextOperations> {
|
||||||
if contain_newline(text) {
|
if contain_newline(text) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ impl InsertExt for PreserveLineFormatOnSplit {
|
|||||||
"PreserveLineFormatOnSplit"
|
"PreserveLineFormatOnSplit"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, delta: &TextDelta, replace_len: usize, text: &str, index: usize) -> Option<TextDelta> {
|
fn apply(&self, delta: &TextOperations, replace_len: usize, text: &str, index: usize) -> Option<TextOperations> {
|
||||||
if !is_newline(text) {
|
if !is_newline(text) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -76,7 +76,7 @@ impl InsertExt for PreserveLineFormatOnSplit {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut new_delta = TextDelta::new();
|
let mut new_delta = TextOperations::new();
|
||||||
new_delta.retain(index + replace_len, empty_attributes());
|
new_delta.retain(index + replace_len, empty_attributes());
|
||||||
|
|
||||||
if newline_status.is_contain() {
|
if newline_status.is_contain() {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::{client_document::InsertExt, util::is_newline};
|
use crate::{client_document::InsertExt, util::is_newline};
|
||||||
use lib_ot::core::Attributes;
|
use lib_ot::core::AttributeHashMap;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{OperationBuilder, OperationIterator, Utf16CodeUnitMetric, NEW_LINE},
|
core::{OperationBuilder, OperationIterator, Utf16CodeUnitMetric, NEW_LINE},
|
||||||
text_delta::{BuildInTextAttributeKey, TextDelta},
|
text_delta::{BuildInTextAttributeKey, TextOperations},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ResetLineFormatOnNewLine {}
|
pub struct ResetLineFormatOnNewLine {}
|
||||||
@ -11,7 +11,7 @@ impl InsertExt for ResetLineFormatOnNewLine {
|
|||||||
"ResetLineFormatOnNewLine"
|
"ResetLineFormatOnNewLine"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self, delta: &TextDelta, replace_len: usize, text: &str, index: usize) -> Option<TextDelta> {
|
fn apply(&self, delta: &TextOperations, replace_len: usize, text: &str, index: usize) -> Option<TextOperations> {
|
||||||
if !is_newline(text) {
|
if !is_newline(text) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -23,7 +23,7 @@ impl InsertExt for ResetLineFormatOnNewLine {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut reset_attribute = Attributes::new();
|
let mut reset_attribute = AttributeHashMap::new();
|
||||||
if next_op
|
if next_op
|
||||||
.get_attributes()
|
.get_attributes()
|
||||||
.contains_key(BuildInTextAttributeKey::Header.as_ref())
|
.contains_key(BuildInTextAttributeKey::Header.as_ref())
|
||||||
|
@ -2,7 +2,7 @@ pub use delete::*;
|
|||||||
pub use format::*;
|
pub use format::*;
|
||||||
pub use insert::*;
|
pub use insert::*;
|
||||||
use lib_ot::core::AttributeEntry;
|
use lib_ot::core::AttributeEntry;
|
||||||
use lib_ot::{core::Interval, text_delta::TextDelta};
|
use lib_ot::{core::Interval, text_delta::TextOperations};
|
||||||
|
|
||||||
mod delete;
|
mod delete;
|
||||||
mod format;
|
mod format;
|
||||||
@ -15,15 +15,15 @@ pub type DeleteExtension = Box<dyn DeleteExt + Send + Sync>;
|
|||||||
|
|
||||||
pub trait InsertExt {
|
pub trait InsertExt {
|
||||||
fn ext_name(&self) -> &str;
|
fn ext_name(&self) -> &str;
|
||||||
fn apply(&self, delta: &TextDelta, replace_len: usize, text: &str, index: usize) -> Option<TextDelta>;
|
fn apply(&self, delta: &TextOperations, replace_len: usize, text: &str, index: usize) -> Option<TextOperations>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FormatExt {
|
pub trait FormatExt {
|
||||||
fn ext_name(&self) -> &str;
|
fn ext_name(&self) -> &str;
|
||||||
fn apply(&self, delta: &TextDelta, interval: Interval, attribute: &AttributeEntry) -> Option<TextDelta>;
|
fn apply(&self, delta: &TextOperations, interval: Interval, attribute: &AttributeEntry) -> Option<TextOperations>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DeleteExt {
|
pub trait DeleteExt {
|
||||||
fn ext_name(&self) -> &str;
|
fn ext_name(&self) -> &str;
|
||||||
fn apply(&self, delta: &TextDelta, interval: Interval) -> Option<TextDelta>;
|
fn apply(&self, delta: &TextOperations, interval: Interval) -> Option<TextOperations>;
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
use lib_ot::text_delta::TextDelta;
|
use lib_ot::text_delta::TextOperations;
|
||||||
|
|
||||||
const MAX_UNDOES: usize = 20;
|
const MAX_UNDOES: usize = 20;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct UndoResult {
|
pub struct UndoResult {
|
||||||
pub delta: TextDelta,
|
pub operations: TextOperations,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct History {
|
pub struct History {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
cur_undo: usize,
|
cur_undo: usize,
|
||||||
undoes: Vec<TextDelta>,
|
undoes: Vec<TextOperations>,
|
||||||
redoes: Vec<TextDelta>,
|
redoes: Vec<TextOperations>,
|
||||||
capacity: usize,
|
capacity: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,15 +40,15 @@ impl History {
|
|||||||
!self.redoes.is_empty()
|
!self.redoes.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_undo(&mut self, delta: TextDelta) {
|
pub fn add_undo(&mut self, delta: TextOperations) {
|
||||||
self.undoes.push(delta);
|
self.undoes.push(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_redo(&mut self, delta: TextDelta) {
|
pub fn add_redo(&mut self, delta: TextOperations) {
|
||||||
self.redoes.push(delta);
|
self.redoes.push(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn record(&mut self, delta: TextDelta) {
|
pub fn record(&mut self, delta: TextOperations) {
|
||||||
if delta.ops.is_empty() {
|
if delta.ops.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ impl History {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undo(&mut self) -> Option<TextDelta> {
|
pub fn undo(&mut self) -> Option<TextOperations> {
|
||||||
if !self.can_undo() {
|
if !self.can_undo() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ impl History {
|
|||||||
Some(delta)
|
Some(delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redo(&mut self) -> Option<TextDelta> {
|
pub fn redo(&mut self) -> Option<TextOperations> {
|
||||||
if !self.can_redo() {
|
if !self.can_redo() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ pub use document_pad::*;
|
|||||||
pub(crate) use extensions::*;
|
pub(crate) use extensions::*;
|
||||||
pub use view::*;
|
pub use view::*;
|
||||||
|
|
||||||
mod data;
|
|
||||||
pub mod default;
|
pub mod default;
|
||||||
mod document_pad;
|
mod document_pad;
|
||||||
mod extensions;
|
mod extensions;
|
||||||
|
@ -3,7 +3,7 @@ use lib_ot::core::AttributeEntry;
|
|||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{trim, Interval},
|
core::{trim, Interval},
|
||||||
errors::{ErrorBuilder, OTError, OTErrorCode},
|
errors::{ErrorBuilder, OTError, OTErrorCode},
|
||||||
text_delta::TextDelta,
|
text_delta::TextOperations,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const RECORD_THRESHOLD: usize = 400; // in milliseconds
|
pub const RECORD_THRESHOLD: usize = 400; // in milliseconds
|
||||||
@ -23,24 +23,29 @@ impl ViewExtensions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn insert(&self, delta: &TextDelta, text: &str, interval: Interval) -> Result<TextDelta, OTError> {
|
pub(crate) fn insert(
|
||||||
let mut new_delta = None;
|
&self,
|
||||||
|
operations: &TextOperations,
|
||||||
|
text: &str,
|
||||||
|
interval: Interval,
|
||||||
|
) -> Result<TextOperations, OTError> {
|
||||||
|
let mut new_operations = None;
|
||||||
for ext in &self.insert_exts {
|
for ext in &self.insert_exts {
|
||||||
if let Some(mut delta) = ext.apply(delta, interval.size(), text, interval.start) {
|
if let Some(mut operations) = ext.apply(operations, interval.size(), text, interval.start) {
|
||||||
trim(&mut delta);
|
trim(&mut operations);
|
||||||
tracing::trace!("[{}] applied, delta: {}", ext.ext_name(), delta);
|
tracing::trace!("[{}] applied, delta: {}", ext.ext_name(), operations);
|
||||||
new_delta = Some(delta);
|
new_operations = Some(operations);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match new_delta {
|
match new_operations {
|
||||||
None => Err(ErrorBuilder::new(OTErrorCode::ApplyInsertFail).build()),
|
None => Err(ErrorBuilder::new(OTErrorCode::ApplyInsertFail).build()),
|
||||||
Some(new_delta) => Ok(new_delta),
|
Some(new_operations) => Ok(new_operations),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn delete(&self, delta: &TextDelta, interval: Interval) -> Result<TextDelta, OTError> {
|
pub(crate) fn delete(&self, delta: &TextOperations, interval: Interval) -> Result<TextOperations, OTError> {
|
||||||
let mut new_delta = None;
|
let mut new_delta = None;
|
||||||
for ext in &self.delete_exts {
|
for ext in &self.delete_exts {
|
||||||
if let Some(mut delta) = ext.apply(delta, interval) {
|
if let Some(mut delta) = ext.apply(delta, interval) {
|
||||||
@ -59,23 +64,23 @@ impl ViewExtensions {
|
|||||||
|
|
||||||
pub(crate) fn format(
|
pub(crate) fn format(
|
||||||
&self,
|
&self,
|
||||||
delta: &TextDelta,
|
operations: &TextOperations,
|
||||||
attribute: AttributeEntry,
|
attribute: AttributeEntry,
|
||||||
interval: Interval,
|
interval: Interval,
|
||||||
) -> Result<TextDelta, OTError> {
|
) -> Result<TextOperations, OTError> {
|
||||||
let mut new_delta = None;
|
let mut new_operations = None;
|
||||||
for ext in &self.format_exts {
|
for ext in &self.format_exts {
|
||||||
if let Some(mut delta) = ext.apply(delta, interval, &attribute) {
|
if let Some(mut operations) = ext.apply(operations, interval, &attribute) {
|
||||||
trim(&mut delta);
|
trim(&mut operations);
|
||||||
tracing::trace!("[{}] applied, delta: {}", ext.ext_name(), delta);
|
tracing::trace!("[{}] applied, delta: {}", ext.ext_name(), operations);
|
||||||
new_delta = Some(delta);
|
new_operations = Some(operations);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match new_delta {
|
match new_operations {
|
||||||
None => Err(ErrorBuilder::new(OTErrorCode::ApplyFormatFail).build()),
|
None => Err(ErrorBuilder::new(OTErrorCode::ApplyFormatFail).build()),
|
||||||
Some(new_delta) => Ok(new_delta),
|
Some(new_operations) => Ok(new_operations),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
use crate::entities::folder::FolderDelta;
|
use crate::util::make_operations_from_revisions;
|
||||||
use crate::util::make_text_delta_from_revisions;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client_folder::{default_folder_delta, FolderPad},
|
client_folder::{default_folder_operations, FolderPad},
|
||||||
entities::revision::Revision,
|
entities::revision::Revision,
|
||||||
errors::CollaborateResult,
|
errors::CollaborateResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::server_folder::FolderOperations;
|
||||||
use flowy_folder_data_model::revision::{TrashRevision, WorkspaceRevision};
|
use flowy_folder_data_model::revision::{TrashRevision, WorkspaceRevision};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -37,11 +36,11 @@ impl FolderPadBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_with_revisions(self, revisions: Vec<Revision>) -> CollaborateResult<FolderPad> {
|
pub(crate) fn build_with_revisions(self, revisions: Vec<Revision>) -> CollaborateResult<FolderPad> {
|
||||||
let mut folder_delta: FolderDelta = make_text_delta_from_revisions(revisions)?;
|
let mut operations: FolderOperations = make_operations_from_revisions(revisions)?;
|
||||||
if folder_delta.is_empty() {
|
if operations.is_empty() {
|
||||||
folder_delta = default_folder_delta();
|
operations = default_folder_operations();
|
||||||
}
|
}
|
||||||
FolderPad::from_delta(folder_delta)
|
FolderPad::from_operations(operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
use crate::errors::internal_error;
|
use crate::errors::internal_error;
|
||||||
|
use crate::server_folder::FolderOperations;
|
||||||
use crate::util::cal_diff;
|
use crate::util::cal_diff;
|
||||||
use crate::{
|
use crate::{
|
||||||
client_folder::builder::FolderPadBuilder,
|
client_folder::builder::FolderPadBuilder,
|
||||||
entities::{
|
entities::revision::{md5, Revision},
|
||||||
folder::FolderDelta,
|
|
||||||
revision::{md5, Revision},
|
|
||||||
},
|
|
||||||
errors::{CollaborateError, CollaborateResult},
|
errors::{CollaborateError, CollaborateResult},
|
||||||
};
|
};
|
||||||
use flowy_folder_data_model::revision::{AppRevision, FolderRevision, TrashRevision, ViewRevision, WorkspaceRevision};
|
use flowy_folder_data_model::revision::{AppRevision, FolderRevision, TrashRevision, ViewRevision, WorkspaceRevision};
|
||||||
use lib_infra::util::move_vec_element;
|
use lib_infra::util::move_vec_element;
|
||||||
use lib_ot::core::*;
|
use lib_ot::core::*;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub type FolderOperationsBuilder = DeltaBuilder;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct FolderPad {
|
pub struct FolderPad {
|
||||||
folder_rev: FolderRevision,
|
folder_rev: FolderRevision,
|
||||||
delta: FolderDelta,
|
operations: FolderOperations,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FolderPad {
|
impl FolderPad {
|
||||||
@ -33,43 +32,43 @@ impl FolderPad {
|
|||||||
pub fn from_folder_rev(folder_rev: FolderRevision) -> CollaborateResult<Self> {
|
pub fn from_folder_rev(folder_rev: FolderRevision) -> CollaborateResult<Self> {
|
||||||
let json = serde_json::to_string(&folder_rev)
|
let json = serde_json::to_string(&folder_rev)
|
||||||
.map_err(|e| CollaborateError::internal().context(format!("Serialize to folder json str failed: {}", e)))?;
|
.map_err(|e| CollaborateError::internal().context(format!("Serialize to folder json str failed: {}", e)))?;
|
||||||
let delta = DeltaBuilder::new().insert(&json).build();
|
let operations = FolderOperationsBuilder::new().insert(&json).build();
|
||||||
|
|
||||||
Ok(Self { folder_rev, delta })
|
Ok(Self { folder_rev, operations })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
|
pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
|
||||||
FolderPadBuilder::new().build_with_revisions(revisions)
|
FolderPadBuilder::new().build_with_revisions(revisions)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_delta(delta: FolderDelta) -> CollaborateResult<Self> {
|
pub fn from_operations(operations: FolderOperations) -> CollaborateResult<Self> {
|
||||||
// TODO: Reconvert from history if delta.to_str() failed.
|
// TODO: Reconvert from history if delta.to_str() failed.
|
||||||
let content = delta.content()?;
|
let content = operations.content()?;
|
||||||
let mut deserializer = serde_json::Deserializer::from_reader(content.as_bytes());
|
let mut deserializer = serde_json::Deserializer::from_reader(content.as_bytes());
|
||||||
|
|
||||||
let folder_rev = FolderRevision::deserialize(&mut deserializer).map_err(|e| {
|
let folder_rev = FolderRevision::deserialize(&mut deserializer).map_err(|e| {
|
||||||
tracing::error!("Deserialize folder from {} failed", content);
|
tracing::error!("Deserialize folder from {} failed", content);
|
||||||
return CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e));
|
return CollaborateError::internal().context(format!("Deserialize operations to folder failed: {}", e));
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Self { folder_rev, delta })
|
Ok(Self { folder_rev, operations })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delta(&self) -> &FolderDelta {
|
pub fn get_operations(&self) -> &FolderOperations {
|
||||||
&self.delta
|
&self.operations
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_folder(&mut self, delta: FolderDelta) -> CollaborateResult<String> {
|
pub fn reset_folder(&mut self, operations: FolderOperations) -> CollaborateResult<String> {
|
||||||
let folder = FolderPad::from_delta(delta)?;
|
let folder = FolderPad::from_operations(operations)?;
|
||||||
self.folder_rev = folder.folder_rev;
|
self.folder_rev = folder.folder_rev;
|
||||||
self.delta = folder.delta;
|
self.operations = folder.operations;
|
||||||
|
|
||||||
Ok(self.md5())
|
Ok(self.md5())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compose_remote_delta(&mut self, delta: FolderDelta) -> CollaborateResult<String> {
|
pub fn compose_remote_operations(&mut self, operations: FolderOperations) -> CollaborateResult<String> {
|
||||||
let composed_delta = self.delta.compose(&delta)?;
|
let composed_operations = self.operations.compose(&operations)?;
|
||||||
self.reset_folder(composed_delta)
|
self.reset_folder(composed_operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
@ -318,7 +317,7 @@ impl FolderPad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn md5(&self) -> String {
|
pub fn md5(&self) -> String {
|
||||||
md5(&self.delta.json_bytes())
|
md5(&self.operations.json_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_json(&self) -> CollaborateResult<String> {
|
pub fn to_json(&self) -> CollaborateResult<String> {
|
||||||
@ -345,9 +344,12 @@ impl FolderPad {
|
|||||||
let new = self.to_json()?;
|
let new = self.to_json()?;
|
||||||
match cal_diff::<EmptyAttributes>(old, new) {
|
match cal_diff::<EmptyAttributes>(old, new) {
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
Some(delta) => {
|
Some(operations) => {
|
||||||
self.delta = self.delta.compose(&delta)?;
|
self.operations = self.operations.compose(&operations)?;
|
||||||
Ok(Some(FolderChangeset { delta, md5: self.md5() }))
|
Ok(Some(FolderChangeset {
|
||||||
|
operations,
|
||||||
|
md5: self.md5(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -380,9 +382,12 @@ impl FolderPad {
|
|||||||
let new = self.to_json()?;
|
let new = self.to_json()?;
|
||||||
match cal_diff::<EmptyAttributes>(old, new) {
|
match cal_diff::<EmptyAttributes>(old, new) {
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
Some(delta) => {
|
Some(operations) => {
|
||||||
self.delta = self.delta.compose(&delta)?;
|
self.operations = self.operations.compose(&operations)?;
|
||||||
Ok(Some(FolderChangeset { delta, md5: self.md5() }))
|
Ok(Some(FolderChangeset {
|
||||||
|
operations,
|
||||||
|
md5: self.md5(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,58 +433,62 @@ impl FolderPad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_folder_delta() -> FolderDelta {
|
pub fn default_folder_operations() -> FolderOperations {
|
||||||
DeltaBuilder::new().insert(r#"{"workspaces":[],"trash":[]}"#).build()
|
FolderOperationsBuilder::new()
|
||||||
|
.insert(r#"{"workspaces":[],"trash":[]}"#)
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initial_folder_delta(folder_pad: &FolderPad) -> CollaborateResult<FolderDelta> {
|
pub fn initial_folder_operations(folder_pad: &FolderPad) -> CollaborateResult<FolderOperations> {
|
||||||
let json = folder_pad.to_json()?;
|
let json = folder_pad.to_json()?;
|
||||||
let delta = DeltaBuilder::new().insert(&json).build();
|
let operations = FolderOperationsBuilder::new().insert(&json).build();
|
||||||
Ok(delta)
|
Ok(operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for FolderPad {
|
impl std::default::Default for FolderPad {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
FolderPad {
|
FolderPad {
|
||||||
folder_rev: FolderRevision::default(),
|
folder_rev: FolderRevision::default(),
|
||||||
delta: default_folder_delta(),
|
operations: default_folder_operations(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FolderChangeset {
|
pub struct FolderChangeset {
|
||||||
pub delta: FolderDelta,
|
pub operations: FolderOperations,
|
||||||
/// md5: the md5 of the FolderPad's delta after applying the change.
|
/// md5: the md5 of the FolderPad's operations after applying the change.
|
||||||
pub md5: String,
|
pub md5: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#![allow(clippy::all)]
|
#![allow(clippy::all)]
|
||||||
use crate::{client_folder::folder_pad::FolderPad, entities::folder::FolderDelta};
|
use crate::client_folder::folder_pad::FolderPad;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::client_folder::FolderOperationsBuilder;
|
||||||
|
use crate::server_folder::FolderOperations;
|
||||||
use flowy_folder_data_model::revision::{
|
use flowy_folder_data_model::revision::{
|
||||||
AppRevision, FolderRevision, TrashRevision, ViewRevision, WorkspaceRevision,
|
AppRevision, FolderRevision, TrashRevision, ViewRevision, WorkspaceRevision,
|
||||||
};
|
};
|
||||||
use lib_ot::core::{Delta, DeltaBuilder, OperationTransform};
|
use lib_ot::core::{DeltaBuilder, OperationTransform};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_add_workspace() {
|
fn folder_add_workspace() {
|
||||||
let (mut folder, initial_delta, _) = test_folder();
|
let (mut folder, initial_operations, _) = test_folder();
|
||||||
|
|
||||||
let _time = Utc::now();
|
let _time = Utc::now();
|
||||||
let mut workspace_1 = WorkspaceRevision::default();
|
let mut workspace_1 = WorkspaceRevision::default();
|
||||||
workspace_1.name = "My first workspace".to_owned();
|
workspace_1.name = "My first workspace".to_owned();
|
||||||
let delta_1 = folder.create_workspace(workspace_1).unwrap().unwrap().delta;
|
let operations_1 = folder.create_workspace(workspace_1).unwrap().unwrap().operations;
|
||||||
|
|
||||||
let mut workspace_2 = WorkspaceRevision::default();
|
let mut workspace_2 = WorkspaceRevision::default();
|
||||||
workspace_2.name = "My second workspace".to_owned();
|
workspace_2.name = "My second workspace".to_owned();
|
||||||
let delta_2 = folder.create_workspace(workspace_2).unwrap().unwrap().delta;
|
let operations_2 = folder.create_workspace(workspace_2).unwrap().unwrap().operations;
|
||||||
|
|
||||||
let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta_1, delta_2]);
|
let folder_from_operations = make_folder_from_operations(initial_operations, vec![operations_1, operations_2]);
|
||||||
assert_eq!(folder, folder_from_delta);
|
assert_eq!(folder, folder_from_operations);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -498,35 +507,35 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_update_workspace() {
|
fn folder_update_workspace() {
|
||||||
let (mut folder, initial_delta, workspace) = test_folder();
|
let (mut folder, initial_operation, workspace) = test_folder();
|
||||||
assert_folder_equal(
|
assert_folder_equal(
|
||||||
&folder,
|
&folder,
|
||||||
&make_folder_from_delta(initial_delta.clone(), vec![]),
|
&make_folder_from_operations(initial_operation.clone(), vec![]),
|
||||||
r#"{"workspaces":[{"id":"1","name":"😁 my first workspace","desc":"","apps":[],"modified_time":0,"create_time":0}],"trash":[]}"#,
|
r#"{"workspaces":[{"id":"1","name":"😁 my first workspace","desc":"","apps":[],"modified_time":0,"create_time":0}],"trash":[]}"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
let delta = folder
|
let operations = folder
|
||||||
.update_workspace(&workspace.id, Some("☺️ rename workspace️".to_string()), None)
|
.update_workspace(&workspace.id, Some("☺️ rename workspace️".to_string()), None)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.delta;
|
.operations;
|
||||||
|
|
||||||
let folder_from_delta = make_folder_from_delta(initial_delta, vec![delta]);
|
let folder_from_operations = make_folder_from_operations(initial_operation, vec![operations]);
|
||||||
assert_folder_equal(
|
assert_folder_equal(
|
||||||
&folder,
|
&folder,
|
||||||
&folder_from_delta,
|
&folder_from_operations,
|
||||||
r#"{"workspaces":[{"id":"1","name":"☺️ rename workspace️","desc":"","apps":[],"modified_time":0,"create_time":0}],"trash":[]}"#,
|
r#"{"workspaces":[{"id":"1","name":"☺️ rename workspace️","desc":"","apps":[],"modified_time":0,"create_time":0}],"trash":[]}"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_add_app() {
|
fn folder_add_app() {
|
||||||
let (folder, initial_delta, _app) = test_app_folder();
|
let (folder, initial_operations, _app) = test_app_folder();
|
||||||
let folder_from_delta = make_folder_from_delta(initial_delta, vec![]);
|
let folder_from_operations = make_folder_from_operations(initial_operations, vec![]);
|
||||||
assert_eq!(folder, folder_from_delta);
|
assert_eq!(folder, folder_from_operations);
|
||||||
assert_folder_equal(
|
assert_folder_equal(
|
||||||
&folder,
|
&folder,
|
||||||
&folder_from_delta,
|
&folder_from_operations,
|
||||||
r#"{
|
r#"{
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
{
|
{
|
||||||
@ -556,14 +565,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_update_app() {
|
fn folder_update_app() {
|
||||||
let (mut folder, initial_delta, app) = test_app_folder();
|
let (mut folder, initial_operations, app) = test_app_folder();
|
||||||
let delta = folder
|
let operations = folder
|
||||||
.update_app(&app.id, Some("🤪 rename app".to_owned()), None)
|
.update_app(&app.id, Some("🤪 rename app".to_owned()), None)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.delta;
|
.operations;
|
||||||
|
|
||||||
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
|
let new_folder = make_folder_from_operations(initial_operations, vec![operations]);
|
||||||
assert_folder_equal(
|
assert_folder_equal(
|
||||||
&folder,
|
&folder,
|
||||||
&new_folder,
|
&new_folder,
|
||||||
@ -596,9 +605,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_delete_app() {
|
fn folder_delete_app() {
|
||||||
let (mut folder, initial_delta, app) = test_app_folder();
|
let (mut folder, initial_operations, app) = test_app_folder();
|
||||||
let delta = folder.delete_app(&app.id).unwrap().unwrap().delta;
|
let operations = folder.delete_app(&app.id).unwrap().unwrap().operations;
|
||||||
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
|
let new_folder = make_folder_from_operations(initial_operations, vec![operations]);
|
||||||
assert_folder_equal(
|
assert_folder_equal(
|
||||||
&folder,
|
&folder,
|
||||||
&new_folder,
|
&new_folder,
|
||||||
@ -620,10 +629,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_add_view() {
|
fn folder_add_view() {
|
||||||
let (folder, initial_delta, _view) = test_view_folder();
|
let (folder, initial_operations, _view) = test_view_folder();
|
||||||
assert_folder_equal(
|
assert_folder_equal(
|
||||||
&folder,
|
&folder,
|
||||||
&make_folder_from_delta(initial_delta, vec![]),
|
&make_folder_from_operations(initial_operations, vec![]),
|
||||||
r#"
|
r#"
|
||||||
{
|
{
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
@ -666,14 +675,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_update_view() {
|
fn folder_update_view() {
|
||||||
let (mut folder, initial_delta, view) = test_view_folder();
|
let (mut folder, initial_operations, view) = test_view_folder();
|
||||||
let delta = folder
|
let operations = folder
|
||||||
.update_view(&view.id, Some("😦 rename view".to_owned()), None, 123)
|
.update_view(&view.id, Some("😦 rename view".to_owned()), None, 123)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.delta;
|
.operations;
|
||||||
|
|
||||||
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
|
let new_folder = make_folder_from_operations(initial_operations, vec![operations]);
|
||||||
assert_folder_equal(
|
assert_folder_equal(
|
||||||
&folder,
|
&folder,
|
||||||
&new_folder,
|
&new_folder,
|
||||||
@ -718,10 +727,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_delete_view() {
|
fn folder_delete_view() {
|
||||||
let (mut folder, initial_delta, view) = test_view_folder();
|
let (mut folder, initial_operations, view) = test_view_folder();
|
||||||
let delta = folder.delete_view(&view.id).unwrap().unwrap().delta;
|
let operations = folder.delete_view(&view.id).unwrap().unwrap().operations;
|
||||||
|
|
||||||
let new_folder = make_folder_from_delta(initial_delta, vec![delta]);
|
let new_folder = make_folder_from_operations(initial_operations, vec![operations]);
|
||||||
assert_folder_equal(
|
assert_folder_equal(
|
||||||
&folder,
|
&folder,
|
||||||
&new_folder,
|
&new_folder,
|
||||||
@ -754,10 +763,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_add_trash() {
|
fn folder_add_trash() {
|
||||||
let (folder, initial_delta, _trash) = test_trash();
|
let (folder, initial_operations, _trash) = test_trash();
|
||||||
assert_folder_equal(
|
assert_folder_equal(
|
||||||
&folder,
|
&folder,
|
||||||
&make_folder_from_delta(initial_delta, vec![]),
|
&make_folder_from_operations(initial_operations, vec![]),
|
||||||
r#"{
|
r#"{
|
||||||
"workspaces": [],
|
"workspaces": [],
|
||||||
"trash": [
|
"trash": [
|
||||||
@ -776,11 +785,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_delete_trash() {
|
fn folder_delete_trash() {
|
||||||
let (mut folder, initial_delta, trash) = test_trash();
|
let (mut folder, initial_operations, trash) = test_trash();
|
||||||
let delta = folder.delete_trash(Some(vec![trash.id])).unwrap().unwrap().delta;
|
let operations = folder.delete_trash(Some(vec![trash.id])).unwrap().unwrap().operations;
|
||||||
assert_folder_equal(
|
assert_folder_equal(
|
||||||
&folder,
|
&folder,
|
||||||
&make_folder_from_delta(initial_delta, vec![delta]),
|
&make_folder_from_operations(initial_operations, vec![operations]),
|
||||||
r#"{
|
r#"{
|
||||||
"workspaces": [],
|
"workspaces": [],
|
||||||
"trash": []
|
"trash": []
|
||||||
@ -789,10 +798,10 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_folder() -> (FolderPad, FolderDelta, WorkspaceRevision) {
|
fn test_folder() -> (FolderPad, FolderOperations, WorkspaceRevision) {
|
||||||
let folder_rev = FolderRevision::default();
|
let folder_rev = FolderRevision::default();
|
||||||
let folder_json = serde_json::to_string(&folder_rev).unwrap();
|
let folder_json = serde_json::to_string(&folder_rev).unwrap();
|
||||||
let mut delta = DeltaBuilder::new().insert(&folder_json).build();
|
let mut operations = FolderOperationsBuilder::new().insert(&folder_json).build();
|
||||||
|
|
||||||
let mut workspace_rev = WorkspaceRevision::default();
|
let mut workspace_rev = WorkspaceRevision::default();
|
||||||
workspace_rev.name = "😁 my first workspace".to_owned();
|
workspace_rev.name = "😁 my first workspace".to_owned();
|
||||||
@ -800,66 +809,75 @@ mod tests {
|
|||||||
|
|
||||||
let mut folder = FolderPad::from_folder_rev(folder_rev).unwrap();
|
let mut folder = FolderPad::from_folder_rev(folder_rev).unwrap();
|
||||||
|
|
||||||
delta = delta
|
operations = operations
|
||||||
.compose(&folder.create_workspace(workspace_rev.clone()).unwrap().unwrap().delta)
|
.compose(
|
||||||
|
&folder
|
||||||
|
.create_workspace(workspace_rev.clone())
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.operations,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
(folder, delta, workspace_rev)
|
(folder, operations, workspace_rev)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_app_folder() -> (FolderPad, FolderDelta, AppRevision) {
|
fn test_app_folder() -> (FolderPad, FolderOperations, AppRevision) {
|
||||||
let (mut folder_rev, mut initial_delta, workspace) = test_folder();
|
let (mut folder_rev, mut initial_operations, workspace) = test_folder();
|
||||||
let mut app_rev = AppRevision::default();
|
let mut app_rev = AppRevision::default();
|
||||||
app_rev.workspace_id = workspace.id;
|
app_rev.workspace_id = workspace.id;
|
||||||
app_rev.name = "😁 my first app".to_owned();
|
app_rev.name = "😁 my first app".to_owned();
|
||||||
|
|
||||||
initial_delta = initial_delta
|
initial_operations = initial_operations
|
||||||
.compose(&folder_rev.create_app(app_rev.clone()).unwrap().unwrap().delta)
|
.compose(&folder_rev.create_app(app_rev.clone()).unwrap().unwrap().operations)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
(folder_rev, initial_delta, app_rev)
|
(folder_rev, initial_operations, app_rev)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_view_folder() -> (FolderPad, FolderDelta, ViewRevision) {
|
fn test_view_folder() -> (FolderPad, FolderOperations, ViewRevision) {
|
||||||
let (mut folder, mut initial_delta, app) = test_app_folder();
|
let (mut folder, mut initial_operations, app) = test_app_folder();
|
||||||
let mut view_rev = ViewRevision::default();
|
let mut view_rev = ViewRevision::default();
|
||||||
view_rev.app_id = app.id.clone();
|
view_rev.app_id = app.id.clone();
|
||||||
view_rev.name = "🎃 my first view".to_owned();
|
view_rev.name = "🎃 my first view".to_owned();
|
||||||
|
|
||||||
initial_delta = initial_delta
|
initial_operations = initial_operations
|
||||||
.compose(&folder.create_view(view_rev.clone()).unwrap().unwrap().delta)
|
.compose(&folder.create_view(view_rev.clone()).unwrap().unwrap().operations)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
(folder, initial_delta, view_rev)
|
(folder, initial_operations, view_rev)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_trash() -> (FolderPad, FolderDelta, TrashRevision) {
|
fn test_trash() -> (FolderPad, FolderOperations, TrashRevision) {
|
||||||
let folder_rev = FolderRevision::default();
|
let folder_rev = FolderRevision::default();
|
||||||
let folder_json = serde_json::to_string(&folder_rev).unwrap();
|
let folder_json = serde_json::to_string(&folder_rev).unwrap();
|
||||||
let mut delta = DeltaBuilder::new().insert(&folder_json).build();
|
let mut operations = FolderOperationsBuilder::new().insert(&folder_json).build();
|
||||||
|
|
||||||
let mut trash_rev = TrashRevision::default();
|
let mut trash_rev = TrashRevision::default();
|
||||||
trash_rev.name = "🚽 my first trash".to_owned();
|
trash_rev.name = "🚽 my first trash".to_owned();
|
||||||
trash_rev.id = "1".to_owned();
|
trash_rev.id = "1".to_owned();
|
||||||
let mut folder = FolderPad::from_folder_rev(folder_rev).unwrap();
|
let mut folder = FolderPad::from_folder_rev(folder_rev).unwrap();
|
||||||
delta = delta
|
operations = operations
|
||||||
.compose(
|
.compose(
|
||||||
&folder
|
&folder
|
||||||
.create_trash(vec![trash_rev.clone().into()])
|
.create_trash(vec![trash_rev.clone().into()])
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.delta,
|
.operations,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
(folder, delta, trash_rev)
|
(folder, operations, trash_rev)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_folder_from_delta(mut initial_delta: FolderDelta, deltas: Vec<Delta>) -> FolderPad {
|
fn make_folder_from_operations(
|
||||||
for delta in deltas {
|
mut initial_operation: FolderOperations,
|
||||||
initial_delta = initial_delta.compose(&delta).unwrap();
|
operations: Vec<FolderOperations>,
|
||||||
|
) -> FolderPad {
|
||||||
|
for operation in operations {
|
||||||
|
initial_operation = initial_operation.compose(&operation).unwrap();
|
||||||
}
|
}
|
||||||
FolderPad::from_delta(initial_delta).unwrap()
|
FolderPad::from_operations(initial_operation).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_folder_equal(old: &FolderPad, new: &FolderPad, expected: &str) {
|
fn assert_folder_equal(old: &FolderPad, new: &FolderPad, expected: &str) {
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
use crate::entities::revision::{md5, RepeatedRevision, Revision};
|
use crate::entities::revision::{md5, RepeatedRevision, Revision};
|
||||||
use crate::errors::{CollaborateError, CollaborateResult};
|
use crate::errors::{CollaborateError, CollaborateResult};
|
||||||
use crate::util::{cal_diff, make_text_delta_from_revisions};
|
use crate::util::{cal_diff, make_operations_from_revisions};
|
||||||
use flowy_grid_data_model::revision::{
|
use flowy_grid_data_model::revision::{
|
||||||
gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowChangeset, RowRevision,
|
gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowChangeset, RowRevision,
|
||||||
};
|
};
|
||||||
use lib_ot::core::{Delta, DeltaBuilder, EmptyAttributes, OperationTransform};
|
use lib_ot::core::{DeltaBuilder, DeltaOperations, EmptyAttributes, OperationTransform};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub type GridBlockOperations = DeltaOperations<EmptyAttributes>;
|
||||||
|
pub type GridBlockOperationsBuilder = DeltaBuilder;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GridBlockRevisionPad {
|
pub struct GridBlockRevisionPad {
|
||||||
block: GridBlockRevision,
|
block: GridBlockRevision,
|
||||||
delta: Delta,
|
operations: GridBlockOperations,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for GridBlockRevisionPad {
|
impl std::ops::Deref for GridBlockRevisionPad {
|
||||||
@ -42,19 +45,22 @@ impl GridBlockRevisionPad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_delta(delta: Delta) -> CollaborateResult<Self> {
|
pub fn from_operations(operations: GridBlockOperations) -> CollaborateResult<Self> {
|
||||||
let s = delta.content()?;
|
let s = operations.content()?;
|
||||||
let revision: GridBlockRevision = serde_json::from_str(&s).map_err(|e| {
|
let revision: GridBlockRevision = serde_json::from_str(&s).map_err(|e| {
|
||||||
let msg = format!("Deserialize delta to GridBlockRevision failed: {}", e);
|
let msg = format!("Deserialize operations to GridBlockRevision failed: {}", e);
|
||||||
tracing::error!("{}", s);
|
tracing::error!("{}", s);
|
||||||
CollaborateError::internal().context(msg)
|
CollaborateError::internal().context(msg)
|
||||||
})?;
|
})?;
|
||||||
Ok(Self { block: revision, delta })
|
Ok(Self {
|
||||||
|
block: revision,
|
||||||
|
operations,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_revisions(_grid_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
|
pub fn from_revisions(_grid_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
|
||||||
let block_delta: Delta = make_text_delta_from_revisions(revisions)?;
|
let operations: GridBlockOperations = make_operations_from_revisions(revisions)?;
|
||||||
Self::from_delta(block_delta)
|
Self::from_operations(operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self, row), err)]
|
#[tracing::instrument(level = "trace", skip(self, row), err)]
|
||||||
@ -202,12 +208,12 @@ impl GridBlockRevisionPad {
|
|||||||
let new = self.revision_json()?;
|
let new = self.revision_json()?;
|
||||||
match cal_diff::<EmptyAttributes>(old, new) {
|
match cal_diff::<EmptyAttributes>(old, new) {
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
Some(delta) => {
|
Some(operations) => {
|
||||||
tracing::trace!("[GridBlockRevision] Composing delta {}", delta.json_str());
|
tracing::trace!("[GridBlockRevision] Composing operations {}", operations.json_str());
|
||||||
self.delta = self.delta.compose(&delta)?;
|
self.operations = self.operations.compose(&operations)?;
|
||||||
Ok(Some(GridBlockRevisionChangeset {
|
Ok(Some(GridBlockRevisionChangeset {
|
||||||
delta,
|
operations,
|
||||||
md5: md5(&self.delta.json_bytes()),
|
md5: md5(&self.operations.json_bytes()),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,25 +240,25 @@ impl GridBlockRevisionPad {
|
|||||||
.map_err(|e| CollaborateError::internal().context(format!("serial block to json failed: {}", e)))
|
.map_err(|e| CollaborateError::internal().context(format!("serial block to json failed: {}", e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn json_str(&self) -> String {
|
pub fn operations_json_str(&self) -> String {
|
||||||
self.delta.json_str()
|
self.operations.json_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GridBlockRevisionChangeset {
|
pub struct GridBlockRevisionChangeset {
|
||||||
pub delta: Delta,
|
pub operations: GridBlockOperations,
|
||||||
/// md5: the md5 of the grid after applying the change.
|
/// md5: the md5 of the grid after applying the change.
|
||||||
pub md5: String,
|
pub md5: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_grid_block_delta(block_rev: &GridBlockRevision) -> Delta {
|
pub fn make_grid_block_operations(block_rev: &GridBlockRevision) -> GridBlockOperations {
|
||||||
let json = serde_json::to_string(&block_rev).unwrap();
|
let json = serde_json::to_string(&block_rev).unwrap();
|
||||||
DeltaBuilder::new().insert(&json).build()
|
GridBlockOperationsBuilder::new().insert(&json).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_grid_block_revisions(user_id: &str, grid_block_meta_data: &GridBlockRevision) -> RepeatedRevision {
|
pub fn make_grid_block_revisions(user_id: &str, grid_block_meta_data: &GridBlockRevision) -> RepeatedRevision {
|
||||||
let delta = make_grid_block_delta(grid_block_meta_data);
|
let operations = make_grid_block_operations(grid_block_meta_data);
|
||||||
let bytes = delta.json_bytes();
|
let bytes = operations.json_bytes();
|
||||||
let revision = Revision::initial_revision(user_id, &grid_block_meta_data.block_id, bytes);
|
let revision = Revision::initial_revision(user_id, &grid_block_meta_data.block_id, bytes);
|
||||||
revision.into()
|
revision.into()
|
||||||
}
|
}
|
||||||
@ -264,19 +270,19 @@ impl std::default::Default for GridBlockRevisionPad {
|
|||||||
rows: vec![],
|
rows: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let delta = make_grid_block_delta(&block_revision);
|
let operations = make_grid_block_operations(&block_revision);
|
||||||
GridBlockRevisionPad {
|
GridBlockRevisionPad {
|
||||||
block: block_revision,
|
block: block_revision,
|
||||||
delta,
|
operations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::client_grid::GridBlockRevisionPad;
|
use crate::client_grid::{GridBlockOperations, GridBlockRevisionPad};
|
||||||
use flowy_grid_data_model::revision::{RowChangeset, RowRevision};
|
use flowy_grid_data_model::revision::{RowChangeset, RowRevision};
|
||||||
use lib_ot::core::Delta;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -293,7 +299,7 @@ mod tests {
|
|||||||
let change = pad.add_row_rev(row.clone(), None).unwrap().unwrap();
|
let change = pad.add_row_rev(row.clone(), None).unwrap().unwrap();
|
||||||
assert_eq!(pad.rows.first().unwrap().as_ref(), &row);
|
assert_eq!(pad.rows.first().unwrap().as_ref(), &row);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.delta.json_str(),
|
change.operations.json_str(),
|
||||||
r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
|
r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -307,19 +313,19 @@ mod tests {
|
|||||||
|
|
||||||
let change = pad.add_row_rev(row_1.clone(), None).unwrap().unwrap();
|
let change = pad.add_row_rev(row_1.clone(), None).unwrap().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.delta.json_str(),
|
change.operations.json_str(),
|
||||||
r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
|
r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
|
||||||
);
|
);
|
||||||
|
|
||||||
let change = pad.add_row_rev(row_2.clone(), None).unwrap().unwrap();
|
let change = pad.add_row_rev(row_2.clone(), None).unwrap().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.delta.json_str(),
|
change.operations.json_str(),
|
||||||
r#"[{"retain":90},{"insert":",{\"id\":\"2\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
|
r#"[{"retain":90},{"insert":",{\"id\":\"2\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
|
||||||
);
|
);
|
||||||
|
|
||||||
let change = pad.add_row_rev(row_3.clone(), Some("2".to_string())).unwrap().unwrap();
|
let change = pad.add_row_rev(row_3.clone(), Some("2".to_string())).unwrap().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.delta.json_str(),
|
change.operations.json_str(),
|
||||||
r#"[{"retain":157},{"insert":",{\"id\":\"3\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
|
r#"[{"retain":157},{"insert":",{\"id\":\"3\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -373,7 +379,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn block_meta_delete_row() {
|
fn block_meta_delete_row() {
|
||||||
let mut pad = test_pad();
|
let mut pad = test_pad();
|
||||||
let pre_delta_str = pad.json_str();
|
let pre_json_str = pad.operations_json_str();
|
||||||
let row = RowRevision {
|
let row = RowRevision {
|
||||||
id: "1".to_string(),
|
id: "1".to_string(),
|
||||||
block_id: pad.block_id.clone(),
|
block_id: pad.block_id.clone(),
|
||||||
@ -384,9 +390,12 @@ mod tests {
|
|||||||
|
|
||||||
let _ = pad.add_row_rev(row.clone(), None).unwrap().unwrap();
|
let _ = pad.add_row_rev(row.clone(), None).unwrap().unwrap();
|
||||||
let change = pad.delete_rows(vec![Cow::Borrowed(&row.id)]).unwrap().unwrap();
|
let change = pad.delete_rows(vec![Cow::Borrowed(&row.id)]).unwrap().unwrap();
|
||||||
assert_eq!(change.delta.json_str(), r#"[{"retain":24},{"delete":66},{"retain":2}]"#);
|
assert_eq!(
|
||||||
|
change.operations.json_str(),
|
||||||
|
r#"[{"retain":24},{"delete":66},{"retain":2}]"#
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(pad.json_str(), pre_delta_str);
|
assert_eq!(pad.operations_json_str(), pre_json_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -411,7 +420,7 @@ mod tests {
|
|||||||
let change = pad.update_row(changeset).unwrap().unwrap();
|
let change = pad.update_row(changeset).unwrap().unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.delta.json_str(),
|
change.operations.json_str(),
|
||||||
r#"[{"retain":69},{"insert":"10"},{"retain":15},{"insert":"tru"},{"delete":4},{"retain":4}]"#
|
r#"[{"retain":69},{"insert":"10"},{"retain":15},{"insert":"tru"},{"delete":4},{"retain":4}]"#
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -422,7 +431,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_pad() -> GridBlockRevisionPad {
|
fn test_pad() -> GridBlockRevisionPad {
|
||||||
let delta = Delta::from_json(r#"[{"insert":"{\"block_id\":\"1\",\"rows\":[]}"}]"#).unwrap();
|
let operations = GridBlockOperations::from_json(r#"[{"insert":"{\"block_id\":\"1\",\"rows\":[]}"}]"#).unwrap();
|
||||||
GridBlockRevisionPad::from_delta(delta).unwrap()
|
GridBlockRevisionPad::from_operations(operations).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
use crate::entities::revision::{md5, RepeatedRevision, Revision};
|
use crate::entities::revision::{md5, RepeatedRevision, Revision};
|
||||||
use crate::errors::{internal_error, CollaborateError, CollaborateResult};
|
use crate::errors::{internal_error, CollaborateError, CollaborateResult};
|
||||||
use crate::util::{cal_diff, make_text_delta_from_revisions};
|
use crate::util::{cal_diff, make_operations_from_revisions};
|
||||||
use bytes::Bytes;
|
|
||||||
use flowy_grid_data_model::revision::{
|
use flowy_grid_data_model::revision::{
|
||||||
gen_block_id, gen_grid_id, FieldRevision, FieldTypeRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset,
|
gen_block_id, gen_grid_id, FieldRevision, FieldTypeRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset,
|
||||||
GridRevision,
|
GridRevision,
|
||||||
};
|
};
|
||||||
use lib_infra::util::move_vec_element;
|
use lib_infra::util::move_vec_element;
|
||||||
use lib_ot::core::{Delta, DeltaBuilder, EmptyAttributes, OperationTransform};
|
use lib_ot::core::{DeltaBuilder, DeltaOperations, EmptyAttributes, OperationTransform};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub type GridRevisionDelta = Delta;
|
pub type GridOperations = DeltaOperations<EmptyAttributes>;
|
||||||
pub type GridRevisionDeltaBuilder = DeltaBuilder;
|
pub type GridOperationsBuilder = DeltaBuilder;
|
||||||
|
|
||||||
pub struct GridRevisionPad {
|
pub struct GridRevisionPad {
|
||||||
grid_rev: Arc<GridRevision>,
|
grid_rev: Arc<GridRevision>,
|
||||||
delta: GridRevisionDelta,
|
operations: GridOperations,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait JsonDeserializer {
|
pub trait JsonDeserializer {
|
||||||
@ -49,23 +49,23 @@ impl GridRevisionPad {
|
|||||||
(fields, blocks)
|
(fields, blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_delta(delta: GridRevisionDelta) -> CollaborateResult<Self> {
|
pub fn from_operations(operations: GridOperations) -> CollaborateResult<Self> {
|
||||||
let content = delta.content()?;
|
let content = operations.content()?;
|
||||||
let grid: GridRevision = serde_json::from_str(&content).map_err(|e| {
|
let grid: GridRevision = serde_json::from_str(&content).map_err(|e| {
|
||||||
let msg = format!("Deserialize delta to grid failed: {}", e);
|
let msg = format!("Deserialize operations to grid failed: {}", e);
|
||||||
tracing::error!("{}", msg);
|
tracing::error!("{}", msg);
|
||||||
CollaborateError::internal().context(msg)
|
CollaborateError::internal().context(msg)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
grid_rev: Arc::new(grid),
|
grid_rev: Arc::new(grid),
|
||||||
delta,
|
operations,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
|
pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
|
||||||
let grid_delta: GridRevisionDelta = make_text_delta_from_revisions(revisions)?;
|
let operations: GridOperations = make_operations_from_revisions(revisions)?;
|
||||||
Self::from_delta(grid_delta)
|
Self::from_operations(operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||||
@ -293,18 +293,14 @@ impl GridRevisionPad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn md5(&self) -> String {
|
pub fn md5(&self) -> String {
|
||||||
md5(&self.delta.json_bytes())
|
md5(&self.operations.json_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delta_str(&self) -> String {
|
pub fn operations_json_str(&self) -> String {
|
||||||
self.delta.json_str()
|
self.operations.json_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delta_bytes(&self) -> Bytes {
|
pub fn get_fields(&self) -> &[Arc<FieldRevision>] {
|
||||||
self.delta.json_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fields(&self) -> &[Arc<FieldRevision>] {
|
|
||||||
&self.grid_rev.fields
|
&self.grid_rev.fields
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,9 +316,12 @@ impl GridRevisionPad {
|
|||||||
let new = self.json_str()?;
|
let new = self.json_str()?;
|
||||||
match cal_diff::<EmptyAttributes>(old, new) {
|
match cal_diff::<EmptyAttributes>(old, new) {
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
Some(delta) => {
|
Some(operations) => {
|
||||||
self.delta = self.delta.compose(&delta)?;
|
self.operations = self.operations.compose(&operations)?;
|
||||||
Ok(Some(GridRevisionChangeset { delta, md5: self.md5() }))
|
Ok(Some(GridRevisionChangeset {
|
||||||
|
operations,
|
||||||
|
md5: self.md5(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -377,19 +376,19 @@ pub fn make_grid_rev_json_str(grid_revision: &GridRevision) -> CollaborateResult
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct GridRevisionChangeset {
|
pub struct GridRevisionChangeset {
|
||||||
pub delta: GridRevisionDelta,
|
pub operations: GridOperations,
|
||||||
/// md5: the md5 of the grid after applying the change.
|
/// md5: the md5 of the grid after applying the change.
|
||||||
pub md5: String,
|
pub md5: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_grid_delta(grid_rev: &GridRevision) -> GridRevisionDelta {
|
pub fn make_grid_operations(grid_rev: &GridRevision) -> GridOperations {
|
||||||
let json = serde_json::to_string(&grid_rev).unwrap();
|
let json = serde_json::to_string(&grid_rev).unwrap();
|
||||||
DeltaBuilder::new().insert(&json).build()
|
GridOperationsBuilder::new().insert(&json).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_grid_revisions(user_id: &str, grid_rev: &GridRevision) -> RepeatedRevision {
|
pub fn make_grid_revisions(user_id: &str, grid_rev: &GridRevision) -> RepeatedRevision {
|
||||||
let delta = make_grid_delta(grid_rev);
|
let operations = make_grid_operations(grid_rev);
|
||||||
let bytes = delta.json_bytes();
|
let bytes = operations.json_bytes();
|
||||||
let revision = Revision::initial_revision(user_id, &grid_rev.grid_id, bytes);
|
let revision = Revision::initial_revision(user_id, &grid_rev.grid_id, bytes);
|
||||||
revision.into()
|
revision.into()
|
||||||
}
|
}
|
||||||
@ -397,10 +396,10 @@ pub fn make_grid_revisions(user_id: &str, grid_rev: &GridRevision) -> RepeatedRe
|
|||||||
impl std::default::Default for GridRevisionPad {
|
impl std::default::Default for GridRevisionPad {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let grid = GridRevision::new(&gen_grid_id());
|
let grid = GridRevision::new(&gen_grid_id());
|
||||||
let delta = make_grid_delta(&grid);
|
let operations = make_grid_operations(&grid);
|
||||||
GridRevisionPad {
|
GridRevisionPad {
|
||||||
grid_rev: Arc::new(grid),
|
grid_rev: Arc::new(grid),
|
||||||
delta,
|
operations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
use crate::entities::revision::{md5, Revision};
|
use crate::entities::revision::{md5, Revision};
|
||||||
use crate::errors::{internal_error, CollaborateError, CollaborateResult};
|
use crate::errors::{internal_error, CollaborateError, CollaborateResult};
|
||||||
use crate::util::{cal_diff, make_text_delta_from_revisions};
|
use crate::util::{cal_diff, make_operations_from_revisions};
|
||||||
use flowy_grid_data_model::revision::{
|
use flowy_grid_data_model::revision::{
|
||||||
FieldRevision, FieldTypeRevision, FilterConfigurationRevision, FilterConfigurationsByFieldId, GridViewRevision,
|
FieldRevision, FieldTypeRevision, FilterConfigurationRevision, FilterConfigurationsByFieldId, GridViewRevision,
|
||||||
GroupConfigurationRevision, GroupConfigurationsByFieldId, LayoutRevision,
|
GroupConfigurationRevision, GroupConfigurationsByFieldId, LayoutRevision,
|
||||||
};
|
};
|
||||||
use lib_ot::core::{Delta, DeltaBuilder, EmptyAttributes, OperationTransform};
|
use lib_ot::core::{DeltaBuilder, DeltaOperations, EmptyAttributes, OperationTransform};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub type GridViewOperations = DeltaOperations<EmptyAttributes>;
|
||||||
|
pub type GridViewOperationsBuilder = DeltaBuilder;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GridViewRevisionPad {
|
pub struct GridViewRevisionPad {
|
||||||
view: Arc<GridViewRevision>,
|
view: Arc<GridViewRevision>,
|
||||||
delta: Delta,
|
operations: GridViewOperations,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for GridViewRevisionPad {
|
impl std::ops::Deref for GridViewRevisionPad {
|
||||||
@ -28,33 +31,33 @@ impl GridViewRevisionPad {
|
|||||||
pub fn new(grid_id: String, view_id: String, layout: LayoutRevision) -> Self {
|
pub fn new(grid_id: String, view_id: String, layout: LayoutRevision) -> Self {
|
||||||
let view = Arc::new(GridViewRevision::new(grid_id, view_id, layout));
|
let view = Arc::new(GridViewRevision::new(grid_id, view_id, layout));
|
||||||
let json = serde_json::to_string(&view).unwrap();
|
let json = serde_json::to_string(&view).unwrap();
|
||||||
let delta = DeltaBuilder::new().insert(&json).build();
|
let operations = GridViewOperationsBuilder::new().insert(&json).build();
|
||||||
Self { view, delta }
|
Self { view, operations }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_delta(view_id: &str, delta: Delta) -> CollaborateResult<Self> {
|
pub fn from_operations(view_id: &str, operations: GridViewOperations) -> CollaborateResult<Self> {
|
||||||
if delta.is_empty() {
|
if operations.is_empty() {
|
||||||
return Ok(GridViewRevisionPad::new(
|
return Ok(GridViewRevisionPad::new(
|
||||||
view_id.to_owned(),
|
view_id.to_owned(),
|
||||||
view_id.to_owned(),
|
view_id.to_owned(),
|
||||||
LayoutRevision::Table,
|
LayoutRevision::Table,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let s = delta.content()?;
|
let s = operations.content()?;
|
||||||
let view: GridViewRevision = serde_json::from_str(&s).map_err(|e| {
|
let view: GridViewRevision = serde_json::from_str(&s).map_err(|e| {
|
||||||
let msg = format!("Deserialize delta to GridViewRevision failed: {}", e);
|
let msg = format!("Deserialize operations to GridViewRevision failed: {}", e);
|
||||||
tracing::error!("parsing json: {}", s);
|
tracing::error!("parsing json: {}", s);
|
||||||
CollaborateError::internal().context(msg)
|
CollaborateError::internal().context(msg)
|
||||||
})?;
|
})?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
view: Arc::new(view),
|
view: Arc::new(view),
|
||||||
delta,
|
operations,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_revisions(view_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
|
pub fn from_revisions(view_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
|
||||||
let delta: Delta = make_text_delta_from_revisions(revisions)?;
|
let operations: GridViewOperations = make_operations_from_revisions(revisions)?;
|
||||||
Self::from_delta(view_id, delta)
|
Self::from_operations(view_id, operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_groups_by_field_revs(&self, field_revs: &[Arc<FieldRevision>]) -> Option<GroupConfigurationsByFieldId> {
|
pub fn get_groups_by_field_revs(&self, field_revs: &[Arc<FieldRevision>]) -> Option<GroupConfigurationsByFieldId> {
|
||||||
@ -183,10 +186,10 @@ impl GridViewRevisionPad {
|
|||||||
let new = self.json_str()?;
|
let new = self.json_str()?;
|
||||||
match cal_diff::<EmptyAttributes>(old, new) {
|
match cal_diff::<EmptyAttributes>(old, new) {
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
Some(delta) => {
|
Some(operations) => {
|
||||||
self.delta = self.delta.compose(&delta)?;
|
self.operations = self.operations.compose(&operations)?;
|
||||||
let md5 = md5(&self.delta.json_bytes());
|
let md5 = md5(&self.operations.json_bytes());
|
||||||
Ok(Some(GridViewRevisionChangeset { delta, md5 }))
|
Ok(Some(GridViewRevisionChangeset { operations, md5 }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,7 +199,7 @@ impl GridViewRevisionPad {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GridViewRevisionChangeset {
|
pub struct GridViewRevisionChangeset {
|
||||||
pub delta: Delta,
|
pub operations: GridViewOperations,
|
||||||
pub md5: String,
|
pub md5: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +209,7 @@ pub fn make_grid_view_rev_json_str(grid_revision: &GridViewRevision) -> Collabor
|
|||||||
Ok(json)
|
Ok(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_grid_view_delta(grid_view: &GridViewRevision) -> Delta {
|
pub fn make_grid_view_operations(grid_view: &GridViewRevision) -> GridViewOperations {
|
||||||
let json = serde_json::to_string(grid_view).unwrap();
|
let json = serde_json::to_string(grid_view).unwrap();
|
||||||
DeltaBuilder::new().insert(&json).build()
|
GridViewOperationsBuilder::new().insert(&json).build()
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
use flowy_derive::ProtoBuf;
|
use flowy_derive::ProtoBuf;
|
||||||
use lib_ot::core::Delta;
|
|
||||||
|
|
||||||
pub type FolderDelta = Delta;
|
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default, Debug, Clone, Eq, PartialEq)]
|
#[derive(ProtoBuf, Default, Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct FolderInfo {
|
pub struct FolderInfo {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||||
use lib_ot::text_delta::TextDelta;
|
|
||||||
use std::{convert::TryFrom, fmt::Formatter, ops::RangeInclusive};
|
use std::{convert::TryFrom, fmt::Formatter, ops::RangeInclusive};
|
||||||
|
|
||||||
|
pub type RevisionObject = lib_ot::text_delta::TextOperations;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Default, ProtoBuf)]
|
#[derive(PartialEq, Eq, Clone, Default, ProtoBuf)]
|
||||||
pub struct Revision {
|
pub struct Revision {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
@ -12,7 +13,7 @@ pub struct Revision {
|
|||||||
pub rev_id: i64,
|
pub rev_id: i64,
|
||||||
|
|
||||||
#[pb(index = 3)]
|
#[pb(index = 3)]
|
||||||
pub delta_data: Vec<u8>,
|
pub bytes: Vec<u8>,
|
||||||
|
|
||||||
#[pb(index = 4)]
|
#[pb(index = 4)]
|
||||||
pub md5: String,
|
pub md5: String,
|
||||||
@ -47,22 +48,15 @@ impl Revision {
|
|||||||
self.rev_id == 0
|
self.rev_id == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initial_revision(user_id: &str, object_id: &str, delta_data: Bytes) -> Self {
|
pub fn initial_revision(user_id: &str, object_id: &str, bytes: Bytes) -> Self {
|
||||||
let md5 = md5(&delta_data);
|
let md5 = md5(&bytes);
|
||||||
Self::new(object_id, 0, 0, delta_data, user_id, md5)
|
Self::new(object_id, 0, 0, bytes, user_id, md5)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(object_id: &str, base_rev_id: i64, rev_id: i64, bytes: Bytes, user_id: &str, md5: String) -> Revision {
|
||||||
object_id: &str,
|
|
||||||
base_rev_id: i64,
|
|
||||||
rev_id: i64,
|
|
||||||
delta_data: Bytes,
|
|
||||||
user_id: &str,
|
|
||||||
md5: String,
|
|
||||||
) -> Revision {
|
|
||||||
let user_id = user_id.to_owned();
|
let user_id = user_id.to_owned();
|
||||||
let object_id = object_id.to_owned();
|
let object_id = object_id.to_owned();
|
||||||
let delta_data = delta_data.to_vec();
|
let bytes = bytes.to_vec();
|
||||||
let base_rev_id = base_rev_id;
|
let base_rev_id = base_rev_id;
|
||||||
let rev_id = rev_id;
|
let rev_id = rev_id;
|
||||||
|
|
||||||
@ -73,7 +67,7 @@ impl Revision {
|
|||||||
Self {
|
Self {
|
||||||
base_rev_id,
|
base_rev_id,
|
||||||
rev_id,
|
rev_id,
|
||||||
delta_data,
|
bytes,
|
||||||
md5,
|
md5,
|
||||||
object_id,
|
object_id,
|
||||||
ty: RevType::DeprecatedLocal,
|
ty: RevType::DeprecatedLocal,
|
||||||
@ -87,12 +81,12 @@ impl std::fmt::Debug for Revision {
|
|||||||
let _ = f.write_fmt(format_args!("object_id {}, ", self.object_id))?;
|
let _ = f.write_fmt(format_args!("object_id {}, ", self.object_id))?;
|
||||||
let _ = f.write_fmt(format_args!("base_rev_id {}, ", self.base_rev_id))?;
|
let _ = f.write_fmt(format_args!("base_rev_id {}, ", self.base_rev_id))?;
|
||||||
let _ = f.write_fmt(format_args!("rev_id {}, ", self.rev_id))?;
|
let _ = f.write_fmt(format_args!("rev_id {}, ", self.rev_id))?;
|
||||||
match TextDelta::from_bytes(&self.delta_data) {
|
match RevisionObject::from_bytes(&self.bytes) {
|
||||||
Ok(delta) => {
|
Ok(object) => {
|
||||||
let _ = f.write_fmt(format_args!("delta {:?}", delta.json_str()))?;
|
let _ = f.write_fmt(format_args!("object {:?}", object.json_str()))?;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = f.write_fmt(format_args!("delta {:?}", e))?;
|
let _ = f.write_fmt(format_args!("object {:?}", e))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
errors::CollaborateError,
|
errors::CollaborateError,
|
||||||
};
|
};
|
||||||
use flowy_derive::ProtoBuf;
|
use flowy_derive::ProtoBuf;
|
||||||
use lib_ot::{errors::OTError, text_delta::TextDelta};
|
use lib_ot::{errors::OTError, text_delta::TextOperations};
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default, Debug, Clone)]
|
#[derive(ProtoBuf, Default, Debug, Clone)]
|
||||||
pub struct CreateTextBlockParams {
|
pub struct CreateTextBlockParams {
|
||||||
@ -30,8 +30,8 @@ pub struct DocumentPB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentPB {
|
impl DocumentPB {
|
||||||
pub fn delta(&self) -> Result<TextDelta, OTError> {
|
pub fn delta(&self) -> Result<TextOperations, OTError> {
|
||||||
let delta = TextDelta::from_bytes(&self.text)?;
|
let delta = TextOperations::from_bytes(&self.text)?;
|
||||||
Ok(delta)
|
Ok(delta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ impl std::convert::TryFrom<Revision> for DocumentPB {
|
|||||||
.context("Revision's rev_id should be 0 when creating the document"));
|
.context("Revision's rev_id should be 0 when creating the document"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let delta = TextDelta::from_bytes(&revision.delta_data)?;
|
let delta = TextOperations::from_bytes(&revision.bytes)?;
|
||||||
let doc_json = delta.json_str();
|
let doc_json = delta.json_str();
|
||||||
|
|
||||||
Ok(DocumentPB {
|
Ok(DocumentPB {
|
||||||
|
@ -9,4 +9,4 @@ pub mod server_folder;
|
|||||||
pub mod synchronizer;
|
pub mod synchronizer;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
pub use lib_ot::text_delta::TextDelta;
|
pub use lib_ot::text_delta::TextOperations;
|
||||||
|
@ -11,8 +11,8 @@ use async_stream::stream;
|
|||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use lib_infra::future::BoxResultFuture;
|
use lib_infra::future::BoxResultFuture;
|
||||||
use lib_ot::core::Attributes;
|
use lib_ot::core::AttributeHashMap;
|
||||||
use lib_ot::text_delta::TextDelta;
|
use lib_ot::text_delta::TextOperations;
|
||||||
use std::{collections::HashMap, fmt::Debug, sync::Arc};
|
use std::{collections::HashMap, fmt::Debug, sync::Arc};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::{mpsc, oneshot, RwLock},
|
sync::{mpsc, oneshot, RwLock},
|
||||||
@ -199,7 +199,7 @@ impl std::ops::Drop for ServerDocumentManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type DocumentRevisionSynchronizer = RevisionSynchronizer<Attributes>;
|
type DocumentRevisionSynchronizer = RevisionSynchronizer<AttributeHashMap>;
|
||||||
|
|
||||||
struct OpenDocumentHandler {
|
struct OpenDocumentHandler {
|
||||||
doc_id: String,
|
doc_id: String,
|
||||||
@ -213,8 +213,8 @@ impl OpenDocumentHandler {
|
|||||||
let (sender, receiver) = mpsc::channel(1000);
|
let (sender, receiver) = mpsc::channel(1000);
|
||||||
let users = DashMap::new();
|
let users = DashMap::new();
|
||||||
|
|
||||||
let delta = TextDelta::from_bytes(&doc.text)?;
|
let operations = TextOperations::from_bytes(&doc.text)?;
|
||||||
let sync_object = ServerDocument::from_delta(&doc_id, delta);
|
let sync_object = ServerDocument::from_operations(&doc_id, operations);
|
||||||
let synchronizer = Arc::new(DocumentRevisionSynchronizer::new(doc.rev_id, sync_object, persistence));
|
let synchronizer = Arc::new(DocumentRevisionSynchronizer::new(doc.rev_id, sync_object, persistence));
|
||||||
|
|
||||||
let queue = DocumentCommandRunner::new(&doc.block_id, receiver, synchronizer);
|
let queue = DocumentCommandRunner::new(&doc.block_id, receiver, synchronizer);
|
||||||
|
@ -1,45 +1,49 @@
|
|||||||
use crate::{client_document::InitialDocumentText, errors::CollaborateError, synchronizer::RevisionSyncObject};
|
use crate::synchronizer::RevisionOperations;
|
||||||
use lib_ot::{core::*, text_delta::TextDelta};
|
use crate::{client_document::InitialDocumentContent, errors::CollaborateError, synchronizer::RevisionSyncObject};
|
||||||
|
use lib_ot::{core::*, text_delta::TextOperations};
|
||||||
|
|
||||||
pub struct ServerDocument {
|
pub struct ServerDocument {
|
||||||
doc_id: String,
|
document_id: String,
|
||||||
delta: TextDelta,
|
operations: TextOperations,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerDocument {
|
impl ServerDocument {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn new<C: InitialDocumentText>(doc_id: &str) -> Self {
|
pub fn new<C: InitialDocumentContent>(doc_id: &str) -> Self {
|
||||||
Self::from_delta(doc_id, C::initial_delta())
|
let operations = TextOperations::from_json(&C::json_str()).unwrap();
|
||||||
|
Self::from_operations(doc_id, operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_delta(doc_id: &str, delta: TextDelta) -> Self {
|
pub fn from_operations(document_id: &str, operations: TextOperations) -> Self {
|
||||||
let doc_id = doc_id.to_owned();
|
let document_id = document_id.to_owned();
|
||||||
ServerDocument { doc_id, delta }
|
ServerDocument {
|
||||||
|
document_id,
|
||||||
|
operations,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RevisionSyncObject<Attributes> for ServerDocument {
|
impl RevisionSyncObject<AttributeHashMap> for ServerDocument {
|
||||||
fn id(&self) -> &str {
|
fn object_id(&self) -> &str {
|
||||||
&self.doc_id
|
&self.document_id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compose(&mut self, other: &TextDelta) -> Result<(), CollaborateError> {
|
fn object_json(&self) -> String {
|
||||||
// tracing::trace!("{} compose {}", &self.delta.to_json(), other.to_json());
|
self.operations.json_str()
|
||||||
let new_delta = self.delta.compose(other)?;
|
}
|
||||||
self.delta = new_delta;
|
|
||||||
|
fn compose(&mut self, other: &TextOperations) -> Result<(), CollaborateError> {
|
||||||
|
let operations = self.operations.compose(other)?;
|
||||||
|
self.operations = operations;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform(&self, other: &TextDelta) -> Result<(TextDelta, TextDelta), CollaborateError> {
|
fn transform(&self, other: &TextOperations) -> Result<(TextOperations, TextOperations), CollaborateError> {
|
||||||
let value = self.delta.transform(other)?;
|
let value = self.operations.transform(other)?;
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_json(&self) -> String {
|
fn set_operations(&mut self, operations: RevisionOperations<AttributeHashMap>) {
|
||||||
self.delta.json_str()
|
self.operations = operations;
|
||||||
}
|
|
||||||
|
|
||||||
fn set_delta(&mut self, new_delta: Operations<Attributes>) {
|
|
||||||
self.delta = new_delta;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
use crate::entities::revision::{RepeatedRevision, Revision};
|
use crate::entities::revision::{RepeatedRevision, Revision};
|
||||||
|
use crate::server_folder::folder_pad::{FolderOperations, FolderRevisionSynchronizer};
|
||||||
use crate::{
|
use crate::{
|
||||||
entities::{
|
entities::{folder::FolderInfo, ws_data::ServerRevisionWSDataBuilder},
|
||||||
folder::{FolderDelta, FolderInfo},
|
|
||||||
ws_data::ServerRevisionWSDataBuilder,
|
|
||||||
},
|
|
||||||
errors::{internal_error, CollaborateError, CollaborateResult},
|
errors::{internal_error, CollaborateError, CollaborateResult},
|
||||||
protobuf::ClientRevisionWSData,
|
protobuf::ClientRevisionWSData,
|
||||||
server_folder::folder_pad::ServerFolder,
|
server_folder::folder_pad::ServerFolder,
|
||||||
synchronizer::{RevisionSyncPersistence, RevisionSyncResponse, RevisionSynchronizer, RevisionUser},
|
synchronizer::{RevisionSyncPersistence, RevisionSyncResponse, RevisionUser},
|
||||||
util::rev_id_from_str,
|
util::rev_id_from_str,
|
||||||
};
|
};
|
||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use lib_infra::future::BoxResultFuture;
|
use lib_infra::future::BoxResultFuture;
|
||||||
use lib_ot::core::EmptyAttributes;
|
|
||||||
use std::{collections::HashMap, fmt::Debug, sync::Arc};
|
use std::{collections::HashMap, fmt::Debug, sync::Arc};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::{mpsc, oneshot, RwLock},
|
sync::{mpsc, oneshot, RwLock},
|
||||||
@ -188,8 +185,6 @@ impl ServerFolderManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type FolderRevisionSynchronizer = RevisionSynchronizer<EmptyAttributes>;
|
|
||||||
|
|
||||||
struct OpenFolderHandler {
|
struct OpenFolderHandler {
|
||||||
folder_id: String,
|
folder_id: String,
|
||||||
sender: mpsc::Sender<FolderCommand>,
|
sender: mpsc::Sender<FolderCommand>,
|
||||||
@ -199,8 +194,8 @@ impl OpenFolderHandler {
|
|||||||
fn new(folder_info: FolderInfo, persistence: Arc<dyn FolderCloudPersistence>) -> CollaborateResult<Self> {
|
fn new(folder_info: FolderInfo, persistence: Arc<dyn FolderCloudPersistence>) -> CollaborateResult<Self> {
|
||||||
let (sender, receiver) = mpsc::channel(1000);
|
let (sender, receiver) = mpsc::channel(1000);
|
||||||
let folder_id = folder_info.folder_id.clone();
|
let folder_id = folder_info.folder_id.clone();
|
||||||
let delta = FolderDelta::from_bytes(&folder_info.text)?;
|
let operations = FolderOperations::from_bytes(&folder_info.text)?;
|
||||||
let sync_object = ServerFolder::from_delta(&folder_id, delta);
|
let sync_object = ServerFolder::from_operations(&folder_id, operations);
|
||||||
let synchronizer = Arc::new(FolderRevisionSynchronizer::new(
|
let synchronizer = Arc::new(FolderRevisionSynchronizer::new(
|
||||||
folder_info.rev_id,
|
folder_info.rev_id,
|
||||||
sync_object,
|
sync_object,
|
||||||
|
@ -1,41 +1,45 @@
|
|||||||
use crate::{entities::folder::FolderDelta, errors::CollaborateError, synchronizer::RevisionSyncObject};
|
use crate::synchronizer::{RevisionOperations, RevisionSynchronizer};
|
||||||
use lib_ot::core::{Delta, EmptyAttributes, OperationTransform};
|
use crate::{errors::CollaborateError, synchronizer::RevisionSyncObject};
|
||||||
|
use lib_ot::core::{DeltaOperations, EmptyAttributes, OperationTransform};
|
||||||
|
|
||||||
|
pub type FolderRevisionSynchronizer = RevisionSynchronizer<EmptyAttributes>;
|
||||||
|
pub type FolderOperations = DeltaOperations<EmptyAttributes>;
|
||||||
|
|
||||||
pub struct ServerFolder {
|
pub struct ServerFolder {
|
||||||
folder_id: String,
|
folder_id: String,
|
||||||
delta: FolderDelta,
|
operations: FolderOperations,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerFolder {
|
impl ServerFolder {
|
||||||
pub fn from_delta(folder_id: &str, delta: FolderDelta) -> Self {
|
pub fn from_operations(folder_id: &str, operations: FolderOperations) -> Self {
|
||||||
Self {
|
Self {
|
||||||
folder_id: folder_id.to_owned(),
|
folder_id: folder_id.to_owned(),
|
||||||
delta,
|
operations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RevisionSyncObject<EmptyAttributes> for ServerFolder {
|
impl RevisionSyncObject<EmptyAttributes> for ServerFolder {
|
||||||
fn id(&self) -> &str {
|
fn object_id(&self) -> &str {
|
||||||
&self.folder_id
|
&self.folder_id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compose(&mut self, other: &Delta) -> Result<(), CollaborateError> {
|
fn object_json(&self) -> String {
|
||||||
let new_delta = self.delta.compose(other)?;
|
self.operations.json_str()
|
||||||
self.delta = new_delta;
|
}
|
||||||
|
|
||||||
|
fn compose(&mut self, other: &FolderOperations) -> Result<(), CollaborateError> {
|
||||||
|
let operations = self.operations.compose(other)?;
|
||||||
|
self.operations = operations;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform(&self, other: &Delta) -> Result<(Delta, Delta), CollaborateError> {
|
fn transform(&self, other: &FolderOperations) -> Result<(FolderOperations, FolderOperations), CollaborateError> {
|
||||||
let value = self.delta.transform(other)?;
|
let value = self.operations.transform(other)?;
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_json(&self) -> String {
|
fn set_operations(&mut self, operations: RevisionOperations<EmptyAttributes>) {
|
||||||
self.delta.json_str()
|
self.operations = operations;
|
||||||
}
|
|
||||||
|
|
||||||
fn set_delta(&mut self, new_delta: Delta) {
|
|
||||||
self.delta = new_delta;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,3 +2,4 @@ mod folder_manager;
|
|||||||
mod folder_pad;
|
mod folder_pad;
|
||||||
|
|
||||||
pub use folder_manager::*;
|
pub use folder_manager::*;
|
||||||
|
pub use folder_pad::*;
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
util::*,
|
util::*,
|
||||||
};
|
};
|
||||||
use lib_infra::future::BoxResultFuture;
|
use lib_infra::future::BoxResultFuture;
|
||||||
use lib_ot::core::{OperationAttributes, Operations};
|
use lib_ot::core::{DeltaOperations, OperationAttributes};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use std::{
|
use std::{
|
||||||
@ -22,6 +22,14 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub type RevisionOperations<Attribute> = DeltaOperations<Attribute>;
|
||||||
|
|
||||||
|
pub trait RevisionOperations2<Attribute>: Send + Sync {
|
||||||
|
fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Result<Self, CollaborateError>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait RevisionUser: Send + Sync + Debug {
|
pub trait RevisionUser: Send + Sync + Debug {
|
||||||
fn user_id(&self) -> String;
|
fn user_id(&self) -> String;
|
||||||
fn receive(&self, resp: RevisionSyncResponse);
|
fn receive(&self, resp: RevisionSyncResponse);
|
||||||
@ -43,12 +51,19 @@ pub trait RevisionSyncPersistence: Send + Sync + 'static {
|
|||||||
) -> BoxResultFuture<(), CollaborateError>;
|
) -> BoxResultFuture<(), CollaborateError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RevisionSyncObject<T: OperationAttributes>: Send + Sync + 'static {
|
pub trait RevisionSyncObject<Attribute: OperationAttributes>: Send + Sync + 'static {
|
||||||
fn id(&self) -> &str;
|
fn object_id(&self) -> &str;
|
||||||
fn compose(&mut self, other: &Operations<T>) -> Result<(), CollaborateError>;
|
|
||||||
fn transform(&self, other: &Operations<T>) -> Result<(Operations<T>, Operations<T>), CollaborateError>;
|
fn object_json(&self) -> String;
|
||||||
fn to_json(&self) -> String;
|
|
||||||
fn set_delta(&mut self, new_delta: Operations<T>);
|
fn compose(&mut self, other: &RevisionOperations<Attribute>) -> Result<(), CollaborateError>;
|
||||||
|
|
||||||
|
fn transform(
|
||||||
|
&self,
|
||||||
|
other: &RevisionOperations<Attribute>,
|
||||||
|
) -> Result<(RevisionOperations<Attribute>, RevisionOperations<Attribute>), CollaborateError>;
|
||||||
|
|
||||||
|
fn set_operations(&mut self, operations: RevisionOperations<Attribute>);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum RevisionSyncResponse {
|
pub enum RevisionSyncResponse {
|
||||||
@ -57,25 +72,25 @@ pub enum RevisionSyncResponse {
|
|||||||
Ack(ServerRevisionWSData),
|
Ack(ServerRevisionWSData),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RevisionSynchronizer<T: OperationAttributes> {
|
pub struct RevisionSynchronizer<Attribute: OperationAttributes> {
|
||||||
object_id: String,
|
object_id: String,
|
||||||
rev_id: AtomicI64,
|
rev_id: AtomicI64,
|
||||||
object: Arc<RwLock<dyn RevisionSyncObject<T>>>,
|
object: Arc<RwLock<dyn RevisionSyncObject<Attribute>>>,
|
||||||
persistence: Arc<dyn RevisionSyncPersistence>,
|
persistence: Arc<dyn RevisionSyncPersistence>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> RevisionSynchronizer<T>
|
impl<Attribute> RevisionSynchronizer<Attribute>
|
||||||
where
|
where
|
||||||
T: OperationAttributes + DeserializeOwned + serde::Serialize + 'static,
|
Attribute: OperationAttributes + DeserializeOwned + serde::Serialize + 'static,
|
||||||
{
|
{
|
||||||
pub fn new<S, P>(rev_id: i64, sync_object: S, persistence: P) -> RevisionSynchronizer<T>
|
pub fn new<S, P>(rev_id: i64, sync_object: S, persistence: P) -> RevisionSynchronizer<Attribute>
|
||||||
where
|
where
|
||||||
S: RevisionSyncObject<T>,
|
S: RevisionSyncObject<Attribute>,
|
||||||
P: RevisionSyncPersistence,
|
P: RevisionSyncPersistence,
|
||||||
{
|
{
|
||||||
let object = Arc::new(RwLock::new(sync_object));
|
let object = Arc::new(RwLock::new(sync_object));
|
||||||
let persistence = Arc::new(persistence);
|
let persistence = Arc::new(persistence);
|
||||||
let object_id = object.read().id().to_owned();
|
let object_id = object.read().object_id().to_owned();
|
||||||
RevisionSynchronizer {
|
RevisionSynchronizer {
|
||||||
object_id,
|
object_id,
|
||||||
rev_id: AtomicI64::new(rev_id),
|
rev_id: AtomicI64::new(rev_id),
|
||||||
@ -117,7 +132,7 @@ where
|
|||||||
}
|
}
|
||||||
let _ = self.persistence.save_revisions(repeated_revision).await?;
|
let _ = self.persistence.save_revisions(repeated_revision).await?;
|
||||||
} else {
|
} else {
|
||||||
// The server delta is outdated, pull the missing revision from the client.
|
// The server ops is outdated, pull the missing revision from the client.
|
||||||
let range = RevisionRange {
|
let range = RevisionRange {
|
||||||
start: server_rev_id,
|
start: server_rev_id,
|
||||||
end: first_revision.rev_id,
|
end: first_revision.rev_id,
|
||||||
@ -131,9 +146,9 @@ where
|
|||||||
tracing::trace!("Applied {} revision rev_id is the same as cur_rev_id", self.object_id);
|
tracing::trace!("Applied {} revision rev_id is the same as cur_rev_id", self.object_id);
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
// The client delta is outdated. Transform the client revision delta and then
|
// The client ops is outdated. Transform the client revision ops and then
|
||||||
// send the prime delta to the client. Client should compose the this prime
|
// send the prime ops to the client. Client should compose the this prime
|
||||||
// delta.
|
// ops.
|
||||||
let from_rev_id = first_revision.rev_id;
|
let from_rev_id = first_revision.rev_id;
|
||||||
let to_rev_id = server_base_rev_id;
|
let to_rev_id = server_base_rev_id;
|
||||||
let _ = self.push_revisions_to_user(user, from_rev_id, to_rev_id).await;
|
let _ = self.push_revisions_to_user(user, from_rev_id, to_rev_id).await;
|
||||||
@ -153,9 +168,9 @@ where
|
|||||||
}
|
}
|
||||||
Ordering::Equal => tracing::trace!("{} is up to date.", object_id),
|
Ordering::Equal => tracing::trace!("{} is up to date.", object_id),
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
// The client delta is outdated. Transform the client revision delta and then
|
// The client ops is outdated. Transform the client revision ops and then
|
||||||
// send the prime delta to the client. Client should compose the this prime
|
// send the prime ops to the client. Client should compose the this prime
|
||||||
// delta.
|
// ops.
|
||||||
let from_rev_id = client_rev_id;
|
let from_rev_id = client_rev_id;
|
||||||
let to_rev_id = server_rev_id;
|
let to_rev_id = server_rev_id;
|
||||||
tracing::trace!("Push revisions to user");
|
tracing::trace!("Push revisions to user");
|
||||||
@ -171,40 +186,43 @@ where
|
|||||||
tracing::Span::current().record("object_id", &object_id.as_str());
|
tracing::Span::current().record("object_id", &object_id.as_str());
|
||||||
let revisions: Vec<Revision> = repeated_revision.clone().into_inner();
|
let revisions: Vec<Revision> = repeated_revision.clone().into_inner();
|
||||||
let (_, rev_id) = pair_rev_id_from_revision_pbs(&revisions);
|
let (_, rev_id) = pair_rev_id_from_revision_pbs(&revisions);
|
||||||
let delta = make_delta_from_revision_pb(revisions)?;
|
let operations = make_operations_from_revisions(revisions)?;
|
||||||
let _ = self.persistence.reset_object(&object_id, repeated_revision).await?;
|
let _ = self.persistence.reset_object(&object_id, repeated_revision).await?;
|
||||||
self.object.write().set_delta(delta);
|
self.object.write().set_operations(operations);
|
||||||
let _ = self.rev_id.fetch_update(SeqCst, SeqCst, |_e| Some(rev_id));
|
let _ = self.rev_id.fetch_update(SeqCst, SeqCst, |_e| Some(rev_id));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn object_json(&self) -> String {
|
pub fn object_json(&self) -> String {
|
||||||
self.object.read().to_json()
|
self.object.read().object_json()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compose_revision(&self, revision: &Revision) -> Result<(), CollaborateError> {
|
fn compose_revision(&self, revision: &Revision) -> Result<(), CollaborateError> {
|
||||||
let delta = Operations::<T>::from_bytes(&revision.delta_data)?;
|
let operations = RevisionOperations::<Attribute>::from_bytes(&revision.bytes)?;
|
||||||
let _ = self.compose_delta(delta)?;
|
let _ = self.compose_operations(operations)?;
|
||||||
let _ = self.rev_id.fetch_update(SeqCst, SeqCst, |_e| Some(revision.rev_id));
|
let _ = self.rev_id.fetch_update(SeqCst, SeqCst, |_e| Some(revision.rev_id));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self, revision))]
|
#[tracing::instrument(level = "debug", skip(self, revision))]
|
||||||
fn transform_revision(&self, revision: &RevisionPB) -> Result<(Operations<T>, Operations<T>), CollaborateError> {
|
fn transform_revision(
|
||||||
let cli_delta = Operations::<T>::from_bytes(&revision.delta_data)?;
|
&self,
|
||||||
let result = self.object.read().transform(&cli_delta)?;
|
revision: &RevisionPB,
|
||||||
|
) -> Result<(RevisionOperations<Attribute>, RevisionOperations<Attribute>), CollaborateError> {
|
||||||
|
let client_operations = RevisionOperations::<Attribute>::from_bytes(&revision.bytes)?;
|
||||||
|
let result = self.object.read().transform(&client_operations)?;
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compose_delta(&self, delta: Operations<T>) -> Result<(), CollaborateError> {
|
fn compose_operations(&self, operations: RevisionOperations<Attribute>) -> Result<(), CollaborateError> {
|
||||||
if delta.is_empty() {
|
if operations.is_empty() {
|
||||||
log::warn!("Composed delta is empty");
|
log::warn!("Composed operations is empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.object.try_write_for(Duration::from_millis(300)) {
|
match self.object.try_write_for(Duration::from_millis(300)) {
|
||||||
None => log::error!("Failed to acquire write lock of object"),
|
None => log::error!("Failed to acquire write lock of object"),
|
||||||
Some(mut write_guard) => {
|
Some(mut write_guard) => {
|
||||||
let _ = write_guard.compose(&delta)?;
|
let _ = write_guard.compose(&operations)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
|
use crate::server_folder::FolderOperations;
|
||||||
use crate::{
|
use crate::{
|
||||||
entities::{
|
entities::{
|
||||||
folder::{FolderDelta, FolderInfo},
|
folder::FolderInfo,
|
||||||
revision::{RepeatedRevision, Revision},
|
revision::{RepeatedRevision, Revision},
|
||||||
text_block::DocumentPB,
|
text_block::DocumentPB,
|
||||||
},
|
},
|
||||||
errors::{CollaborateError, CollaborateResult},
|
errors::{CollaborateError, CollaborateResult},
|
||||||
};
|
};
|
||||||
use dissimilar::Chunk;
|
use dissimilar::Chunk;
|
||||||
use lib_ot::core::{Delta, EmptyAttributes, OTString, OperationAttributes, OperationBuilder};
|
use lib_ot::core::{OTString, OperationAttributes, OperationBuilder};
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{OperationTransform, Operations, NEW_LINE, WHITESPACE},
|
core::{DeltaOperations, OperationTransform, NEW_LINE, WHITESPACE},
|
||||||
text_delta::TextDelta,
|
text_delta::TextOperations,
|
||||||
};
|
};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use std::sync::atomic::{AtomicI64, Ordering::SeqCst};
|
use std::sync::atomic::{AtomicI64, Ordering::SeqCst};
|
||||||
@ -62,42 +63,24 @@ impl RevIdCounter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(revisions), err)]
|
#[tracing::instrument(level = "trace", skip(revisions), err)]
|
||||||
pub fn make_delta_from_revisions<T>(revisions: Vec<Revision>) -> CollaborateResult<Operations<T>>
|
pub fn make_operations_from_revisions<T>(revisions: Vec<Revision>) -> CollaborateResult<DeltaOperations<T>>
|
||||||
where
|
where
|
||||||
T: OperationAttributes + DeserializeOwned,
|
T: OperationAttributes + DeserializeOwned,
|
||||||
{
|
{
|
||||||
let mut delta = Operations::<T>::new();
|
let mut new_operations = DeltaOperations::<T>::new();
|
||||||
for revision in revisions {
|
for revision in revisions {
|
||||||
if revision.delta_data.is_empty() {
|
if revision.bytes.is_empty() {
|
||||||
tracing::warn!("revision delta_data is empty");
|
tracing::warn!("revision delta_data is empty");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let revision_delta = Operations::<T>::from_bytes(revision.delta_data).map_err(|e| {
|
let operations = DeltaOperations::<T>::from_bytes(revision.bytes).map_err(|e| {
|
||||||
let err_msg = format!("Deserialize remote revision failed: {:?}", e);
|
let err_msg = format!("Deserialize remote revision failed: {:?}", e);
|
||||||
CollaborateError::internal().context(err_msg)
|
CollaborateError::internal().context(err_msg)
|
||||||
})?;
|
})?;
|
||||||
delta = delta.compose(&revision_delta)?;
|
new_operations = new_operations.compose(&operations)?;
|
||||||
}
|
}
|
||||||
Ok(delta)
|
Ok(new_operations)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_text_delta_from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Delta> {
|
|
||||||
make_delta_from_revisions::<EmptyAttributes>(revisions)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_delta_from_revision_pb<T>(revisions: Vec<Revision>) -> CollaborateResult<Operations<T>>
|
|
||||||
where
|
|
||||||
T: OperationAttributes + DeserializeOwned,
|
|
||||||
{
|
|
||||||
let mut new_delta = Operations::<T>::new();
|
|
||||||
for revision in revisions {
|
|
||||||
let delta = Operations::<T>::from_bytes(revision.delta_data).map_err(|e| {
|
|
||||||
let err_msg = format!("Deserialize remote revision failed: {:?}", e);
|
|
||||||
CollaborateError::internal().context(err_msg)
|
|
||||||
})?;
|
|
||||||
new_delta = new_delta.compose(&delta)?;
|
|
||||||
}
|
|
||||||
Ok(new_delta)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pair_rev_id_from_revision_pbs(revisions: &[Revision]) -> (i64, i64) {
|
pub fn pair_rev_id_from_revision_pbs(revisions: &[Revision]) -> (i64, i64) {
|
||||||
@ -140,16 +123,16 @@ pub fn make_folder_from_revisions_pb(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut folder_delta = FolderDelta::new();
|
let mut folder_delta = FolderOperations::new();
|
||||||
let mut base_rev_id = 0;
|
let mut base_rev_id = 0;
|
||||||
let mut rev_id = 0;
|
let mut rev_id = 0;
|
||||||
for revision in revisions {
|
for revision in revisions {
|
||||||
base_rev_id = revision.base_rev_id;
|
base_rev_id = revision.base_rev_id;
|
||||||
rev_id = revision.rev_id;
|
rev_id = revision.rev_id;
|
||||||
if revision.delta_data.is_empty() {
|
if revision.bytes.is_empty() {
|
||||||
tracing::warn!("revision delta_data is empty");
|
tracing::warn!("revision delta_data is empty");
|
||||||
}
|
}
|
||||||
let delta = FolderDelta::from_bytes(revision.delta_data)?;
|
let delta = FolderOperations::from_bytes(revision.bytes)?;
|
||||||
folder_delta = folder_delta.compose(&delta)?;
|
folder_delta = folder_delta.compose(&delta)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,18 +155,18 @@ pub fn make_document_from_revision_pbs(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut delta = TextDelta::new();
|
let mut delta = TextOperations::new();
|
||||||
let mut base_rev_id = 0;
|
let mut base_rev_id = 0;
|
||||||
let mut rev_id = 0;
|
let mut rev_id = 0;
|
||||||
for revision in revisions {
|
for revision in revisions {
|
||||||
base_rev_id = revision.base_rev_id;
|
base_rev_id = revision.base_rev_id;
|
||||||
rev_id = revision.rev_id;
|
rev_id = revision.rev_id;
|
||||||
|
|
||||||
if revision.delta_data.is_empty() {
|
if revision.bytes.is_empty() {
|
||||||
tracing::warn!("revision delta_data is empty");
|
tracing::warn!("revision delta_data is empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_delta = TextDelta::from_bytes(revision.delta_data)?;
|
let new_delta = TextOperations::from_bytes(revision.bytes)?;
|
||||||
delta = delta.compose(&new_delta)?;
|
delta = delta.compose(&new_delta)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +189,7 @@ pub fn rev_id_from_str(s: &str) -> Result<i64, CollaborateError> {
|
|||||||
Ok(rev_id)
|
Ok(rev_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cal_diff<T: OperationAttributes>(old: String, new: String) -> Option<Operations<T>> {
|
pub fn cal_diff<T: OperationAttributes>(old: String, new: String) -> Option<DeltaOperations<T>> {
|
||||||
let chunks = dissimilar::diff(&old, &new);
|
let chunks = dissimilar::diff(&old, &new);
|
||||||
let mut delta_builder = OperationBuilder::<T>::new();
|
let mut delta_builder = OperationBuilder::<T>::new();
|
||||||
for chunk in &chunks {
|
for chunk in &chunks {
|
||||||
|
@ -5,8 +5,6 @@ use std::collections::HashMap;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
pub type AttributeMap = HashMap<AttributeKey, AttributeValue>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AttributeEntry {
|
pub struct AttributeEntry {
|
||||||
pub key: AttributeKey,
|
pub key: AttributeKey,
|
||||||
@ -20,37 +18,37 @@ impl AttributeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<AttributeEntry> for Attributes {
|
impl std::convert::From<AttributeEntry> for AttributeHashMap {
|
||||||
fn from(entry: AttributeEntry) -> Self {
|
fn from(entry: AttributeEntry) -> Self {
|
||||||
let mut attributes = Attributes::new();
|
let mut attributes = AttributeHashMap::new();
|
||||||
attributes.insert_entry(entry);
|
attributes.insert_entry(entry);
|
||||||
attributes
|
attributes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
|
#[derive(Default, Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
|
||||||
pub struct Attributes(AttributeMap);
|
pub struct AttributeHashMap(HashMap<AttributeKey, AttributeValue>);
|
||||||
|
|
||||||
impl std::ops::Deref for Attributes {
|
impl std::ops::Deref for AttributeHashMap {
|
||||||
type Target = AttributeMap;
|
type Target = HashMap<AttributeKey, AttributeValue>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for Attributes {
|
impl std::ops::DerefMut for AttributeHashMap {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attributes {
|
impl AttributeHashMap {
|
||||||
pub fn new() -> Attributes {
|
pub fn new() -> AttributeHashMap {
|
||||||
Attributes(HashMap::new())
|
AttributeHashMap(HashMap::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_value(attribute_map: AttributeMap) -> Self {
|
pub fn from_value(attribute_map: HashMap<AttributeKey, AttributeValue>) -> Self {
|
||||||
Self(attribute_map)
|
Self(attribute_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +89,7 @@ impl Attributes {
|
|||||||
|
|
||||||
/// Create a new key/value map by constructing new attributes from the other
|
/// Create a new key/value map by constructing new attributes from the other
|
||||||
/// if it's not None and replace the key/value with self key/value.
|
/// if it's not None and replace the key/value with self key/value.
|
||||||
pub fn merge(&mut self, other: Option<Attributes>) {
|
pub fn merge(&mut self, other: Option<AttributeHashMap>) {
|
||||||
if other.is_none() {
|
if other.is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -108,7 +106,7 @@ impl Attributes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Attributes {
|
impl Display for AttributeHashMap {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
for (key, value) in self.0.iter() {
|
for (key, value) in self.0.iter() {
|
||||||
let _ = f.write_str(&format!("{:?}:{:?}", key, value))?;
|
let _ = f.write_str(&format!("{:?}:{:?}", key, value))?;
|
||||||
@ -117,21 +115,21 @@ impl Display for Attributes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OperationAttributes for Attributes {
|
impl OperationAttributes for AttributeHashMap {
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
self.is_empty()
|
self.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_empty(&mut self) {
|
fn remove(&mut self) {
|
||||||
self.retain(|_, v| v.value.is_some());
|
self.retain(|_, v| v.value.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend_other(&mut self, other: Self) {
|
fn extend(&mut self, other: Self) {
|
||||||
self.0.extend(other.0);
|
self.0.extend(other.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OperationTransform for Attributes {
|
impl OperationTransform for AttributeHashMap {
|
||||||
fn compose(&self, other: &Self) -> Result<Self, OTError>
|
fn compose(&self, other: &Self) -> Result<Self, OTError>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
@ -145,25 +143,27 @@ impl OperationTransform for Attributes {
|
|||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
let a = self.iter().fold(Attributes::new(), |mut new_attributes, (k, v)| {
|
let a = self.iter().fold(AttributeHashMap::new(), |mut new_attributes, (k, v)| {
|
||||||
if !other.contains_key(k) {
|
if !other.contains_key(k) {
|
||||||
new_attributes.insert(k.clone(), v.clone());
|
new_attributes.insert(k.clone(), v.clone());
|
||||||
}
|
}
|
||||||
new_attributes
|
new_attributes
|
||||||
});
|
});
|
||||||
|
|
||||||
let b = other.iter().fold(Attributes::new(), |mut new_attributes, (k, v)| {
|
let b = other
|
||||||
if !self.contains_key(k) {
|
.iter()
|
||||||
new_attributes.insert(k.clone(), v.clone());
|
.fold(AttributeHashMap::new(), |mut new_attributes, (k, v)| {
|
||||||
}
|
if !self.contains_key(k) {
|
||||||
new_attributes
|
new_attributes.insert(k.clone(), v.clone());
|
||||||
});
|
}
|
||||||
|
new_attributes
|
||||||
|
});
|
||||||
|
|
||||||
Ok((a, b))
|
Ok((a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invert(&self, other: &Self) -> Self {
|
fn invert(&self, other: &Self) -> Self {
|
||||||
let base_inverted = other.iter().fold(Attributes::new(), |mut attributes, (k, v)| {
|
let base_inverted = other.iter().fold(AttributeHashMap::new(), |mut attributes, (k, v)| {
|
||||||
if other.get(k) != self.get(k) && self.contains_key(k) {
|
if other.get(k) != self.get(k) && self.contains_key(k) {
|
||||||
attributes.insert(k.clone(), v.clone());
|
attributes.insert(k.clone(), v.clone());
|
||||||
}
|
}
|
||||||
@ -275,7 +275,7 @@ impl std::convert::From<String> for AttributeValue {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct AttributeBuilder {
|
pub struct AttributeBuilder {
|
||||||
attributes: Attributes,
|
attributes: AttributeHashMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttributeBuilder {
|
impl AttributeBuilder {
|
||||||
@ -298,7 +298,7 @@ impl AttributeBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Attributes {
|
pub fn build(self) -> AttributeHashMap {
|
||||||
self.attributes
|
self.attributes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::core::delta::operation::OperationAttributes;
|
use crate::core::delta::operation::OperationAttributes;
|
||||||
use crate::core::delta::{trim, Operations};
|
use crate::core::delta::{trim, DeltaOperations};
|
||||||
use crate::core::Operation;
|
use crate::core::DeltaOperation;
|
||||||
|
|
||||||
/// A builder for creating new [Operations] objects.
|
/// A builder for creating new [Operations] objects.
|
||||||
///
|
///
|
||||||
@ -17,7 +17,7 @@ use crate::core::Operation;
|
|||||||
/// assert_eq!(delta.content().unwrap(), "AppFlowy");
|
/// assert_eq!(delta.content().unwrap(), "AppFlowy");
|
||||||
/// ```
|
/// ```
|
||||||
pub struct OperationBuilder<T: OperationAttributes> {
|
pub struct OperationBuilder<T: OperationAttributes> {
|
||||||
delta: Operations<T>,
|
delta: DeltaOperations<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> std::default::Default for OperationBuilder<T>
|
impl<T> std::default::Default for OperationBuilder<T>
|
||||||
@ -26,7 +26,7 @@ where
|
|||||||
{
|
{
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
delta: Operations::new(),
|
delta: DeltaOperations::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ where
|
|||||||
OperationBuilder::default()
|
OperationBuilder::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_operations(operations: Vec<Operation<T>>) -> Operations<T> {
|
pub fn from_operations(operations: Vec<DeltaOperation<T>>) -> DeltaOperations<T> {
|
||||||
let mut delta = OperationBuilder::default().build();
|
let mut delta = OperationBuilder::default().build();
|
||||||
operations.into_iter().for_each(|operation| {
|
operations.into_iter().for_each(|operation| {
|
||||||
delta.add(operation);
|
delta.add(operation);
|
||||||
@ -52,10 +52,10 @@ where
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use lib_ot::text_delta::{BuildInTextAttribute, TextDelta, TextDeltaBuilder};
|
/// use lib_ot::text_delta::{BuildInTextAttribute, TextOperations, TextOperationBuilder};
|
||||||
///
|
///
|
||||||
/// let mut attribute = BuildInTextAttribute::Bold(true);
|
/// let mut attribute = BuildInTextAttribute::Bold(true);
|
||||||
/// let delta = TextDeltaBuilder::new().retain_with_attributes(7, attribute.into()).build();
|
/// let delta = TextOperationBuilder::new().retain_with_attributes(7, attribute.into()).build();
|
||||||
///
|
///
|
||||||
/// assert_eq!(delta.json_str(), r#"[{"retain":7,"attributes":{"bold":true}}]"#);
|
/// assert_eq!(delta.json_str(), r#"[{"retain":7,"attributes":{"bold":true}}]"#);
|
||||||
/// ```
|
/// ```
|
||||||
@ -111,14 +111,14 @@ where
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use lib_ot::core::{OperationTransform, DeltaBuilder};
|
/// use lib_ot::core::{OperationTransform, DeltaBuilder};
|
||||||
/// use lib_ot::text_delta::{BuildInTextAttribute, TextDeltaBuilder};
|
/// use lib_ot::text_delta::{BuildInTextAttribute, TextOperationBuilder};
|
||||||
/// let delta = DeltaBuilder::new()
|
/// let delta = DeltaBuilder::new()
|
||||||
/// .retain(3)
|
/// .retain(3)
|
||||||
/// .trim()
|
/// .trim()
|
||||||
/// .build();
|
/// .build();
|
||||||
/// assert_eq!(delta.ops.len(), 0);
|
/// assert_eq!(delta.ops.len(), 0);
|
||||||
///
|
///
|
||||||
/// let delta = TextDeltaBuilder::new()
|
/// let delta = TextOperationBuilder::new()
|
||||||
/// .retain_with_attributes(3, BuildInTextAttribute::Bold(true).into())
|
/// .retain_with_attributes(3, BuildInTextAttribute::Bold(true).into())
|
||||||
/// .trim()
|
/// .trim()
|
||||||
/// .build();
|
/// .build();
|
||||||
@ -130,7 +130,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the `Delta`
|
/// Builds the `Delta`
|
||||||
pub fn build(self) -> Operations<T> {
|
pub fn build(self) -> DeltaOperations<T> {
|
||||||
self.delta
|
self.delta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#![allow(clippy::while_let_on_iterator)]
|
#![allow(clippy::while_let_on_iterator)]
|
||||||
use crate::core::delta::operation::{Operation, OperationAttributes};
|
use crate::core::delta::operation::{DeltaOperation, OperationAttributes};
|
||||||
use crate::core::delta::Operations;
|
use crate::core::delta::DeltaOperations;
|
||||||
use crate::core::interval::Interval;
|
use crate::core::interval::Interval;
|
||||||
use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
|
use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
|
||||||
use std::{cmp::min, iter::Enumerate, slice::Iter};
|
use std::{cmp::min, iter::Enumerate, slice::Iter};
|
||||||
@ -8,13 +8,13 @@ use std::{cmp::min, iter::Enumerate, slice::Iter};
|
|||||||
/// A [OperationsCursor] is used to iterate the delta and return the corresponding delta.
|
/// A [OperationsCursor] is used to iterate the delta and return the corresponding delta.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct OperationsCursor<'a, T: OperationAttributes> {
|
pub struct OperationsCursor<'a, T: OperationAttributes> {
|
||||||
pub(crate) delta: &'a Operations<T>,
|
pub(crate) delta: &'a DeltaOperations<T>,
|
||||||
pub(crate) origin_iv: Interval,
|
pub(crate) origin_iv: Interval,
|
||||||
pub(crate) consume_iv: Interval,
|
pub(crate) consume_iv: Interval,
|
||||||
pub(crate) consume_count: usize,
|
pub(crate) consume_count: usize,
|
||||||
pub(crate) op_offset: usize,
|
pub(crate) op_offset: usize,
|
||||||
iter: Enumerate<Iter<'a, Operation<T>>>,
|
iter: Enumerate<Iter<'a, DeltaOperation<T>>>,
|
||||||
next_op: Option<Operation<T>>,
|
next_op: Option<DeltaOperation<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> OperationsCursor<'a, T>
|
impl<'a, T> OperationsCursor<'a, T>
|
||||||
@ -29,19 +29,19 @@ where
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use lib_ot::core::{OperationsCursor, OperationIterator, Interval, Operation};
|
/// use lib_ot::core::{OperationsCursor, OperationIterator, Interval, DeltaOperation};
|
||||||
/// use lib_ot::text_delta::TextDelta;
|
/// use lib_ot::text_delta::TextOperations;
|
||||||
/// let mut delta = TextDelta::default();
|
/// let mut delta = TextOperations::default();
|
||||||
/// delta.add(Operation::insert("123"));
|
/// delta.add(DeltaOperation::insert("123"));
|
||||||
/// delta.add(Operation::insert("4"));
|
/// delta.add(DeltaOperation::insert("4"));
|
||||||
///
|
///
|
||||||
/// let mut cursor = OperationsCursor::new(&delta, Interval::new(0, 3));
|
/// let mut cursor = OperationsCursor::new(&delta, Interval::new(0, 3));
|
||||||
/// assert_eq!(cursor.next_iv(), Interval::new(0,3));
|
/// assert_eq!(cursor.next_iv(), Interval::new(0,3));
|
||||||
/// assert_eq!(cursor.next_with_len(Some(2)).unwrap(), Operation::insert("12"));
|
/// assert_eq!(cursor.next_with_len(Some(2)).unwrap(), DeltaOperation::insert("12"));
|
||||||
/// assert_eq!(cursor.get_next_op().unwrap(), Operation::insert("3"));
|
/// assert_eq!(cursor.get_next_op().unwrap(), DeltaOperation::insert("3"));
|
||||||
/// assert_eq!(cursor.get_next_op(), None);
|
/// assert_eq!(cursor.get_next_op(), None);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(delta: &'a Operations<T>, interval: Interval) -> OperationsCursor<'a, T> {
|
pub fn new(delta: &'a DeltaOperations<T>, interval: Interval) -> OperationsCursor<'a, T> {
|
||||||
// debug_assert!(interval.start <= delta.target_len);
|
// debug_assert!(interval.start <= delta.target_len);
|
||||||
let mut cursor = Self {
|
let mut cursor = Self {
|
||||||
delta,
|
delta,
|
||||||
@ -62,12 +62,12 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the next operation
|
/// Returns the next operation
|
||||||
pub fn get_next_op(&mut self) -> Option<Operation<T>> {
|
pub fn get_next_op(&mut self) -> Option<DeltaOperation<T>> {
|
||||||
self.next_with_len(None)
|
self.next_with_len(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the reference of the next operation
|
/// Returns the reference of the next operation
|
||||||
pub fn next_op(&self) -> Option<&Operation<T>> {
|
pub fn next_op(&self) -> Option<&DeltaOperation<T>> {
|
||||||
let mut next_op = self.next_op.as_ref();
|
let mut next_op = self.next_op.as_ref();
|
||||||
if next_op.is_none() {
|
if next_op.is_none() {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
@ -87,7 +87,7 @@ where
|
|||||||
/// * `expected_len`: Return the next operation with the specified length.
|
/// * `expected_len`: Return the next operation with the specified length.
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
pub fn next_with_len(&mut self, expected_len: Option<usize>) -> Option<Operation<T>> {
|
pub fn next_with_len(&mut self, expected_len: Option<usize>) -> Option<DeltaOperation<T>> {
|
||||||
let mut find_op = None;
|
let mut find_op = None;
|
||||||
let holder = self.next_op.clone();
|
let holder = self.next_op.clone();
|
||||||
let mut next_op = holder.as_ref();
|
let mut next_op = holder.as_ref();
|
||||||
@ -182,7 +182,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_next<'a, T>(cursor: &mut OperationsCursor<'a, T>) -> Option<&'a Operation<T>>
|
fn find_next<'a, T>(cursor: &mut OperationsCursor<'a, T>) -> Option<&'a DeltaOperation<T>>
|
||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use super::cursor::*;
|
use super::cursor::*;
|
||||||
use crate::core::delta::operation::{Operation, OperationAttributes};
|
use crate::core::delta::operation::{DeltaOperation, OperationAttributes};
|
||||||
use crate::core::delta::{Operations, NEW_LINE};
|
use crate::core::delta::{DeltaOperations, NEW_LINE};
|
||||||
use crate::core::interval::Interval;
|
use crate::core::interval::Interval;
|
||||||
use crate::core::Attributes;
|
use crate::core::AttributeHashMap;
|
||||||
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
@ -14,19 +14,19 @@ pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize;
|
|||||||
/// You could check [this](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/backend/delta) out for more information.
|
/// You could check [this](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/backend/delta) out for more information.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use lib_ot::core::{OperationIterator, Interval, Operation};
|
/// use lib_ot::core::{OperationIterator, Interval, DeltaOperation};
|
||||||
/// use lib_ot::text_delta::TextDelta;
|
/// use lib_ot::text_delta::TextOperations;
|
||||||
/// let mut delta = TextDelta::default();
|
/// let mut delta = TextOperations::default();
|
||||||
/// delta.add(Operation::insert("123"));
|
/// delta.add(DeltaOperation::insert("123"));
|
||||||
/// delta.add(Operation::insert("4"));
|
/// delta.add(DeltaOperation::insert("4"));
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// OperationIterator::from_interval(&delta, Interval::new(0, 2)).ops(),
|
/// OperationIterator::from_interval(&delta, Interval::new(0, 2)).ops(),
|
||||||
/// vec![Operation::insert("12")]
|
/// vec![DeltaOperation::insert("12")]
|
||||||
/// );
|
/// );
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// OperationIterator::from_interval(&delta, Interval::new(1, 3)).ops(),
|
/// OperationIterator::from_interval(&delta, Interval::new(1, 3)).ops(),
|
||||||
/// vec![Operation::insert("23")]
|
/// vec![DeltaOperation::insert("23")]
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub struct OperationIterator<'a, T: OperationAttributes> {
|
pub struct OperationIterator<'a, T: OperationAttributes> {
|
||||||
@ -37,24 +37,24 @@ impl<'a, T> OperationIterator<'a, T>
|
|||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
pub fn new(delta: &'a Operations<T>) -> Self {
|
pub fn new(delta: &'a DeltaOperations<T>) -> Self {
|
||||||
let interval = Interval::new(0, MAX_IV_LEN);
|
let interval = Interval::new(0, MAX_IV_LEN);
|
||||||
Self::from_interval(delta, interval)
|
Self::from_interval(delta, interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_offset(delta: &'a Operations<T>, offset: usize) -> Self {
|
pub fn from_offset(delta: &'a DeltaOperations<T>, offset: usize) -> Self {
|
||||||
let interval = Interval::new(0, MAX_IV_LEN);
|
let interval = Interval::new(0, MAX_IV_LEN);
|
||||||
let mut iter = Self::from_interval(delta, interval);
|
let mut iter = Self::from_interval(delta, interval);
|
||||||
iter.seek::<Utf16CodeUnitMetric>(offset);
|
iter.seek::<Utf16CodeUnitMetric>(offset);
|
||||||
iter
|
iter
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_interval(delta: &'a Operations<T>, interval: Interval) -> Self {
|
pub fn from_interval(delta: &'a DeltaOperations<T>, interval: Interval) -> Self {
|
||||||
let cursor = OperationsCursor::new(delta, interval);
|
let cursor = OperationsCursor::new(delta, interval);
|
||||||
Self { cursor }
|
Self { cursor }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ops(&mut self) -> Vec<Operation<T>> {
|
pub fn ops(&mut self) -> Vec<DeltaOperation<T>> {
|
||||||
self.collect::<Vec<_>>()
|
self.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,16 +67,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_op(&mut self) -> Option<Operation<T>> {
|
pub fn next_op(&mut self) -> Option<DeltaOperation<T>> {
|
||||||
self.cursor.get_next_op()
|
self.cursor.get_next_op()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_op_with_len(&mut self, len: usize) -> Option<Operation<T>> {
|
pub fn next_op_with_len(&mut self, len: usize) -> Option<DeltaOperation<T>> {
|
||||||
self.cursor.next_with_len(Some(len))
|
self.cursor.next_with_len(Some(len))
|
||||||
}
|
}
|
||||||
|
|
||||||
// find next op contains NEW_LINE
|
// find next op contains NEW_LINE
|
||||||
pub fn next_op_with_newline(&mut self) -> Option<(Operation<T>, usize)> {
|
pub fn next_op_with_newline(&mut self) -> Option<(DeltaOperation<T>, usize)> {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
while self.has_next() {
|
while self.has_next() {
|
||||||
if let Some(op) = self.next_op() {
|
if let Some(op) = self.next_op() {
|
||||||
@ -127,13 +127,13 @@ impl<'a, T> Iterator for OperationIterator<'a, T>
|
|||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
type Item = Operation<T>;
|
type Item = DeltaOperation<T>;
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
self.next_op()
|
self.next_op()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty_line_at_index(delta: &Operations<Attributes>, index: usize) -> bool {
|
pub fn is_empty_line_at_index(delta: &DeltaOperations<AttributeHashMap>, index: usize) -> bool {
|
||||||
let mut iter = OperationIterator::new(delta);
|
let mut iter = OperationIterator::new(delta);
|
||||||
let (prev, next) = (iter.next_op_with_len(index), iter.next_op());
|
let (prev, next) = (iter.next_op_with_len(index), iter.next_op());
|
||||||
if prev.is_none() {
|
if prev.is_none() {
|
||||||
@ -157,12 +157,12 @@ impl<'a, T> AttributesIter<'a, T>
|
|||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
pub fn new(delta: &'a Operations<T>) -> Self {
|
pub fn new(delta: &'a DeltaOperations<T>) -> Self {
|
||||||
let interval = Interval::new(0, usize::MAX);
|
let interval = Interval::new(0, usize::MAX);
|
||||||
Self::from_interval(delta, interval)
|
Self::from_interval(delta, interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_interval(delta: &'a Operations<T>, interval: Interval) -> Self {
|
pub fn from_interval(delta: &'a DeltaOperations<T>, interval: Interval) -> Self {
|
||||||
let delta_iter = OperationIterator::from_interval(delta, interval);
|
let delta_iter = OperationIterator::from_interval(delta, interval);
|
||||||
Self { delta_iter }
|
Self { delta_iter }
|
||||||
}
|
}
|
||||||
@ -207,16 +207,16 @@ where
|
|||||||
let mut attributes = T::default();
|
let mut attributes = T::default();
|
||||||
|
|
||||||
match next_op.unwrap() {
|
match next_op.unwrap() {
|
||||||
Operation::<T>::Delete(_n) => {}
|
DeltaOperation::<T>::Delete(_n) => {}
|
||||||
Operation::<T>::Retain(retain) => {
|
DeltaOperation::<T>::Retain(retain) => {
|
||||||
tracing::trace!("extend retain attributes with {} ", &retain.attributes);
|
tracing::trace!("extend retain attributes with {} ", &retain.attributes);
|
||||||
attributes.extend_other(retain.attributes.clone());
|
attributes.extend(retain.attributes.clone());
|
||||||
|
|
||||||
length = retain.n;
|
length = retain.n;
|
||||||
}
|
}
|
||||||
Operation::<T>::Insert(insert) => {
|
DeltaOperation::<T>::Insert(insert) => {
|
||||||
tracing::trace!("extend insert attributes with {} ", &insert.attributes);
|
tracing::trace!("extend insert attributes with {} ", &insert.attributes);
|
||||||
attributes.extend_other(insert.attributes.clone());
|
attributes.extend(insert.attributes.clone());
|
||||||
length = insert.utf16_size();
|
length = insert.utf16_size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,7 +235,7 @@ pub enum OpNewline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OpNewline {
|
impl OpNewline {
|
||||||
pub fn parse<T: OperationAttributes>(op: &Operation<T>) -> OpNewline {
|
pub fn parse<T: OperationAttributes>(op: &DeltaOperation<T>) -> OpNewline {
|
||||||
let s = op.get_data();
|
let s = op.get_data();
|
||||||
|
|
||||||
if s == NEW_LINE {
|
if s == NEW_LINE {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use crate::core::delta::operation::{EmptyAttributes, Operation, OperationAttributes};
|
use crate::core::delta::operation::{DeltaOperation, EmptyAttributes, OperationAttributes};
|
||||||
|
|
||||||
// pub type RichTextOpBuilder = OperationsBuilder<TextAttributes>;
|
// pub type RichTextOpBuilder = OperationsBuilder<TextAttributes>;
|
||||||
pub type PlainTextOpBuilder = OperationsBuilder<EmptyAttributes>;
|
pub type PlainTextOpBuilder = OperationsBuilder<EmptyAttributes>;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct OperationsBuilder<T: OperationAttributes> {
|
pub struct OperationsBuilder<T: OperationAttributes> {
|
||||||
operations: Vec<Operation<T>>,
|
operations: Vec<DeltaOperation<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OperationsBuilder<T>
|
impl<T> OperationsBuilder<T>
|
||||||
@ -17,35 +17,35 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn retain_with_attributes(mut self, n: usize, attributes: T) -> OperationsBuilder<T> {
|
pub fn retain_with_attributes(mut self, n: usize, attributes: T) -> OperationsBuilder<T> {
|
||||||
let retain = Operation::retain_with_attributes(n, attributes);
|
let retain = DeltaOperation::retain_with_attributes(n, attributes);
|
||||||
self.operations.push(retain);
|
self.operations.push(retain);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn retain(mut self, n: usize) -> OperationsBuilder<T> {
|
pub fn retain(mut self, n: usize) -> OperationsBuilder<T> {
|
||||||
let retain = Operation::retain(n);
|
let retain = DeltaOperation::retain(n);
|
||||||
self.operations.push(retain);
|
self.operations.push(retain);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(mut self, n: usize) -> OperationsBuilder<T> {
|
pub fn delete(mut self, n: usize) -> OperationsBuilder<T> {
|
||||||
self.operations.push(Operation::Delete(n));
|
self.operations.push(DeltaOperation::Delete(n));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_with_attributes(mut self, s: &str, attributes: T) -> OperationsBuilder<T> {
|
pub fn insert_with_attributes(mut self, s: &str, attributes: T) -> OperationsBuilder<T> {
|
||||||
let insert = Operation::insert_with_attributes(s, attributes);
|
let insert = DeltaOperation::insert_with_attributes(s, attributes);
|
||||||
self.operations.push(insert);
|
self.operations.push(insert);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(mut self, s: &str) -> OperationsBuilder<T> {
|
pub fn insert(mut self, s: &str) -> OperationsBuilder<T> {
|
||||||
let insert = Operation::insert(s);
|
let insert = DeltaOperation::insert(s);
|
||||||
self.operations.push(insert);
|
self.operations.push(insert);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Vec<Operation<T>> {
|
pub fn build(self) -> Vec<DeltaOperation<T>> {
|
||||||
self.operations
|
self.operations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,17 +75,13 @@ pub trait OperationAttributes: Default + Display + Eq + PartialEq + Clone + Debu
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the empty attribute which value is None.
|
/// Remove the attribute which value is None.
|
||||||
fn remove_empty(&mut self) {
|
fn remove(&mut self) {}
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extend_other(&mut self, _other: Self) {
|
fn extend(&mut self, _other: Self) {}
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [Operation] consists of three types.
|
/// [DeltaOperation] consists of three types.
|
||||||
/// * Delete
|
/// * Delete
|
||||||
/// * Retain
|
/// * Retain
|
||||||
/// * Insert
|
/// * Insert
|
||||||
@ -96,13 +92,13 @@ pub trait OperationAttributes: Default + Display + Eq + PartialEq + Clone + Debu
|
|||||||
/// to json string. You could check out the operation_serde.rs for more information.
|
/// to json string. You could check out the operation_serde.rs for more information.
|
||||||
///
|
///
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum Operation<T: OperationAttributes> {
|
pub enum DeltaOperation<T: OperationAttributes> {
|
||||||
Delete(usize),
|
Delete(usize),
|
||||||
Retain(Retain<T>),
|
Retain(Retain<T>),
|
||||||
Insert(Insert<T>),
|
Insert(Insert<T>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Operation<T>
|
impl<T> DeltaOperation<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
@ -142,25 +138,25 @@ where
|
|||||||
/// Return the String if the operation is [Insert] operation, otherwise return the empty string.
|
/// Return the String if the operation is [Insert] operation, otherwise return the empty string.
|
||||||
pub fn get_data(&self) -> &str {
|
pub fn get_data(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Operation::Delete(_) => "",
|
DeltaOperation::Delete(_) => "",
|
||||||
Operation::Retain(_) => "",
|
DeltaOperation::Retain(_) => "",
|
||||||
Operation::Insert(insert) => &insert.s,
|
DeltaOperation::Insert(insert) => &insert.s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_attributes(&self) -> T {
|
pub fn get_attributes(&self) -> T {
|
||||||
match self {
|
match self {
|
||||||
Operation::Delete(_) => T::default(),
|
DeltaOperation::Delete(_) => T::default(),
|
||||||
Operation::Retain(retain) => retain.attributes.clone(),
|
DeltaOperation::Retain(retain) => retain.attributes.clone(),
|
||||||
Operation::Insert(insert) => insert.attributes.clone(),
|
DeltaOperation::Insert(insert) => insert.attributes.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_attributes(&mut self, attributes: T) {
|
pub fn set_attributes(&mut self, attributes: T) {
|
||||||
match self {
|
match self {
|
||||||
Operation::Delete(_) => log::error!("Delete should not contains attributes"),
|
DeltaOperation::Delete(_) => log::error!("Delete should not contains attributes"),
|
||||||
Operation::Retain(retain) => retain.attributes = attributes,
|
DeltaOperation::Retain(retain) => retain.attributes = attributes,
|
||||||
Operation::Insert(insert) => insert.attributes = attributes,
|
DeltaOperation::Insert(insert) => insert.attributes = attributes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,9 +166,9 @@ where
|
|||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Operation::Delete(n) => *n,
|
DeltaOperation::Delete(n) => *n,
|
||||||
Operation::Retain(r) => r.n,
|
DeltaOperation::Retain(r) => r.n,
|
||||||
Operation::Insert(i) => i.utf16_size(),
|
DeltaOperation::Insert(i) => i.utf16_size(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,26 +177,26 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn split(&self, index: usize) -> (Option<Operation<T>>, Option<Operation<T>>) {
|
pub fn split(&self, index: usize) -> (Option<DeltaOperation<T>>, Option<DeltaOperation<T>>) {
|
||||||
debug_assert!(index < self.len());
|
debug_assert!(index < self.len());
|
||||||
let left;
|
let left;
|
||||||
let right;
|
let right;
|
||||||
match self {
|
match self {
|
||||||
Operation::Delete(n) => {
|
DeltaOperation::Delete(n) => {
|
||||||
left = Some(Operation::<T>::delete(index));
|
left = Some(DeltaOperation::<T>::delete(index));
|
||||||
right = Some(Operation::<T>::delete(*n - index));
|
right = Some(DeltaOperation::<T>::delete(*n - index));
|
||||||
}
|
}
|
||||||
Operation::Retain(retain) => {
|
DeltaOperation::Retain(retain) => {
|
||||||
left = Some(Operation::<T>::delete(index));
|
left = Some(DeltaOperation::<T>::delete(index));
|
||||||
right = Some(Operation::<T>::delete(retain.n - index));
|
right = Some(DeltaOperation::<T>::delete(retain.n - index));
|
||||||
}
|
}
|
||||||
Operation::Insert(insert) => {
|
DeltaOperation::Insert(insert) => {
|
||||||
let attributes = self.get_attributes();
|
let attributes = self.get_attributes();
|
||||||
left = Some(Operation::<T>::insert_with_attributes(
|
left = Some(DeltaOperation::<T>::insert_with_attributes(
|
||||||
&insert.s[0..index],
|
&insert.s[0..index],
|
||||||
attributes.clone(),
|
attributes.clone(),
|
||||||
));
|
));
|
||||||
right = Some(Operation::<T>::insert_with_attributes(
|
right = Some(DeltaOperation::<T>::insert_with_attributes(
|
||||||
&insert.s[index..insert.utf16_size()],
|
&insert.s[index..insert.utf16_size()],
|
||||||
attributes,
|
attributes,
|
||||||
));
|
));
|
||||||
@ -218,27 +214,27 @@ where
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use lib_ot::core::{Interval, Operation, EmptyAttributes};
|
/// use lib_ot::core::{Interval, DeltaOperation, EmptyAttributes};
|
||||||
/// let operation = Operation::<EmptyAttributes>::insert("1234");
|
/// let operation = DeltaOperation::<EmptyAttributes>::insert("1234");
|
||||||
///
|
///
|
||||||
/// let op1 = operation.shrink(Interval::new(0,3)).unwrap();
|
/// let op1 = operation.shrink(Interval::new(0,3)).unwrap();
|
||||||
/// assert_eq!(op1 , Operation::insert("123"));
|
/// assert_eq!(op1 , DeltaOperation::insert("123"));
|
||||||
///
|
///
|
||||||
/// let op2= operation.shrink(Interval::new(3,4)).unwrap();
|
/// let op2= operation.shrink(Interval::new(3,4)).unwrap();
|
||||||
/// assert_eq!(op2, Operation::insert("4"));
|
/// assert_eq!(op2, DeltaOperation::insert("4"));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn shrink(&self, interval: Interval) -> Option<Operation<T>> {
|
pub fn shrink(&self, interval: Interval) -> Option<DeltaOperation<T>> {
|
||||||
let op = match self {
|
let op = match self {
|
||||||
Operation::Delete(n) => Operation::delete(min(*n, interval.size())),
|
DeltaOperation::Delete(n) => DeltaOperation::delete(min(*n, interval.size())),
|
||||||
Operation::Retain(retain) => {
|
DeltaOperation::Retain(retain) => {
|
||||||
Operation::retain_with_attributes(min(retain.n, interval.size()), retain.attributes.clone())
|
DeltaOperation::retain_with_attributes(min(retain.n, interval.size()), retain.attributes.clone())
|
||||||
}
|
}
|
||||||
Operation::Insert(insert) => {
|
DeltaOperation::Insert(insert) => {
|
||||||
if interval.start > insert.utf16_size() {
|
if interval.start > insert.utf16_size() {
|
||||||
Operation::insert("")
|
DeltaOperation::insert("")
|
||||||
} else {
|
} else {
|
||||||
let s = insert.s.sub_str(interval).unwrap_or_else(|| "".to_owned());
|
let s = insert.s.sub_str(interval).unwrap_or_else(|| "".to_owned());
|
||||||
Operation::insert_with_attributes(&s, insert.attributes.clone())
|
DeltaOperation::insert_with_attributes(&s, insert.attributes.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -250,21 +246,21 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_delete(&self) -> bool {
|
pub fn is_delete(&self) -> bool {
|
||||||
if let Operation::Delete(_) = self {
|
if let DeltaOperation::Delete(_) = self {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_insert(&self) -> bool {
|
pub fn is_insert(&self) -> bool {
|
||||||
if let Operation::Insert(_) = self {
|
if let DeltaOperation::Insert(_) = self {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_retain(&self) -> bool {
|
pub fn is_retain(&self) -> bool {
|
||||||
if let Operation::Retain(_) = self {
|
if let DeltaOperation::Retain(_) = self {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@ -272,27 +268,27 @@ where
|
|||||||
|
|
||||||
pub fn is_plain(&self) -> bool {
|
pub fn is_plain(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Operation::Delete(_) => true,
|
DeltaOperation::Delete(_) => true,
|
||||||
Operation::Retain(retain) => retain.is_plain(),
|
DeltaOperation::Retain(retain) => retain.is_plain(),
|
||||||
Operation::Insert(insert) => insert.is_plain(),
|
DeltaOperation::Insert(insert) => insert.is_plain(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> fmt::Display for Operation<T>
|
impl<T> fmt::Display for DeltaOperation<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.write_str("{")?;
|
f.write_str("{")?;
|
||||||
match self {
|
match self {
|
||||||
Operation::Delete(n) => {
|
DeltaOperation::Delete(n) => {
|
||||||
f.write_fmt(format_args!("delete: {}", n))?;
|
f.write_fmt(format_args!("delete: {}", n))?;
|
||||||
}
|
}
|
||||||
Operation::Retain(r) => {
|
DeltaOperation::Retain(r) => {
|
||||||
f.write_fmt(format_args!("{}", r))?;
|
f.write_fmt(format_args!("{}", r))?;
|
||||||
}
|
}
|
||||||
Operation::Insert(i) => {
|
DeltaOperation::Insert(i) => {
|
||||||
f.write_fmt(format_args!("{}", i))?;
|
f.write_fmt(format_args!("{}", i))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -324,7 +320,7 @@ impl<T> Retain<T>
|
|||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
pub fn merge_or_new(&mut self, n: usize, attributes: T) -> Option<Operation<T>> {
|
pub fn merge_or_new(&mut self, n: usize, attributes: T) -> Option<DeltaOperation<T>> {
|
||||||
// tracing::trace!(
|
// tracing::trace!(
|
||||||
// "merge_retain_or_new_op: len: {:?}, l: {} - r: {}",
|
// "merge_retain_or_new_op: len: {:?}, l: {} - r: {}",
|
||||||
// n,
|
// n,
|
||||||
@ -335,7 +331,7 @@ where
|
|||||||
self.n += n;
|
self.n += n;
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(Operation::retain_with_attributes(n, attributes))
|
Some(DeltaOperation::retain_with_attributes(n, attributes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,12 +407,12 @@ where
|
|||||||
self.s.utf16_len()
|
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<DeltaOperation<T>> {
|
||||||
if self.attributes == attributes {
|
if self.attributes == attributes {
|
||||||
self.s += s;
|
self.s += s;
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(Operation::<T>::insert_with_attributes(s, attributes))
|
Some(DeltaOperation::<T>::insert_with_attributes(s, attributes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::core::delta::operation::{Insert, Operation, OperationAttributes, Retain};
|
use crate::core::delta::operation::{DeltaOperation, Insert, OperationAttributes, Retain};
|
||||||
use crate::core::ot_str::OTString;
|
use crate::core::ot_str::OTString;
|
||||||
use serde::{
|
use serde::{
|
||||||
de,
|
de,
|
||||||
@ -8,7 +8,7 @@ use serde::{
|
|||||||
};
|
};
|
||||||
use std::{fmt, marker::PhantomData};
|
use std::{fmt, marker::PhantomData};
|
||||||
|
|
||||||
impl<T> Serialize for Operation<T>
|
impl<T> Serialize for DeltaOperation<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes + Serialize,
|
T: OperationAttributes + Serialize,
|
||||||
{
|
{
|
||||||
@ -17,22 +17,22 @@ where
|
|||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
Operation::Retain(retain) => retain.serialize(serializer),
|
DeltaOperation::Retain(retain) => retain.serialize(serializer),
|
||||||
Operation::Delete(i) => {
|
DeltaOperation::Delete(i) => {
|
||||||
let mut map = serializer.serialize_map(Some(1))?;
|
let mut map = serializer.serialize_map(Some(1))?;
|
||||||
map.serialize_entry("delete", i)?;
|
map.serialize_entry("delete", i)?;
|
||||||
map.end()
|
map.end()
|
||||||
}
|
}
|
||||||
Operation::Insert(insert) => insert.serialize(serializer),
|
DeltaOperation::Insert(insert) => insert.serialize(serializer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de, T> Deserialize<'de> for Operation<T>
|
impl<'de, T> Deserialize<'de> for DeltaOperation<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes + Deserialize<'de>,
|
T: OperationAttributes + Deserialize<'de>,
|
||||||
{
|
{
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Operation<T>, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<DeltaOperation<T>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
@ -42,7 +42,7 @@ where
|
|||||||
where
|
where
|
||||||
T: OperationAttributes + Deserialize<'de>,
|
T: OperationAttributes + Deserialize<'de>,
|
||||||
{
|
{
|
||||||
type Value = Operation<T>;
|
type Value = DeltaOperation<T>;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
formatter.write_str("an integer between -2^64 and 2^63 or a string")
|
formatter.write_str("an integer between -2^64 and 2^63 or a string")
|
||||||
@ -61,21 +61,21 @@ where
|
|||||||
if operation.is_some() {
|
if operation.is_some() {
|
||||||
return Err(de::Error::duplicate_field("operation"));
|
return Err(de::Error::duplicate_field("operation"));
|
||||||
}
|
}
|
||||||
operation = Some(Operation::<T>::Delete(map.next_value()?));
|
operation = Some(DeltaOperation::<T>::Delete(map.next_value()?));
|
||||||
}
|
}
|
||||||
"retain" => {
|
"retain" => {
|
||||||
if operation.is_some() {
|
if operation.is_some() {
|
||||||
return Err(de::Error::duplicate_field("operation"));
|
return Err(de::Error::duplicate_field("operation"));
|
||||||
}
|
}
|
||||||
let i: usize = map.next_value()?;
|
let i: usize = map.next_value()?;
|
||||||
operation = Some(Operation::<T>::Retain(i.into()));
|
operation = Some(DeltaOperation::<T>::Retain(i.into()));
|
||||||
}
|
}
|
||||||
"insert" => {
|
"insert" => {
|
||||||
if operation.is_some() {
|
if operation.is_some() {
|
||||||
return Err(de::Error::duplicate_field("operation"));
|
return Err(de::Error::duplicate_field("operation"));
|
||||||
}
|
}
|
||||||
let i: String = map.next_value()?;
|
let i: String = map.next_value()?;
|
||||||
operation = Some(Operation::<T>::Insert(i.into()));
|
operation = Some(DeltaOperation::<T>::Insert(i.into()));
|
||||||
}
|
}
|
||||||
"attributes" => {
|
"attributes" => {
|
||||||
if attributes.is_some() {
|
if attributes.is_some() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
|
use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
|
||||||
|
|
||||||
use crate::core::delta::operation::{EmptyAttributes, Operation, OperationAttributes, OperationTransform};
|
use crate::core::delta::operation::{DeltaOperation, EmptyAttributes, OperationAttributes, OperationTransform};
|
||||||
use crate::core::delta::{OperationIterator, MAX_IV_LEN};
|
use crate::core::delta::{OperationIterator, MAX_IV_LEN};
|
||||||
use crate::core::interval::Interval;
|
use crate::core::interval::Interval;
|
||||||
use crate::core::ot_str::OTString;
|
use crate::core::ot_str::OTString;
|
||||||
@ -15,7 +15,7 @@ use std::{
|
|||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Delta = Operations<EmptyAttributes>;
|
pub type Delta = DeltaOperations<EmptyAttributes>;
|
||||||
pub type DeltaBuilder = OperationBuilder<EmptyAttributes>;
|
pub type DeltaBuilder = OperationBuilder<EmptyAttributes>;
|
||||||
|
|
||||||
/// A [Delta] contains list of operations that consists of 'Retain', 'Delete' and 'Insert' operation.
|
/// A [Delta] contains list of operations that consists of 'Retain', 'Delete' and 'Insert' operation.
|
||||||
@ -28,8 +28,8 @@ pub type DeltaBuilder = OperationBuilder<EmptyAttributes>;
|
|||||||
/// a JSON string.
|
/// a JSON string.
|
||||||
///
|
///
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Operations<T: OperationAttributes> {
|
pub struct DeltaOperations<Attribute: OperationAttributes> {
|
||||||
pub ops: Vec<Operation<T>>,
|
pub ops: Vec<DeltaOperation<Attribute>>,
|
||||||
|
|
||||||
/// 'Delete' and 'Retain' operation will update the [utf16_base_len]
|
/// 'Delete' and 'Retain' operation will update the [utf16_base_len]
|
||||||
/// Transforming the other delta, it requires the utf16_base_len must be equal.
|
/// Transforming the other delta, it requires the utf16_base_len must be equal.
|
||||||
@ -40,7 +40,7 @@ pub struct Operations<T: OperationAttributes> {
|
|||||||
pub utf16_target_len: usize,
|
pub utf16_target_len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for Operations<T>
|
impl<T> Default for DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
@ -53,7 +53,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> fmt::Display for Operations<T>
|
impl<T> fmt::Display for DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
@ -68,12 +68,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> FromIterator<Operation<T>> for Operations<T>
|
impl<T> FromIterator<DeltaOperation<T>> for DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
fn from_iter<I: IntoIterator<Item = Operation<T>>>(ops: I) -> Self {
|
fn from_iter<I: IntoIterator<Item = DeltaOperation<T>>>(ops: I) -> Self {
|
||||||
let mut operations = Operations::default();
|
let mut operations = DeltaOperations::default();
|
||||||
for op in ops {
|
for op in ops {
|
||||||
operations.add(op);
|
operations.add(op);
|
||||||
}
|
}
|
||||||
@ -81,7 +81,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Operations<T>
|
impl<T> DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
@ -99,11 +99,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Adding an operation. It will be added in sequence.
|
/// Adding an operation. It will be added in sequence.
|
||||||
pub fn add(&mut self, op: Operation<T>) {
|
pub fn add(&mut self, op: DeltaOperation<T>) {
|
||||||
match op {
|
match op {
|
||||||
Operation::Delete(i) => self.delete(i),
|
DeltaOperation::Delete(i) => self.delete(i),
|
||||||
Operation::Insert(i) => self.insert(&i.s, i.attributes),
|
DeltaOperation::Insert(i) => self.insert(&i.s, i.attributes),
|
||||||
Operation::Retain(r) => self.retain(r.n, r.attributes),
|
DeltaOperation::Retain(r) => self.retain(r.n, r.attributes),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,10 +113,10 @@ where
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.utf16_base_len += n as usize;
|
self.utf16_base_len += n as usize;
|
||||||
if let Some(Operation::Delete(n_last)) = self.ops.last_mut() {
|
if let Some(DeltaOperation::Delete(n_last)) = self.ops.last_mut() {
|
||||||
*n_last += n;
|
*n_last += n;
|
||||||
} else {
|
} else {
|
||||||
self.ops.push(Operation::delete(n));
|
self.ops.push(DeltaOperation::delete(n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,20 +129,20 @@ where
|
|||||||
|
|
||||||
self.utf16_target_len += s.utf16_len();
|
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)] => {
|
[.., DeltaOperation::<T>::Insert(insert)] => {
|
||||||
//
|
//
|
||||||
insert.merge_or_new_op(&s, attributes)
|
insert.merge_or_new_op(&s, attributes)
|
||||||
}
|
}
|
||||||
[.., Operation::<T>::Insert(pre_insert), Operation::Delete(_)] => {
|
[.., DeltaOperation::<T>::Insert(pre_insert), DeltaOperation::Delete(_)] => {
|
||||||
//
|
//
|
||||||
pre_insert.merge_or_new_op(&s, attributes)
|
pre_insert.merge_or_new_op(&s, attributes)
|
||||||
}
|
}
|
||||||
[.., op_last @ Operation::<T>::Delete(_)] => {
|
[.., op_last @ DeltaOperation::<T>::Delete(_)] => {
|
||||||
let new_last = op_last.clone();
|
let new_last = op_last.clone();
|
||||||
*op_last = Operation::<T>::insert_with_attributes(&s, attributes);
|
*op_last = DeltaOperation::<T>::insert_with_attributes(&s, attributes);
|
||||||
Some(new_last)
|
Some(new_last)
|
||||||
}
|
}
|
||||||
_ => Some(Operation::<T>::insert_with_attributes(&s, attributes)),
|
_ => Some(DeltaOperation::<T>::insert_with_attributes(&s, attributes)),
|
||||||
};
|
};
|
||||||
|
|
||||||
match new_last {
|
match new_last {
|
||||||
@ -159,12 +159,13 @@ where
|
|||||||
self.utf16_base_len += n as usize;
|
self.utf16_base_len += n as usize;
|
||||||
self.utf16_target_len += n as usize;
|
self.utf16_target_len += n as usize;
|
||||||
|
|
||||||
if let Some(Operation::<T>::Retain(retain)) = self.ops.last_mut() {
|
if let Some(DeltaOperation::<T>::Retain(retain)) = self.ops.last_mut() {
|
||||||
if let Some(new_op) = retain.merge_or_new(n, attributes) {
|
if let Some(new_op) = retain.merge_or_new(n, attributes) {
|
||||||
self.ops.push(new_op);
|
self.ops.push(new_op);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.ops.push(Operation::<T>::retain_with_attributes(n, attributes));
|
self.ops
|
||||||
|
.push(DeltaOperation::<T>::retain_with_attributes(n, attributes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,17 +208,17 @@ where
|
|||||||
let code_point_iter = &mut applied_str.utf16_iter();
|
let code_point_iter = &mut applied_str.utf16_iter();
|
||||||
for op in &self.ops {
|
for op in &self.ops {
|
||||||
match &op {
|
match &op {
|
||||||
Operation::Retain(retain) => {
|
DeltaOperation::Retain(retain) => {
|
||||||
for c in code_point_iter.take(retain.n as usize) {
|
for c in code_point_iter.take(retain.n as usize) {
|
||||||
new_s.push_str(str::from_utf8(c.0).unwrap_or(""));
|
new_s.push_str(str::from_utf8(c.0).unwrap_or(""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Delete(delete) => {
|
DeltaOperation::Delete(delete) => {
|
||||||
for _ in 0..*delete {
|
for _ in 0..*delete {
|
||||||
code_point_iter.next();
|
code_point_iter.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Insert(insert) => {
|
DeltaOperation::Insert(insert) => {
|
||||||
new_s += &insert.s;
|
new_s += &insert.s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,22 +250,22 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
pub fn invert_str(&self, inverted_s: &str) -> Self {
|
pub fn invert_str(&self, inverted_s: &str) -> Self {
|
||||||
let mut inverted = Operations::default();
|
let mut inverted = DeltaOperations::default();
|
||||||
let inverted_s: OTString = inverted_s.into();
|
let inverted_s: OTString = inverted_s.into();
|
||||||
let code_point_iter = &mut inverted_s.utf16_iter();
|
let code_point_iter = &mut inverted_s.utf16_iter();
|
||||||
|
|
||||||
for op in &self.ops {
|
for op in &self.ops {
|
||||||
match &op {
|
match &op {
|
||||||
Operation::Retain(retain) => {
|
DeltaOperation::Retain(retain) => {
|
||||||
inverted.retain(retain.n, T::default());
|
inverted.retain(retain.n, T::default());
|
||||||
for _ in 0..retain.n {
|
for _ in 0..retain.n {
|
||||||
code_point_iter.next();
|
code_point_iter.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Insert(insert) => {
|
DeltaOperation::Insert(insert) => {
|
||||||
inverted.delete(insert.utf16_size());
|
inverted.delete(insert.utf16_size());
|
||||||
}
|
}
|
||||||
Operation::Delete(delete) => {
|
DeltaOperation::Delete(delete) => {
|
||||||
let bytes = code_point_iter
|
let bytes = code_point_iter
|
||||||
.take(*delete as usize)
|
.take(*delete as usize)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -280,7 +281,7 @@ where
|
|||||||
|
|
||||||
/// Return true if the delta doesn't contain any [Insert] or [Delete] operations.
|
/// Return true if the delta doesn't contain any [Insert] or [Delete] operations.
|
||||||
pub fn is_noop(&self) -> bool {
|
pub fn is_noop(&self) -> bool {
|
||||||
matches!(self.ops.as_slice(), [] | [Operation::Retain(_)])
|
matches!(self.ops.as_slice(), [] | [DeltaOperation::Retain(_)])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
@ -292,7 +293,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OperationTransform for Operations<T>
|
impl<T> OperationTransform for DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
@ -300,7 +301,7 @@ where
|
|||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
let mut new_delta = Operations::default();
|
let mut new_delta = DeltaOperations::default();
|
||||||
let mut iter = OperationIterator::new(self);
|
let mut iter = OperationIterator::new(self);
|
||||||
let mut other_iter = OperationIterator::new(other);
|
let mut other_iter = OperationIterator::new(other);
|
||||||
|
|
||||||
@ -322,25 +323,25 @@ where
|
|||||||
|
|
||||||
let op = iter
|
let op = iter
|
||||||
.next_op_with_len(length)
|
.next_op_with_len(length)
|
||||||
.unwrap_or_else(|| Operation::retain(length));
|
.unwrap_or_else(|| DeltaOperation::retain(length));
|
||||||
let other_op = other_iter
|
let other_op = other_iter
|
||||||
.next_op_with_len(length)
|
.next_op_with_len(length)
|
||||||
.unwrap_or_else(|| Operation::retain(length));
|
.unwrap_or_else(|| DeltaOperation::retain(length));
|
||||||
|
|
||||||
// debug_assert_eq!(op.len(), other_op.len(), "Composing delta failed,");
|
// debug_assert_eq!(op.len(), other_op.len(), "Composing delta failed,");
|
||||||
|
|
||||||
match (&op, &other_op) {
|
match (&op, &other_op) {
|
||||||
(Operation::Retain(retain), Operation::Retain(other_retain)) => {
|
(DeltaOperation::Retain(retain), DeltaOperation::Retain(other_retain)) => {
|
||||||
let composed_attrs = retain.attributes.compose(&other_retain.attributes)?;
|
let composed_attrs = retain.attributes.compose(&other_retain.attributes)?;
|
||||||
|
|
||||||
new_delta.add(Operation::retain_with_attributes(retain.n, composed_attrs))
|
new_delta.add(DeltaOperation::retain_with_attributes(retain.n, composed_attrs))
|
||||||
}
|
}
|
||||||
(Operation::Insert(insert), Operation::Retain(other_retain)) => {
|
(DeltaOperation::Insert(insert), DeltaOperation::Retain(other_retain)) => {
|
||||||
let mut composed_attrs = insert.attributes.compose(&other_retain.attributes)?;
|
let mut composed_attrs = insert.attributes.compose(&other_retain.attributes)?;
|
||||||
composed_attrs.remove_empty();
|
composed_attrs.remove();
|
||||||
new_delta.add(Operation::insert_with_attributes(op.get_data(), composed_attrs))
|
new_delta.add(DeltaOperation::insert_with_attributes(op.get_data(), composed_attrs))
|
||||||
}
|
}
|
||||||
(Operation::Retain(_), Operation::Delete(_)) => {
|
(DeltaOperation::Retain(_), DeltaOperation::Delete(_)) => {
|
||||||
new_delta.add(other_op);
|
new_delta.add(other_op);
|
||||||
}
|
}
|
||||||
(a, b) => {
|
(a, b) => {
|
||||||
@ -366,8 +367,8 @@ where
|
|||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut a_prime = Operations::default();
|
let mut a_prime = DeltaOperations::default();
|
||||||
let mut b_prime = Operations::default();
|
let mut b_prime = DeltaOperations::default();
|
||||||
|
|
||||||
let mut ops1 = self.ops.iter().cloned();
|
let mut ops1 = self.ops.iter().cloned();
|
||||||
let mut ops2 = other.ops.iter().cloned();
|
let mut ops2 = other.ops.iter().cloned();
|
||||||
@ -377,13 +378,13 @@ where
|
|||||||
loop {
|
loop {
|
||||||
match (&next_op1, &next_op2) {
|
match (&next_op1, &next_op2) {
|
||||||
(None, None) => break,
|
(None, None) => break,
|
||||||
(Some(Operation::Insert(insert)), _) => {
|
(Some(DeltaOperation::Insert(insert)), _) => {
|
||||||
// let composed_attrs = transform_attributes(&next_op1, &next_op2, true);
|
// let composed_attrs = transform_attributes(&next_op1, &next_op2, true);
|
||||||
a_prime.insert(&insert.s, insert.attributes.clone());
|
a_prime.insert(&insert.s, insert.attributes.clone());
|
||||||
b_prime.retain(insert.utf16_size(), insert.attributes.clone());
|
b_prime.retain(insert.utf16_size(), insert.attributes.clone());
|
||||||
next_op1 = ops1.next();
|
next_op1 = ops1.next();
|
||||||
}
|
}
|
||||||
(_, Some(Operation::Insert(o_insert))) => {
|
(_, Some(DeltaOperation::Insert(o_insert))) => {
|
||||||
let composed_attrs = transform_op_attribute(&next_op1, &next_op2)?;
|
let composed_attrs = transform_op_attribute(&next_op1, &next_op2)?;
|
||||||
a_prime.retain(o_insert.utf16_size(), composed_attrs.clone());
|
a_prime.retain(o_insert.utf16_size(), composed_attrs.clone());
|
||||||
b_prime.insert(&o_insert.s, composed_attrs);
|
b_prime.insert(&o_insert.s, composed_attrs);
|
||||||
@ -395,13 +396,13 @@ where
|
|||||||
(_, None) => {
|
(_, None) => {
|
||||||
return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength).build());
|
return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength).build());
|
||||||
}
|
}
|
||||||
(Some(Operation::Retain(retain)), Some(Operation::Retain(o_retain))) => {
|
(Some(DeltaOperation::Retain(retain)), Some(DeltaOperation::Retain(o_retain))) => {
|
||||||
let composed_attrs = transform_op_attribute(&next_op1, &next_op2)?;
|
let composed_attrs = transform_op_attribute(&next_op1, &next_op2)?;
|
||||||
match retain.cmp(o_retain) {
|
match retain.cmp(o_retain) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
a_prime.retain(retain.n, composed_attrs.clone());
|
a_prime.retain(retain.n, composed_attrs.clone());
|
||||||
b_prime.retain(retain.n, composed_attrs.clone());
|
b_prime.retain(retain.n, composed_attrs.clone());
|
||||||
next_op2 = Some(Operation::retain(o_retain.n - retain.n));
|
next_op2 = Some(DeltaOperation::retain(o_retain.n - retain.n));
|
||||||
next_op1 = ops1.next();
|
next_op1 = ops1.next();
|
||||||
}
|
}
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
@ -413,14 +414,14 @@ where
|
|||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
a_prime.retain(o_retain.n, composed_attrs.clone());
|
a_prime.retain(o_retain.n, composed_attrs.clone());
|
||||||
b_prime.retain(o_retain.n, composed_attrs.clone());
|
b_prime.retain(o_retain.n, composed_attrs.clone());
|
||||||
next_op1 = Some(Operation::retain(retain.n - o_retain.n));
|
next_op1 = Some(DeltaOperation::retain(retain.n - o_retain.n));
|
||||||
next_op2 = ops2.next();
|
next_op2 = ops2.next();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(Some(Operation::Delete(i)), Some(Operation::Delete(j))) => match i.cmp(j) {
|
(Some(DeltaOperation::Delete(i)), Some(DeltaOperation::Delete(j))) => match i.cmp(j) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
next_op2 = Some(Operation::delete(*j - *i));
|
next_op2 = Some(DeltaOperation::delete(*j - *i));
|
||||||
next_op1 = ops1.next();
|
next_op1 = ops1.next();
|
||||||
}
|
}
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
@ -428,15 +429,15 @@ where
|
|||||||
next_op2 = ops2.next();
|
next_op2 = ops2.next();
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
next_op1 = Some(Operation::delete(*i - *j));
|
next_op1 = Some(DeltaOperation::delete(*i - *j));
|
||||||
next_op2 = ops2.next();
|
next_op2 = ops2.next();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(Some(Operation::Delete(i)), Some(Operation::Retain(o_retain))) => {
|
(Some(DeltaOperation::Delete(i)), Some(DeltaOperation::Retain(o_retain))) => {
|
||||||
match i.cmp(o_retain) {
|
match i.cmp(o_retain) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
a_prime.delete(*i);
|
a_prime.delete(*i);
|
||||||
next_op2 = Some(Operation::retain(o_retain.n - *i));
|
next_op2 = Some(DeltaOperation::retain(o_retain.n - *i));
|
||||||
next_op1 = ops1.next();
|
next_op1 = ops1.next();
|
||||||
}
|
}
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
@ -446,16 +447,16 @@ where
|
|||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
a_prime.delete(o_retain.n);
|
a_prime.delete(o_retain.n);
|
||||||
next_op1 = Some(Operation::delete(*i - o_retain.n));
|
next_op1 = Some(DeltaOperation::delete(*i - o_retain.n));
|
||||||
next_op2 = ops2.next();
|
next_op2 = ops2.next();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(Some(Operation::Retain(retain)), Some(Operation::Delete(j))) => {
|
(Some(DeltaOperation::Retain(retain)), Some(DeltaOperation::Delete(j))) => {
|
||||||
match retain.cmp(j) {
|
match retain.cmp(j) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
b_prime.delete(retain.n);
|
b_prime.delete(retain.n);
|
||||||
next_op2 = Some(Operation::delete(*j - retain.n));
|
next_op2 = Some(DeltaOperation::delete(*j - retain.n));
|
||||||
next_op1 = ops1.next();
|
next_op1 = ops1.next();
|
||||||
}
|
}
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
@ -465,7 +466,7 @@ where
|
|||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
b_prime.delete(*j);
|
b_prime.delete(*j);
|
||||||
next_op1 = Some(Operation::retain(retain.n - *j));
|
next_op1 = Some(DeltaOperation::retain(retain.n - *j));
|
||||||
next_op2 = ops2.next();
|
next_op2 = ops2.next();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -476,16 +477,16 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn invert(&self, other: &Self) -> Self {
|
fn invert(&self, other: &Self) -> Self {
|
||||||
let mut inverted = Operations::default();
|
let mut inverted = DeltaOperations::default();
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
for op in &self.ops {
|
for op in &self.ops {
|
||||||
let len: usize = op.len() as usize;
|
let len: usize = op.len() as usize;
|
||||||
match op {
|
match op {
|
||||||
Operation::Delete(n) => {
|
DeltaOperation::Delete(n) => {
|
||||||
invert_other(&mut inverted, other, op, index, index + *n);
|
invert_other(&mut inverted, other, op, index, index + *n);
|
||||||
index += len;
|
index += len;
|
||||||
}
|
}
|
||||||
Operation::Retain(_) => {
|
DeltaOperation::Retain(_) => {
|
||||||
match op.has_attribute() {
|
match op.has_attribute() {
|
||||||
true => invert_other(&mut inverted, other, op, index, index + len),
|
true => invert_other(&mut inverted, other, op, index, index + len),
|
||||||
false => {
|
false => {
|
||||||
@ -496,7 +497,7 @@ where
|
|||||||
}
|
}
|
||||||
index += len;
|
index += len;
|
||||||
}
|
}
|
||||||
Operation::Insert(_) => {
|
DeltaOperation::Insert(_) => {
|
||||||
// tracing::trace!("invert insert: {} by delete {}", op, len);
|
// tracing::trace!("invert insert: {} by delete {}", op, len);
|
||||||
inverted.delete(len as usize);
|
inverted.delete(len as usize);
|
||||||
}
|
}
|
||||||
@ -507,7 +508,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Removes trailing retain operation with empty attributes, if present.
|
/// Removes trailing retain operation with empty attributes, if present.
|
||||||
pub fn trim<T>(delta: &mut Operations<T>)
|
pub fn trim<T>(delta: &mut DeltaOperations<T>)
|
||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
@ -519,20 +520,20 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn invert_other<T: OperationAttributes>(
|
fn invert_other<T: OperationAttributes>(
|
||||||
base: &mut Operations<T>,
|
base: &mut DeltaOperations<T>,
|
||||||
other: &Operations<T>,
|
other: &DeltaOperations<T>,
|
||||||
operation: &Operation<T>,
|
operation: &DeltaOperation<T>,
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
) {
|
) {
|
||||||
tracing::trace!("invert op: {} [{}:{}]", operation, start, end);
|
tracing::trace!("invert op: {} [{}:{}]", operation, start, end);
|
||||||
let other_ops = OperationIterator::from_interval(other, Interval::new(start, end)).ops();
|
let other_ops = OperationIterator::from_interval(other, Interval::new(start, end)).ops();
|
||||||
other_ops.into_iter().for_each(|other_op| match operation {
|
other_ops.into_iter().for_each(|other_op| match operation {
|
||||||
Operation::Delete(_n) => {
|
DeltaOperation::Delete(_n) => {
|
||||||
// tracing::trace!("invert delete: {} by add {}", n, other_op);
|
// tracing::trace!("invert delete: {} by add {}", n, other_op);
|
||||||
base.add(other_op);
|
base.add(other_op);
|
||||||
}
|
}
|
||||||
Operation::Retain(_retain) => {
|
DeltaOperation::Retain(_retain) => {
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
"invert attributes: {:?}, {:?}",
|
"invert attributes: {:?}, {:?}",
|
||||||
operation.get_attributes(),
|
operation.get_attributes(),
|
||||||
@ -541,15 +542,15 @@ fn invert_other<T: OperationAttributes>(
|
|||||||
let inverted_attrs = operation.get_attributes().invert(&other_op.get_attributes());
|
let inverted_attrs = operation.get_attributes().invert(&other_op.get_attributes());
|
||||||
base.retain(other_op.len(), inverted_attrs);
|
base.retain(other_op.len(), inverted_attrs);
|
||||||
}
|
}
|
||||||
Operation::Insert(_) => {
|
DeltaOperation::Insert(_) => {
|
||||||
log::error!("Impossible to here. Insert operation should be treated as delete")
|
log::error!("Impossible to here. Insert operation should be treated as delete")
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform_op_attribute<T: OperationAttributes>(
|
fn transform_op_attribute<T: OperationAttributes>(
|
||||||
left: &Option<Operation<T>>,
|
left: &Option<DeltaOperation<T>>,
|
||||||
right: &Option<Operation<T>>,
|
right: &Option<DeltaOperation<T>>,
|
||||||
) -> Result<T, OTError> {
|
) -> Result<T, OTError> {
|
||||||
if left.is_none() {
|
if left.is_none() {
|
||||||
if right.is_none() {
|
if right.is_none() {
|
||||||
@ -563,7 +564,7 @@ fn transform_op_attribute<T: OperationAttributes>(
|
|||||||
Ok(left.transform(&right)?.0)
|
Ok(left.transform(&right)?.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Operations<T>
|
impl<T> DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes + DeserializeOwned,
|
T: OperationAttributes + DeserializeOwned,
|
||||||
{
|
{
|
||||||
@ -571,11 +572,11 @@ where
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use lib_ot::core::OperationBuilder;
|
/// use lib_ot::core::OperationBuilder;
|
||||||
/// use lib_ot::text_delta::{TextDelta};
|
/// use lib_ot::text_delta::{TextOperations};
|
||||||
/// let json = r#"[
|
/// let json = r#"[
|
||||||
/// {"retain":7,"attributes":{"bold":null}}
|
/// {"retain":7,"attributes":{"bold":null}}
|
||||||
/// ]"#;
|
/// ]"#;
|
||||||
/// let delta = TextDelta::from_json(json).unwrap();
|
/// let delta = TextOperations::from_json(json).unwrap();
|
||||||
/// assert_eq!(delta.json_str(), r#"[{"retain":7,"attributes":{"bold":null}}]"#);
|
/// assert_eq!(delta.json_str(), r#"[{"retain":7,"attributes":{"bold":null}}]"#);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from_json(json: &str) -> Result<Self, OTError> {
|
pub fn from_json(json: &str) -> Result<Self, OTError> {
|
||||||
@ -595,7 +596,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Operations<T>
|
impl<T> DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes + serde::Serialize,
|
T: OperationAttributes + serde::Serialize,
|
||||||
{
|
{
|
||||||
@ -616,36 +617,36 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> FromStr for Operations<T>
|
impl<T> FromStr for DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes,
|
T: OperationAttributes,
|
||||||
{
|
{
|
||||||
type Err = ();
|
type Err = ();
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Operations<T>, Self::Err> {
|
fn from_str(s: &str) -> Result<DeltaOperations<T>, Self::Err> {
|
||||||
let mut delta = Operations::with_capacity(1);
|
let mut delta = DeltaOperations::with_capacity(1);
|
||||||
delta.add(Operation::Insert(s.into()));
|
delta.add(DeltaOperation::Insert(s.into()));
|
||||||
Ok(delta)
|
Ok(delta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> std::convert::TryFrom<Vec<u8>> for Operations<T>
|
impl<T> std::convert::TryFrom<Vec<u8>> for DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes + DeserializeOwned,
|
T: OperationAttributes + DeserializeOwned,
|
||||||
{
|
{
|
||||||
type Error = OTError;
|
type Error = OTError;
|
||||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||||
Operations::from_bytes(bytes)
|
DeltaOperations::from_bytes(bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> std::convert::TryFrom<Bytes> for Operations<T>
|
impl<T> std::convert::TryFrom<Bytes> for DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes + DeserializeOwned,
|
T: OperationAttributes + DeserializeOwned,
|
||||||
{
|
{
|
||||||
type Error = OTError;
|
type Error = OTError;
|
||||||
|
|
||||||
fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
|
fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
|
||||||
Operations::from_bytes(&bytes)
|
DeltaOperations::from_bytes(&bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::core::delta::operation::OperationAttributes;
|
use crate::core::delta::operation::OperationAttributes;
|
||||||
use crate::core::delta::Operations;
|
use crate::core::delta::DeltaOperations;
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{SeqAccess, Visitor},
|
de::{SeqAccess, Visitor},
|
||||||
ser::SerializeSeq,
|
ser::SerializeSeq,
|
||||||
@ -7,7 +7,7 @@ use serde::{
|
|||||||
};
|
};
|
||||||
use std::{fmt, marker::PhantomData};
|
use std::{fmt, marker::PhantomData};
|
||||||
|
|
||||||
impl<T> Serialize for Operations<T>
|
impl<T> Serialize for DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes + Serialize,
|
T: OperationAttributes + Serialize,
|
||||||
{
|
{
|
||||||
@ -23,11 +23,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de, T> Deserialize<'de> for Operations<T>
|
impl<'de, T> Deserialize<'de> for DeltaOperations<T>
|
||||||
where
|
where
|
||||||
T: OperationAttributes + Deserialize<'de>,
|
T: OperationAttributes + Deserialize<'de>,
|
||||||
{
|
{
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Operations<T>, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<DeltaOperations<T>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
@ -37,7 +37,7 @@ where
|
|||||||
where
|
where
|
||||||
T: OperationAttributes + Deserialize<'de>,
|
T: OperationAttributes + Deserialize<'de>,
|
||||||
{
|
{
|
||||||
type Value = Operations<T>;
|
type Value = DeltaOperations<T>;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
formatter.write_str("a sequence")
|
formatter.write_str("a sequence")
|
||||||
@ -48,7 +48,7 @@ where
|
|||||||
where
|
where
|
||||||
A: SeqAccess<'de>,
|
A: SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut o = Operations::default();
|
let mut o = DeltaOperations::default();
|
||||||
while let Some(op) = seq.next_element()? {
|
while let Some(op) = seq.next_element()? {
|
||||||
o.add(op);
|
o.add(op);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use super::node_serde::*;
|
use super::node_serde::*;
|
||||||
use crate::core::attributes::{AttributeKey, AttributeValue, Attributes};
|
use crate::core::attributes::{AttributeHashMap, AttributeKey, AttributeValue};
|
||||||
use crate::core::NodeBody::Delta;
|
use crate::core::NodeBody::Delta;
|
||||||
use crate::core::OperationTransform;
|
use crate::core::OperationTransform;
|
||||||
use crate::errors::OTError;
|
use crate::errors::OTError;
|
||||||
use crate::text_delta::TextDelta;
|
use crate::text_delta::TextOperations;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
#[derive(Default, Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
@ -11,9 +11,9 @@ pub struct NodeData {
|
|||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub node_type: String,
|
pub node_type: String,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Attributes::is_empty")]
|
#[serde(skip_serializing_if = "AttributeHashMap::is_empty")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub attributes: Attributes,
|
pub attributes: AttributeHashMap,
|
||||||
|
|
||||||
#[serde(serialize_with = "serialize_body")]
|
#[serde(serialize_with = "serialize_body")]
|
||||||
#[serde(deserialize_with = "deserialize_body")]
|
#[serde(deserialize_with = "deserialize_body")]
|
||||||
@ -94,7 +94,7 @@ impl NodeDataBuilder {
|
|||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum NodeBody {
|
pub enum NodeBody {
|
||||||
Empty,
|
Empty,
|
||||||
Delta(TextDelta),
|
Delta(TextOperations),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for NodeBody {
|
impl std::default::Default for NodeBody {
|
||||||
@ -159,7 +159,10 @@ impl OperationTransform for NodeBody {
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum NodeBodyChangeset {
|
pub enum NodeBodyChangeset {
|
||||||
Delta { delta: TextDelta, inverted: TextDelta },
|
Delta {
|
||||||
|
delta: TextOperations,
|
||||||
|
inverted: TextOperations,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeBodyChangeset {
|
impl NodeBodyChangeset {
|
||||||
@ -179,14 +182,14 @@ impl NodeBodyChangeset {
|
|||||||
pub struct Node {
|
pub struct Node {
|
||||||
pub node_type: String,
|
pub node_type: String,
|
||||||
pub body: NodeBody,
|
pub body: NodeBody,
|
||||||
pub attributes: Attributes,
|
pub attributes: AttributeHashMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
pub fn new(node_type: &str) -> Node {
|
pub fn new(node_type: &str) -> Node {
|
||||||
Node {
|
Node {
|
||||||
node_type: node_type.into(),
|
node_type: node_type.into(),
|
||||||
attributes: Attributes::new(),
|
attributes: AttributeHashMap::new(),
|
||||||
body: NodeBody::Empty,
|
body: NodeBody::Empty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::NodeBody;
|
use super::NodeBody;
|
||||||
use crate::text_delta::TextDelta;
|
use crate::text_delta::TextOperations;
|
||||||
use serde::de::{self, MapAccess, Visitor};
|
use serde::de::{self, MapAccess, Visitor};
|
||||||
use serde::ser::SerializeMap;
|
use serde::ser::SerializeMap;
|
||||||
use serde::{Deserializer, Serializer};
|
use serde::{Deserializer, Serializer};
|
||||||
@ -37,7 +37,7 @@ where
|
|||||||
where
|
where
|
||||||
A: de::SeqAccess<'de>,
|
A: de::SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut delta = TextDelta::default();
|
let mut delta = TextOperations::default();
|
||||||
while let Some(op) = seq.next_element()? {
|
while let Some(op) = seq.next_element()? {
|
||||||
delta.add(op);
|
delta.add(op);
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ where
|
|||||||
where
|
where
|
||||||
V: MapAccess<'de>,
|
V: MapAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut delta: Option<TextDelta> = None;
|
let mut delta: Option<TextOperations> = None;
|
||||||
while let Some(key) = map.next_key()? {
|
while let Some(key) = map.next_key()? {
|
||||||
match key {
|
match key {
|
||||||
"delta" => {
|
"delta" => {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::core::attributes::Attributes;
|
use crate::core::attributes::AttributeHashMap;
|
||||||
use crate::core::{NodeBodyChangeset, NodeData, Path};
|
use crate::core::{NodeBodyChangeset, NodeData, Path};
|
||||||
use crate::errors::OTError;
|
use crate::errors::OTError;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -13,8 +13,8 @@ pub enum NodeOperation {
|
|||||||
#[serde(rename = "update-attribute")]
|
#[serde(rename = "update-attribute")]
|
||||||
UpdateAttributes {
|
UpdateAttributes {
|
||||||
path: Path,
|
path: Path,
|
||||||
new: Attributes,
|
new: AttributeHashMap,
|
||||||
old: Attributes,
|
old: AttributeHashMap,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[serde(rename = "update-body")]
|
#[serde(rename = "update-body")]
|
||||||
@ -121,11 +121,11 @@ impl NodeOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct NodeOperationList {
|
pub struct NodeOperations {
|
||||||
operations: Vec<Rc<NodeOperation>>,
|
operations: Vec<Rc<NodeOperation>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeOperationList {
|
impl NodeOperations {
|
||||||
pub fn into_inner(self) -> Vec<Rc<NodeOperation>> {
|
pub fn into_inner(self) -> Vec<Rc<NodeOperation>> {
|
||||||
self.operations
|
self.operations
|
||||||
}
|
}
|
||||||
@ -135,7 +135,7 @@ impl NodeOperationList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for NodeOperationList {
|
impl std::ops::Deref for NodeOperations {
|
||||||
type Target = Vec<Rc<NodeOperation>>;
|
type Target = Vec<Rc<NodeOperation>>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@ -143,19 +143,19 @@ impl std::ops::Deref for NodeOperationList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for NodeOperationList {
|
impl std::ops::DerefMut for NodeOperations {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.operations
|
&mut self.operations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<Vec<NodeOperation>> for NodeOperationList {
|
impl std::convert::From<Vec<NodeOperation>> for NodeOperations {
|
||||||
fn from(operations: Vec<NodeOperation>) -> Self {
|
fn from(operations: Vec<NodeOperation>) -> Self {
|
||||||
Self::new(operations)
|
Self::new(operations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeOperationList {
|
impl NodeOperations {
|
||||||
pub fn new(operations: Vec<NodeOperation>) -> Self {
|
pub fn new(operations: Vec<NodeOperation>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
operations: operations.into_iter().map(Rc::new).collect(),
|
operations: operations.into_iter().map(Rc::new).collect(),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::core::{NodeBodyChangeset, Path};
|
use crate::core::{NodeBodyChangeset, Path};
|
||||||
use crate::text_delta::TextDelta;
|
use crate::text_delta::TextOperations;
|
||||||
use serde::de::{self, MapAccess, Visitor};
|
use serde::de::{self, MapAccess, Visitor};
|
||||||
use serde::ser::SerializeMap;
|
use serde::ser::SerializeMap;
|
||||||
use serde::{Deserializer, Serializer};
|
use serde::{Deserializer, Serializer};
|
||||||
@ -88,8 +88,8 @@ where
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
struct DeltaBodyChangeset<E> {
|
struct DeltaBodyChangeset<E> {
|
||||||
delta: Option<TextDelta>,
|
delta: Option<TextOperations>,
|
||||||
inverted: Option<TextDelta>,
|
inverted: Option<TextOperations>,
|
||||||
error: PhantomData<E>,
|
error: PhantomData<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use crate::core::attributes::Attributes;
|
use crate::core::attributes::AttributeHashMap;
|
||||||
use crate::core::{NodeData, NodeOperation, NodeTree, Path};
|
use crate::core::{NodeData, NodeOperation, NodeTree, Path};
|
||||||
use crate::errors::OTError;
|
use crate::errors::OTError;
|
||||||
use indextree::NodeId;
|
use indextree::NodeId;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{NodeBodyChangeset, NodeOperationList};
|
use super::{NodeBodyChangeset, NodeOperations};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
operations: NodeOperationList,
|
operations: NodeOperations,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transaction {
|
impl Transaction {
|
||||||
@ -16,7 +16,7 @@ impl Transaction {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_operations<T: Into<NodeOperationList>>(operations: T) -> Self {
|
pub fn from_operations<T: Into<NodeOperations>>(operations: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
operations: operations.into(),
|
operations: operations.into(),
|
||||||
}
|
}
|
||||||
@ -66,14 +66,14 @@ impl std::ops::DerefMut for Transaction {
|
|||||||
|
|
||||||
pub struct TransactionBuilder<'a> {
|
pub struct TransactionBuilder<'a> {
|
||||||
node_tree: &'a NodeTree,
|
node_tree: &'a NodeTree,
|
||||||
operations: NodeOperationList,
|
operations: NodeOperations,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TransactionBuilder<'a> {
|
impl<'a> TransactionBuilder<'a> {
|
||||||
pub fn new(node_tree: &'a NodeTree) -> TransactionBuilder {
|
pub fn new(node_tree: &'a NodeTree) -> TransactionBuilder {
|
||||||
TransactionBuilder {
|
TransactionBuilder {
|
||||||
node_tree,
|
node_tree,
|
||||||
operations: NodeOperationList::default(),
|
operations: NodeOperations::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,10 +132,10 @@ impl<'a> TransactionBuilder<'a> {
|
|||||||
self.insert_nodes_at_path(path, vec![node])
|
self.insert_nodes_at_path(path, vec![node])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_attributes_at_path(mut self, path: &Path, attributes: Attributes) -> Self {
|
pub fn update_attributes_at_path(mut self, path: &Path, attributes: AttributeHashMap) -> Self {
|
||||||
match self.node_tree.get_node_at_path(path) {
|
match self.node_tree.get_node_at_path(path) {
|
||||||
Some(node) => {
|
Some(node) => {
|
||||||
let mut old_attributes = Attributes::new();
|
let mut old_attributes = AttributeHashMap::new();
|
||||||
for key in attributes.keys() {
|
for key in attributes.keys() {
|
||||||
let old_attrs = &node.attributes;
|
let old_attrs = &node.attributes;
|
||||||
if let Some(value) = old_attrs.get(key.as_str()) {
|
if let Some(value) = old_attrs.get(key.as_str()) {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::core::attributes::Attributes;
|
use crate::core::attributes::AttributeHashMap;
|
||||||
use crate::core::{Node, NodeBodyChangeset, NodeData, NodeOperation, OperationTransform, Path, Transaction};
|
use crate::core::{Node, NodeBodyChangeset, NodeData, NodeOperation, OperationTransform, Path, Transaction};
|
||||||
use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
|
use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
|
||||||
use indextree::{Arena, Children, FollowingSiblings, NodeId};
|
use indextree::{Arena, Children, FollowingSiblings, NodeId};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::NodeOperationList;
|
use super::NodeOperations;
|
||||||
|
|
||||||
///
|
///
|
||||||
pub struct NodeTree {
|
pub struct NodeTree {
|
||||||
@ -26,11 +26,11 @@ impl NodeTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(root_name: &str, bytes: Vec<u8>) -> Result<Self, OTError> {
|
pub fn from_bytes(root_name: &str, bytes: Vec<u8>) -> Result<Self, OTError> {
|
||||||
let operations = NodeOperationList::from_bytes(bytes)?;
|
let operations = NodeOperations::from_bytes(bytes)?;
|
||||||
Self::from_operations(root_name, operations)
|
Self::from_operations(root_name, operations)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_operations(root_name: &str, operations: NodeOperationList) -> Result<Self, OTError> {
|
pub fn from_operations(root_name: &str, operations: NodeOperations) -> Result<Self, OTError> {
|
||||||
let mut node_tree = NodeTree::new(root_name);
|
let mut node_tree = NodeTree::new(root_name);
|
||||||
for operation in operations.into_inner().into_iter() {
|
for operation in operations.into_inner().into_iter() {
|
||||||
let _ = node_tree.apply_op(operation)?;
|
let _ = node_tree.apply_op(operation)?;
|
||||||
@ -252,9 +252,9 @@ impl NodeTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_attributes(&mut self, path: &Path, attributes: Attributes) -> Result<(), OTError> {
|
fn update_attributes(&mut self, path: &Path, attributes: AttributeHashMap) -> Result<(), OTError> {
|
||||||
self.mut_node_at_path(path, |node| {
|
self.mut_node_at_path(path, |node| {
|
||||||
let new_attributes = Attributes::compose(&node.attributes, &attributes)?;
|
let new_attributes = AttributeHashMap::compose(&node.attributes, &attributes)?;
|
||||||
node.attributes = new_attributes;
|
node.attributes = new_attributes;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
use crate::core::{AttributeEntry, AttributeKey, Attributes};
|
use crate::core::{AttributeEntry, AttributeHashMap, AttributeKey};
|
||||||
use crate::text_delta::TextOperation;
|
use crate::text_delta::TextOperation;
|
||||||
use crate::{inline_attribute_entry, inline_list_attribute_entry};
|
use crate::{inline_attribute_entry, inline_list_attribute_entry};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
@ -8,11 +8,11 @@ use std::{collections::HashSet, iter::FromIterator};
|
|||||||
use strum_macros::{AsRefStr, Display, EnumString};
|
use strum_macros::{AsRefStr, Display, EnumString};
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn empty_attributes() -> Attributes {
|
pub fn empty_attributes() -> AttributeHashMap {
|
||||||
Attributes::default()
|
AttributeHashMap::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attributes_except_header(op: &TextOperation) -> Attributes {
|
pub fn attributes_except_header(op: &TextOperation) -> AttributeHashMap {
|
||||||
let mut attributes = op.get_attributes();
|
let mut attributes = op.get_attributes();
|
||||||
attributes.remove_key(BuildInTextAttributeKey::Header);
|
attributes.remove_key(BuildInTextAttributeKey::Header);
|
||||||
attributes
|
attributes
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
use crate::core::{Attributes, Operation, OperationBuilder, Operations};
|
use crate::core::{AttributeHashMap, DeltaOperation, DeltaOperations, OperationBuilder, OperationTransform};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
pub type TextDelta = Operations<Attributes>;
|
pub type TextOperations = DeltaOperations<AttributeHashMap>;
|
||||||
pub type TextDeltaBuilder = OperationBuilder<Attributes>;
|
pub type TextOperationBuilder = OperationBuilder<AttributeHashMap>;
|
||||||
pub type TextOperation = Operation<Attributes>;
|
pub type TextOperation = DeltaOperation<AttributeHashMap>;
|
||||||
|
|
||||||
|
// pub trait TextOperation2: Default + Debug + OperationTransform {}
|
||||||
|
//
|
||||||
|
// impl TextOperation2 for TextOperations {}
|
||||||
|
@ -2,7 +2,7 @@ use super::script::{NodeScript::*, *};
|
|||||||
use lib_ot::core::AttributeBuilder;
|
use lib_ot::core::AttributeBuilder;
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{NodeData, Path},
|
core::{NodeData, Path},
|
||||||
text_delta::TextDeltaBuilder,
|
text_delta::TextOperationBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -11,7 +11,7 @@ fn editor_deserialize_node_test() {
|
|||||||
let node: NodeData = serde_json::from_str(EXAMPLE_JSON).unwrap();
|
let node: NodeData = serde_json::from_str(EXAMPLE_JSON).unwrap();
|
||||||
let path: Path = 0.into();
|
let path: Path = 0.into();
|
||||||
|
|
||||||
let expected_delta = TextDeltaBuilder::new()
|
let expected_delta = TextOperationBuilder::new()
|
||||||
.insert("👋 ")
|
.insert("👋 ")
|
||||||
.insert_with_attributes(
|
.insert_with_attributes(
|
||||||
"Welcome to ",
|
"Welcome to ",
|
||||||
|
@ -3,7 +3,7 @@ use crate::node::script::NodeTest;
|
|||||||
use lib_ot::core::{AttributeBuilder, Node};
|
use lib_ot::core::{AttributeBuilder, Node};
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::{NodeBodyChangeset, NodeData, NodeDataBuilder, NodeOperation, Path},
|
core::{NodeBodyChangeset, NodeData, NodeDataBuilder, NodeOperation, Path},
|
||||||
text_delta::TextDeltaBuilder,
|
text_delta::TextOperationBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -48,7 +48,7 @@ fn operation_update_node_attributes_serde_test() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn operation_update_node_body_serialize_test() {
|
fn operation_update_node_body_serialize_test() {
|
||||||
let delta = TextDeltaBuilder::new().insert("AppFlowy...").build();
|
let delta = TextOperationBuilder::new().insert("AppFlowy...").build();
|
||||||
let inverted = delta.invert_str("");
|
let inverted = delta.invert_str("");
|
||||||
let changeset = NodeBodyChangeset::Delta { delta, inverted };
|
let changeset = NodeBodyChangeset::Delta { delta, inverted };
|
||||||
let insert = NodeOperation::UpdateBody {
|
let insert = NodeOperation::UpdateBody {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use lib_ot::core::{Node, Transaction};
|
use lib_ot::core::{Node, Transaction};
|
||||||
use lib_ot::{
|
use lib_ot::{
|
||||||
core::attributes::Attributes,
|
core::attributes::AttributeHashMap,
|
||||||
core::{NodeBody, NodeBodyChangeset, NodeData, NodeTree, Path, TransactionBuilder},
|
core::{NodeBody, NodeBodyChangeset, NodeData, NodeTree, Path, TransactionBuilder},
|
||||||
text_delta::TextDelta,
|
text_delta::TextOperations,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ pub enum NodeScript {
|
|||||||
},
|
},
|
||||||
UpdateAttributes {
|
UpdateAttributes {
|
||||||
path: Path,
|
path: Path,
|
||||||
attributes: Attributes,
|
attributes: AttributeHashMap,
|
||||||
},
|
},
|
||||||
UpdateBody {
|
UpdateBody {
|
||||||
path: Path,
|
path: Path,
|
||||||
@ -38,7 +38,7 @@ pub enum NodeScript {
|
|||||||
},
|
},
|
||||||
AssertNodeDelta {
|
AssertNodeDelta {
|
||||||
path: Path,
|
path: Path,
|
||||||
expected: TextDelta,
|
expected: TextOperations,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use lib_ot::core::NodeBody;
|
|||||||
use lib_ot::core::NodeBodyChangeset;
|
use lib_ot::core::NodeBodyChangeset;
|
||||||
use lib_ot::core::OperationTransform;
|
use lib_ot::core::OperationTransform;
|
||||||
use lib_ot::core::{NodeData, NodeDataBuilder, Path};
|
use lib_ot::core::{NodeData, NodeDataBuilder, Path};
|
||||||
use lib_ot::text_delta::TextDeltaBuilder;
|
use lib_ot::text_delta::TextOperationBuilder;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn node_insert_test() {
|
fn node_insert_test() {
|
||||||
@ -293,8 +293,8 @@ fn node_update_body_test() {
|
|||||||
let path: Path = 0.into();
|
let path: Path = 0.into();
|
||||||
|
|
||||||
let s = "Hello".to_owned();
|
let s = "Hello".to_owned();
|
||||||
let init_delta = TextDeltaBuilder::new().insert(&s).build();
|
let init_delta = TextOperationBuilder::new().insert(&s).build();
|
||||||
let delta = TextDeltaBuilder::new().retain(s.len()).insert(" AppFlowy").build();
|
let delta = TextOperationBuilder::new().retain(s.len()).insert(" AppFlowy").build();
|
||||||
let inverted = delta.invert(&init_delta);
|
let inverted = delta.invert(&init_delta);
|
||||||
let expected = init_delta.compose(&delta).unwrap();
|
let expected = init_delta.compose(&delta).unwrap();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user