nvim_gtk/ui_model/
mod.rs

1use crate::highlight::Highlight;
2use std::rc::Rc;
3
4mod cell;
5mod item;
6mod line;
7mod model_layout;
8mod model_rect;
9
10pub use self::cell::Cell;
11pub use self::item::Item;
12pub use self::line::{Line, StyledLine};
13pub use self::model_layout::ModelLayout;
14pub use self::model_rect::{ModelRect, ModelRectVec};
15
16pub struct UiModel {
17    pub columns: usize,
18    pub rows: usize,
19    cur_row: usize,
20    cur_col: usize,
21    model: Box<[Line]>,
22}
23
24impl UiModel {
25    pub fn new(rows: u64, columns: u64) -> UiModel {
26        let mut model = Vec::with_capacity(rows as usize);
27        for _ in 0..rows as usize {
28            model.push(Line::new(columns as usize));
29        }
30
31        UiModel {
32            columns: columns as usize,
33            rows: rows as usize,
34            cur_row: 0,
35            cur_col: 0,
36            model: model.into_boxed_slice(),
37        }
38    }
39
40    pub fn empty() -> UiModel {
41        UiModel {
42            columns: 0,
43            rows: 0,
44            cur_row: 0,
45            cur_col: 0,
46            model: Box::new([]),
47        }
48    }
49
50    #[inline]
51    pub fn model(&self) -> &[Line] {
52        &self.model
53    }
54
55    #[inline]
56    pub fn model_mut(&mut self) -> &mut [Line] {
57        &mut self.model
58    }
59
60    pub fn cur_point(&self) -> ModelRect {
61        ModelRect::point(self.cur_col, self.cur_row)
62    }
63
64    pub fn set_cursor(&mut self, row: usize, col: usize) -> ModelRectVec {
65        // it is possible in some cases that cursor moved out of visible rect
66        // see https://github.com/daa84/neovim-gtk/issues/20
67        if row >= self.model.len() || col >= self.model[row].line.len() {
68            return ModelRectVec::empty();
69        }
70
71        let mut changed_region = ModelRectVec::new(self.cur_point());
72
73        self.cur_row = row;
74        self.cur_col = col;
75
76        changed_region.join(&self.cur_point());
77
78        changed_region
79    }
80
81    pub fn get_cursor(&self) -> (usize, usize) {
82        (self.cur_row, self.cur_col)
83    }
84
85    pub fn put_one(&mut self, row: usize, col: usize, ch: &str, double_width: bool, hl: Rc<Highlight>) {
86        self.put(row, col, ch, double_width, 1, hl);
87    }
88
89    pub fn put(
90        &mut self,
91        row: usize,
92        col: usize,
93        ch: &str,
94        double_width: bool,
95        repeat: usize,
96        hl: Rc<Highlight>,
97    ) {
98        let line = &mut self.model[row];
99        line.dirty_line = true;
100
101        for offset in 0..repeat {
102            let cell = &mut line[col + offset];
103            cell.ch.clear();
104            cell.ch.push_str(ch);
105            cell.hl = hl.clone();
106            cell.double_width = double_width;
107            cell.dirty = true;
108        }
109    }
110
111    /// Copy rows from 0 to to_row, col from 0 self.columns
112    ///
113    /// Don't do any validation!
114    pub fn swap_rows(&mut self, target: &mut UiModel, to_row: usize) {
115        for (row_idx, line) in self.model[0..to_row + 1].iter_mut().enumerate() {
116            let target_row = &mut target.model[row_idx];
117            line.swap_with(target_row, 0, self.columns - 1);
118        }
119    }
120
121    #[inline]
122    fn swap_row(&mut self, target_row: i64, offset: i64, left_col: usize, right_col: usize) {
123        debug_assert_ne!(0, offset);
124
125        let from_row = (target_row + offset) as usize;
126
127        let (left, right) = if offset > 0 {
128            self.model.split_at_mut(from_row)
129        } else {
130            self.model.split_at_mut(target_row as usize)
131        };
132
133        let (source_row, target_row) = if offset > 0 {
134            (&mut right[0], &mut left[target_row as usize])
135        } else {
136            (&mut left[from_row], &mut right[0])
137        };
138
139        source_row.swap_with(target_row, left_col, right_col);
140    }
141
142    pub fn scroll(&mut self, top: i64, bot: i64, left: usize, right: usize, count: i64, default_hl: &Rc<Highlight>) -> ModelRect {
143        if count > 0 {
144            for row in top..(bot - count + 1) {
145                self.swap_row(row, count, left, right);
146            }
147        } else {
148            for row in ((top - count)..(bot + 1)).rev() {
149                self.swap_row(row, count, left, right);
150            }
151        }
152
153        if count > 0 {
154            self.clear_region((bot - count + 1) as usize, bot as usize, left, right, default_hl);
155        } else {
156            self.clear_region(top as usize, (top - count - 1) as usize, left, right, default_hl);
157        }
158
159        ModelRect::new(top as usize, bot as usize, left, right)
160    }
161
162    pub fn clear(&mut self, default_hl: &Rc<Highlight>) {
163        let (rows, columns) = (self.rows, self.columns);
164        self.clear_region(0, rows - 1, 0, columns - 1, default_hl);
165    }
166
167    fn clear_region(&mut self, top: usize, bot: usize, left: usize, right: usize, default_hl: &Rc<Highlight>) {
168        for row in &mut self.model[top..bot + 1] {
169            row.clear(left, right, default_hl);
170        }
171    }
172
173    pub fn clear_glyphs(&mut self) {
174        for row in &mut self.model.iter_mut() {
175            row.clear_glyphs();
176        }
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn test_vec_join_inside() {
186        let mut list = ModelRectVec::new(ModelRect::new(0, 23, 0, 69));
187
188        let inside = ModelRect::new(23, 23, 68, 69);
189
190        list.join(&inside);
191        assert_eq!(1, list.list.len());
192    }
193
194    #[test]
195    fn test_vec_join_top() {
196        let mut list = ModelRectVec::new(ModelRect::point(0, 0));
197
198        let neighbor = ModelRect::point(1, 0);
199
200        list.join(&neighbor);
201        assert_eq!(1, list.list.len());
202    }
203
204    #[test]
205    fn test_model_vec_join_right() {
206        let mut list = ModelRectVec::new(ModelRect::new(23, 23, 69, 69));
207
208        let neighbor = ModelRect::new(23, 23, 69, 70);
209
210        list.join(&neighbor);
211        assert_eq!(1, list.list.len());
212    }
213
214    #[test]
215    fn test_model_vec_join_right2() {
216        let mut list = ModelRectVec::new(ModelRect::new(0, 1, 0, 9));
217
218        let neighbor = ModelRect::new(1, 1, 9, 10);
219
220        list.join(&neighbor);
221        assert_eq!(1, list.list.len());
222    }
223
224    #[test]
225    fn test_model_vec_join() {
226        let mut list = ModelRectVec::new(ModelRect::point(5, 5));
227
228        let neighbor = ModelRect::point(6, 5);
229
230        list.join(&neighbor);
231        assert_eq!(1, list.list.len());
232    }
233
234    #[test]
235    fn test_model_vec_no_join() {
236        let mut list = ModelRectVec::new(ModelRect::point(5, 5));
237
238        let not_neighbor = ModelRect::point(6, 6);
239
240        list.join(&not_neighbor);
241        assert_eq!(2, list.list.len());
242    }
243
244    #[test]
245    fn test_cursor_area() {
246        let mut model = UiModel::new(10, 20);
247
248        model.set_cursor(1, 1);
249
250        let rect = model.set_cursor(5, 5);
251
252        assert_eq!(2, rect.list.len());
253
254        assert_eq!(1, rect.list[0].top);
255        assert_eq!(1, rect.list[0].left);
256        assert_eq!(1, rect.list[0].bot);
257        assert_eq!(1, rect.list[0].right);
258
259        assert_eq!(5, rect.list[1].top);
260        assert_eq!(5, rect.list[1].left);
261        assert_eq!(5, rect.list[1].bot);
262        assert_eq!(5, rect.list[1].right);
263    }
264
265    #[test]
266    fn test_scroll_area() {
267        let mut model = UiModel::new(10, 20);
268
269        let rect = model.scroll(1, 5, 1, 5, 3, &Rc::new(Highlight::new()));
270
271        assert_eq!(1, rect.top);
272        assert_eq!(1, rect.left);
273        assert_eq!(5, rect.bot);
274        assert_eq!(5, rect.right);
275    }
276}