document_pad.rs 7.7 KB

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