cola/
text_edit.rs

1use core::ops::Range;
2
3use crate::*;
4
5/// A range of text inserted into a [`Replica`].
6///
7/// Despite the name, this type does not contain the text string itself, only
8/// the [`ReplicaId`] of the [`Replica`] that inserted it and its temporal
9/// range in it. These can be accessed via the
10/// [`inserted_by`](Text::inserted_by) and
11/// [`temporal_range`](Text::temporal_range) methods respectively.
12#[derive(Clone, PartialEq, Eq, Hash)]
13#[cfg_attr(feature = "encode", derive(serde::Serialize, serde::Deserialize))]
14pub struct Text {
15    pub(crate) inserted_by: ReplicaId,
16    pub(crate) range: Range<Length>,
17}
18
19impl core::fmt::Debug for Text {
20    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
21        write!(f, "{:x}.{:?}", self.inserted_by, self.range)
22    }
23}
24
25impl Text {
26    #[inline]
27    pub(crate) fn end(&self) -> Length {
28        self.range.end
29    }
30
31    /// Returns the [`ReplicaId`] of the [`Replica`] that inserted this text.
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// # use cola::Replica;
37    /// let mut replica1 = Replica::new(1, 0);
38    ///
39    /// let insertion = replica1.inserted(0, 1);
40    ///
41    /// assert_eq!(insertion.text().inserted_by(), replica1.id());
42    /// ```
43    #[inline]
44    pub fn inserted_by(&self) -> ReplicaId {
45        self.inserted_by
46    }
47
48    #[inline]
49    pub(crate) fn len(&self) -> Length {
50        self.range.len()
51    }
52
53    #[inline]
54    pub(crate) fn new(inserted_by: ReplicaId, range: Range<Length>) -> Self {
55        Self { inserted_by, range }
56    }
57
58    #[inline]
59    pub(crate) fn start(&self) -> Length {
60        self.range.start
61    }
62
63    /// Returns the temporal range of this text in the `Replica` that inserted
64    /// it.
65    ///
66    /// Each `Replica` keeps an internal character clock that starts at zero
67    /// and is incremented each time the [`inserted`](crate::Replica::inserted)
68    /// method is called by an amount equal to the length of the inserted text.
69    ///
70    /// Since the insertion history of *a single* `Replica` is linear and
71    /// immutable, this clock can be used to uniquely identify each character
72    /// in the document without knowing what the actual text associated with
73    /// its insertion is.
74    ///
75    /// Note that this range has absolutely nothing to do with the *spatial
76    /// offset* at which the text was inserted. Its start and end simply refer
77    /// to the values of the character clock of the `Replica` that inserted the
78    /// `Text` before and after the insertion.
79    ///
80    /// It's up to you to decide how to map these temporal ranges to the actual
81    /// text contents inserted by the various peers.
82    ///
83    /// # Examples
84    ///
85    /// ```
86    /// # use cola::Replica;
87    /// let mut replica1 = Replica::new(1, 0);
88    ///
89    /// // Peer 1 inserts 1, 2, 3 and 4 characters at the start of the
90    /// // document.
91    /// let insertion1 = replica1.inserted(0, 1);
92    /// let insertion2 = replica1.inserted(0, 2);
93    /// let insertion3 = replica1.inserted(0, 3);
94    /// let insertion4 = replica1.inserted(0, 4);
95    ///
96    /// // Notice how:
97    /// // - the temporal range of the first insertion starts at zero;
98    /// // - the start of each range is equal to the end of the previous one;
99    /// // - the length of each range matches the one passed to
100    /// //   `replica1.inserted`.
101    ///
102    /// assert_eq!(insertion1.text().temporal_range(), 0..1);
103    /// assert_eq!(insertion2.text().temporal_range(), 1..3);
104    /// assert_eq!(insertion3.text().temporal_range(), 3..6);
105    /// assert_eq!(insertion4.text().temporal_range(), 6..10);
106    /// ```
107    #[inline]
108    pub fn temporal_range(&self) -> Range<Length> {
109        self.range.clone()
110    }
111}