vt100/
row.rs

1use crate::term::BufWrite as _;
2
3#[derive(Clone, Debug)]
4pub struct Row {
5    cells: Vec<crate::Cell>,
6    wrapped: bool,
7}
8
9impl Row {
10    pub fn new(cols: u16) -> Self {
11        Self {
12            cells: vec![crate::Cell::new(); usize::from(cols)],
13            wrapped: false,
14        }
15    }
16
17    fn cols(&self) -> u16 {
18        self.cells
19            .len()
20            .try_into()
21            // we limit the number of cols to a u16 (see Size)
22            .unwrap()
23    }
24
25    pub fn clear(&mut self, attrs: crate::attrs::Attrs) {
26        for cell in &mut self.cells {
27            cell.clear(attrs);
28        }
29        self.wrapped = false;
30    }
31
32    fn cells(&self) -> impl Iterator<Item = &crate::Cell> {
33        self.cells.iter()
34    }
35
36    pub fn get(&self, col: u16) -> Option<&crate::Cell> {
37        self.cells.get(usize::from(col))
38    }
39
40    pub fn get_mut(&mut self, col: u16) -> Option<&mut crate::Cell> {
41        self.cells.get_mut(usize::from(col))
42    }
43
44    pub fn insert(&mut self, i: u16, cell: crate::Cell) {
45        self.cells.insert(usize::from(i), cell);
46        self.wrapped = false;
47    }
48
49    pub fn remove(&mut self, i: u16) {
50        self.clear_wide(i);
51        self.cells.remove(usize::from(i));
52        self.wrapped = false;
53    }
54
55    pub fn erase(&mut self, i: u16, attrs: crate::attrs::Attrs) {
56        let wide = self.cells[usize::from(i)].is_wide();
57        self.clear_wide(i);
58        self.cells[usize::from(i)].clear(attrs);
59        if i == self.cols() - if wide { 2 } else { 1 } {
60            self.wrapped = false;
61        }
62    }
63
64    pub fn truncate(&mut self, len: u16) {
65        self.cells.truncate(usize::from(len));
66        self.wrapped = false;
67        let last_cell = &mut self.cells[usize::from(len) - 1];
68        if last_cell.is_wide() {
69            last_cell.clear(*last_cell.attrs());
70        }
71    }
72
73    pub fn resize(&mut self, len: u16, cell: crate::Cell) {
74        self.cells.resize(usize::from(len), cell);
75        self.wrapped = false;
76    }
77
78    pub fn wrap(&mut self, wrap: bool) {
79        self.wrapped = wrap;
80    }
81
82    pub fn wrapped(&self) -> bool {
83        self.wrapped
84    }
85
86    pub fn clear_wide(&mut self, col: u16) {
87        let cell = &self.cells[usize::from(col)];
88        let other = if cell.is_wide() {
89            &mut self.cells[usize::from(col + 1)]
90        } else if cell.is_wide_continuation() {
91            &mut self.cells[usize::from(col - 1)]
92        } else {
93            return;
94        };
95        other.clear(*other.attrs());
96    }
97
98    pub fn write_contents(
99        &self,
100        contents: &mut String,
101        start: u16,
102        width: u16,
103        wrapping: bool,
104    ) {
105        let mut prev_was_wide = false;
106
107        let mut prev_col = start;
108        for (col, cell) in self
109            .cells()
110            .enumerate()
111            .skip(usize::from(start))
112            .take(usize::from(width))
113        {
114            if prev_was_wide {
115                prev_was_wide = false;
116                continue;
117            }
118            prev_was_wide = cell.is_wide();
119
120            // we limit the number of cols to a u16 (see Size)
121            let col: u16 = col.try_into().unwrap();
122            if cell.has_contents() {
123                for _ in 0..(col - prev_col) {
124                    contents.push(' ');
125                }
126                prev_col += col - prev_col;
127
128                contents.push_str(cell.contents());
129                prev_col += if cell.is_wide() { 2 } else { 1 };
130            }
131        }
132        if prev_col == start && wrapping {
133            contents.push('\n');
134        }
135    }
136
137    pub fn write_contents_formatted(
138        &self,
139        contents: &mut Vec<u8>,
140        start: u16,
141        width: u16,
142        row: u16,
143        wrapping: bool,
144        prev_pos: Option<crate::grid::Pos>,
145        prev_attrs: Option<crate::attrs::Attrs>,
146    ) -> (crate::grid::Pos, crate::attrs::Attrs) {
147        let mut prev_was_wide = false;
148        let default_cell = crate::Cell::new();
149
150        let mut prev_pos = prev_pos.unwrap_or_else(|| {
151            if wrapping {
152                crate::grid::Pos {
153                    row: row - 1,
154                    col: self.cols(),
155                }
156            } else {
157                crate::grid::Pos { row, col: start }
158            }
159        });
160        let mut prev_attrs = prev_attrs.unwrap_or_default();
161
162        let first_cell = &self.cells[usize::from(start)];
163        if wrapping && first_cell == &default_cell {
164            let default_attrs = default_cell.attrs();
165            if &prev_attrs != default_attrs {
166                default_attrs.write_escape_code_diff(contents, &prev_attrs);
167                prev_attrs = *default_attrs;
168            }
169            contents.push(b' ');
170            crate::term::Backspace.write_buf(contents);
171            crate::term::EraseChar::new(1).write_buf(contents);
172            prev_pos = crate::grid::Pos { row, col: 0 };
173        }
174
175        let mut erase: Option<(u16, &crate::attrs::Attrs)> = None;
176        for (col, cell) in self
177            .cells()
178            .enumerate()
179            .skip(usize::from(start))
180            .take(usize::from(width))
181        {
182            if prev_was_wide {
183                prev_was_wide = false;
184                continue;
185            }
186            prev_was_wide = cell.is_wide();
187
188            // we limit the number of cols to a u16 (see Size)
189            let col: u16 = col.try_into().unwrap();
190            let pos = crate::grid::Pos { row, col };
191
192            if let Some((prev_col, attrs)) = erase {
193                if cell.has_contents() || cell.attrs() != attrs {
194                    let new_pos = crate::grid::Pos { row, col: prev_col };
195                    if wrapping
196                        && prev_pos.row + 1 == new_pos.row
197                        && prev_pos.col >= self.cols()
198                    {
199                        if new_pos.col > 0 {
200                            contents.extend(
201                                " ".repeat(usize::from(new_pos.col))
202                                    .as_bytes(),
203                            );
204                        } else {
205                            contents.extend(b" ");
206                            crate::term::Backspace.write_buf(contents);
207                        }
208                    } else {
209                        crate::term::MoveFromTo::new(prev_pos, new_pos)
210                            .write_buf(contents);
211                    }
212                    prev_pos = new_pos;
213                    if &prev_attrs != attrs {
214                        attrs.write_escape_code_diff(contents, &prev_attrs);
215                        prev_attrs = *attrs;
216                    }
217                    crate::term::EraseChar::new(pos.col - prev_col)
218                        .write_buf(contents);
219                    erase = None;
220                }
221            }
222
223            if cell != &default_cell {
224                let attrs = cell.attrs();
225                if cell.has_contents() {
226                    if pos != prev_pos {
227                        if !wrapping
228                            || prev_pos.row + 1 != pos.row
229                            || prev_pos.col
230                                < self.cols() - u16::from(cell.is_wide())
231                            || pos.col != 0
232                        {
233                            crate::term::MoveFromTo::new(prev_pos, pos)
234                                .write_buf(contents);
235                        }
236                        prev_pos = pos;
237                    }
238
239                    if &prev_attrs != attrs {
240                        attrs.write_escape_code_diff(contents, &prev_attrs);
241                        prev_attrs = *attrs;
242                    }
243
244                    prev_pos.col += if cell.is_wide() { 2 } else { 1 };
245                    let cell_contents = cell.contents();
246                    contents.extend(cell_contents.as_bytes());
247                } else if erase.is_none() {
248                    erase = Some((pos.col, attrs));
249                }
250            }
251        }
252        if let Some((prev_col, attrs)) = erase {
253            let new_pos = crate::grid::Pos { row, col: prev_col };
254            if wrapping
255                && prev_pos.row + 1 == new_pos.row
256                && prev_pos.col >= self.cols()
257            {
258                if new_pos.col > 0 {
259                    contents.extend(
260                        " ".repeat(usize::from(new_pos.col)).as_bytes(),
261                    );
262                } else {
263                    contents.extend(b" ");
264                    crate::term::Backspace.write_buf(contents);
265                }
266            } else {
267                crate::term::MoveFromTo::new(prev_pos, new_pos)
268                    .write_buf(contents);
269            }
270            prev_pos = new_pos;
271            if &prev_attrs != attrs {
272                attrs.write_escape_code_diff(contents, &prev_attrs);
273                prev_attrs = *attrs;
274            }
275            crate::term::ClearRowForward.write_buf(contents);
276        }
277
278        (prev_pos, prev_attrs)
279    }
280
281    // while it's true that most of the logic in this is identical to
282    // write_contents_formatted, i can't figure out how to break out the
283    // common parts without making things noticeably slower.
284    pub fn write_contents_diff(
285        &self,
286        contents: &mut Vec<u8>,
287        prev: &Self,
288        start: u16,
289        width: u16,
290        row: u16,
291        wrapping: bool,
292        prev_wrapping: bool,
293        mut prev_pos: crate::grid::Pos,
294        mut prev_attrs: crate::attrs::Attrs,
295    ) -> (crate::grid::Pos, crate::attrs::Attrs) {
296        let mut prev_was_wide = false;
297
298        let first_cell = &self.cells[usize::from(start)];
299        let prev_first_cell = &prev.cells[usize::from(start)];
300        if wrapping
301            && !prev_wrapping
302            && first_cell == prev_first_cell
303            && prev_pos.row + 1 == row
304            && prev_pos.col
305                >= self.cols() - u16::from(prev_first_cell.is_wide())
306        {
307            let first_cell_attrs = first_cell.attrs();
308            if &prev_attrs != first_cell_attrs {
309                first_cell_attrs
310                    .write_escape_code_diff(contents, &prev_attrs);
311                prev_attrs = *first_cell_attrs;
312            }
313            let mut cell_contents = prev_first_cell.contents();
314            let need_erase = if cell_contents.is_empty() {
315                cell_contents = " ";
316                true
317            } else {
318                false
319            };
320            contents.extend(cell_contents.as_bytes());
321            crate::term::Backspace.write_buf(contents);
322            if prev_first_cell.is_wide() {
323                crate::term::Backspace.write_buf(contents);
324            }
325            if need_erase {
326                crate::term::EraseChar::new(1).write_buf(contents);
327            }
328            prev_pos = crate::grid::Pos { row, col: 0 };
329        }
330
331        let mut erase: Option<(u16, &crate::attrs::Attrs)> = None;
332        for (col, (cell, prev_cell)) in self
333            .cells()
334            .zip(prev.cells())
335            .enumerate()
336            .skip(usize::from(start))
337            .take(usize::from(width))
338        {
339            if prev_was_wide {
340                prev_was_wide = false;
341                continue;
342            }
343            prev_was_wide = cell.is_wide();
344
345            // we limit the number of cols to a u16 (see Size)
346            let col: u16 = col.try_into().unwrap();
347            let pos = crate::grid::Pos { row, col };
348
349            if let Some((prev_col, attrs)) = erase {
350                if cell.has_contents() || cell.attrs() != attrs {
351                    let new_pos = crate::grid::Pos { row, col: prev_col };
352                    if wrapping
353                        && prev_pos.row + 1 == new_pos.row
354                        && prev_pos.col >= self.cols()
355                    {
356                        if new_pos.col > 0 {
357                            contents.extend(
358                                " ".repeat(usize::from(new_pos.col))
359                                    .as_bytes(),
360                            );
361                        } else {
362                            contents.extend(b" ");
363                            crate::term::Backspace.write_buf(contents);
364                        }
365                    } else {
366                        crate::term::MoveFromTo::new(prev_pos, new_pos)
367                            .write_buf(contents);
368                    }
369                    prev_pos = new_pos;
370                    if &prev_attrs != attrs {
371                        attrs.write_escape_code_diff(contents, &prev_attrs);
372                        prev_attrs = *attrs;
373                    }
374                    crate::term::EraseChar::new(pos.col - prev_col)
375                        .write_buf(contents);
376                    erase = None;
377                }
378            }
379
380            if cell != prev_cell {
381                let attrs = cell.attrs();
382                if cell.has_contents() {
383                    if pos != prev_pos {
384                        if !wrapping
385                            || prev_pos.row + 1 != pos.row
386                            || prev_pos.col
387                                < self.cols() - u16::from(cell.is_wide())
388                            || pos.col != 0
389                        {
390                            crate::term::MoveFromTo::new(prev_pos, pos)
391                                .write_buf(contents);
392                        }
393                        prev_pos = pos;
394                    }
395
396                    if &prev_attrs != attrs {
397                        attrs.write_escape_code_diff(contents, &prev_attrs);
398                        prev_attrs = *attrs;
399                    }
400
401                    prev_pos.col += if cell.is_wide() { 2 } else { 1 };
402                    contents.extend(cell.contents().as_bytes());
403                } else if erase.is_none() {
404                    erase = Some((pos.col, attrs));
405                }
406            }
407        }
408        if let Some((prev_col, attrs)) = erase {
409            let new_pos = crate::grid::Pos { row, col: prev_col };
410            if wrapping
411                && prev_pos.row + 1 == new_pos.row
412                && prev_pos.col >= self.cols()
413            {
414                if new_pos.col > 0 {
415                    contents.extend(
416                        " ".repeat(usize::from(new_pos.col)).as_bytes(),
417                    );
418                } else {
419                    contents.extend(b" ");
420                    crate::term::Backspace.write_buf(contents);
421                }
422            } else {
423                crate::term::MoveFromTo::new(prev_pos, new_pos)
424                    .write_buf(contents);
425            }
426            prev_pos = new_pos;
427            if &prev_attrs != attrs {
428                attrs.write_escape_code_diff(contents, &prev_attrs);
429                prev_attrs = *attrs;
430            }
431            crate::term::ClearRowForward.write_buf(contents);
432        }
433
434        // if this row is going from wrapped to not wrapped, we need to erase
435        // and redraw the last character to break wrapping. if this row is
436        // wrapped, we need to redraw the last character without erasing it to
437        // position the cursor after the end of the line correctly so that
438        // drawing the next line can just start writing and be wrapped.
439        if (!self.wrapped && prev.wrapped) || (!prev.wrapped && self.wrapped)
440        {
441            let end_pos = if self.cells[usize::from(self.cols() - 1)]
442                .is_wide_continuation()
443            {
444                crate::grid::Pos {
445                    row,
446                    col: self.cols() - 2,
447                }
448            } else {
449                crate::grid::Pos {
450                    row,
451                    col: self.cols() - 1,
452                }
453            };
454            crate::term::MoveFromTo::new(prev_pos, end_pos)
455                .write_buf(contents);
456            prev_pos = end_pos;
457            if !self.wrapped {
458                crate::term::EraseChar::new(1).write_buf(contents);
459            }
460            let end_cell = &self.cells[usize::from(end_pos.col)];
461            if end_cell.has_contents() {
462                let attrs = end_cell.attrs();
463                if &prev_attrs != attrs {
464                    attrs.write_escape_code_diff(contents, &prev_attrs);
465                    prev_attrs = *attrs;
466                }
467                contents.extend(end_cell.contents().as_bytes());
468                prev_pos.col += if end_cell.is_wide() { 2 } else { 1 };
469            }
470        }
471
472        (prev_pos, prev_attrs)
473    }
474}