document.rs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. use tokio::sync::mpsc;
  2. use lib_ot::{
  3. core::*,
  4. rich_text::{RichTextAttribute, RichTextDelta},
  5. };
  6. use crate::{
  7. core::document::{
  8. default::initial_delta,
  9. history::{History, UndoResult},
  10. view::{View, RECORD_THRESHOLD},
  11. },
  12. errors::CollaborateError,
  13. };
  14. pub trait CustomDocument {
  15. fn init_delta() -> RichTextDelta;
  16. }
  17. pub struct PlainDoc();
  18. impl CustomDocument for PlainDoc {
  19. fn init_delta() -> RichTextDelta { RichTextDelta::new() }
  20. }
  21. pub struct FlowyDoc();
  22. impl CustomDocument for FlowyDoc {
  23. fn init_delta() -> RichTextDelta { initial_delta() }
  24. }
  25. pub struct Document {
  26. delta: RichTextDelta,
  27. history: History,
  28. view: View,
  29. last_edit_time: usize,
  30. notify: Option<mpsc::UnboundedSender<()>>,
  31. }
  32. impl Document {
  33. pub fn new<C: CustomDocument>() -> Self { Self::from_delta(C::init_delta()) }
  34. pub fn from_delta(delta: RichTextDelta) -> Self {
  35. Document {
  36. delta,
  37. history: History::new(),
  38. view: View::new(),
  39. last_edit_time: 0,
  40. notify: None,
  41. }
  42. }
  43. pub fn from_json(json: &str) -> Result<Self, CollaborateError> {
  44. let delta = RichTextDelta::from_json(json)?;
  45. Ok(Self::from_delta(delta))
  46. }
  47. pub fn to_json(&self) -> String { self.delta.to_json() }
  48. pub fn to_bytes(&self) -> Vec<u8> { self.delta.clone().to_bytes().to_vec() }
  49. pub fn to_plain_string(&self) -> String { self.delta.apply("").unwrap() }
  50. pub fn delta(&self) -> &RichTextDelta { &self.delta }
  51. pub fn md5(&self) -> String {
  52. // TODO: Optimize the cost of calculating the md5
  53. let bytes = self.to_bytes();
  54. format!("{:x}", md5::compute(bytes))
  55. }
  56. pub fn set_notify(&mut self, notify: mpsc::UnboundedSender<()>) { self.notify = Some(notify); }
  57. pub fn set_delta(&mut self, data: RichTextDelta) {
  58. self.delta = data;
  59. match &self.notify {
  60. None => {},
  61. Some(notify) => {
  62. let _ = notify.send(());
  63. },
  64. }
  65. }
  66. pub fn compose_delta(&mut self, mut delta: RichTextDelta) -> Result<(), CollaborateError> {
  67. tracing::trace!("👉 receive change: {}", delta);
  68. trim(&mut delta);
  69. tracing::trace!("{} compose {}", &self.delta.to_json(), delta.to_json());
  70. let mut composed_delta = self.delta.compose(&delta)?;
  71. let mut undo_delta = delta.invert(&self.delta);
  72. let now = chrono::Utc::now().timestamp_millis() as usize;
  73. if now - self.last_edit_time < RECORD_THRESHOLD {
  74. if let Some(last_delta) = self.history.undo() {
  75. tracing::trace!("compose previous change");
  76. tracing::trace!("current = {}", undo_delta);
  77. tracing::trace!("previous = {}", last_delta);
  78. undo_delta = undo_delta.compose(&last_delta)?;
  79. }
  80. } else {
  81. self.last_edit_time = now;
  82. }
  83. tracing::trace!("👉 receive change undo: {}", undo_delta);
  84. if !undo_delta.is_empty() {
  85. self.history.record(undo_delta);
  86. }
  87. tracing::trace!("compose result: {}", composed_delta.to_json());
  88. trim(&mut composed_delta);
  89. self.set_delta(composed_delta);
  90. Ok(())
  91. }
  92. pub fn insert<T: ToString>(&mut self, index: usize, data: T) -> Result<RichTextDelta, CollaborateError> {
  93. let interval = Interval::new(index, index);
  94. let _ = validate_interval(&self.delta, &interval)?;
  95. let text = data.to_string();
  96. let delta = self.view.insert(&self.delta, &text, interval)?;
  97. self.compose_delta(delta.clone())?;
  98. Ok(delta)
  99. }
  100. pub fn delete(&mut self, interval: Interval) -> Result<RichTextDelta, CollaborateError> {
  101. let _ = validate_interval(&self.delta, &interval)?;
  102. debug_assert_eq!(interval.is_empty(), false);
  103. let delete = self.view.delete(&self.delta, interval)?;
  104. if !delete.is_empty() {
  105. let _ = self.compose_delta(delete.clone())?;
  106. }
  107. Ok(delete)
  108. }
  109. pub fn format(
  110. &mut self,
  111. interval: Interval,
  112. attribute: RichTextAttribute,
  113. ) -> Result<RichTextDelta, CollaborateError> {
  114. let _ = validate_interval(&self.delta, &interval)?;
  115. tracing::trace!("format with {} at {}", attribute, interval);
  116. let format_delta = self.view.format(&self.delta, attribute, interval).unwrap();
  117. self.compose_delta(format_delta.clone())?;
  118. Ok(format_delta)
  119. }
  120. pub fn replace<T: ToString>(&mut self, interval: Interval, data: T) -> Result<RichTextDelta, CollaborateError> {
  121. let _ = validate_interval(&self.delta, &interval)?;
  122. let mut delta = RichTextDelta::default();
  123. let text = data.to_string();
  124. if !text.is_empty() {
  125. delta = self.view.insert(&self.delta, &text, interval)?;
  126. self.compose_delta(delta.clone())?;
  127. }
  128. if !interval.is_empty() {
  129. let delete = self.delete(interval)?;
  130. delta = delta.compose(&delete)?;
  131. }
  132. Ok(delta)
  133. }
  134. pub fn can_undo(&self) -> bool { self.history.can_undo() }
  135. pub fn can_redo(&self) -> bool { self.history.can_redo() }
  136. pub fn undo(&mut self) -> Result<UndoResult, CollaborateError> {
  137. match self.history.undo() {
  138. None => Err(CollaborateError::undo().context("Undo stack is empty")),
  139. Some(undo_delta) => {
  140. let (new_delta, inverted_delta) = self.invert(&undo_delta)?;
  141. let result = UndoResult::success(new_delta.target_len as usize);
  142. self.set_delta(new_delta);
  143. self.history.add_redo(inverted_delta);
  144. Ok(result)
  145. },
  146. }
  147. }
  148. pub fn redo(&mut self) -> Result<UndoResult, CollaborateError> {
  149. match self.history.redo() {
  150. None => Err(CollaborateError::redo()),
  151. Some(redo_delta) => {
  152. let (new_delta, inverted_delta) = self.invert(&redo_delta)?;
  153. let result = UndoResult::success(new_delta.target_len as usize);
  154. self.set_delta(new_delta);
  155. self.history.add_undo(inverted_delta);
  156. Ok(result)
  157. },
  158. }
  159. }
  160. }
  161. impl Document {
  162. fn invert(&self, delta: &RichTextDelta) -> Result<(RichTextDelta, RichTextDelta), CollaborateError> {
  163. // c = a.compose(b)
  164. // d = b.invert(a)
  165. // a = c.compose(d)
  166. tracing::trace!("Invert {}", delta);
  167. let new_delta = self.delta.compose(delta)?;
  168. let inverted_delta = delta.invert(&self.delta);
  169. Ok((new_delta, inverted_delta))
  170. }
  171. }
  172. fn validate_interval(delta: &RichTextDelta, interval: &Interval) -> Result<(), CollaborateError> {
  173. if delta.target_len < interval.end {
  174. log::error!("{:?} out of bounds. should 0..{}", interval, delta.target_len);
  175. return Err(CollaborateError::out_of_bound());
  176. }
  177. Ok(())
  178. }
  179. /// Removes trailing retain operation with empty attributes, if present.
  180. pub fn trim(delta: &mut RichTextDelta) {
  181. if let Some(last) = delta.ops.last() {
  182. if last.is_retain() && last.is_plain() {
  183. delta.ops.pop();
  184. }
  185. }
  186. }