document_pad.rs 6.8 KB

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