1use crate::term::BufWrite as _;
2
3#[derive(Clone, Debug)]
4pub struct Grid {
5 size: Size,
6 pos: Pos,
7 saved_pos: Pos,
8 rows: Vec<crate::row::Row>,
9 scroll_top: u16,
10 scroll_bottom: u16,
11 origin_mode: bool,
12 saved_origin_mode: bool,
13 scrollback: std::collections::VecDeque<crate::row::Row>,
14 scrollback_len: usize,
15 scrollback_offset: usize,
16}
17
18impl Grid {
19 pub fn new(size: Size, scrollback_len: usize) -> Self {
20 Self {
21 size,
22 pos: Pos::default(),
23 saved_pos: Pos::default(),
24 rows: vec![],
25 scroll_top: 0,
26 scroll_bottom: size.rows - 1,
27 origin_mode: false,
28 saved_origin_mode: false,
29 scrollback: std::collections::VecDeque::new(),
30 scrollback_len,
31 scrollback_offset: 0,
32 }
33 }
34
35 pub fn allocate_rows(&mut self) {
36 if self.rows.is_empty() {
37 self.rows.extend(
38 std::iter::repeat_with(|| {
39 crate::row::Row::new(self.size.cols)
40 })
41 .take(usize::from(self.size.rows)),
42 );
43 }
44 }
45
46 fn new_row(&self) -> crate::row::Row {
47 crate::row::Row::new(self.size.cols)
48 }
49
50 pub fn clear(&mut self) {
51 self.pos = Pos::default();
52 self.saved_pos = Pos::default();
53 for row in self.drawing_rows_mut() {
54 row.clear(crate::attrs::Attrs::default());
55 }
56 self.scroll_top = 0;
57 self.scroll_bottom = self.size.rows - 1;
58 self.origin_mode = false;
59 self.saved_origin_mode = false;
60 }
61
62 pub fn size(&self) -> Size {
63 self.size
64 }
65
66 pub fn set_size(&mut self, size: Size) {
67 if size.cols != self.size.cols {
68 for row in &mut self.rows {
69 row.wrap(false);
70 }
71 }
72
73 if self.scroll_bottom == self.size.rows - 1 {
74 self.scroll_bottom = size.rows - 1;
75 }
76
77 self.size = size;
78 for row in &mut self.rows {
79 row.resize(size.cols, crate::cell::Cell::default());
80 }
81 self.rows.resize(usize::from(size.rows), self.new_row());
82
83 if self.scroll_bottom >= size.rows {
84 self.scroll_bottom = size.rows - 1;
85 }
86 if self.scroll_bottom < self.scroll_top {
87 self.scroll_top = 0;
88 }
89
90 self.row_clamp_top(false);
91 self.row_clamp_bottom(false);
92 self.col_clamp();
93 }
94
95 pub fn pos(&self) -> Pos {
96 self.pos
97 }
98
99 pub fn set_pos(&mut self, mut pos: Pos) {
100 if self.origin_mode {
101 pos.row = pos.row.saturating_add(self.scroll_top);
102 }
103 self.pos = pos;
104 self.row_clamp_top(self.origin_mode);
105 self.row_clamp_bottom(self.origin_mode);
106 self.col_clamp();
107 }
108
109 pub fn save_cursor(&mut self) {
110 self.saved_pos = self.pos;
111 self.saved_origin_mode = self.origin_mode;
112 }
113
114 pub fn restore_cursor(&mut self) {
115 self.pos = self.saved_pos;
116 self.origin_mode = self.saved_origin_mode;
117 }
118
119 pub fn visible_rows(&self) -> impl Iterator<Item = &crate::row::Row> {
120 let scrollback_len = self.scrollback.len();
121 let rows_len = self.rows.len();
122 self.scrollback
123 .iter()
124 .skip(scrollback_len - self.scrollback_offset)
125 .chain(self.rows.iter().take(rows_len - self.scrollback_offset))
126 }
127
128 pub fn drawing_rows(&self) -> impl Iterator<Item = &crate::row::Row> {
129 self.rows.iter()
130 }
131
132 pub fn drawing_rows_mut(
133 &mut self,
134 ) -> impl Iterator<Item = &mut crate::row::Row> {
135 self.rows.iter_mut()
136 }
137
138 pub fn visible_row(&self, row: u16) -> Option<&crate::row::Row> {
139 self.visible_rows().nth(usize::from(row))
140 }
141
142 pub fn drawing_row(&self, row: u16) -> Option<&crate::row::Row> {
143 self.drawing_rows().nth(usize::from(row))
144 }
145
146 pub fn drawing_row_mut(
147 &mut self,
148 row: u16,
149 ) -> Option<&mut crate::row::Row> {
150 self.drawing_rows_mut().nth(usize::from(row))
151 }
152
153 pub fn current_row_mut(&mut self) -> &mut crate::row::Row {
154 self.drawing_row_mut(self.pos.row)
155 .unwrap()
157 }
158
159 pub fn visible_cell(&self, pos: Pos) -> Option<&crate::cell::Cell> {
160 self.visible_row(pos.row).and_then(|r| r.get(pos.col))
161 }
162
163 pub fn drawing_cell(&self, pos: Pos) -> Option<&crate::cell::Cell> {
164 self.drawing_row(pos.row).and_then(|r| r.get(pos.col))
165 }
166
167 pub fn drawing_cell_mut(
168 &mut self,
169 pos: Pos,
170 ) -> Option<&mut crate::cell::Cell> {
171 self.drawing_row_mut(pos.row)
172 .and_then(|r| r.get_mut(pos.col))
173 }
174
175 pub fn scrollback_len(&self) -> usize {
176 self.scrollback_len
177 }
178
179 pub fn scrollback(&self) -> usize {
180 self.scrollback_offset
181 }
182
183 pub fn set_scrollback(&mut self, rows: usize) {
184 self.scrollback_offset = rows.min(self.scrollback.len());
185 }
186
187 pub fn write_contents(&self, contents: &mut String) {
188 let mut wrapping = false;
189 for row in self.visible_rows() {
190 row.write_contents(contents, 0, self.size.cols, wrapping);
191 if !row.wrapped() {
192 contents.push('\n');
193 }
194 wrapping = row.wrapped();
195 }
196
197 while contents.ends_with('\n') {
198 contents.truncate(contents.len() - 1);
199 }
200 }
201
202 pub fn write_contents_formatted(
203 &self,
204 contents: &mut Vec<u8>,
205 ) -> crate::attrs::Attrs {
206 crate::term::ClearAttrs::default().write_buf(contents);
207 crate::term::ClearScreen::default().write_buf(contents);
208
209 let mut prev_attrs = crate::attrs::Attrs::default();
210 let mut prev_pos = Pos::default();
211 let mut wrapping = false;
212 for (i, row) in self.visible_rows().enumerate() {
213 let i = i.try_into().unwrap();
216 let (new_pos, new_attrs) = row.write_contents_formatted(
217 contents,
218 0,
219 self.size.cols,
220 i,
221 wrapping,
222 Some(prev_pos),
223 Some(prev_attrs),
224 );
225 prev_pos = new_pos;
226 prev_attrs = new_attrs;
227 wrapping = row.wrapped();
228 }
229
230 self.write_cursor_position_formatted(
231 contents,
232 Some(prev_pos),
233 Some(prev_attrs),
234 );
235
236 prev_attrs
237 }
238
239 pub fn write_contents_diff(
240 &self,
241 contents: &mut Vec<u8>,
242 prev: &Self,
243 mut prev_attrs: crate::attrs::Attrs,
244 ) -> crate::attrs::Attrs {
245 let mut prev_pos = prev.pos;
246 let mut wrapping = false;
247 let mut prev_wrapping = false;
248 for (i, (row, prev_row)) in
249 self.visible_rows().zip(prev.visible_rows()).enumerate()
250 {
251 let i = i.try_into().unwrap();
254 let (new_pos, new_attrs) = row.write_contents_diff(
255 contents,
256 prev_row,
257 0,
258 self.size.cols,
259 i,
260 wrapping,
261 prev_wrapping,
262 prev_pos,
263 prev_attrs,
264 );
265 prev_pos = new_pos;
266 prev_attrs = new_attrs;
267 wrapping = row.wrapped();
268 prev_wrapping = prev_row.wrapped();
269 }
270
271 self.write_cursor_position_formatted(
272 contents,
273 Some(prev_pos),
274 Some(prev_attrs),
275 );
276
277 prev_attrs
278 }
279
280 pub fn write_cursor_position_formatted(
281 &self,
282 contents: &mut Vec<u8>,
283 prev_pos: Option<Pos>,
284 prev_attrs: Option<crate::attrs::Attrs>,
285 ) {
286 let prev_attrs = prev_attrs.unwrap_or_default();
287 if prev_pos != Some(self.pos) && self.pos.col >= self.size.cols {
293 let mut pos = Pos {
294 row: self.pos.row,
295 col: self.size.cols - 1,
296 };
297 if self
298 .drawing_cell(pos)
299 .unwrap()
302 .is_wide_continuation()
303 {
304 pos.col = self.size.cols - 2;
305 }
306 let cell =
307 self.drawing_cell(pos).unwrap();
314 if cell.has_contents() {
315 if let Some(prev_pos) = prev_pos {
316 crate::term::MoveFromTo::new(prev_pos, pos)
317 .write_buf(contents);
318 } else {
319 crate::term::MoveTo::new(pos).write_buf(contents);
320 }
321 cell.attrs().write_escape_code_diff(contents, &prev_attrs);
322 contents.extend(cell.contents().as_bytes());
323 prev_attrs.write_escape_code_diff(contents, cell.attrs());
324 } else {
325 let mut found = false;
334 for i in (0..self.pos.row).rev() {
335 pos.row = i;
336 pos.col = self.size.cols - 1;
337 if self
338 .drawing_cell(pos)
339 .unwrap()
343 .is_wide_continuation()
344 {
345 pos.col = self.size.cols - 2;
346 }
347 let cell = self
348 .drawing_cell(pos)
349 .unwrap();
358 if cell.has_contents() {
359 if let Some(prev_pos) = prev_pos {
360 if prev_pos.row != i
361 || prev_pos.col < self.size.cols
362 {
363 crate::term::MoveFromTo::new(prev_pos, pos)
364 .write_buf(contents);
365 cell.attrs().write_escape_code_diff(
366 contents,
367 &prev_attrs,
368 );
369 contents.extend(cell.contents().as_bytes());
370 prev_attrs.write_escape_code_diff(
371 contents,
372 cell.attrs(),
373 );
374 }
375 } else {
376 crate::term::MoveTo::new(pos).write_buf(contents);
377 cell.attrs().write_escape_code_diff(
378 contents,
379 &prev_attrs,
380 );
381 contents.extend(cell.contents().as_bytes());
382 prev_attrs.write_escape_code_diff(
383 contents,
384 cell.attrs(),
385 );
386 }
387 contents.extend(
388 "\n".repeat(usize::from(self.pos.row - i))
389 .as_bytes(),
390 );
391 found = true;
392 break;
393 }
394 }
395
396 if !found {
403 pos = Pos {
404 row: self.pos.row,
405 col: self.size.cols - 1,
406 };
407 if let Some(prev_pos) = prev_pos {
408 crate::term::MoveFromTo::new(prev_pos, pos)
409 .write_buf(contents);
410 } else {
411 crate::term::MoveTo::new(pos).write_buf(contents);
412 }
413 contents.push(b' ');
414 let end_cell = self
417 .drawing_cell(pos)
418 .unwrap();
421 end_cell
422 .attrs()
423 .write_escape_code_diff(contents, &prev_attrs);
424 crate::term::SaveCursor::default().write_buf(contents);
425 crate::term::Backspace::default().write_buf(contents);
426 crate::term::EraseChar::new(1).write_buf(contents);
427 crate::term::RestoreCursor::default().write_buf(contents);
428 prev_attrs
429 .write_escape_code_diff(contents, end_cell.attrs());
430 }
431 }
432 } else if let Some(prev_pos) = prev_pos {
433 crate::term::MoveFromTo::new(prev_pos, self.pos)
434 .write_buf(contents);
435 } else {
436 crate::term::MoveTo::new(self.pos).write_buf(contents);
437 }
438 }
439
440 pub fn erase_all(&mut self, attrs: crate::attrs::Attrs) {
441 for row in self.drawing_rows_mut() {
442 row.clear(attrs);
443 }
444 }
445
446 pub fn erase_all_forward(&mut self, attrs: crate::attrs::Attrs) {
447 let pos = self.pos;
448 for row in self.drawing_rows_mut().skip(usize::from(pos.row) + 1) {
449 row.clear(attrs);
450 }
451
452 self.erase_row_forward(attrs);
453 }
454
455 pub fn erase_all_backward(&mut self, attrs: crate::attrs::Attrs) {
456 let pos = self.pos;
457 for row in self.drawing_rows_mut().take(usize::from(pos.row)) {
458 row.clear(attrs);
459 }
460
461 self.erase_row_backward(attrs);
462 }
463
464 pub fn erase_row(&mut self, attrs: crate::attrs::Attrs) {
465 self.current_row_mut().clear(attrs);
466 }
467
468 pub fn erase_row_forward(&mut self, attrs: crate::attrs::Attrs) {
469 let size = self.size;
470 let pos = self.pos;
471 let row = self.current_row_mut();
472 for col in pos.col..size.cols {
473 row.erase(col, attrs);
474 }
475 }
476
477 pub fn erase_row_backward(&mut self, attrs: crate::attrs::Attrs) {
478 let size = self.size;
479 let pos = self.pos;
480 let row = self.current_row_mut();
481 for col in 0..=pos.col.min(size.cols - 1) {
482 row.erase(col, attrs);
483 }
484 }
485
486 pub fn insert_cells(&mut self, count: u16) {
487 let size = self.size;
488 let pos = self.pos;
489 let wide = pos.col < size.cols
490 && self
491 .drawing_cell(pos)
492 .unwrap()
496 .is_wide_continuation();
497 let row = self.current_row_mut();
498 for _ in 0..count {
499 if wide {
500 row.get_mut(pos.col).unwrap().set_wide_continuation(false);
501 }
502 row.insert(pos.col, crate::cell::Cell::default());
503 if wide {
504 row.get_mut(pos.col).unwrap().set_wide_continuation(true);
505 }
506 }
507 row.truncate(size.cols);
508 }
509
510 pub fn delete_cells(&mut self, count: u16) {
511 let size = self.size;
512 let pos = self.pos;
513 let row = self.current_row_mut();
514 for _ in 0..(count.min(size.cols - pos.col)) {
515 row.remove(pos.col);
516 }
517 row.resize(size.cols, crate::cell::Cell::default());
518 }
519
520 pub fn erase_cells(&mut self, count: u16, attrs: crate::attrs::Attrs) {
521 let size = self.size;
522 let pos = self.pos;
523 let row = self.current_row_mut();
524 for col in pos.col..((pos.col.saturating_add(count)).min(size.cols)) {
525 row.erase(col, attrs);
526 }
527 }
528
529 pub fn insert_lines(&mut self, count: u16) {
530 for _ in 0..count {
531 self.rows.remove(usize::from(self.scroll_bottom));
532 self.rows.insert(usize::from(self.pos.row), self.new_row());
533 self.rows[usize::from(self.scroll_bottom)].wrap(false);
535 }
536 }
537
538 pub fn delete_lines(&mut self, count: u16) {
539 for _ in 0..(count.min(self.size.rows - self.pos.row)) {
540 self.rows
541 .insert(usize::from(self.scroll_bottom) + 1, self.new_row());
542 self.rows.remove(usize::from(self.pos.row));
543 }
544 }
545
546 pub fn scroll_up(&mut self, count: u16) {
547 for _ in 0..(count.min(self.size.rows - self.scroll_top)) {
548 self.rows
549 .insert(usize::from(self.scroll_bottom) + 1, self.new_row());
550 let removed = self.rows.remove(usize::from(self.scroll_top));
551 if self.scrollback_len > 0 && !self.scroll_region_active() {
552 self.scrollback.push_back(removed);
553 while self.scrollback.len() > self.scrollback_len {
554 self.scrollback.pop_front();
555 }
556 if self.scrollback_offset > 0 {
557 self.scrollback_offset =
558 self.scrollback.len().min(self.scrollback_offset + 1);
559 }
560 }
561 }
562 }
563
564 pub fn scroll_down(&mut self, count: u16) {
565 for _ in 0..count {
566 self.rows.remove(usize::from(self.scroll_bottom));
567 self.rows
568 .insert(usize::from(self.scroll_top), self.new_row());
569 self.rows[usize::from(self.scroll_bottom)].wrap(false);
571 }
572 }
573
574 pub fn set_scroll_region(&mut self, top: u16, bottom: u16) {
575 let bottom = bottom.min(self.size().rows - 1);
576 if top < bottom {
577 self.scroll_top = top;
578 self.scroll_bottom = bottom;
579 } else {
580 self.scroll_top = 0;
581 self.scroll_bottom = self.size().rows - 1;
582 }
583 self.pos.row = self.scroll_top;
584 self.pos.col = 0;
585 }
586
587 fn in_scroll_region(&self) -> bool {
588 self.pos.row >= self.scroll_top && self.pos.row <= self.scroll_bottom
589 }
590
591 fn scroll_region_active(&self) -> bool {
592 self.scroll_top != 0 || self.scroll_bottom != self.size.rows - 1
593 }
594
595 pub fn set_origin_mode(&mut self, mode: bool) {
596 self.origin_mode = mode;
597 self.set_pos(Pos { row: 0, col: 0 });
598 }
599
600 pub fn row_inc_clamp(&mut self, count: u16) {
601 let in_scroll_region = self.in_scroll_region();
602 self.pos.row = self.pos.row.saturating_add(count);
603 self.row_clamp_bottom(in_scroll_region);
604 }
605
606 pub fn row_inc_scroll(&mut self, count: u16) -> u16 {
607 let in_scroll_region = self.in_scroll_region();
608 self.pos.row = self.pos.row.saturating_add(count);
609 let lines = self.row_clamp_bottom(in_scroll_region);
610 if in_scroll_region {
611 self.scroll_up(lines);
612 lines
613 } else {
614 0
615 }
616 }
617
618 pub fn row_dec_clamp(&mut self, count: u16) {
619 let in_scroll_region = self.in_scroll_region();
620 self.pos.row = self.pos.row.saturating_sub(count);
621 self.row_clamp_top(in_scroll_region);
622 }
623
624 pub fn row_dec_scroll(&mut self, count: u16) {
625 let in_scroll_region = self.in_scroll_region();
626 let extra_lines = if count > self.pos.row {
629 count - self.pos.row
630 } else {
631 0
632 };
633 self.pos.row = self.pos.row.saturating_sub(count);
634 let lines = self.row_clamp_top(in_scroll_region);
635 self.scroll_down(lines + extra_lines);
636 }
637
638 pub fn row_set(&mut self, i: u16) {
639 self.pos.row = i;
640 self.row_clamp();
641 }
642
643 pub fn col_inc(&mut self, count: u16) {
644 self.pos.col = self.pos.col.saturating_add(count);
645 }
646
647 pub fn col_inc_clamp(&mut self, count: u16) {
648 self.pos.col = self.pos.col.saturating_add(count);
649 self.col_clamp();
650 }
651
652 pub fn col_dec(&mut self, count: u16) {
653 self.pos.col = self.pos.col.saturating_sub(count);
654 }
655
656 pub fn col_tab(&mut self) {
657 self.pos.col -= self.pos.col % 8;
658 self.pos.col += 8;
659 self.col_clamp();
660 }
661
662 pub fn col_set(&mut self, i: u16) {
663 self.pos.col = i;
664 self.col_clamp();
665 }
666
667 pub fn col_wrap(&mut self, width: u16, wrap: bool) {
668 if self.pos.col > self.size.cols - width {
669 let mut prev_pos = self.pos;
670 self.pos.col = 0;
671 let scrolled = self.row_inc_scroll(1);
672 prev_pos.row -= scrolled;
673 let new_pos = self.pos;
674 self.drawing_row_mut(prev_pos.row)
675 .unwrap()
679 .wrap(wrap && prev_pos.row + 1 == new_pos.row);
680 }
681 }
682
683 fn row_clamp_top(&mut self, limit_to_scroll_region: bool) -> u16 {
684 if limit_to_scroll_region && self.pos.row < self.scroll_top {
685 let rows = self.scroll_top - self.pos.row;
686 self.pos.row = self.scroll_top;
687 rows
688 } else {
689 0
690 }
691 }
692
693 fn row_clamp_bottom(&mut self, limit_to_scroll_region: bool) -> u16 {
694 let bottom = if limit_to_scroll_region {
695 self.scroll_bottom
696 } else {
697 self.size.rows - 1
698 };
699 if self.pos.row > bottom {
700 let rows = self.pos.row - bottom;
701 self.pos.row = bottom;
702 rows
703 } else {
704 0
705 }
706 }
707
708 fn row_clamp(&mut self) {
709 if self.pos.row > self.size.rows - 1 {
710 self.pos.row = self.size.rows - 1;
711 }
712 }
713
714 fn col_clamp(&mut self) {
715 if self.pos.col > self.size.cols - 1 {
716 self.pos.col = self.size.cols - 1;
717 }
718 }
719}
720
721#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
722pub struct Size {
723 pub rows: u16,
724 pub cols: u16,
725}
726
727#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
728pub struct Pos {
729 pub row: u16,
730 pub col: u16,
731}