mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 05:12:40 +03:00
move screen to own file
This commit is contained in:
parent
604d1c27d7
commit
b967bfe3fd
262
term/src/lib.rs
262
term/src/lib.rs
@ -35,6 +35,9 @@ pub use cell::*;
|
||||
pub mod line;
|
||||
pub use line::*;
|
||||
|
||||
pub mod screen;
|
||||
pub use screen::*;
|
||||
|
||||
pub mod selection;
|
||||
use selection::{SelectionCoordinate, SelectionRange};
|
||||
|
||||
@ -112,265 +115,6 @@ pub const ST: &[u8] = b"\x1b\\";
|
||||
#[allow(dead_code)]
|
||||
pub const DCS: &[u8] = b"\x1bP";
|
||||
|
||||
/// Holds the model of a screen. This can either be the primary screen
|
||||
/// which includes lines of scrollback text, or the alternate screen
|
||||
/// which holds no scrollback. The intent is to have one instance of
|
||||
/// Screen for each of these things.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Screen {
|
||||
/// Holds the line data that comprises the screen contents.
|
||||
/// This is allocated with capacity for the entire scrollback.
|
||||
/// The last N lines are the visible lines, with those prior being
|
||||
/// the lines that have scrolled off the top of the screen.
|
||||
/// Index 0 is the topmost line of the screen/scrollback (depending
|
||||
/// on the current window size) and will be the first line to be
|
||||
/// popped off the front of the screen when a new line is added that
|
||||
/// would otherwise have exceeded the line capacity
|
||||
lines: Vec<Line>,
|
||||
|
||||
/// Maximum number of lines of scrollback
|
||||
scrollback_size: usize,
|
||||
|
||||
/// Physical, visible height of the screen (not including scrollback)
|
||||
physical_rows: usize,
|
||||
/// Physical, visible width of the screen
|
||||
physical_cols: usize,
|
||||
}
|
||||
|
||||
impl Screen {
|
||||
/// Create a new Screen with the specified dimensions.
|
||||
/// The Cells in the viewable portion of the screen are set to the
|
||||
/// default cell attributes.
|
||||
pub fn new(physical_rows: usize, physical_cols: usize, scrollback_size: usize) -> Screen {
|
||||
let mut lines = Vec::with_capacity(physical_rows + scrollback_size);
|
||||
for _ in 0..physical_rows {
|
||||
lines.push(Line::new(physical_cols));
|
||||
}
|
||||
|
||||
Screen {
|
||||
lines,
|
||||
scrollback_size,
|
||||
physical_rows,
|
||||
physical_cols,
|
||||
}
|
||||
}
|
||||
|
||||
/// Resize the physical, viewable portion of the screen
|
||||
pub fn resize(&mut self, physical_rows: usize, physical_cols: usize) {
|
||||
let capacity = physical_rows + self.scrollback_size;
|
||||
let current_capacity = self.lines.capacity();
|
||||
if capacity > current_capacity {
|
||||
self.lines.reserve(capacity - current_capacity);
|
||||
}
|
||||
|
||||
if physical_rows > self.physical_rows {
|
||||
// Enlarging the viewable portion? Add more lines at the bottom
|
||||
for _ in self.physical_rows..physical_rows {
|
||||
self.lines.push(Line::new(physical_cols));
|
||||
}
|
||||
}
|
||||
self.physical_rows = physical_rows;
|
||||
self.physical_cols = physical_cols;
|
||||
}
|
||||
|
||||
/// Get mutable reference to a line, relative to start of scrollback.
|
||||
/// Sets the line dirty.
|
||||
fn line_mut(&mut self, idx: PhysRowIndex) -> &mut Line {
|
||||
let line = &mut self.lines[idx];
|
||||
line.set_dirty();
|
||||
line
|
||||
}
|
||||
|
||||
/// Sets a line dirty. The line is relative to the visible origin.
|
||||
#[inline]
|
||||
fn dirty_line(&mut self, idx: VisibleRowIndex) {
|
||||
let line_idx = self.phys_row(idx);
|
||||
if line_idx < self.lines.len() {
|
||||
self.lines[line_idx].set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears the dirty flag for a line. The line is relative to the visible origin.
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn clean_line(&mut self, idx: VisibleRowIndex) {
|
||||
let line_idx = self.phys_row(idx);
|
||||
self.lines[line_idx].set_clean();
|
||||
}
|
||||
|
||||
/// Returns a slice over the visible lines in the screen (no scrollback)
|
||||
#[cfg(test)]
|
||||
fn visible_lines(&self) -> &[Line] {
|
||||
let line_idx = self.lines.len() - self.physical_rows;
|
||||
&self.lines[line_idx..line_idx + self.physical_rows]
|
||||
}
|
||||
|
||||
/// Set a cell. the x and y coordinates are relative to the visible screeen
|
||||
/// origin. 0,0 is the top left.
|
||||
pub fn set_cell(
|
||||
&mut self,
|
||||
x: usize,
|
||||
y: VisibleRowIndex,
|
||||
c: char,
|
||||
attr: &CellAttributes,
|
||||
) -> &Cell {
|
||||
let line_idx = self.phys_row(y);
|
||||
debug!(
|
||||
"set_cell x,y {},{}, line_idx = {} {} {:?}",
|
||||
x,
|
||||
y,
|
||||
line_idx,
|
||||
c,
|
||||
attr
|
||||
);
|
||||
|
||||
let cells = &mut self.line_mut(line_idx).cells;
|
||||
let width = cells.len();
|
||||
// if the line isn't wide enough, pad it out with the default attributes
|
||||
if x >= width {
|
||||
cells.resize(x + 1, Cell::default());
|
||||
}
|
||||
cells[x] = Cell::from_char(c, attr);
|
||||
&cells[x]
|
||||
}
|
||||
|
||||
pub fn clear_line(&mut self, y: VisibleRowIndex, cols: std::ops::Range<usize>) {
|
||||
let blank = Cell::default();
|
||||
let line_idx = self.phys_row(y);
|
||||
let line = self.line_mut(line_idx);
|
||||
let max_col = line.cells.len();
|
||||
for x in cols {
|
||||
if x >= max_col {
|
||||
break;
|
||||
}
|
||||
line.cells[x] = blank.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate a VisibleRowIndex into a PhysRowIndex. The resultant index
|
||||
/// will be invalidated by inserting or removing rows!
|
||||
#[inline]
|
||||
fn phys_row(&self, row: VisibleRowIndex) -> PhysRowIndex {
|
||||
assert!(row >= 0);
|
||||
(self.lines.len() - self.physical_rows) + row as usize
|
||||
}
|
||||
|
||||
/// Given a possibly negative row number, return the corresponding physical
|
||||
/// row. This is similar to phys_row() but allows indexing backwards into
|
||||
/// the scrollback.
|
||||
#[inline]
|
||||
fn scrollback_or_visible_row(&self, row: ScrollbackOrVisibleRowIndex) -> PhysRowIndex {
|
||||
((self.lines.len() - self.physical_rows) as ScrollbackOrVisibleRowIndex + row).max(0) as
|
||||
usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn scrollback_or_visible_range(
|
||||
&self,
|
||||
range: &Range<ScrollbackOrVisibleRowIndex>,
|
||||
) -> Range<PhysRowIndex> {
|
||||
self.scrollback_or_visible_row(range.start)..self.scrollback_or_visible_row(range.end)
|
||||
}
|
||||
|
||||
/// Translate a range of VisibleRowIndex to a range of PhysRowIndex.
|
||||
/// The resultant range will be invalidated by inserting or removing rows!
|
||||
#[inline]
|
||||
fn phys_range(&self, range: &Range<VisibleRowIndex>) -> Range<PhysRowIndex> {
|
||||
self.phys_row(range.start)..self.phys_row(range.end)
|
||||
}
|
||||
|
||||
/// ---------
|
||||
/// |
|
||||
/// |--- top
|
||||
/// |
|
||||
/// |--- bottom
|
||||
///
|
||||
/// scroll the region up by num_rows. Any rows that would be scrolled
|
||||
/// beyond the top get removed from the screen.
|
||||
/// In other words, we remove (top..top+num_rows) and then insert num_rows
|
||||
/// at bottom.
|
||||
/// If the top of the region is the top of the visible display, rather than
|
||||
/// removing the lines we let them go into the scrollback.
|
||||
fn scroll_up(&mut self, scroll_region: &Range<VisibleRowIndex>, num_rows: usize) {
|
||||
let phys_scroll = self.phys_range(&scroll_region);
|
||||
assert!(num_rows <= phys_scroll.end - phys_scroll.start);
|
||||
|
||||
// Invalidate the lines that will move before they move so that
|
||||
// the indices of the lines are stable (we may remove lines below)
|
||||
for y in phys_scroll.clone() {
|
||||
self.line_mut(y).set_dirty();
|
||||
}
|
||||
|
||||
// if we're going to remove lines due to lack of scrollback capacity,
|
||||
// remember how many so that we can adjust our insertion point later.
|
||||
let lines_removed = if scroll_region.start > 0 {
|
||||
// No scrollback available for these;
|
||||
// Remove the scrolled lines
|
||||
num_rows
|
||||
} else {
|
||||
let max_allowed = self.physical_rows + self.scrollback_size;
|
||||
if self.lines.len() + num_rows >= max_allowed {
|
||||
(self.lines.len() + num_rows) - max_allowed
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
// Perform the removal
|
||||
for _ in 0..lines_removed {
|
||||
self.lines.remove(phys_scroll.start);
|
||||
}
|
||||
|
||||
if scroll_region.start == 0 {
|
||||
// All of the lines above the top are now effectively dirty because
|
||||
// they were moved into scrollback by the scroll operation.
|
||||
for y in 0..phys_scroll.start {
|
||||
self.line_mut(y).set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..num_rows {
|
||||
self.lines.insert(
|
||||
phys_scroll.end - lines_removed,
|
||||
Line::new(self.physical_cols),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ---------
|
||||
/// |
|
||||
/// |--- top
|
||||
/// |
|
||||
/// |--- bottom
|
||||
///
|
||||
/// scroll the region down by num_rows. Any rows that would be scrolled
|
||||
/// beyond the bottom get removed from the screen.
|
||||
/// In other words, we remove (bottom-num_rows..bottom) and then insert num_rows
|
||||
/// at scroll_top.
|
||||
fn scroll_down(&mut self, scroll_region: &Range<VisibleRowIndex>, num_rows: usize) {
|
||||
let phys_scroll = self.phys_range(&scroll_region);
|
||||
assert!(num_rows <= phys_scroll.end - phys_scroll.start);
|
||||
|
||||
let middle = phys_scroll.end - num_rows;
|
||||
|
||||
// dirty the rows in the region
|
||||
for y in phys_scroll.start..middle {
|
||||
self.line_mut(y).set_dirty();
|
||||
}
|
||||
|
||||
for _ in 0..num_rows {
|
||||
self.lines.remove(middle);
|
||||
}
|
||||
|
||||
for _ in 0..num_rows {
|
||||
self.lines.insert(
|
||||
phys_scroll.start,
|
||||
Line::new(self.physical_cols),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a little helper that keeps track of the "click streak",
|
||||
/// which is the number of successive clicks of the same mouse button
|
||||
/// within the CLICK_INTERVAL. The streak is reset to 1 each time
|
||||
|
261
term/src/screen.rs
Normal file
261
term/src/screen.rs
Normal file
@ -0,0 +1,261 @@
|
||||
use super::*;
|
||||
|
||||
/// Holds the model of a screen. This can either be the primary screen
|
||||
/// which includes lines of scrollback text, or the alternate screen
|
||||
/// which holds no scrollback. The intent is to have one instance of
|
||||
/// Screen for each of these things.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Screen {
|
||||
/// Holds the line data that comprises the screen contents.
|
||||
/// This is allocated with capacity for the entire scrollback.
|
||||
/// The last N lines are the visible lines, with those prior being
|
||||
/// the lines that have scrolled off the top of the screen.
|
||||
/// Index 0 is the topmost line of the screen/scrollback (depending
|
||||
/// on the current window size) and will be the first line to be
|
||||
/// popped off the front of the screen when a new line is added that
|
||||
/// would otherwise have exceeded the line capacity
|
||||
pub lines: Vec<Line>,
|
||||
|
||||
/// Maximum number of lines of scrollback
|
||||
pub scrollback_size: usize,
|
||||
|
||||
/// Physical, visible height of the screen (not including scrollback)
|
||||
pub physical_rows: usize,
|
||||
/// Physical, visible width of the screen
|
||||
pub physical_cols: usize,
|
||||
}
|
||||
|
||||
impl Screen {
|
||||
/// Create a new Screen with the specified dimensions.
|
||||
/// The Cells in the viewable portion of the screen are set to the
|
||||
/// default cell attributes.
|
||||
pub fn new(physical_rows: usize, physical_cols: usize, scrollback_size: usize) -> Screen {
|
||||
let mut lines = Vec::with_capacity(physical_rows + scrollback_size);
|
||||
for _ in 0..physical_rows {
|
||||
lines.push(Line::new(physical_cols));
|
||||
}
|
||||
|
||||
Screen {
|
||||
lines,
|
||||
scrollback_size,
|
||||
physical_rows,
|
||||
physical_cols,
|
||||
}
|
||||
}
|
||||
|
||||
/// Resize the physical, viewable portion of the screen
|
||||
pub fn resize(&mut self, physical_rows: usize, physical_cols: usize) {
|
||||
let capacity = physical_rows + self.scrollback_size;
|
||||
let current_capacity = self.lines.capacity();
|
||||
if capacity > current_capacity {
|
||||
self.lines.reserve(capacity - current_capacity);
|
||||
}
|
||||
|
||||
if physical_rows > self.physical_rows {
|
||||
// Enlarging the viewable portion? Add more lines at the bottom
|
||||
for _ in self.physical_rows..physical_rows {
|
||||
self.lines.push(Line::new(physical_cols));
|
||||
}
|
||||
}
|
||||
self.physical_rows = physical_rows;
|
||||
self.physical_cols = physical_cols;
|
||||
}
|
||||
|
||||
/// Get mutable reference to a line, relative to start of scrollback.
|
||||
/// Sets the line dirty.
|
||||
#[inline]
|
||||
pub fn line_mut(&mut self, idx: PhysRowIndex) -> &mut Line {
|
||||
let line = &mut self.lines[idx];
|
||||
line.set_dirty();
|
||||
line
|
||||
}
|
||||
|
||||
/// Sets a line dirty. The line is relative to the visible origin.
|
||||
#[inline]
|
||||
pub fn dirty_line(&mut self, idx: VisibleRowIndex) {
|
||||
let line_idx = self.phys_row(idx);
|
||||
if line_idx < self.lines.len() {
|
||||
self.lines[line_idx].set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears the dirty flag for a line. The line is relative to the visible origin.
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn clean_line(&mut self, idx: VisibleRowIndex) {
|
||||
let line_idx = self.phys_row(idx);
|
||||
self.lines[line_idx].set_clean();
|
||||
}
|
||||
|
||||
/// Returns a slice over the visible lines in the screen (no scrollback)
|
||||
#[cfg(test)]
|
||||
pub fn visible_lines(&self) -> &[Line] {
|
||||
let line_idx = self.lines.len() - self.physical_rows;
|
||||
&self.lines[line_idx..line_idx + self.physical_rows]
|
||||
}
|
||||
|
||||
/// Set a cell. the x and y coordinates are relative to the visible screeen
|
||||
/// origin. 0,0 is the top left.
|
||||
pub fn set_cell(
|
||||
&mut self,
|
||||
x: usize,
|
||||
y: VisibleRowIndex,
|
||||
c: char,
|
||||
attr: &CellAttributes,
|
||||
) -> &Cell {
|
||||
let line_idx = self.phys_row(y);
|
||||
debug!(
|
||||
"set_cell x,y {},{}, line_idx = {} {} {:?}",
|
||||
x,
|
||||
y,
|
||||
line_idx,
|
||||
c,
|
||||
attr
|
||||
);
|
||||
|
||||
let cells = &mut self.line_mut(line_idx).cells;
|
||||
let width = cells.len();
|
||||
// if the line isn't wide enough, pad it out with the default attributes
|
||||
if x >= width {
|
||||
cells.resize(x + 1, Cell::default());
|
||||
}
|
||||
cells[x] = Cell::from_char(c, attr);
|
||||
&cells[x]
|
||||
}
|
||||
|
||||
pub fn clear_line(&mut self, y: VisibleRowIndex, cols: std::ops::Range<usize>) {
|
||||
let blank = Cell::default();
|
||||
let line_idx = self.phys_row(y);
|
||||
let line = self.line_mut(line_idx);
|
||||
let max_col = line.cells.len();
|
||||
for x in cols {
|
||||
if x >= max_col {
|
||||
break;
|
||||
}
|
||||
line.cells[x] = blank.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate a VisibleRowIndex into a PhysRowIndex. The resultant index
|
||||
/// will be invalidated by inserting or removing rows!
|
||||
#[inline]
|
||||
pub fn phys_row(&self, row: VisibleRowIndex) -> PhysRowIndex {
|
||||
assert!(row >= 0);
|
||||
(self.lines.len() - self.physical_rows) + row as usize
|
||||
}
|
||||
|
||||
/// Given a possibly negative row number, return the corresponding physical
|
||||
/// row. This is similar to phys_row() but allows indexing backwards into
|
||||
/// the scrollback.
|
||||
#[inline]
|
||||
pub fn scrollback_or_visible_row(&self, row: ScrollbackOrVisibleRowIndex) -> PhysRowIndex {
|
||||
((self.lines.len() - self.physical_rows) as ScrollbackOrVisibleRowIndex + row).max(0) as
|
||||
usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scrollback_or_visible_range(
|
||||
&self,
|
||||
range: &Range<ScrollbackOrVisibleRowIndex>,
|
||||
) -> Range<PhysRowIndex> {
|
||||
self.scrollback_or_visible_row(range.start)..self.scrollback_or_visible_row(range.end)
|
||||
}
|
||||
|
||||
/// Translate a range of VisibleRowIndex to a range of PhysRowIndex.
|
||||
/// The resultant range will be invalidated by inserting or removing rows!
|
||||
#[inline]
|
||||
pub fn phys_range(&self, range: &Range<VisibleRowIndex>) -> Range<PhysRowIndex> {
|
||||
self.phys_row(range.start)..self.phys_row(range.end)
|
||||
}
|
||||
|
||||
/// ---------
|
||||
/// |
|
||||
/// |--- top
|
||||
/// |
|
||||
/// |--- bottom
|
||||
///
|
||||
/// scroll the region up by num_rows. Any rows that would be scrolled
|
||||
/// beyond the top get removed from the screen.
|
||||
/// In other words, we remove (top..top+num_rows) and then insert num_rows
|
||||
/// at bottom.
|
||||
/// If the top of the region is the top of the visible display, rather than
|
||||
/// removing the lines we let them go into the scrollback.
|
||||
pub fn scroll_up(&mut self, scroll_region: &Range<VisibleRowIndex>, num_rows: usize) {
|
||||
let phys_scroll = self.phys_range(&scroll_region);
|
||||
assert!(num_rows <= phys_scroll.end - phys_scroll.start);
|
||||
|
||||
// Invalidate the lines that will move before they move so that
|
||||
// the indices of the lines are stable (we may remove lines below)
|
||||
for y in phys_scroll.clone() {
|
||||
self.line_mut(y).set_dirty();
|
||||
}
|
||||
|
||||
// if we're going to remove lines due to lack of scrollback capacity,
|
||||
// remember how many so that we can adjust our insertion point later.
|
||||
let lines_removed = if scroll_region.start > 0 {
|
||||
// No scrollback available for these;
|
||||
// Remove the scrolled lines
|
||||
num_rows
|
||||
} else {
|
||||
let max_allowed = self.physical_rows + self.scrollback_size;
|
||||
if self.lines.len() + num_rows >= max_allowed {
|
||||
(self.lines.len() + num_rows) - max_allowed
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
// Perform the removal
|
||||
for _ in 0..lines_removed {
|
||||
self.lines.remove(phys_scroll.start);
|
||||
}
|
||||
|
||||
if scroll_region.start == 0 {
|
||||
// All of the lines above the top are now effectively dirty because
|
||||
// they were moved into scrollback by the scroll operation.
|
||||
for y in 0..phys_scroll.start {
|
||||
self.line_mut(y).set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..num_rows {
|
||||
self.lines.insert(
|
||||
phys_scroll.end - lines_removed,
|
||||
Line::new(self.physical_cols),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ---------
|
||||
/// |
|
||||
/// |--- top
|
||||
/// |
|
||||
/// |--- bottom
|
||||
///
|
||||
/// scroll the region down by num_rows. Any rows that would be scrolled
|
||||
/// beyond the bottom get removed from the screen.
|
||||
/// In other words, we remove (bottom-num_rows..bottom) and then insert num_rows
|
||||
/// at scroll_top.
|
||||
pub fn scroll_down(&mut self, scroll_region: &Range<VisibleRowIndex>, num_rows: usize) {
|
||||
let phys_scroll = self.phys_range(&scroll_region);
|
||||
assert!(num_rows <= phys_scroll.end - phys_scroll.start);
|
||||
|
||||
let middle = phys_scroll.end - num_rows;
|
||||
|
||||
// dirty the rows in the region
|
||||
for y in phys_scroll.start..middle {
|
||||
self.line_mut(y).set_dirty();
|
||||
}
|
||||
|
||||
for _ in 0..num_rows {
|
||||
self.lines.remove(middle);
|
||||
}
|
||||
|
||||
for _ in 0..num_rows {
|
||||
self.lines.insert(
|
||||
phys_scroll.start,
|
||||
Line::new(self.physical_cols),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user