123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- use crate::{
- client_document::{
- history::{History, UndoResult},
- view::{ViewExtensions, RECORD_THRESHOLD},
- },
- errors::SyncError,
- };
- use bytes::Bytes;
- use lib_infra::util::md5;
- use lib_ot::text_delta::DeltaTextOperationBuilder;
- use lib_ot::{core::*, text_delta::DeltaTextOperations};
- use tokio::sync::mpsc;
- pub trait InitialDocument {
- fn json_str() -> String;
- }
- pub struct EmptyDocument();
- impl InitialDocument for EmptyDocument {
- fn json_str() -> String {
- DeltaTextOperations::default().json_str()
- }
- }
- pub struct NewlineDocument();
- impl InitialDocument for NewlineDocument {
- fn json_str() -> String {
- initial_delta_document_content()
- }
- }
- pub fn initial_delta_document_content() -> String {
- DeltaTextOperationBuilder::new().insert("\n").build().json_str()
- }
- pub struct ClientDocument {
- operations: DeltaTextOperations,
- history: History,
- view: ViewExtensions,
- last_edit_time: usize,
- notify: Option<mpsc::UnboundedSender<()>>,
- }
- impl ClientDocument {
- pub fn new<C: InitialDocument>() -> Self {
- let content = C::json_str();
- Self::from_json(&content).unwrap()
- }
- pub fn from_operations(operations: DeltaTextOperations) -> Self {
- ClientDocument {
- operations,
- history: History::new(),
- view: ViewExtensions::new(),
- last_edit_time: 0,
- notify: None,
- }
- }
- pub fn from_json(json: &str) -> Result<Self, SyncError> {
- let operations = DeltaTextOperations::from_json(json)?;
- Ok(Self::from_operations(operations))
- }
- pub fn get_operations_json(&self) -> String {
- self.operations.json_str()
- }
- pub fn to_bytes(&self) -> Bytes {
- self.operations.json_bytes()
- }
- pub fn to_content(&self) -> String {
- self.operations.content().unwrap()
- }
- pub fn get_operations(&self) -> &DeltaTextOperations {
- &self.operations
- }
- pub fn document_md5(&self) -> String {
- let bytes = self.to_bytes();
- md5(&bytes)
- }
- pub fn set_notify(&mut self, notify: mpsc::UnboundedSender<()>) {
- self.notify = Some(notify);
- }
- pub fn set_operations(&mut self, operations: DeltaTextOperations) {
- tracing::trace!("document: {}", operations.json_str());
- self.operations = operations;
- match &self.notify {
- None => {}
- Some(notify) => {
- let _ = notify.send(());
- }
- }
- }
- pub fn compose_operations(&mut self, operations: DeltaTextOperations) -> Result<(), SyncError> {
- tracing::trace!("{} compose {}", &self.operations.json_str(), operations.json_str());
- let composed_operations = self.operations.compose(&operations)?;
- let mut undo_operations = operations.invert(&self.operations);
- let now = chrono::Utc::now().timestamp_millis() as usize;
- if now - self.last_edit_time < RECORD_THRESHOLD {
- if let Some(last_operation) = self.history.undo() {
- tracing::trace!("compose previous change");
- tracing::trace!("current = {}", undo_operations);
- tracing::trace!("previous = {}", last_operation);
- undo_operations = undo_operations.compose(&last_operation)?;
- }
- } else {
- self.last_edit_time = now;
- }
- if !undo_operations.is_empty() {
- tracing::trace!("add history operations: {}", undo_operations);
- self.history.record(undo_operations);
- }
- self.set_operations(composed_operations);
- Ok(())
- }
- pub fn insert<T: ToString>(&mut self, index: usize, data: T) -> Result<DeltaTextOperations, SyncError> {
- let text = data.to_string();
- let interval = Interval::new(index, index);
- validate_interval(&self.operations, &interval)?;
- let operations = self.view.insert(&self.operations, &text, interval)?;
- self.compose_operations(operations.clone())?;
- Ok(operations)
- }
- pub fn delete(&mut self, interval: Interval) -> Result<DeltaTextOperations, SyncError> {
- validate_interval(&self.operations, &interval)?;
- debug_assert!(!interval.is_empty());
- let operations = self.view.delete(&self.operations, interval)?;
- if !operations.is_empty() {
- self.compose_operations(operations.clone())?;
- }
- Ok(operations)
- }
- pub fn format(&mut self, interval: Interval, attribute: AttributeEntry) -> Result<DeltaTextOperations, SyncError> {
- validate_interval(&self.operations, &interval)?;
- tracing::trace!("format {} with {:?}", interval, attribute);
- let operations = self.view.format(&self.operations, attribute, interval).unwrap();
- self.compose_operations(operations.clone())?;
- Ok(operations)
- }
- pub fn replace<T: ToString>(&mut self, interval: Interval, data: T) -> Result<DeltaTextOperations, SyncError> {
- validate_interval(&self.operations, &interval)?;
- let mut operations = DeltaTextOperations::default();
- let text = data.to_string();
- if !text.is_empty() {
- operations = self.view.insert(&self.operations, &text, interval)?;
- self.compose_operations(operations.clone())?;
- }
- if !interval.is_empty() {
- let delete = self.delete(interval)?;
- operations = operations.compose(&delete)?;
- }
- Ok(operations)
- }
- pub fn can_undo(&self) -> bool {
- self.history.can_undo()
- }
- pub fn can_redo(&self) -> bool {
- self.history.can_redo()
- }
- pub fn undo(&mut self) -> Result<UndoResult, SyncError> {
- match self.history.undo() {
- None => Err(SyncError::undo().context("Undo stack is empty")),
- Some(undo_operations) => {
- let (new_operations, inverted_operations) = self.invert(&undo_operations)?;
- self.set_operations(new_operations);
- self.history.add_redo(inverted_operations);
- Ok(UndoResult {
- operations: undo_operations,
- })
- }
- }
- }
- pub fn redo(&mut self) -> Result<UndoResult, SyncError> {
- match self.history.redo() {
- None => Err(SyncError::redo()),
- Some(redo_operations) => {
- let (new_operations, inverted_operations) = self.invert(&redo_operations)?;
- self.set_operations(new_operations);
- self.history.add_undo(inverted_operations);
- Ok(UndoResult {
- operations: redo_operations,
- })
- }
- }
- }
- pub fn is_empty(&self) -> bool {
- // The document is empty if its text is equal to the initial text.
- self.operations.json_str() == NewlineDocument::json_str()
- }
- }
- impl ClientDocument {
- fn invert(
- &self,
- operations: &DeltaTextOperations,
- ) -> Result<(DeltaTextOperations, DeltaTextOperations), SyncError> {
- // c = a.compose(b)
- // d = b.invert(a)
- // a = c.compose(d)
- let new_operations = self.operations.compose(operations)?;
- let inverted_operations = operations.invert(&self.operations);
- Ok((new_operations, inverted_operations))
- }
- }
- fn validate_interval(operations: &DeltaTextOperations, interval: &Interval) -> Result<(), SyncError> {
- if operations.utf16_target_len < interval.end {
- tracing::error!(
- "{:?} out of bounds. should 0..{}",
- interval,
- operations.utf16_target_len
- );
- return Err(SyncError::out_of_bound());
- }
- Ok(())
- }
|