1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
//! The text storage structures

use std::cell::{Cell, RefCell};
use std::rc::Rc;

use super::*;

/// Error type for [`LineText`]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LineTextError {
  pub text: String,
}
impl std::fmt::Display for LineTextError {
  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    write!(f, "Invalid line text given: {}", self.text)
  }
}
impl std::error::Error for LineTextError {}

/// An immutable text data container for a single line of text
///
/// Upon creation verifies that the text is newline terminated and contains no
/// other newlines. Also uses reference counting to prevent data duplication
/// when cloning (as this will be done very often within `add-ed`'s logic).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LineText {
  inner: Rc<String>,
}
impl LineText {
  /// Create new LineText instance
  ///
  /// Returns error if the text you are creating it from isn't a single valid
  /// line (Exactly one newline should be in it, the last character).
  pub fn new<T: Into<String>>(
    text: T,
  ) -> Result<Self, LineTextError> {
    let text: String = text.into();
    // We can safely subtract 1 from len after verifying it contains a '\n'
    if !text.ends_with('\n') || text[..text.len()-1].contains('\n') {
      Err(LineTextError{text})
    } else {
      Ok(Self{ inner: Rc::new(text) })
    }
  }
}
impl std::ops::Deref for LineText {
  type Target = String;
  fn deref(&self) -> &Self::Target {
    &(*self.inner)
  }
}
impl TryFrom<&str> for LineText {
  type Error = LineTextError;
  fn try_from(t: &str) -> Result<Self, Self::Error> {
    Self::new(t)
  }
}

/// Text data and metadata for a single line of text
///
/// Note that the internal field accessed by `.tag()` and `.set_tag()` is shared
/// throughout the historical instances of the Line.
///
/// The main way to create, move around or clone Line instances is through
/// [`PubLine`]. For this purpose PubLine implements From<&Line> and Line
/// implements From<PubLine>. (Creating a PubLine from a Line is basically free,
/// creating a Line from a PubLine includes some allocations but still cheap.)
/// This is to ensure that the internal pointers that Line uses internally
/// (sharing objects between historical states) aren't pointing to somewhere
/// they shouldn't.

// We don't derive Clone, since it wouldn't be  what library users expect.
#[derive(Debug, PartialEq, Eq)]
pub struct Line {
  // Tracks if the line has been matched in a 'g' or similar command in a shared
  // instance throughout the line's lifetime (to save on allocations)
  // (A change to BitVec would be good, TODO.)
  //
  // To support nested invocations we have a vector, where index 0 is the
  // outermost invocation and nested invocation have incrementing indices.
  //
  // Note that this has one main gotchas that must be handled where this is
  // used:
  //   There mustn't be any old data on an index when an invocation uses it,
  //   not even outside the selection acted upon.
  //   (Handled by src/cmd/regex_commands.rs : mark_matching, which resizes
  //   Vec to the correct length and overwrites the soon to be relevant index
  //   across all lines. Since mark_matching is called for each nested
  //   invocation this should be run on every relevant index before it's used.)
  //
  // Also note that this will be empty on newly created lines, but get_matching
  // handles this by defaulting to false and mark_matching explicitly resizes to
  // the size it needs.
  pub(crate) matched: Rc<RefCell<Vec<bool>>>,
  // The tag set on the given line
  //
  // Rc<Cell> makes it so we can have the same tag throughout all snapshots of
  // the same line, but also requires us to hide the variable (so library users
  // can't clone the Rc and cause strange behaviour.
  tag: Rc<Cell<char>>,
  /// The text data for a given line
  ///
  /// [`LineText`] ensures that the text data is valid for a single line and
  /// implements reference counted cloning, allowing easy re-use of the same
  /// data (through history, clipboard and even multiple identical lines
  /// (depending on how they are created)).
  pub text: LineText,
}
impl Line {
  /// Create a new line with given text data
  ///
  /// Returns LineTextError if the data is not newline terminated or contains
  /// other newlines than the terminating one.
  ///
  /// Sets the tag to `'\0'`, which is the data representation of not tagged.
  pub (crate) fn new<T: Into<String>>(
    text: T,
  ) -> Result<Self, LineTextError> {
    Ok(Self{
      matched: Rc::new(RefCell::new(Vec::new())),
      tag: Rc::new(Cell::new('\0')),
      text: LineText::new(text)?,
    })
  }
  /// Get the current value of the tag field.
  pub fn tag(&self) -> char {
    self.tag.get()
  }
  /// Set the tag to given character
  ///
  /// Note that this changes all historical states of this line.
  pub fn set_tag(&self, new: char) {
    self.tag.set(new)
  }
}
// Our internal-only Clone implementation, to enable snapshotting without
// misleading library users that they can Clone Lines.
impl Snapshot for Line {
  fn create_snapshot(&self) -> Self {
    Line{
      tag: self.tag.clone(),
      matched: self.matched.clone(),
      text: self.text.clone(),
    }
  }
}
impl From<&PubLine> for Line {
  fn from(l: &PubLine) -> Self {
    Self{
      text: l.text.clone(),
      tag: Rc::new(Cell::new(l.tag)),
      matched: Rc::new(RefCell::new(Vec::new())),
    }
  }
}
impl TryFrom<&str> for Line {
  type Error = LineTextError;
  fn try_from(t: &str) -> Result<Self, Self::Error> {
    Self::new(t)
  }
}

/// A fully public version of the [`Line`] struct
///
/// Intended for API interaction, since it cannot represent the internal state
/// in Line which could cause trouble if invalid.
///
/// [`From`] is implemented both ways, to make it easy to convert into and from
/// [`Line`]. Some TryFrom implementations that may be useful also exist.
///
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PubLine {
  /// The tag set on the line
  ///
  /// See [`Line`] `.tag()` and `.set_tag()`, but note that we disconnect the
  /// shared tag state through history by converting into this.
  pub tag: char,
  /// The text data for the line
  ///
  /// See [`Line'].text.
  pub text: LineText,
}
impl<'a> TryFrom<&'a str> for PubLine {
  type Error = LineTextError;
  fn try_from(t: &str) -> Result<Self, Self::Error> {
    Ok(Self{tag: '\0', text: LineText::new(t)?})
  }
}
impl<'a> TryFrom<&'a &'a str> for PubLine {
  type Error = LineTextError;
  fn try_from(t: &&str) -> Result<Self, Self::Error> {
    Ok(Self{tag: '\0', text: LineText::new(*t)?})
  }
}
impl<'a> TryFrom<(char, &'a str)> for PubLine {
  type Error = LineTextError;
  fn try_from(l: (char, &str)) -> Result<Self, Self::Error> {
    (&l).try_into()
  }
}
impl<'a> TryFrom<&'a (char, &'a str)> for PubLine {
  type Error = LineTextError;
  fn try_from(l: &(char, &str)) -> Result<Self, Self::Error> {
    Ok(Self{tag: l.0, text: LineText::new(l.1)?})
  }
}
impl From<&Line> for PubLine {
  fn from(l: &Line) -> Self {
    Self{tag: l.tag.get(), text: l.text.clone()}
  }
}