vt100/
row.rs

1use crate::term::BufWrite as _;
2
3#[derive(Clone, Debug)]
4pub struct Row {
5    cells: Vec<crate::cell::Cell>,
6    wrapped: bool,
7}
8
9impl Row {
10    pub fn new(cols: u16) -> Self {
11        Self {
12            cells: vec![crate::cell::Cell::default(); 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::Cell> {
33        self.cells.iter()
34    }
35
36    pub fn get(&self, col: u16) -> Option<&crate::cell::Cell> {
37        self.cells.get(usize::from(col))
38    }
39
40    pub fn get_mut(&mut self, col: u16) -> Option<&mut crate::cell::Cell> {
41        self.cells.get_mut(usize::from(col))
42    }
43
44    pub fn insert(&mut self, i: u16, cell: crate::cell::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::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::Cell::default();
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::default().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::default()
207                                .write_buf(contents);
208                        }
209                    } else {
210                        crate::term::MoveFromTo::new(prev_pos, new_pos)
211                            .write_buf(contents);
212                    }
213                    prev_pos = new_pos;
214                    if &prev_attrs != attrs {
215                        attrs.write_escape_code_diff(contents, &prev_attrs);
216                        prev_attrs = *attrs;
217                    }
218                    crate::term::EraseChar::new(pos.col - prev_col)
219                        .write_buf(contents);
220                    erase = None;
221                }
222            }
223
224            if cell != &default_cell {
225                let attrs = cell.attrs();
226                if cell.has_contents() {
227                    if pos != prev_pos {
228                        if !wrapping
229                            || prev_pos.row + 1 != pos.row
230                            || prev_pos.col
231                                < self.cols() - u16::from(cell.is_wide())
232                            || pos.col != 0
233                        {
234                            crate::term::MoveFromTo::new(prev_pos, pos)
235                                .write_buf(contents);
236                        }
237                        prev_pos = pos;
238                    }
239
240                    if &prev_attrs != attrs {
241                        attrs.write_escape_code_diff(contents, &prev_attrs);
242                        prev_attrs = *attrs;
243                    }
244
245                    prev_pos.col += if cell.is_wide() { 2 } else { 1 };
246                    let cell_contents = cell.contents();
247                    contents.extend(cell_contents.as_bytes());
248                } else if erase.is_none() {
249                    erase = Some((pos.col, attrs));
250                }
251            }
252        }
253        if let Some((prev_col, attrs)) = erase {
254            let new_pos = crate::grid::Pos { row, col: prev_col };
255            if wrapping
256                && prev_pos.row + 1 == new_pos.row
257                && prev_pos.col >= self.cols()
258            {
259                if new_pos.col > 0 {
260                    contents.extend(
261                        " ".repeat(usize::from(new_pos.col)).as_bytes(),
262                    );
263                } else {
264                    contents.extend(b" ");
265                    crate::term::Backspace::default().write_buf(contents);
266                }
267            } else {
268                crate::term::MoveFromTo::new(prev_pos, new_pos)
269                    .write_buf(contents);
270            }
271            prev_pos = new_pos;
272            if &prev_attrs != attrs {
273                attrs.write_escape_code_diff(contents, &prev_attrs);
274                prev_attrs = *attrs;
275            }
276            crate::term::ClearRowForward::default().write_buf(contents);
277        }
278
279        (prev_pos, prev_attrs)
280    }
281
282    // while it's true that most of the logic in this is identical to
283    // write_contents_formatted, i can't figure out how to break out the
284    // common parts without making things noticeably slower.
285    pub fn write_contents_diff(
286        &self,
287        contents: &mut Vec<u8>,
288        prev: &Self,
289        start: u16,
290        width: u16,
291        row: u16,
292        wrapping: bool,
293        prev_wrapping: bool,
294        mut prev_pos: crate::grid::Pos,
295        mut prev_attrs: crate::attrs::Attrs,
296    ) -> (crate::grid::Pos, crate::attrs::Attrs) {
297        let mut prev_was_wide = false;
298
299        let first_cell = &self.cells[usize::from(start)];
300        let prev_first_cell = &prev.cells[usize::from(start)];
301        if wrapping
302            && !prev_wrapping
303            && first_cell == prev_first_cell
304            && prev_pos.row + 1 == row
305            && prev_pos.col
306                >= self.cols() - u16::from(prev_first_cell.is_wide())
307        {
308            let first_cell_attrs = first_cell.attrs();
309            if &prev_attrs != first_cell_attrs {
310                first_cell_attrs
311                    .write_escape_code_diff(contents, &prev_attrs);
312                prev_attrs = *first_cell_attrs;
313            }
314            let mut cell_contents = prev_first_cell.contents();
315            let need_erase = if cell_contents.is_empty() {
316                cell_contents = " ".to_string();
317                true
318            } else {
319                false
320            };
321            contents.extend(cell_contents.as_bytes());
322            crate::term::Backspace::default().write_buf(contents);
323            if prev_first_cell.is_wide() {
324                crate::term::Backspace::default().write_buf(contents);
325            }
326            if need_erase {
327                crate::term::EraseChar::new(1).write_buf(contents);
328            }
329            prev_pos = crate::grid::Pos { row, col: 0 };
330        }
331
332        let mut erase: Option<(u16, &crate::attrs::Attrs)> = None;
333        for (col, (cell, prev_cell)) in self
334            .cells()
335            .zip(prev.cells())
336            .enumerate()
337            .skip(usize::from(start))
338            .take(usize::from(width))
339        {
340            if prev_was_wide {
341                prev_was_wide = false;
342                continue;
343            }
344            prev_was_wide = cell.is_wide();
345
346            // we limit the number of cols to a u16 (see Size)
347            let col: u16 = col.try_into().unwrap();
348            let pos = crate::grid::Pos { row, col };
349
350            if let Some((prev_col, attrs)) = erase {
351                if cell.has_contents() || cell.attrs() != attrs {
352                    let new_pos = crate::grid::Pos { row, col: prev_col };
353                    if wrapping
354                        && prev_pos.row + 1 == new_pos.row
355                        && prev_pos.col >= self.cols()
356                    {
357                        if new_pos.col > 0 {
358                            contents.extend(
359                                " ".repeat(usize::from(new_pos.col))
360                                    .as_bytes(),
361                            );
362                        } else {
363                            contents.extend(b" ");
364                            crate::term::Backspace::default()
365                                .write_buf(contents);
366                        }
367                    } else {
368                        crate::term::MoveFromTo::new(prev_pos, new_pos)
369                            .write_buf(contents);
370                    }
371                    prev_pos = new_pos;
372                    if &prev_attrs != attrs {
373                        attrs.write_escape_code_diff(contents, &prev_attrs);
374                        prev_attrs = *attrs;
375                    }
376                    crate::term::EraseChar::new(pos.col - prev_col)
377                        .write_buf(contents);
378                    erase = None;
379                }
380            }
381
382            if cell != prev_cell {
383                let attrs = cell.attrs();
384                if cell.has_contents() {
385                    if pos != prev_pos {
386                        if !wrapping
387                            || prev_pos.row + 1 != pos.row
388                            || prev_pos.col
389                                < self.cols() - u16::from(cell.is_wide())
390                            || pos.col != 0
391                        {
392                            crate::term::MoveFromTo::new(prev_pos, pos)
393                                .write_buf(contents);
394                        }
395                        prev_pos = pos;
396                    }
397
398                    if &prev_attrs != attrs {
399                        attrs.write_escape_code_diff(contents, &prev_attrs);
400                        prev_attrs = *attrs;
401                    }
402
403                    prev_pos.col += if cell.is_wide() { 2 } else { 1 };
404                    contents.extend(cell.contents().as_bytes());
405                } else if erase.is_none() {
406                    erase = Some((pos.col, attrs));
407                }
408            }
409        }
410        if let Some((prev_col, attrs)) = erase {
411            let new_pos = crate::grid::Pos { row, col: prev_col };
412            if wrapping
413                && prev_pos.row + 1 == new_pos.row
414                && prev_pos.col >= self.cols()
415            {
416                if new_pos.col > 0 {
417                    contents.extend(
418                        " ".repeat(usize::from(new_pos.col)).as_bytes(),
419                    );
420                } else {
421                    contents.extend(b" ");
422                    crate::term::Backspace::default().write_buf(contents);
423                }
424            } else {
425                crate::term::MoveFromTo::new(prev_pos, new_pos)
426                    .write_buf(contents);
427            }
428            prev_pos = new_pos;
429            if &prev_attrs != attrs {
430                attrs.write_escape_code_diff(contents, &prev_attrs);
431                prev_attrs = *attrs;
432            }
433            crate::term::ClearRowForward::default().write_buf(contents);
434        }
435
436        // if this row is going from wrapped to not wrapped, we need to erase
437        // and redraw the last character to break wrapping. if this row is
438        // wrapped, we need to redraw the last character without erasing it to
439        // position the cursor after the end of the line correctly so that
440        // drawing the next line can just start writing and be wrapped.
441        if (!self.wrapped && prev.wrapped) || (!prev.wrapped && self.wrapped)
442        {
443            let end_pos = if self.cells[usize::from(self.cols() - 1)]
444                .is_wide_continuation()
445            {
446                crate::grid::Pos {
447                    row,
448                    col: self.cols() - 2,
449                }
450            } else {
451                crate::grid::Pos {
452                    row,
453                    col: self.cols() - 1,
454                }
455            };
456            crate::term::MoveFromTo::new(prev_pos, end_pos)
457                .write_buf(contents);
458            prev_pos = end_pos;
459            if !self.wrapped {
460                crate::term::EraseChar::new(1).write_buf(contents);
461            }
462            let end_cell = &self.cells[usize::from(end_pos.col)];
463            if end_cell.has_contents() {
464                let attrs = end_cell.attrs();
465                if &prev_attrs != attrs {
466                    attrs.write_escape_code_diff(contents, &prev_attrs);
467                    prev_attrs = *attrs;
468                }
469                contents.extend(end_cell.contents().as_bytes());
470                prev_pos.col += if end_cell.is_wide() { 2 } else { 1 };
471            }
472        }
473
474        (prev_pos, prev_attrs)
475    }
476}