1 use crate::{Line, Operation, OperationType, Update};
2 
3 /// Line cache struct to work with xi update protocol.
4 #[derive(Clone, Debug, Default)]
5 pub struct LineCache {
6     invalid_before: u64,
7     lines: Vec<Line>,
8     invalid_after: u64,
9 }
10 
11 impl LineCache {
12     /// Retrieve the number of invalid lines before
13     /// the start of the line cache.
before(&self) -> u6414     pub fn before(&self) -> u64 {
15         self.invalid_before
16     }
17 
18     /// Retrieve the number of invalid lines after
19     /// the line cache.
after(&self) -> u6420     pub fn after(&self) -> u64 {
21         self.invalid_after
22     }
23 
24     /// Retrieve all lines in the cache.
lines(&self) -> &Vec<Line>25     pub fn lines(&self) -> &Vec<Line> {
26         &self.lines
27     }
28 
29     /// Retrieve the total height of the linecache
height(&self) -> u6430     pub fn height(&self) -> u64 {
31         self.before() + self.lines.len() as u64 + self.after()
32     }
33 
34     /// Handle an xi-core update.
update(&mut self, update: Update)35     pub fn update(&mut self, update: Update) {
36         debug!("line cache before update: {:?}", self);
37         debug!(
38             "operations to be applied to the line cache: {:?}",
39             &update.operations
40         );
41         let LineCache {
42             ref mut lines,
43             ref mut invalid_before,
44             ref mut invalid_after,
45         } = *self;
46         let helper = UpdateHelper {
47             old_lines: lines,
48             old_invalid_before: invalid_before,
49             old_invalid_after: invalid_after,
50             new_lines: Vec::new(),
51             new_invalid_before: 0,
52             new_invalid_after: 0,
53         };
54         helper.update(update.operations);
55     }
56 
is_empty(&self) -> bool57     pub fn is_empty(&self) -> bool {
58         self.lines.is_empty()
59     }
60 }
61 
62 #[derive(Debug)]
63 struct UpdateHelper<'a, 'b, 'c> {
64     old_lines: &'a mut Vec<Line>,
65     old_invalid_before: &'b mut u64,
66     old_invalid_after: &'c mut u64,
67     new_lines: Vec<Line>,
68     new_invalid_before: u64,
69     new_invalid_after: u64,
70 }
71 
72 impl<'a, 'b, 'c> UpdateHelper<'a, 'b, 'c> {
apply_copy(&mut self, nb_lines: u64, first_line_num: Option<u64>)73     fn apply_copy(&mut self, nb_lines: u64, first_line_num: Option<u64>) {
74         debug!("copying {} lines", nb_lines);
75         let UpdateHelper {
76             ref mut old_lines,
77             ref mut old_invalid_before,
78             ref mut old_invalid_after,
79             ref mut new_lines,
80             ref mut new_invalid_before,
81             ref mut new_invalid_after,
82             ..
83         } = *self;
84 
85         // The number of lines left to copy
86         let mut nb_lines = nb_lines;
87 
88         // STEP 1: Handle the invalid lines that precede the valid ones
89         // ------------------------------------------------------------
90 
91         if **old_invalid_before >= nb_lines {
92             // case 1: there are more (or equal) invalid lines than lines to copy
93 
94             // decrement old_invalid_lines by nb_lines
95             **old_invalid_before -= nb_lines;
96 
97             // and increment new_invalid_lines by the same amount
98             *new_invalid_before += nb_lines;
99 
100             // there is no more line to copy so we're done
101             return;
102         } else if **old_invalid_after > 0 {
103             // case 2: there are more lines to copy than invalid lines
104 
105             // decrement the nb of lines to copy by the number of invalid lines
106             nb_lines -= **old_invalid_before;
107 
108             // increment new_invalid_lines by the same amount
109             *new_invalid_before += **old_invalid_before;
110 
111             // we don't have any invalid lines left
112             **old_invalid_before = 0;
113         }
114 
115         // STEP 2: Handle the valid lines
116         // ------------------------------------------------------------
117 
118         let nb_valid_lines = old_lines.len();
119         let range;
120 
121         if nb_lines <= (nb_valid_lines as u64) {
122             // case 1: the are more (or equal) valid lines than lines to copy
123 
124             // the range of lines to copy: from the start to nb_lines - 1;
125             range = 0..nb_lines as usize;
126 
127             // after the copy, we won't have any line remaining to copy
128             nb_lines = 0;
129         } else {
130             // case 2: there are more lines to copy than valid lines
131 
132             // we copy all the valid lines
133             range = 0..nb_valid_lines;
134 
135             // after the operation we'll have (nb_lines - nb_valid_lines) left to copy
136             nb_lines -= nb_valid_lines as u64;
137         }
138 
139         // we'll only apply the copy if there actually are valid lines to copy
140         if nb_valid_lines > 0 {
141             let diff = if let Some(new_first_line_num) = first_line_num {
142                 // find the first "real" line (ie non-wrapped), and
143                 // compute the difference between its line number and
144                 // its *new* line number, given by the "copy"
145                 // operation. This will be used to update the line
146                 // number for all the lines we copy.
147                 old_lines
148                     .iter()
149                     .find_map(|line| {
150                         line.line_num
151                             .map(|num| new_first_line_num as i64 - num as i64)
152                     })
153                     .unwrap_or(0)
154             } else {
155                 // if the "copy" operation does not specify a new line
156                 // number, just set the diff to 0
157                 0
158             };
159 
160             let copied_lines = old_lines.drain(range).map(|mut line| {
161                 line.line_num = line
162                     .line_num
163                     .map(|line_num| (line_num as i64 + diff) as u64);
164                 line
165             });
166             new_lines.extend(copied_lines);
167         }
168 
169         // if there are no more lines to copy we're done
170         if nb_lines == 0 {
171             return;
172         }
173 
174         // STEP 3: Handle the remaining invalid lines
175         // ------------------------------------------------------------
176 
177         // We should have at least enought invalid lines to copy, otherwise it indicates there's a
178         // problem, and we panic.
179         if **old_invalid_after >= nb_lines {
180             **old_invalid_after -= nb_lines;
181             *new_invalid_after += nb_lines;
182         } else {
183             error!(
184                 "{} lines left to copy, but only {} lines in the old cache",
185                 nb_lines, **old_invalid_after
186             );
187             panic!("cache update failed");
188         }
189     }
190 
apply_skip(&mut self, nb_lines: u64)191     fn apply_skip(&mut self, nb_lines: u64) {
192         debug!("skipping {} lines", nb_lines);
193 
194         let UpdateHelper {
195             ref mut old_lines,
196             ref mut old_invalid_before,
197             ref mut old_invalid_after,
198             ..
199         } = *self;
200 
201         let mut nb_lines = nb_lines;
202 
203         // Skip invalid lines that comes before the valid ones.
204         if **old_invalid_before > nb_lines {
205             **old_invalid_before -= nb_lines;
206             return;
207         } else if **old_invalid_before > 0 {
208             nb_lines -= **old_invalid_before;
209             **old_invalid_before = 0;
210         }
211 
212         // Skip the valid lines
213         let nb_valid_lines = old_lines.len();
214         if nb_lines < nb_valid_lines as u64 {
215             old_lines.drain(0..nb_lines as usize).last();
216             return;
217         } else {
218             old_lines.drain(..).last();
219             nb_lines -= nb_valid_lines as u64;
220         }
221 
222         // Skip the remaining invalid lines
223         if **old_invalid_after >= nb_lines {
224             **old_invalid_after -= nb_lines;
225             return;
226         }
227 
228         error!(
229             "{} lines left to skip, but only {} lines in the old cache",
230             nb_lines, **old_invalid_after
231         );
232         panic!("cache update failed");
233     }
234 
apply_invalidate(&mut self, nb_lines: u64)235     fn apply_invalidate(&mut self, nb_lines: u64) {
236         debug!("invalidating {} lines", nb_lines);
237         if self.new_lines.is_empty() {
238             self.new_invalid_before += nb_lines;
239         } else {
240             self.new_invalid_after += nb_lines;
241         }
242     }
243 
apply_insert(&mut self, mut lines: Vec<Line>)244     fn apply_insert(&mut self, mut lines: Vec<Line>) {
245         debug!("inserting {} lines", lines.len());
246         self.new_lines.extend(lines.drain(..).map(|mut line| {
247             trim_new_line(&mut line.text);
248             line
249         }));
250     }
251 
apply_update(&mut self, nb_lines: u64, lines: Vec<Line>)252     fn apply_update(&mut self, nb_lines: u64, lines: Vec<Line>) {
253         debug!("updating {} lines", nb_lines);
254         let UpdateHelper {
255             ref mut old_lines,
256             ref mut new_lines,
257             ..
258         } = *self;
259         if nb_lines > old_lines.len() as u64 {
260             error!(
261                 "{} lines to update, but only {} lines in cache",
262                 nb_lines,
263                 old_lines.len()
264             );
265             panic!("failed to update the cache");
266         }
267         new_lines.extend(
268             old_lines
269                 .drain(0..nb_lines as usize)
270                 .zip(lines.into_iter())
271                 .map(|(mut old_line, update)| {
272                     old_line.cursor = update.cursor;
273                     old_line.styles = update.styles;
274                     old_line
275                 }),
276         )
277     }
278 
update(mut self, operations: Vec<Operation>)279     fn update(mut self, operations: Vec<Operation>) {
280         trace!("updating the line cache");
281         trace!("cache state before: {:?}", &self);
282         trace!("operations to be applied: {:?}", &operations);
283         for op in operations {
284             debug!("operation: {:?}", &op);
285             debug!("cache helper before operation {:?}", &self);
286             match op.operation_type {
287                 OperationType::Copy_ => (&mut self).apply_copy(op.nb_lines, op.line_num),
288                 OperationType::Skip => (&mut self).apply_skip(op.nb_lines),
289                 OperationType::Invalidate => (&mut self).apply_invalidate(op.nb_lines),
290                 OperationType::Insert => (&mut self).apply_insert(op.lines),
291                 OperationType::Update => (&mut self).apply_update(op.nb_lines, op.lines),
292             }
293             debug!("cache helper after operation {:?}", &self);
294         }
295         *self.old_lines = self.new_lines;
296         *self.old_invalid_before = self.new_invalid_before;
297         *self.old_invalid_after = self.new_invalid_after;
298     }
299 }
300 
trim_new_line(text: &mut String)301 fn trim_new_line(text: &mut String) {
302     if let Some('\n') = text.chars().last() {
303         text.pop();
304     }
305 }
306