From 8126841ed37a9cc249f646b830b3d3d48aaf4ed7 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Wed, 8 Jun 2016 10:39:49 -0700 Subject: Add support for scrolling regions It's now possible to move around within Vim without the screen becoming corrupt! The ANSI parser now calls a (new) `set_scrolling_region` on the handler when the DECSTBM CSI is received. In order to provide a sensible default in case that the sequence doesn't include arguments, a TermInfo trait was added which currently has methods for inspecting number of rows and columns. This was added as an additional trait instead of being included on Handler since they have semantically different purposes. The tests had to be updated to account for the additional trait bounds. The utilities module now has a `Rotate` trait which is implemented for the built-in slice type. This means that slices and anything derefing to a slice can be rotated. Since VecDeque doesn't support slicing (it's a circular buffer), the grid rows are now held in a Vec to support rotation. For ergomomic access to the grid for scrolling and clearing regions, additional Index/IndexMut implementations were added to the grid::Row type. Finally, a `reset` method was added to `Cell` which properly resets the state to default (instead of just clearing the char). This supports region clearing and also fixed a bug where cell backgrounds would remain after being cleared. --- src/term.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 9 deletions(-) (limited to 'src/term.rs') diff --git a/src/term.rs b/src/term.rs index dedd9d9c..0bcf6b24 100644 --- a/src/term.rs +++ b/src/term.rs @@ -1,5 +1,6 @@ /// Exports the `Term` type which is a high-level API for the Grid use std::sync::Arc; +use std::ops::Range; use ansi::{self, Attr, DebugHandler}; use grid::{self, Grid, CellFlags}; @@ -109,6 +110,9 @@ pub struct Term { /// Mode flags mode: TermMode, + + /// Scroll region + scroll_region: Range, } impl Term { @@ -119,6 +123,7 @@ impl Term { tabs[0] = false; let alt = grid.clone(); + let scroll_region = 0..grid.num_rows(); Term { grid: grid, @@ -133,6 +138,7 @@ impl Term { attr: CellFlags::empty(), dirty: false, mode: TermMode::empty(), + scroll_region: scroll_region, } } @@ -208,6 +214,33 @@ impl Term { self.cursor.x = 0; self.cursor.y += 1; } + + /// Convenience function for scrolling + fn scroll(&mut self, count: isize) { + println!("[TERM] scrolling {} lines", count); + self.grid.scroll(self.scroll_region.clone(), count); + if count > 0 { + // Scrolled down, so need to clear from bottom + let start = self.scroll_region.end - (count as usize); + self.grid.clear_region(start..self.scroll_region.end); + } else { + // Scrolled up, clear from top + let end = self.scroll_region.start + ((-count) as usize); + self.grid.clear_region(self.scroll_region.start..end); + } + } +} + +impl ansi::TermInfo for Term { + #[inline] + fn rows(&self) -> usize { + self.grid.num_rows() + } + + #[inline] + fn cols(&self) -> usize { + self.grid.num_cols() + } } impl ansi::Handler for Term { @@ -306,8 +339,8 @@ impl ansi::Handler for Term { self.dirty = true; println!("linefeed"); // TODO handle scroll? not clear what parts of this the pty handle - if self.cursor_y() + 1 == self.grid.num_rows() as u16 { - self.grid.feed(); + if self.cursor_y() + 1 >= self.scroll_region.end as u16 { + self.scroll(1); self.clear_line(ansi::LineClearMode::Right); } else { self.cursor.y += 1; @@ -319,10 +352,25 @@ impl ansi::Handler for Term { fn substitute(&mut self) { println!("substitute"); } fn newline(&mut self) { println!("newline"); } fn set_horizontal_tabstop(&mut self) { println!("set_horizontal_tabstop"); } - fn scroll_up(&mut self, rows: i64) { println!("scroll_up: {}", rows); } - fn scroll_down(&mut self, rows: i64) { println!("scroll_down: {}", rows); } - fn insert_blank_lines(&mut self, count: i64) { println!("insert_blank_lines: {}", count); } - fn delete_lines(&mut self, count: i64) { println!("delete_lines: {}", count); } + fn scroll_up(&mut self, rows: i64) { + println!("scroll_up: {}", rows); + self.scroll(-rows as isize); + } + fn scroll_down(&mut self, rows: i64) { + println!("scroll_down: {}", rows); + self.scroll(rows as isize); + } + fn insert_blank_lines(&mut self, count: i64) { + println!("insert_blank_lines: {}", count); + if self.scroll_region.contains(self.cursor_y() as usize) { + self.scroll(-count as isize); + } + } + fn delete_lines(&mut self, count: i64) { + if self.scroll_region.contains(self.cursor_y() as usize) { + self.scroll(count as isize); + } + } fn erase_chars(&mut self, count: i64) { println!("erase_chars: {}", count); } fn delete_chars(&mut self, count: i64) { println!("delete_chars: {}", count); } fn move_backward_tabs(&mut self, count: i64) { println!("move_backward_tabs: {}", count); } @@ -337,8 +385,8 @@ impl ansi::Handler for Term { let cols = self.grid.num_cols(); let row = &mut self.grid[self.cursor.y as usize]; let start = self.cursor.x as usize; - for col in start..cols { - row[col].c = ' '; + for cell in row[start..].iter_mut() { + cell.reset(); } }, _ => (), @@ -373,7 +421,7 @@ impl ansi::Handler for Term { println!("reverse_index"); // if cursor is at the top if self.cursor.y == 0 { - self.grid.unfeed(); + self.scroll(-1); } else { // can't wait for nonlexical lifetimes.. omg borrowck let x = self.cursor.x; @@ -443,4 +491,10 @@ impl ansi::Handler for Term { } } } + + fn set_scrolling_region(&mut self, top: i64, bot: i64) { + println!("set scroll region: {:?} - {:?}", top, bot); + // 1 is added to bottom for inclusive range + self.scroll_region = (top as usize)..((bot as usize) + 1); + } } -- cgit