File size: 5,101 Bytes
41a96ee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
use crate::cell::Cell;

/// A line of terminal cells.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Line {
    pub cells: Vec<Cell>,
    pub dirty: bool,
    pub wrapped: bool,
}

impl Line {
    pub fn new(cols: usize) -> Self {
        Self {
            cells: vec![Cell::default(); cols],
            dirty: false,
            wrapped: false,
        }
    }

    pub fn clear(&mut self) {
        for cell in &mut self.cells {
            cell.reset();
        }
        self.dirty = true;
        self.wrapped = false;
    }

    pub fn resize(&mut self, cols: usize) {
        if self.cells.len() < cols {
            self.cells.resize(cols, Cell::default());
        } else if self.cells.len() > cols {
            self.cells.truncate(cols);
        }
    }
}

/// Terminal grid with scrollback buffer.
pub struct Grid {
    pub lines: Vec<Line>,
    pub scrollback: Vec<Line>,
    pub cols: usize,
    pub rows: usize,
    pub scrollback_limit: usize,
    pub cursor_row: usize,
    pub cursor_col: usize,
    pub saved_cursor_row: usize,
    pub saved_cursor_col: usize,
    pub top_margin: usize,
    pub bottom_margin: usize,
}

impl Grid {
    pub fn new(cols: usize, rows: usize, scrollback_limit: usize) -> Self {
        Self {
            lines: (0..rows).map(|_| Line::new(cols)).collect(),
            scrollback: Vec::new(),
            cols,
            rows,
            scrollback_limit,
            cursor_row: 0,
            cursor_col: 0,
            saved_cursor_row: 0,
            saved_cursor_col: 0,
            top_margin: 0,
            bottom_margin: rows.saturating_sub(1),
        }
    }

    pub fn resize(&mut self, cols: usize, rows: usize) {
        let old_rows = self.lines.len();

        for line in &mut self.lines {
            line.resize(cols);
        }

        if rows > old_rows {
            self.lines.reserve(rows - old_rows);
            for _ in old_rows..rows {
                self.lines.push(Line::new(cols));
            }
        } else if rows < old_rows {
            let excess = old_rows - rows;
            let limit = self.scrollback_limit;
            for line in self.lines.drain(0..excess) {
                if self.scrollback.len() >= limit {
                    self.scrollback.remove(0);
                }
                self.scrollback.push(line);
            }
        }

        self.cols = cols;
        self.rows = rows;
        self.cursor_row = self.cursor_row.min(rows.saturating_sub(1));
        self.cursor_col = self.cursor_col.min(cols.saturating_sub(1));
        self.top_margin = 0;
        self.bottom_margin = rows.saturating_sub(1);
    }

    pub fn clear_screen(&mut self) {
        for line in &mut self.lines {
            line.clear();
        }
    }

    pub fn clear_line(&mut self, row: usize) {
        if let Some(line) = self.lines.get_mut(row) {
            line.clear();
        }
    }

    pub fn clear_line_right(&mut self, row: usize, col: usize) {
        if let Some(line) = self.lines.get_mut(row) {
            for cell in &mut line.cells[col..] {
                cell.reset();
            }
            line.dirty = true;
        }
    }

    pub fn clear_line_left(&mut self, row: usize, col: usize) {
        if let Some(line) = self.lines.get_mut(row) {
            for cell in &mut line.cells[..=col] {
                cell.reset();
            }
            line.dirty = true;
        }
    }

    pub fn scroll_up(&mut self, n: usize) {
        let top = self.top_margin;
        let bottom = self.bottom_margin;
        let n = n.min(bottom.saturating_sub(top) + 1);

        for _ in 0..n {
            let removed = self.lines.remove(top);
            if top == 0 && self.scrollback.len() < self.scrollback_limit {
                self.scrollback.push(removed);
            }
            let mut new_line = Line::new(self.cols);
            new_line.dirty = true;
            self.lines.insert(bottom, new_line);
        }

        for i in top..=bottom {
            if let Some(line) = self.lines.get_mut(i) {
                line.dirty = true;
            }
        }
    }

    pub fn scroll_down(&mut self, n: usize) {
        let top = self.top_margin;
        let bottom = self.bottom_margin;
        let n = n.min(bottom.saturating_sub(top) + 1);

        for _ in 0..n {
            self.lines.remove(bottom);
            let mut new_line = Line::new(self.cols);
            new_line.dirty = true;
            self.lines.insert(top, new_line);
        }

        for i in top..=bottom {
            if let Some(line) = self.lines.get_mut(i) {
                line.dirty = true;
            }
        }
    }

    pub fn line(&self, row: usize) -> Option<&Line> {
        self.lines.get(row)
    }

    pub fn line_mut(&mut self, row: usize) -> Option<&mut Line> {
        self.lines.get_mut(row)
    }

    pub fn cell(&self, row: usize, col: usize) -> Option<&Cell> {
        self.lines.get(row)?.cells.get(col)
    }

    pub fn cell_mut(&mut self, row: usize, col: usize) -> Option<&mut Cell> {
        self.lines.get_mut(row)?.cells.get_mut(col)
    }
}