document_pad.rs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. use crate::{
  2. client_document::{
  3. history::{History, UndoResult},
  4. view::{ViewExtensions, RECORD_THRESHOLD},
  5. },
  6. errors::SyncError,
  7. };
  8. use bytes::Bytes;
  9. use lib_infra::util::md5;
  10. use lib_ot::text_delta::DeltaTextOperationBuilder;
  11. use lib_ot::{core::*, text_delta::DeltaTextOperations};
  12. use tokio::sync::mpsc;
  13. pub trait InitialDocument {
  14. fn json_str() -> String;
  15. }
  16. pub struct EmptyDocument();
  17. impl InitialDocument for EmptyDocument {
  18. fn json_str() -> String {
  19. DeltaTextOperations::default().json_str()
  20. }
  21. }
  22. pub struct NewlineDocument();
  23. impl InitialDocument for NewlineDocument {
  24. fn json_str() -> String {
  25. initial_delta_document_content()
  26. }
  27. }
  28. pub fn initial_delta_document_content() -> String {
  29. DeltaTextOperationBuilder::new()
  30. .insert("\n")
  31. .build()
  32. .json_str()
  33. }
  34. pub struct ClientDocument {
  35. operations: DeltaTextOperations,
  36. history: History,
  37. view: ViewExtensions,
  38. last_edit_time: usize,
  39. notify: Option<mpsc::UnboundedSender<()>>,
  40. }
  41. impl ClientDocument {
  42. pub fn new<C: InitialDocument>() -> Self {
  43. let content = C::json_str();
  44. Self::from_json(&content).unwrap()
  45. }
  46. pub fn from_operations(operations: DeltaTextOperations) -> Self {
  47. ClientDocument {
  48. operations,
  49. history: History::new(),
  50. view: ViewExtensions::new(),
  51. last_edit_time: 0,
  52. notify: None,
  53. }
  54. }
  55. pub fn from_json(json: &str) -> Result<Self, SyncError> {
  56. let operations = DeltaTextOperations::from_json(json)?;
  57. Ok(Self::from_operations(operations))
  58. }
  59. pub fn get_operations_json(&self) -> String {
  60. self.operations.json_str()
  61. }
  62. pub fn to_bytes(&self) -> Bytes {
  63. self.operations.json_bytes()
  64. }
  65. pub fn to_content(&self) -> String {
  66. self.operations.content().unwrap()
  67. }
  68. pub fn get_operations(&self) -> &DeltaTextOperations {
  69. &self.operations
  70. }
  71. pub fn document_md5(&self) -> String {
  72. let bytes = self.to_bytes();
  73. md5(&bytes)
  74. }
  75. pub fn set_notify(&mut self, notify: mpsc::UnboundedSender<()>) {
  76. self.notify = Some(notify);
  77. }
  78. pub fn set_operations(&mut self, operations: DeltaTextOperations) {
  79. tracing::trace!("document: {}", operations.json_str());
  80. self.operations = operations;
  81. match &self.notify {
  82. None => {},
  83. Some(notify) => {
  84. let _ = notify.send(());
  85. },
  86. }
  87. }
  88. pub fn compose_operations(&mut self, operations: DeltaTextOperations) -> Result<(), SyncError> {
  89. tracing::trace!(
  90. "{} compose {}",
  91. &self.operations.json_str(),
  92. operations.json_str()
  93. );
  94. let composed_operations = self.operations.compose(&operations)?;
  95. let mut undo_operations = operations.invert(&self.operations);
  96. let now = chrono::Utc::now().timestamp_millis() as usize;
  97. if now - self.last_edit_time < RECORD_THRESHOLD {
  98. if let Some(last_operation) = self.history.undo() {
  99. tracing::trace!("compose previous change");
  100. tracing::trace!("current = {}", undo_operations);
  101. tracing::trace!("previous = {}", last_operation);
  102. undo_operations = undo_operations.compose(&last_operation)?;
  103. }
  104. } else {
  105. self.last_edit_time = now;
  106. }
  107. if !undo_operations.is_empty() {
  108. tracing::trace!("add history operations: {}", undo_operations);
  109. self.history.record(undo_operations);
  110. }
  111. self.set_operations(composed_operations);
  112. Ok(())
  113. }
  114. pub fn insert<T: ToString>(
  115. &mut self,
  116. index: usize,
  117. data: T,
  118. ) -> Result<DeltaTextOperations, SyncError> {
  119. let text = data.to_string();
  120. let interval = Interval::new(index, index);
  121. validate_interval(&self.operations, &interval)?;
  122. let operations = self.view.insert(&self.operations, &text, interval)?;
  123. self.compose_operations(operations.clone())?;
  124. Ok(operations)
  125. }
  126. pub fn delete(&mut self, interval: Interval) -> Result<DeltaTextOperations, SyncError> {
  127. validate_interval(&self.operations, &interval)?;
  128. debug_assert!(!interval.is_empty());
  129. let operations = self.view.delete(&self.operations, interval)?;
  130. if !operations.is_empty() {
  131. self.compose_operations(operations.clone())?;
  132. }
  133. Ok(operations)
  134. }
  135. pub fn format(
  136. &mut self,
  137. interval: Interval,
  138. attribute: AttributeEntry,
  139. ) -> Result<DeltaTextOperations, SyncError> {
  140. validate_interval(&self.operations, &interval)?;
  141. tracing::trace!("format {} with {:?}", interval, attribute);
  142. let operations = self
  143. .view
  144. .format(&self.operations, attribute, interval)
  145. .unwrap();
  146. self.compose_operations(operations.clone())?;
  147. Ok(operations)
  148. }
  149. pub fn replace<T: ToString>(
  150. &mut self,
  151. interval: Interval,
  152. data: T,
  153. ) -> Result<DeltaTextOperations, SyncError> {
  154. validate_interval(&self.operations, &interval)?;
  155. let mut operations = DeltaTextOperations::default();
  156. let text = data.to_string();
  157. if !text.is_empty() {
  158. operations = self.view.insert(&self.operations, &text, interval)?;
  159. self.compose_operations(operations.clone())?;
  160. }
  161. if !interval.is_empty() {
  162. let delete = self.delete(interval)?;
  163. operations = operations.compose(&delete)?;
  164. }
  165. Ok(operations)
  166. }
  167. pub fn can_undo(&self) -> bool {
  168. self.history.can_undo()
  169. }
  170. pub fn can_redo(&self) -> bool {
  171. self.history.can_redo()
  172. }
  173. pub fn undo(&mut self) -> Result<UndoResult, SyncError> {
  174. match self.history.undo() {
  175. None => Err(SyncError::undo().context("Undo stack is empty")),
  176. Some(undo_operations) => {
  177. let (new_operations, inverted_operations) = self.invert(&undo_operations)?;
  178. self.set_operations(new_operations);
  179. self.history.add_redo(inverted_operations);
  180. Ok(UndoResult {
  181. operations: undo_operations,
  182. })
  183. },
  184. }
  185. }
  186. pub fn redo(&mut self) -> Result<UndoResult, SyncError> {
  187. match self.history.redo() {
  188. None => Err(SyncError::redo()),
  189. Some(redo_operations) => {
  190. let (new_operations, inverted_operations) = self.invert(&redo_operations)?;
  191. self.set_operations(new_operations);
  192. self.history.add_undo(inverted_operations);
  193. Ok(UndoResult {
  194. operations: redo_operations,
  195. })
  196. },
  197. }
  198. }
  199. pub fn is_empty(&self) -> bool {
  200. // The document is empty if its text is equal to the initial text.
  201. self.operations.json_str() == NewlineDocument::json_str()
  202. }
  203. }
  204. impl ClientDocument {
  205. fn invert(
  206. &self,
  207. operations: &DeltaTextOperations,
  208. ) -> Result<(DeltaTextOperations, DeltaTextOperations), SyncError> {
  209. // c = a.compose(b)
  210. // d = b.invert(a)
  211. // a = c.compose(d)
  212. let new_operations = self.operations.compose(operations)?;
  213. let inverted_operations = operations.invert(&self.operations);
  214. Ok((new_operations, inverted_operations))
  215. }
  216. }
  217. fn validate_interval(
  218. operations: &DeltaTextOperations,
  219. interval: &Interval,
  220. ) -> Result<(), SyncError> {
  221. if operations.utf16_target_len < interval.end {
  222. tracing::error!(
  223. "{:?} out of bounds. should 0..{}",
  224. interval,
  225. operations.utf16_target_len
  226. );
  227. return Err(SyncError::out_of_bound());
  228. }
  229. Ok(())
  230. }