aboutsummaryrefslogtreecommitdiffstats
path: root/src/client
diff options
context:
space:
mode:
authorKunal Mohan <kunalmohan99@gmail.com>2021-05-16 16:42:50 +0530
committerKunal Mohan <kunalmohan99@gmail.com>2021-05-16 21:41:56 +0530
commit2038947a14d32d89da2bdbcaa1381c3015ac9c7a (patch)
tree5a332470e32b380816098594a89d3c3db1f6eb53 /src/client
parenthandle error on termios initialization (#511) (diff)
downloadsl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.tar.gz
sl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.tar.xz
sl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.zip
Big refactor: separate crates for client, server and utilities
Diffstat (limited to 'src/client')
-rw-r--r--src/client/boundaries.rs630
-rw-r--r--src/client/layout.rs237
-rw-r--r--src/client/mod.rs244
-rw-r--r--src/client/pane_resizer.rs532
-rw-r--r--src/client/panes/grid.rs1558
-rw-r--r--src/client/panes/mod.rs9
-rw-r--r--src/client/panes/plugin_pane.rs227
-rw-r--r--src/client/panes/terminal_character.rs775
-rw-r--r--src/client/panes/terminal_pane.rs362
-rw-r--r--src/client/panes/unit/grid_tests.rs397
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_b.snap8
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_i.snap8
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_z.snap8
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__terminal_reports.snap6
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_0.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_1.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_10.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_11.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_12.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_13.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_14.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_2.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_3.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_4.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_5.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_6.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_7.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_8.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_9.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest3_0.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_0.snap57
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_1.snap57
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_2.snap57
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_3.snap57
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_4.snap57
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_5.snap57
-rw-r--r--src/client/tab.rs2274
43 files changed, 0 insertions, 8651 deletions
diff --git a/src/client/boundaries.rs b/src/client/boundaries.rs
deleted file mode 100644
index 86fb37fc..00000000
--- a/src/client/boundaries.rs
+++ /dev/null
@@ -1,630 +0,0 @@
-use crate::tab::Pane;
-use crate::utils::shared::colors;
-use ansi_term::Colour::{Fixed, RGB};
-use std::collections::HashMap;
-use zellij_tile::data::{InputMode, Palette, PaletteColor};
-
-use std::fmt::{Display, Error, Formatter};
-pub mod boundary_type {
- pub const TOP_RIGHT: &str = "┐";
- pub const VERTICAL: &str = "│";
- pub const HORIZONTAL: &str = "─";
- pub const TOP_LEFT: &str = "┌";
- pub const BOTTOM_RIGHT: &str = "┘";
- pub const BOTTOM_LEFT: &str = "└";
- pub const VERTICAL_LEFT: &str = "┤";
- pub const VERTICAL_RIGHT: &str = "├";
- pub const HORIZONTAL_DOWN: &str = "┬";
- pub const HORIZONTAL_UP: &str = "┴";
- pub const CROSS: &str = "┼";
-}
-
-pub type BoundaryType = &'static str; // easy way to refer to boundary_type above
-
-#[derive(Clone, Copy, Debug)]
-pub struct BoundarySymbol {
- boundary_type: BoundaryType,
- invisible: bool,
- color: Option<PaletteColor>,
-}
-
-impl BoundarySymbol {
- pub fn new(boundary_type: BoundaryType) -> Self {
- BoundarySymbol {
- boundary_type,
- invisible: false,
- color: Some(PaletteColor::EightBit(colors::GRAY)),
- }
- }
- pub fn invisible(mut self) -> Self {
- self.invisible = true;
- self
- }
- pub fn color(&mut self, color: Option<PaletteColor>) -> Self {
- self.color = color;
- *self
- }
-}
-
-impl Display for BoundarySymbol {
- fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
- match self.invisible {
- true => write!(f, " "),
- false => match self.color {
- Some(color) => match color {
- PaletteColor::Rgb((r, g, b)) => {
- write!(f, "{}", RGB(r, g, b).paint(self.boundary_type))
- }
- PaletteColor::EightBit(color) => {
- write!(f, "{}", Fixed(color).paint(self.boundary_type))
- }
- },
- None => write!(f, "{}", self.boundary_type),
- },
- }
- }
-}
-
-fn combine_symbols(
- current_symbol: BoundarySymbol,
- next_symbol: BoundarySymbol,
-) -> Option<BoundarySymbol> {
- use boundary_type::*;
- let invisible = current_symbol.invisible || next_symbol.invisible;
- let color = current_symbol.color.or(next_symbol.color);
- match (current_symbol.boundary_type, next_symbol.boundary_type) {
- (CROSS, _) | (_, CROSS) => {
- // (┼, *) or (*, ┼) => Some(┼)
- let boundary_type = CROSS;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (TOP_RIGHT, TOP_RIGHT) => {
- // (┐, ┐) => Some(┐)
- let boundary_type = TOP_RIGHT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (TOP_RIGHT, VERTICAL) | (TOP_RIGHT, BOTTOM_RIGHT) | (TOP_RIGHT, VERTICAL_LEFT) => {
- // (┐, │) => Some(┤)
- // (┐, ┘) => Some(┤)
- // (─, ┤) => Some(┤)
- let boundary_type = VERTICAL_LEFT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (TOP_RIGHT, HORIZONTAL) | (TOP_RIGHT, TOP_LEFT) | (TOP_RIGHT, HORIZONTAL_DOWN) => {
- // (┐, ─) => Some(┬)
- // (┐, ┌) => Some(┬)
- // (┐, ┬) => Some(┬)
- let boundary_type = HORIZONTAL_DOWN;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (TOP_RIGHT, BOTTOM_LEFT) | (TOP_RIGHT, VERTICAL_RIGHT) | (TOP_RIGHT, HORIZONTAL_UP) => {
- // (┐, └) => Some(┼)
- // (┐, ├) => Some(┼)
- // (┐, ┴) => Some(┼)
- let boundary_type = CROSS;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (HORIZONTAL, HORIZONTAL) => {
- // (─, ─) => Some(─)
- let boundary_type = HORIZONTAL;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (HORIZONTAL, VERTICAL) | (HORIZONTAL, VERTICAL_LEFT) | (HORIZONTAL, VERTICAL_RIGHT) => {
- // (─, │) => Some(┼)
- // (─, ┤) => Some(┼)
- // (─, ├) => Some(┼)
- let boundary_type = CROSS;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (HORIZONTAL, TOP_LEFT) | (HORIZONTAL, HORIZONTAL_DOWN) => {
- // (─, ┌) => Some(┬)
- // (─, ┬) => Some(┬)
- let boundary_type = HORIZONTAL_DOWN;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (HORIZONTAL, BOTTOM_RIGHT) | (HORIZONTAL, BOTTOM_LEFT) | (HORIZONTAL, HORIZONTAL_UP) => {
- // (─, ┘) => Some(┴)
- // (─, └) => Some(┴)
- // (─, ┴) => Some(┴)
- let boundary_type = HORIZONTAL_UP;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (VERTICAL, VERTICAL) => {
- // (│, │) => Some(│)
- let boundary_type = VERTICAL;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (VERTICAL, TOP_LEFT) | (VERTICAL, BOTTOM_LEFT) | (VERTICAL, VERTICAL_RIGHT) => {
- // (│, ┌) => Some(├)
- // (│, └) => Some(├)
- // (│, ├) => Some(├)
- let boundary_type = VERTICAL_RIGHT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (VERTICAL, BOTTOM_RIGHT) | (VERTICAL, VERTICAL_LEFT) => {
- // (│, ┘) => Some(┤)
- // (│, ┤) => Some(┤)
- let boundary_type = VERTICAL_LEFT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (VERTICAL, HORIZONTAL_DOWN) | (VERTICAL, HORIZONTAL_UP) => {
- // (│, ┬) => Some(┼)
- // (│, ┴) => Some(┼)
- let boundary_type = CROSS;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (TOP_LEFT, TOP_LEFT) => {
- // (┌, ┌) => Some(┌)
- let boundary_type = TOP_LEFT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (TOP_LEFT, BOTTOM_RIGHT) | (TOP_LEFT, VERTICAL_LEFT) | (TOP_LEFT, HORIZONTAL_UP) => {
- // (┌, ┘) => Some(┼)
- // (┌, ┤) => Some(┼)
- // (┌, ┴) => Some(┼)
- let boundary_type = CROSS;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (TOP_LEFT, BOTTOM_LEFT) | (TOP_LEFT, VERTICAL_RIGHT) => {
- // (┌, └) => Some(├)
- // (┌, ├) => Some(├)
- let boundary_type = VERTICAL_RIGHT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (TOP_LEFT, HORIZONTAL_DOWN) => {
- // (┌, ┬) => Some(┬)
- let boundary_type = HORIZONTAL_DOWN;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (BOTTOM_RIGHT, BOTTOM_RIGHT) => {
- // (┘, ┘) => Some(┘)
- let boundary_type = BOTTOM_RIGHT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (BOTTOM_RIGHT, BOTTOM_LEFT) | (BOTTOM_RIGHT, HORIZONTAL_UP) => {
- // (┘, └) => Some(┴)
- // (┘, ┴) => Some(┴)
- let boundary_type = HORIZONTAL_UP;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (BOTTOM_RIGHT, VERTICAL_LEFT) => {
- // (┘, ┤) => Some(┤)
- let boundary_type = VERTICAL_LEFT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (BOTTOM_RIGHT, VERTICAL_RIGHT) | (BOTTOM_RIGHT, HORIZONTAL_DOWN) => {
- // (┘, ├) => Some(┼)
- // (┘, ┬) => Some(┼)
- let boundary_type = CROSS;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (BOTTOM_LEFT, BOTTOM_LEFT) => {
- // (└, └) => Some(└)
- let boundary_type = BOTTOM_LEFT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (BOTTOM_LEFT, VERTICAL_LEFT) | (BOTTOM_LEFT, HORIZONTAL_DOWN) => {
- // (└, ┤) => Some(┼)
- // (└, ┬) => Some(┼)
- let boundary_type = CROSS;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (BOTTOM_LEFT, VERTICAL_RIGHT) => {
- // (└, ├) => Some(├)
- let boundary_type = VERTICAL_RIGHT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (BOTTOM_LEFT, HORIZONTAL_UP) => {
- // (└, ┴) => Some(┴)
- let boundary_type = HORIZONTAL_UP;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (VERTICAL_LEFT, VERTICAL_LEFT) => {
- // (┤, ┤) => Some(┤)
- let boundary_type = VERTICAL_LEFT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (VERTICAL_LEFT, VERTICAL_RIGHT)
- | (VERTICAL_LEFT, HORIZONTAL_DOWN)
- | (VERTICAL_LEFT, HORIZONTAL_UP) => {
- // (┤, ├) => Some(┼)
- // (┤, ┬) => Some(┼)
- // (┤, ┴) => Some(┼)
- let boundary_type = CROSS;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (VERTICAL_RIGHT, VERTICAL_RIGHT) => {
- // (├, ├) => Some(├)
- let boundary_type = VERTICAL_RIGHT;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (VERTICAL_RIGHT, HORIZONTAL_DOWN) | (VERTICAL_RIGHT, HORIZONTAL_UP) => {
- // (├, ┬) => Some(┼)
- // (├, ┴) => Some(┼)
- let boundary_type = CROSS;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (HORIZONTAL_DOWN, HORIZONTAL_DOWN) => {
- // (┬, ┬) => Some(┬)
- let boundary_type = HORIZONTAL_DOWN;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (HORIZONTAL_DOWN, HORIZONTAL_UP) => {
- // (┬, ┴) => Some(┼)
- let boundary_type = CROSS;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (HORIZONTAL_UP, HORIZONTAL_UP) => {
- // (┴, ┴) => Some(┴)
- let boundary_type = HORIZONTAL_UP;
- Some(BoundarySymbol {
- boundary_type,
- invisible,
- color,
- })
- }
- (_, _) => combine_symbols(next_symbol, current_symbol),
- }
-}
-
-#[derive(PartialEq, Eq, Hash, Debug)]
-pub struct Coordinates {
- x: usize,
- y: usize,
-}
-
-impl Coordinates {
- pub fn new(x: usize, y: usize) -> Self {
- Coordinates { x, y }
- }
-}
-
-pub trait Rect {
- fn x(&self) -> usize;
- fn y(&self) -> usize;
- fn rows(&self) -> usize;
- fn columns(&self) -> usize;
- fn right_boundary_x_coords(&self) -> usize {
- self.x() + self.columns()
- }
- fn bottom_boundary_y_coords(&self) -> usize {
- self.y() + self.rows()
- }
- fn is_directly_right_of(&self, other: &Self) -> bool {
- self.x() == other.x() + other.columns() + 1
- }
- fn is_directly_left_of(&self, other: &Self) -> bool {
- self.x() + self.columns() + 1 == other.x()
- }
- fn is_directly_below(&self, other: &Self) -> bool {
- self.y() == other.y() + other.rows() + 1
- }
- fn is_directly_above(&self, other: &Self) -> bool {
- self.y() + self.rows() + 1 == other.y()
- }
- fn horizontally_overlaps_with(&self, other: &Self) -> bool {
- (self.y() >= other.y() && self.y() <= (other.y() + other.rows()))
- || ((self.y() + self.rows()) <= (other.y() + other.rows())
- && (self.y() + self.rows()) > other.y())
- || (self.y() <= other.y() && (self.y() + self.rows() >= (other.y() + other.rows())))
- || (other.y() <= self.y() && (other.y() + other.rows() >= (self.y() + self.rows())))
- }
- fn get_horizontal_overlap_with(&self, other: &Self) -> usize {
- std::cmp::min(self.y() + self.rows(), other.y() + other.rows())
- - std::cmp::max(self.y(), other.y())
- }
- fn vertically_overlaps_with(&self, other: &Self) -> bool {
- (self.x() >= other.x() && self.x() <= (other.x() + other.columns()))
- || ((self.x() + self.columns()) <= (other.x() + other.columns())
- && (self.x() + self.columns()) > other.x())
- || (self.x() <= other.x()
- && (self.x() + self.columns() >= (other.x() + other.columns())))
- || (other.x() <= self.x()
- && (other.x() + other.columns() >= (self.x() + self.columns())))
- }
- fn get_vertical_overlap_with(&self, other: &Self) -> usize {
- std::cmp::min(self.x() + self.columns(), other.x() + other.columns())
- - std::cmp::max(self.x(), other.x())
- }
-}
-
-pub struct Boundaries {
- columns: usize,
- rows: usize,
- // boundary_characters: HashMap<Coordinates, BoundaryType>,
- boundary_characters: HashMap<Coordinates, BoundarySymbol>,
-}
-
-impl Boundaries {
- pub fn new(columns: u16, rows: u16) -> Self {
- let columns = columns as usize;
- let rows = rows as usize;
- Boundaries {
- columns,
- rows,
- boundary_characters: HashMap::new(),
- }
- }
- pub fn add_rect(&mut self, rect: &dyn Pane, input_mode: InputMode, palette: Option<Palette>) {
- let color = match palette.is_some() {
- true => match input_mode {
- InputMode::Normal | InputMode::Locked => Some(palette.unwrap().green),
- _ => Some(palette.unwrap().orange),
- },
- false => None,
- };
- if rect.x() > 0 {
- let boundary_x_coords = rect.x() - 1;
- let first_row_coordinates = self.rect_right_boundary_row_start(rect);
- let last_row_coordinates = self.rect_right_boundary_row_end(rect);
- for row in first_row_coordinates..last_row_coordinates {
- let coordinates = Coordinates::new(boundary_x_coords, row);
- let mut symbol_to_add = if row == first_row_coordinates && row != 0 {
- BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
- } else if row == last_row_coordinates - 1 && row != self.rows - 1 {
- BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color)
- } else {
- BoundarySymbol::new(boundary_type::VERTICAL).color(color)
- };
- if rect.invisible_borders() {
- symbol_to_add = symbol_to_add.invisible();
- }
- let next_symbol = self
- .boundary_characters
- .remove(&coordinates)
- .and_then(|current_symbol| combine_symbols(current_symbol, symbol_to_add))
- .unwrap_or(symbol_to_add);
- self.boundary_characters.insert(coordinates, next_symbol);
- }
- }
- if rect.y() > 0 {
- let boundary_y_coords = rect.y() - 1;
- let first_col_coordinates = self.rect_bottom_boundary_col_start(rect);
- let last_col_coordinates = self.rect_bottom_boundary_col_end(rect);
- for col in first_col_coordinates..last_col_coordinates {
- let coordinates = Coordinates::new(col, boundary_y_coords);
- let mut symbol_to_add = if col == first_col_coordinates && col != 0 {
- BoundarySymbol::new(boundary_type::TOP_LEFT).color(color)
- } else if col == last_col_coordinates - 1 && col != self.columns - 1 {
- BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color)
- } else {
- BoundarySymbol::new(boundary_type::HORIZONTAL).color(color)
- };
- if rect.invisible_borders() {
- symbol_to_add = symbol_to_add.invisible();
- }
- let next_symbol = self
- .boundary_characters
- .remove(&coordinates)
- .and_then(|current_symbol| combine_symbols(current_symbol, symbol_to_add))
- .unwrap_or(symbol_to_add);
- self.boundary_characters.insert(coordinates, next_symbol);
- }
- }
- if self.rect_right_boundary_is_before_screen_edge(rect) {
- // let boundary_x_coords = self.rect_right_boundary_x_coords(rect);
- let boundary_x_coords = rect.right_boundary_x_coords();
- let first_row_coordinates = self.rect_right_boundary_row_start(rect);
- let last_row_coordinates = self.rect_right_boundary_row_end(rect);
- for row in first_row_coordinates..last_row_coordinates {
- let coordinates = Coordinates::new(boundary_x_coords, row);
- let mut symbol_to_add = if row == first_row_coordinates && row != 0 {
- BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color)
- } else if row == last_row_coordinates - 1 && row != self.rows - 1 {
- BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color)
- } else {
- BoundarySymbol::new(boundary_type::VERTICAL).color(color)
- };
- if rect.invisible_borders() {
- symbol_to_add = symbol_to_add.invisible();
- }
- let next_symbol = self
- .boundary_characters
- .remove(&coordinates)
- .and_then(|current_symbol| combine_symbols(current_symbol, symbol_to_add))
- .unwrap_or(symbol_to_add);
- self.boundary_characters.insert(coordinates, next_symbol);
- }
- }
- if self.rect_bottom_boundary_is_before_screen_edge(rect) {
- let boundary_y_coords = rect.bottom_boundary_y_coords();
- let first_col_coordinates = self.rect_bottom_boundary_col_start(rect);
- let last_col_coordinates = self.rect_bottom_boundary_col_end(rect);
- for col in first_col_coordinates..last_col_coordinates {
- let coordinates = Coordinates::new(col, boundary_y_coords);
- let mut symbol_to_add = if col == first_col_coordinates && col != 0 {
- BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color)
- } else if col == last_col_coordinates - 1 && col != self.columns - 1 {
- BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color)
- } else {
- BoundarySymbol::new(boundary_type::HORIZONTAL).color(color)
- };
- if rect.invisible_borders() {
- symbol_to_add = symbol_to_add.invisible();
- }
- let next_symbol = self
- .boundary_characters
- .remove(&coordinates)
- .and_then(|current_symbol| combine_symbols(current_symbol, symbol_to_add))
- .unwrap_or(symbol_to_add);
- self.boundary_characters.insert(coordinates, next_symbol);
- }
- }
- }
- pub fn vte_output(&self) -> String {
- let mut vte_output = String::new();
- for (coordinates, boundary_character) in &self.boundary_characters {
- vte_output.push_str(&format!(
- "\u{1b}[{};{}H\u{1b}[m{}",
- coordinates.y + 1,
- coordinates.x + 1,
- boundary_character
- )); // goto row/col + boundary character
- }
- vte_output
- }
- fn rect_right_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool {
- rect.x() + rect.columns() < self.columns
- }
- fn rect_bottom_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool {
- rect.y() + rect.rows() < self.rows
- }
- fn rect_right_boundary_row_start(&self, rect: &dyn Pane) -> usize {
- if rect.y() == 0 {
- 0
- } else {
- rect.y() - 1
- }
- }
- fn rect_right_boundary_row_end(&self, rect: &dyn Pane) -> usize {
- let rect_bottom_row = rect.y() + rect.rows();
- // we do this because unless we're on the screen edge, we'd like to go one extra row to
- // connect to whatever boundary is beneath us
- if rect_bottom_row == self.rows {
- rect_bottom_row
- } else {
- rect_bottom_row + 1
- }
- }
- fn rect_bottom_boundary_col_start(&self, rect: &dyn Pane) -> usize {
- if rect.x() == 0 {
- 0
- } else {
- rect.x() - 1
- }
- }
- fn rect_bottom_boundary_col_end(&self, rect: &dyn Pane) -> usize {
- let rect_right_col = rect.x() + rect.columns();
- // we do this because unless we're on the screen edge, we'd like to go one extra column to
- // connect to whatever boundary is right of us
- if rect_right_col == self.columns {
- rect_right_col
- } else {
- rect_right_col + 1
- }
- }
-}
diff --git a/src/client/layout.rs b/src/client/layout.rs
deleted file mode 100644
index 3648ece9..00000000
--- a/src/client/layout.rs
+++ /dev/null
@@ -1,237 +0,0 @@
-use serde::{Deserialize, Serialize};
-use std::path::{Path, PathBuf};
-use std::{fs::File, io::prelude::*};
-
-use crate::panes::PositionAndSize;
-
-fn split_space_to_parts_vertically(
- space_to_split: &PositionAndSize,
- sizes: Vec<Option<SplitSize>>,
-) -> Vec<PositionAndSize> {
- let mut split_parts = Vec::new();
- let mut current_x_position = space_to_split.x;
- let mut current_width = 0;
- let max_width = space_to_split.columns - (sizes.len() - 1); // minus space for gaps
-
- let mut parts_to_grow = Vec::new();
-
- // First fit in the parameterized sizes
- for size in sizes {
- let (columns, max_columns) = match size {
- Some(SplitSize::Percent(percent)) => {
- ((max_width as f32 * (percent as f32 / 100.0)) as usize, None)
- } // TODO: round properly
- Some(SplitSize::Fixed(size)) => (size as usize, Some(size as usize)),
- None => {
- parts_to_grow.push(current_x_position);
- (
- 1, // This is grown later on
- None,
- )
- }
- };
- split_parts.push(PositionAndSize {
- x: current_x_position,
- y: space_to_split.y,
- columns,
- rows: space_to_split.rows,
- max_columns,
- ..Default::default()
- });
- current_width += columns;
- current_x_position += columns + 1; // 1 for gap
- }
-
- if current_width > max_width {
- panic!("Layout contained too many columns to fit onto the screen!");
- }
-
- let mut last_flexible_index = split_parts.len() - 1;
- if let Some(new_columns) = (max_width - current_width).checked_div(parts_to_grow.len()) {
- current_width = 0;
- current_x_position = 0;
- for (idx, part) in split_parts.iter_mut().enumerate() {
- part.x = current_x_position;
- if parts_to_grow.contains(&part.x) {
- part.columns = new_columns;
- last_flexible_index = idx;
- }
- current_width += part.columns;
- current_x_position += part.columns + 1; // 1 for gap
- }
- }
-
- if current_width < max_width {
- // we have some extra space left, let's add it to the last flexible part
- let extra = max_width - current_width;
- let mut last_part = split_parts.get_mut(last_flexible_index).unwrap();
- last_part.columns += extra;
- for part in (&mut split_parts[last_flexible_index + 1..]).iter_mut() {
- part.x += extra;
- }
- }
- split_parts
-}
-
-fn split_space_to_parts_horizontally(
- space_to_split: &PositionAndSize,
- sizes: Vec<Option<SplitSize>>,
-) -> Vec<PositionAndSize> {
- let mut split_parts = Vec::new();
- let mut current_y_position = space_to_split.y;
- let mut current_height = 0;
- let max_height = space_to_split.rows - (sizes.len() - 1); // minus space for gaps
-
- let mut parts_to_grow = Vec::new();
-
- for size in sizes {
- let (rows, max_rows) = match size {
- Some(SplitSize::Percent(percent)) => (
- (max_height as f32 * (percent as f32 / 100.0)) as usize,
- None,
- ), // TODO: round properly
- Some(SplitSize::Fixed(size)) => (size as usize, Some(size as usize)),
- None => {
- parts_to_grow.push(current_y_position);
- (
- 1, // This is grown later on
- None,
- )
- }
- };
- split_parts.push(PositionAndSize {
- x: space_to_split.x,
- y: current_y_position,
- columns: space_to_split.columns,
- rows,
- max_rows,
- ..Default::default()
- });
- current_height += rows;
- current_y_position += rows + 1; // 1 for gap
- }
-
- if current_height > max_height {
- panic!("Layout contained too many rows to fit onto the screen!");
- }
-
- let mut last_flexible_index = split_parts.len() - 1;
- if let Some(new_rows) = (max_height - current_height).checked_div(parts_to_grow.len()) {
- current_height = 0;
- current_y_position = 0;
-
- for (idx, part) in split_parts.iter_mut().enumerate() {
- part.y = current_y_position;
- if parts_to_grow.contains(&part.y) {
- part.rows = new_rows;
- last_flexible_index = idx;
- }
- current_height += part.rows;
- current_y_position += part.rows + 1; // 1 for gap
- }
- }
-
- if current_height < max_height {
- // we have some extra space left, let's add it to the last flexible part
- let extra = max_height - current_height;
- let mut last_part = split_parts.get_mut(last_flexible_index).unwrap();
- last_part.rows += extra;
- for part in (&mut split_parts[last_flexible_index + 1..]).iter_mut() {
- part.y += extra;
- }
- }
- split_parts
-}
-
-fn split_space(
- space_to_split: &PositionAndSize,
- layout: &Layout,
-) -> Vec<(Layout, PositionAndSize)> {
- let mut pane_positions = Vec::new();
- let sizes: Vec<Option<SplitSize>> = layout.parts.iter().map(|part| part.split_size).collect();
-
- let split_parts = match layout.direction {
- Direction::Vertical => split_space_to_parts_vertically(space_to_split, sizes),
- Direction::Horizontal => split_space_to_parts_horizontally(space_to_split, sizes),
- };
- for (i, part) in layout.parts.iter().enumerate() {
- let part_position_and_size = split_parts.get(i).unwrap();
- if !part.parts.is_empty() {
- let mut part_positions = split_space(&part_position_and_size, part);
- pane_positions.append(&mut part_positions);
- } else {
- pane_positions.push((part.clone(), *part_position_and_size));
- }
- }
- pane_positions
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub enum Direction {
- Horizontal,
- Vertical,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
-pub enum SplitSize {
- Percent(u8), // 1 to 100
- Fixed(u16), // An absolute number of columns or rows
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct Layout {
- pub direction: Direction,
- #[serde(default, skip_serializing_if = "Vec::is_empty")]
- pub parts: Vec<Layout>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub split_size: Option<SplitSize>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub plugin: Option<PathBuf>,
-}
-
-impl Layout {
- pub fn new(layout_path: &Path, data_dir: &Path) -> Self {
- let layout_dir = data_dir.join("layouts/");
- let mut layout_file = File::open(&layout_path)
- .or_else(|_| File::open(&layout_dir.join(&layout_path).with_extension("yaml")))
- .unwrap_or_else(|_| panic!("cannot find layout {}", &layout_path.display()));
-
- let mut layout = String::new();
- layout_file
- .read_to_string(&mut layout)
- .unwrap_or_else(|_| panic!("could not read layout {}", &layout_path.display()));
- let layout: Layout = serde_yaml::from_str(&layout)
- .unwrap_or_else(|_| panic!("could not parse layout {}", &layout_path.display()));
- layout
- }
-
- // It wants to use Path here, but that doesn't compile.
- #[allow(clippy::ptr_arg)]
- pub fn from_defaults(layout_path: &PathBuf, data_dir: &Path) -> Self {
- Self::new(
- &data_dir
- .join("layouts/")
- .join(layout_path)
- .with_extension("yaml"),
- &data_dir,
- )
- }
-
- pub fn total_terminal_panes(&self) -> usize {
- let mut total_panes = 0;
- total_panes += self.parts.len();
- for part in self.parts.iter() {
- if part.plugin.is_none() {
- total_panes += part.total_terminal_panes();
- }
- }
- total_panes
- }
-
- pub fn position_panes_in_space(
- &self,
- space: &PositionAndSize,
- ) -> Vec<(Layout, PositionAndSize)> {
- split_space(space, &self)
- }
-}
diff --git a/src/client/mod.rs b/src/client/mod.rs
deleted file mode 100644
index 862c13cc..00000000
--- a/src/client/mod.rs
+++ /dev/null
@@ -1,244 +0,0 @@
-pub mod boundaries;
-pub mod layout;
-pub mod pane_resizer;
-pub mod panes;
-pub mod tab;
-
-use std::env::current_exe;
-use std::io::{self, Write};
-use std::path::Path;
-use std::process::Command;
-use std::sync::mpsc;
-use std::thread;
-
-use crate::cli::CliArgs;
-use crate::common::{
- command_is_executing::CommandIsExecuting,
- errors::ContextType,
- input::config::Config,
- input::handler::input_loop,
- input::options::Options,
- ipc::{ClientToServerMsg, ServerToClientMsg},
- os_input_output::ClientOsApi,
- thread_bus::{SenderType, SenderWithContext, SyncChannelWithContext},
- utils::consts::ZELLIJ_IPC_PIPE,
-};
-
-/// Instructions related to the client-side application and sent from server to client
-#[derive(Debug, Clone)]
-pub enum ClientInstruction {
- Error(String),
- Render(Option<String>),
- UnblockInputThread,
- Exit,
- ServerError(String),
-}
-
-impl From<ServerToClientMsg> for ClientInstruction {
- fn from(instruction: ServerToClientMsg) -> Self {
- match instruction {
- ServerToClientMsg::Exit => ClientInstruction::Exit,
- ServerToClientMsg::Render(buffer) => ClientInstruction::Render(buffer),
- ServerToClientMsg::UnblockInputThread => ClientInstruction::UnblockInputThread,
- ServerToClientMsg::ServerError(backtrace) => ClientInstruction::ServerError(backtrace),
- }
- }
-}
-
-fn spawn_server(socket_path: &Path) -> io::Result<()> {
- let status = Command::new(current_exe()?)
- .arg("--server")
- .arg(socket_path)
- .status()?;
- if status.success() {
- Ok(())
- } else {
- let msg = "Process returned non-zero exit code";
- let err_msg = match status.code() {
- Some(c) => format!("{}: {}", msg, c),
- None => msg.to_string(),
- };
- Err(io::Error::new(io::ErrorKind::Other, err_msg))
- }
-}
-
-pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: Config) {
- let clear_client_terminal_attributes = "\u{1b}[?1l\u{1b}=\u{1b}[r\u{1b}12l\u{1b}[?1000l\u{1b}[?1002l\u{1b}[?1003l\u{1b}[?1005l\u{1b}[?1006l\u{1b}[?12l";
- let take_snapshot = "\u{1b}[?1049h";
- let bracketed_paste = "\u{1b}[?2004h";
- os_input.unset_raw_mode(0);
- let _ = os_input
- .get_stdout_writer()
- .write(take_snapshot.as_bytes())
- .unwrap();
- let _ = os_input
- .get_stdout_writer()
- .write(clear_client_terminal_attributes.as_bytes())
- .unwrap();
- std::env::set_var(&"ZELLIJ", "0");
-
- #[cfg(not(test))]
- spawn_server(&*ZELLIJ_IPC_PIPE).unwrap();
-
- let mut command_is_executing = CommandIsExecuting::new();
-
- let config_options = Options::from_cli(&config.options, opts.option.clone());
-
- let full_screen_ws = os_input.get_terminal_size_using_fd(0);
- os_input.connect_to_server(&*ZELLIJ_IPC_PIPE);
- os_input.send_to_server(ClientToServerMsg::NewClient(
- full_screen_ws,
- opts,
- config_options,
- ));
- os_input.set_raw_mode(0);
- let _ = os_input
- .get_stdout_writer()
- .write(bracketed_paste.as_bytes())
- .unwrap();
-
- let (send_client_instructions, receive_client_instructions): SyncChannelWithContext<
- ClientInstruction,
- > = mpsc::sync_channel(50);
- let send_client_instructions =
- SenderWithContext::new(SenderType::SyncSender(send_client_instructions));
-
- #[cfg(not(test))]
- std::panic::set_hook({
- use crate::errors::handle_panic;
- let send_client_instructions = send_client_instructions.clone();
- Box::new(move |info| {
- handle_panic(info, &send_client_instructions);
- })
- });
-
- let _stdin_thread = thread::Builder::new()
- .name("stdin_handler".to_string())
- .spawn({
- let send_client_instructions = send_client_instructions.clone();
- let command_is_executing = command_is_executing.clone();
- let os_input = os_input.clone();
- move || {
- input_loop(
- os_input,
- config,
- command_is_executing,
- send_client_instructions,
- )
- }
- });
-
- let _signal_thread = thread::Builder::new()
- .name("signal_listener".to_string())
- .spawn({
- let os_input = os_input.clone();
- let send_client_instructions = send_client_instructions.clone();
- move || {
- os_input.handle_signals(
- Box::new({
- let os_api = os_input.clone();
- move || {
- os_api.send_to_server(ClientToServerMsg::TerminalResize(
- os_api.get_terminal_size_using_fd(0),
- ));
- }
- }),
- Box::new({
- let send_client_instructions = send_client_instructions.clone();
- move || {
- send_client_instructions
- .send(ClientInstruction::Exit)
- .unwrap()
- }
- }),
- );
- }
- })
- .unwrap();
-
- let router_thread = thread::Builder::new()
- .name("router".to_string())
- .spawn({
- let os_input = os_input.clone();
- let mut should_break = false;
- move || loop {
- let (instruction, err_ctx) = os_input.recv_from_server();
- err_ctx.update_thread_ctx();
- match instruction {
- ServerToClientMsg::Exit | ServerToClientMsg::ServerError(_) => {
- should_break = true;
- }
- _ => {}
- }
- send_client_instructions.send(instruction.into()).unwrap();
- if should_break {
- break;
- }
- }
- })
- .unwrap();
-
- let handle_error = |backtrace: String| {
- os_input.unset_raw_mode(0);
- let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
- let restore_snapshot = "\u{1b}[?1049l";
- let error = format!(
- "{}\n{}{}",
- goto_start_of_last_line, restore_snapshot, backtrace
- );
- let _ = os_input
- .get_stdout_writer()
- .write(error.as_bytes())
- .unwrap();
- std::process::exit(1);
- };
-
- loop {
- let (client_instruction, mut err_ctx) = receive_client_instructions
- .recv()
- .expect("failed to receive app instruction on channel");
-
- err_ctx.add_call(ContextType::Client((&client_instruction).into()));
- match client_instruction {
- ClientInstruction::Exit => break,
- ClientInstruction::Error(backtrace) => {
- let _ = os_input.send_to_server(ClientToServerMsg::ClientExit);
- handle_error(backtrace);
- }
- ClientInstruction::ServerError(backtrace) => {
- handle_error(backtrace);
- }
- ClientInstruction::Render(output) => {
- if output.is_none() {
- break;
- }
- let mut stdout = os_input.get_stdout_writer();
- stdout
- .write_all(&output.unwrap().as_bytes())
- .expect("cannot write to stdout");
- stdout.flush().expect("could not flush");
- }
- ClientInstruction::UnblockInputThread => {
- command_is_executing.unblock_input_thread();
- }
- }
- }
-
- let _ = os_input.send_to_server(ClientToServerMsg::ClientExit);
- router_thread.join().unwrap();
-
- // cleanup();
- let reset_style = "\u{1b}[m";
- let show_cursor = "\u{1b}[?25h";
- let restore_snapshot = "\u{1b}[?1049l";
- let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
- let goodbye_message = format!(
- "{}\n{}{}{}Bye from Zellij!\n",
- goto_start_of_last_line, restore_snapshot, reset_style, show_cursor
- );
-
- os_input.unset_raw_mode(0);
- let mut stdout = os_input.get_stdout_writer();
- let _ = stdout.write(goodbye_message.as_bytes()).unwrap();
- stdout.flush().unwrap();
-}
diff --git a/src/client/pane_resizer.rs b/src/client/pane_resizer.rs
deleted file mode 100644
index 73678499..00000000
--- a/src/client/pane_resizer.rs
+++ /dev/null
@@ -1,532 +0,0 @@
-use crate::os_input_output::ServerOsApi;
-use crate::panes::{PaneId, PositionAndSize};
-use crate::tab::Pane;
-use std::{
- cmp::Ordering,
- collections::{BTreeMap, HashSet},
-};
-
-pub struct PaneResizer<'a> {
- panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
- os_api: &'a mut Box<dyn ServerOsApi>,
-}
-
-// TODO: currently there are some functions here duplicated with Tab
-// all resizing functions should move here
-
-impl<'a> PaneResizer<'a> {
- pub fn new(
- panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
- os_api: &'a mut Box<dyn ServerOsApi>,
- ) -> Self {
- PaneResizer { panes, os_api }
- }
- pub fn resize(
- &mut self,
- mut current_size: PositionAndSize,
- new_size: PositionAndSize,
- ) -> Option<(isize, isize)> {
- // (column_difference, row_difference)
- let mut successfully_resized = false;
- let mut column_difference: isize = 0;
- let mut row_difference: isize = 0;
- match new_size.columns.cmp(&current_size.columns) {
- Ordering::Greater => {
- let increase_by = new_size.columns - current_size.columns;
- if let Some(panes_to_resize) = find_increasable_vertical_chain(
- &self.panes,
- increase_by,
- current_size.columns,
- current_size.rows,
- ) {
- self.increase_panes_right_and_push_adjacents_right(
- panes_to_resize,
- increase_by,
- );
- column_difference = new_size.columns as isize - current_size.columns as isize;
- current_size.columns =
- (current_size.columns as isize + column_difference) as usize;
- successfully_resized = true;
- };
- }
- Ordering::Less => {
- let reduce_by = current_size.columns - new_size.columns;
- if let Some(panes_to_resize) = find_reducible_vertical_chain(
- &self.panes,
- reduce_by,
- current_size.columns,
- current_size.rows,
- ) {
- self.reduce_panes_left_and_pull_adjacents_left(panes_to_resize, reduce_by);
- column_difference = new_size.columns as isize - current_size.columns as isize;
- current_size.columns =
- (current_size.columns as isize + column_difference) as usize;
- successfully_resized = true;
- };
- }
- Ordering::Equal => (),
- }
- match new_size.rows.cmp(&current_size.rows) {
- Ordering::Greater => {
- let increase_by = new_size.rows - current_size.rows;
- if let Some(panes_to_resize) = find_increasable_horizontal_chain(
- &self.panes,
- increase_by,
- current_size.columns,
- current_size.rows,
- ) {
- self.increase_panes_down_and_push_down_adjacents(panes_to_resize, increase_by);
- row_difference = new_size.rows as isize - current_size.rows as isize;
- current_size.rows = (current_size.rows as isize + row_difference) as usize;
- successfully_resized = true;
- };
- }
- Ordering::Less => {
- let reduce_by = current_size.rows - new_size.rows;
- if let Some(panes_to_resize) = find_reducible_horizontal_chain(
- &self.panes,
- reduce_by,
- current_size.columns,
- current_size.rows,
- ) {
- self.reduce_panes_up_and_pull_adjacents_up(panes_to_resize, reduce_by);
- row_difference = new_size.rows as isize - current_size.rows as isize;
- current_size.rows = (current_size.rows as isize + row_difference) as usize;
- successfully_resized = true;
- };
- }
- Ordering::Equal => (),
- }
- if successfully_resized {
- Some((column_difference, row_difference))
- } else {
- None
- }
- }
- fn reduce_panes_left_and_pull_adjacents_left(
- &mut self,
- panes_to_reduce: Vec<PaneId>,
- reduce_by: usize,
- ) {
- let mut pulled_panes: HashSet<PaneId> = HashSet::new();
- for pane_id in panes_to_reduce {
- let (pane_x, pane_y, pane_columns, pane_rows) = {
- let pane = self.panes.get(&pane_id).unwrap();
- (pane.x(), pane.y(), pane.columns(), pane.rows())
- };
- let panes_to_pull = self.panes.values_mut().filter(|p| {
- p.x() > pane_x + pane_columns
- && (p.y() <= pane_y && p.y() + p.rows() >= pane_y
- || p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows)
- });
- for pane in panes_to_pull {
- if !pulled_panes.contains(&pane.pid()) {
- pane.pull_left(reduce_by);
- pulled_panes.insert(pane.pid());
- }
- }
- self.reduce_pane_width_left(&pane_id, reduce_by);
- }
- }
- fn reduce_panes_up_and_pull_adjacents_up(
- &mut self,
- panes_to_reduce: Vec<PaneId>,
- reduce_by: usize,
- ) {
- let mut pulled_panes: HashSet<PaneId> = HashSet::new();
- for pane_id in panes_to_reduce {
- let (pane_x, pane_y, pane_columns, pane_rows) = {
- let pane = self.panes.get(&pane_id).unwrap();
- (pane.x(), pane.y(), pane.columns(), pane.rows())
- };
- let panes_to_pull = self.panes.values_mut().filter(|p| {
- p.y() > pane_y + pane_rows
- && (p.x() <= pane_x && p.x() + p.columns() >= pane_x
- || p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns)
- });
- for pane in panes_to_pull {
- if !pulled_panes.contains(&pane.pid()) {
- pane.pull_up(reduce_by);
- pulled_panes.insert(pane.pid());
- }
- }
- self.reduce_pane_height_up(&pane_id, reduce_by);
- }
- }
- fn increase_panes_down_and_push_down_adjacents(
- &mut self,
- panes_to_increase: Vec<PaneId>,
- increase_by: usize,
- ) {
- let mut pushed_panes: HashSet<PaneId> = HashSet::new();
- for pane_id in panes_to_increase {
- let (pane_x, pane_y, pane_columns, pane_rows) = {
- let pane = self.panes.get(&pane_id).unwrap();
- (pane.x(), pane.y(), pane.columns(), pane.rows())
- };
- let panes_to_push = self.panes.values_mut().filter(|p| {
- p.y() > pane_y + pane_rows
- && (p.x() <= pane_x && p.x() + p.columns() >= pane_x
- || p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns)
- });
- for pane in panes_to_push {
- if !pushed_panes.contains(&pane.pid()) {
- pane.push_down(increase_by);
- pushed_panes.insert(pane.pid());
- }
- }
- self.increase_pane_height_down(&pane_id, increase_by);
- }
- }
- fn increase_panes_right_and_push_adjacents_right(
- &mut self,
- panes_to_increase: Vec<PaneId>,
- increase_by: usize,
- ) {
- let mut pushed_panes: HashSet<PaneId> = HashSet::new();
- for pane_id in panes_to_increase {
- let (pane_x, pane_y, pane_columns, pane_rows) = {
- let pane = self.panes.get(&pane_id).unwrap();
- (pane.x(), pane.y(), pane.columns(), pane.rows())
- };
- let panes_to_push = self.panes.values_mut().filter(|p| {
- p.x() > pane_x + pane_columns
- && (p.y() <= pane_y && p.y() + p.rows() >= pane_y
- || p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows)
- });
- for pane in panes_to_push {
- if !pushed_panes.contains(&pane.pid()) {
- pane.push_right(increase_by);
- pushed_panes.insert(pane.pid());
- }
- }
- self.increase_pane_width_right(&pane_id, increase_by);
- }
- }
- fn reduce_pane_height_up(&mut self, id: &PaneId, count: usize) {
- let pane = self.panes.get_mut(id).unwrap();
- pane.reduce_height_up(count);
- if let PaneId::Terminal(pid) = id {
- self.os_api
- .set_terminal_size_using_fd(*pid, pane.columns() as u16, pane.rows() as u16);
- }
- }
- fn increase_pane_height_down(&mut self, id: &PaneId, count: usize) {
- let pane = self.panes.get_mut(id).unwrap();
- pane.increase_height_down(count);
- if let PaneId::Terminal(pid) = pane.pid() {
- self.os_api
- .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16);
- }
- }
- fn increase_pane_width_right(&mut self, id: &PaneId, count: usize) {
- let pane = self.panes.get_mut(id).unwrap();
- pane.increase_width_right(count);
- if let PaneId::Terminal(pid) = pane.pid() {
- self.os_api
- .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16);
- }
- }
- fn reduce_pane_width_left(&mut self, id: &PaneId, count: usize) {
- let pane = self.panes.get_mut(id).unwrap();
- pane.reduce_width_left(count);
- if let PaneId::Terminal(pid) = pane.pid() {
- self.os_api
- .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16);
- }
- }
-}
-
-fn find_next_increasable_horizontal_pane(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- right_of: &dyn Pane,
- increase_by: usize,
-) -> Option<PaneId> {
- let next_pane_candidates = panes.values().filter(
- |p| {
- p.x() == right_of.x() + right_of.columns() + 1 && p.horizontally_overlaps_with(right_of)
- }, // TODO: the name here is wrong, it should be vertically_overlaps_with
- );
- let resizable_candidates =
- next_pane_candidates.filter(|p| p.can_increase_height_by(increase_by));
- resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
- Some(next_pane) => {
- let next_pane = panes.get(&next_pane).unwrap();
- if next_pane.y() < p.y() {
- next_pane_id
- } else {
- Some(p.pid())
- }
- }
- None => Some(p.pid()),
- })
-}
-
-fn find_next_increasable_vertical_pane(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- below: &dyn Pane,
- increase_by: usize,
-) -> Option<PaneId> {
- let next_pane_candidates = panes.values().filter(
- |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with
- );
- let resizable_candidates =
- next_pane_candidates.filter(|p| p.can_increase_width_by(increase_by));
- resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
- Some(next_pane) => {
- let next_pane = panes.get(&next_pane).unwrap();
- if next_pane.x() < p.x() {
- next_pane_id
- } else {
- Some(p.pid())
- }
- }
- None => Some(p.pid()),
- })
-}
-
-fn find_next_reducible_vertical_pane(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- below: &dyn Pane,
- reduce_by: usize,
-) -> Option<PaneId> {
- let next_pane_candidates = panes.values().filter(
- |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with
- );
- let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_width_by(reduce_by));
- resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
- Some(next_pane) => {
- let next_pane = panes.get(&next_pane).unwrap();
- if next_pane.x() < p.x() {
- next_pane_id
- } else {
- Some(p.pid())
- }
- }
- None => Some(p.pid()),
- })
-}
-
-fn find_next_reducible_horizontal_pane(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- right_of: &dyn Pane,
- reduce_by: usize,
-) -> Option<PaneId> {
- let next_pane_candidates = panes.values().filter(
- |p| {
- p.x() == right_of.x() + right_of.columns() + 1 && p.horizontally_overlaps_with(right_of)
- }, // TODO: the name here is wrong, it should be vertically_overlaps_with
- );
- let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_height_by(reduce_by));
- resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
- Some(next_pane) => {
- let next_pane = panes.get(&next_pane).unwrap();
- if next_pane.y() < p.y() {
- next_pane_id
- } else {
- Some(p.pid())
- }
- }
- None => Some(p.pid()),
- })
-}
-
-fn find_increasable_horizontal_chain(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- increase_by: usize,
- screen_width: usize,
- screen_height: usize, // TODO: this is the previous size (make this clearer)
-) -> Option<Vec<PaneId>> {
- let mut horizontal_coordinate = 0;
- loop {
- if horizontal_coordinate == screen_height {
- return None;
- }
-
- match panes
- .values()
- .find(|p| p.x() == 0 && p.y() == horizontal_coordinate)
- {
- Some(leftmost_pane) => {
- if !leftmost_pane.can_increase_height_by(increase_by) {
- horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1;
- continue;
- }
- let mut panes_to_resize = vec![];
- let mut current_pane = leftmost_pane;
- loop {
- panes_to_resize.push(current_pane.pid());
- if current_pane.x() + current_pane.columns() == screen_width {
- return Some(panes_to_resize);
- }
- match find_next_increasable_horizontal_pane(
- panes,
- current_pane.as_ref(),
- increase_by,
- ) {
- Some(next_pane_id) => {
- current_pane = panes.get(&next_pane_id).unwrap();
- }
- None => {
- horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1;
- break;
- }
- };
- }
- }
- None => {
- return None;
- }
- }
- }
-}
-
-fn find_increasable_vertical_chain(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- increase_by: usize,
- screen_width: usize,
- screen_height: usize, // TODO: this is the previous size (make this clearer)
-) -> Option<Vec<PaneId>> {
- let mut vertical_coordinate = 0;
- loop {
- if vertical_coordinate == screen_width {
- return None;
- }
-
- match panes
- .values()
- .find(|p| p.y() == 0 && p.x() == vertical_coordinate)
- {
- Some(topmost_pane) => {
- if !topmost_pane.can_increase_width_by(increase_by) {
- vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1;
- continue;
- }
- let mut panes_to_resize = vec![];
- let mut current_pane = topmost_pane;
- loop {
- panes_to_resize.push(current_pane.pid());
- if current_pane.y() + current_pane.rows() == screen_height {
- return Some(panes_to_resize);
- }
- match find_next_increasable_vertical_pane(
- panes,
- current_pane.as_ref(),
- increase_by,
- ) {
- Some(next_pane_id) => {
- current_pane = panes.get(&next_pane_id).unwrap();
- }
- None => {
- vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1;
- break;
- }
- };
- }
- }
- None => {
- return None;
- }
- }
- }
-}
-
-fn find_reducible_horizontal_chain(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- reduce_by: usize,
- screen_width: usize,
- screen_height: usize, // TODO: this is the previous size (make this clearer)
-) -> Option<Vec<PaneId>> {
- let mut horizontal_coordinate = 0;
- loop {
- if horizontal_coordinate == screen_height {
- return None;
- }
-
- match panes
- .values()
- .find(|p| p.x() == 0 && p.y() == horizontal_coordinate)
- {
- Some(leftmost_pane) => {
- if !leftmost_pane.can_reduce_height_by(reduce_by) {
- horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1;
- continue;
- }
- let mut panes_to_resize = vec![];
- let mut current_pane = leftmost_pane;
- loop {
- panes_to_resize.push(current_pane.pid());
- if current_pane.x() + current_pane.columns() == screen_width {
- return Some(panes_to_resize);
- }
- match find_next_reducible_horizontal_pane(
- panes,
- current_pane.as_ref(),
- reduce_by,
- ) {
- Some(next_pane_id) => {
- current_pane = panes.get(&next_pane_id).unwrap();
- }
- None => {
- horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1;
- break;
- }
- };
- }
- }
- None => {
- return None;
- }
- }
- }
-}
-
-fn find_reducible_vertical_chain(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- increase_by: usize,
- screen_width: usize,
- screen_height: usize, // TODO: this is the previous size (make this clearer)
-) -> Option<Vec<PaneId>> {
- let mut vertical_coordinate = 0;
- loop {
- if vertical_coordinate == screen_width {
- return None;
- }
-
- match panes
- .values()
- .find(|p| p.y() == 0 && p.x() == vertical_coordinate)
- {
- Some(topmost_pane) => {
- if !topmost_pane.can_reduce_width_by(increase_by) {
- vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1;
- continue;
- }
- let mut panes_to_resize = vec![];
- let mut current_pane = topmost_pane;
- loop {
- panes_to_resize.push(current_pane.pid());
- if current_pane.y() + current_pane.rows() == screen_height {
- return Some(panes_to_resize);
- }
- match find_next_reducible_vertical_pane(
- panes,
- current_pane.as_ref(),
- increase_by,
- ) {
- Some(next_pane_id) => {
- current_pane = panes.get(&next_pane_id).unwrap();
- }
- None => {
- vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1;
- break;
- }
- };
- }
- }
- None => {
- return None;
- }
- }
- }
-}
diff --git a/src/client/panes/grid.rs b/src/client/panes/grid.rs
deleted file mode 100644
index e51b1ed5..00000000
--- a/src/client/panes/grid.rs
+++ /dev/null
@@ -1,1558 +0,0 @@
-use std::{
- cmp::Ordering,
- collections::{BTreeSet, VecDeque},
- fmt::{self, Debug, Formatter},
-};
-
-use vte::{Params, Perform};
-
-const TABSTOP_WIDTH: usize = 8; // TODO: is this always right?
-const SCROLL_BACK: usize = 10_000;
-
-use crate::utils::consts::VERSION;
-use crate::utils::logging::debug_log_to_file;
-use crate::utils::shared::version_number;
-
-use crate::panes::terminal_character::{
- CharacterStyles, CharsetIndex, Cursor, CursorShape, StandardCharset, TerminalCharacter,
- EMPTY_TERMINAL_CHARACTER,
-};
-
-fn get_top_non_canonical_rows(rows: &mut Vec<Row>) -> Vec<Row> {
- let mut index_of_last_non_canonical_row = None;
- for (i, row) in rows.iter().enumerate() {
- if row.is_canonical {
- break;
- } else {
- index_of_last_non_canonical_row = Some(i);
- }
- }
- match index_of_last_non_canonical_row {
- Some(index_of_last_non_canonical_row) => {
- rows.drain(..=index_of_last_non_canonical_row).collect()
- }
- None => vec![],
- }
-}
-
-fn get_bottom_canonical_row_and_wraps(rows: &mut VecDeque<Row>) -> Vec<Row> {
- let mut index_of_last_non_canonical_row = None;
- for (i, row) in rows.iter().enumerate().rev() {
- index_of_last_non_canonical_row = Some(i);
- if row.is_canonical {
- break;
- }
- }
- match index_of_last_non_canonical_row {
- Some(index_of_last_non_canonical_row) => {
- rows.drain(index_of_last_non_canonical_row..).collect()
- }
- None => vec![],
- }
-}
-
-fn transfer_rows_down(
- source: &mut VecDeque<Row>,
- destination: &mut Vec<Row>,
- count: usize,
- max_src_width: Option<usize>,
- max_dst_width: Option<usize>,
-) {
- let mut next_lines: Vec<Row> = vec![];
- let mut lines_added_to_destination: isize = 0;
- loop {
- if lines_added_to_destination as usize == count {
- break;
- }
- if next_lines.is_empty() {
- match source.pop_back() {
- Some(next_line) => {
- let mut top_non_canonical_rows_in_dst = get_top_non_canonical_rows(destination);
- lines_added_to_destination -= top_non_canonical_rows_in_dst.len() as isize;
- next_lines.push(next_line);
- next_lines.append(&mut top_non_canonical_rows_in_dst);
- next_lines = match max_dst_width {
- Some(max_row_width) => {
- Row::from_rows(next_lines).split_to_rows_of_length(max_row_width)
- }
- None => vec![Row::from_rows(next_lines)],
- };
- if next_lines.is_empty() {
- // no more lines at source, the line we popped was probably empty
- break;
- }
- }
- None => break, // no more rows
- }
- }
- destination.insert(0, next_lines.pop().unwrap());
- lines_added_to_destination += 1;
- }
- if !next_lines.is_empty() {
- match max_src_width {
- Some(max_row_width) => {
- let excess_rows = Row::from_rows(next_lines).split_to_rows_of_length(max_row_width);
- source.extend(excess_rows);
- }
- None => {
- let excess_row = Row::from_rows(next_lines);
- bounded_push(source, excess_row);
- }
- }
- }
-}
-
-fn transfer_rows_up(
- source: &mut Vec<Row>,
- destination: &mut VecDeque<Row>,
- count: usize,
- max_src_width: Option<usize>,
- max_dst_width: Option<usize>,
-) {
- let mut next_lines: Vec<Row> = vec![];
- for _ in 0..count {
- if next_lines.is_empty() {
- if !source.is_empty() {
- let next_line = source.remove(0);
- if !next_line.is_canonical {
- let mut bottom_canonical_row_and_wraps_in_dst =
- get_bottom_canonical_row_and_wraps(destination);
- next_lines.append(&mut bottom_canonical_row_and_wraps_in_dst);
- }
- next_lines.push(next_line);
- next_lines = match max_dst_width {
- Some(max_row_width) => {
- Row::from_rows(next_lines).split_to_rows_of_length(max_row_width)
- }
- None => vec![Row::from_rows(next_lines)],
- };
- } else {
- break; // no more rows
- }
- }
- bounded_push(destination, next_lines.remove(0));
- }
- if !next_lines.is_empty() {
- match max_src_width {
- Some(max_row_width) => {
- let excess_rows = Row::from_rows(next_lines).split_to_rows_of_length(max_row_width);
- for row in excess_rows {
- source.insert(0, row);
- }
- }
- None => {
- let excess_row = Row::from_rows(next_lines);
- source.insert(0, excess_row);
- }
- }
- }
-}
-
-fn bounded_push(vec: &mut VecDeque<Row>, value: Row) {
- if vec.len() >= SCROLL_BACK {
- vec.pop_front();
- }
- vec.push_back(value)
-}
-
-pub fn create_horizontal_tabstops(columns: usize) -> BTreeSet<usize> {
- let mut i = TABSTOP_WIDTH;
- let mut horizontal_tabstops = BTreeSet::new();
- loop {
- if i > columns {
- break;
- }
- horizontal_tabstops.insert(i);
- i += TABSTOP_WIDTH;
- }
- horizontal_tabstops
-}
-
-#[derive(Clone)]
-pub struct Grid {
- lines_above: VecDeque<Row>,
- viewport: Vec<Row>,
- lines_below: Vec<Row>,
- horizontal_tabstops: BTreeSet<usize>,
- alternative_lines_above_viewport_and_cursor: Option<(VecDeque<Row>, Vec<Row>, Cursor)>,
- cursor: Cursor,
- saved_cursor_position: Option<Cursor>,
- scroll_region: Option<(usize, usize)>,
- active_charset: CharsetIndex,
- preceding_char: Option<TerminalCharacter>,
- pub should_render: bool,
- pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "")
- pub erasure_mode: bool, // ERM
- pub insert_mode: bool,
- pub disable_linewrap: bool,
- pub clear_viewport_before_rendering: bool,
- pub width: usize,
- pub height: usize,
- pub pending_messages_to_pty: Vec<Vec<u8>>,
-}
-
-impl Debug for Grid {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- for (i, row) in self.viewport.iter().enumerate() {
- if row.is_canonical {
- writeln!(f, "{:02?} (C): {:?}", i, row)?;
- } else {
- writeln!(f, "{:02?} (W): {:?}", i, row)?;
- }
- }
- Ok(())
- }
-}
-
-impl Grid {
- pub fn new(rows: usize, columns: usize) -> Self {
- Grid {
- lines_above: VecDeque::with_capacity(SCROLL_BACK),
- viewport: vec![Row::new().canonical()],
- lines_below: vec![],
- horizontal_tabstops: create_horizontal_tabstops(columns),
- cursor: Cursor::new(0, 0),
- saved_cursor_position: None,
- scroll_region: None,
- preceding_char: None,
- width: columns,
- height: rows,
- should_render: true,
- cursor_key_mode: false,
- erasure_mode: false,
- insert_mode: false,
- disable_linewrap: false,
- alternative_lines_above_viewport_and_cursor: None,
- clear_viewport_before_rendering: false,
- active_charset: Default::default(),
- pending_messages_to_pty: vec![],
- }
- }
- pub fn contains_widechar(&self) -> bool {
- self.viewport.iter().any(|c| c.contains_widechar())
- }
- pub fn advance_to_next_tabstop(&mut self, styles: CharacterStyles) {
- let mut next_tabstop = None;
- for tabstop in self.horizontal_tabstops.iter() {
- if *tabstop > self.cursor.x {
- next_tabstop = Some(tabstop);
- break;
- }
- }
- match next_tabstop {
- Some(tabstop) => {
- self.cursor.x = *tabstop;
- }
- None => {
- self.cursor.x = self.width.saturating_sub(1);
- }
- }
- let mut empty_character = EMPTY_TERMINAL_CHARACTER;
- empty_character.styles = styles;
- self.pad_current_line_until(self.cursor.x);
- }
- pub fn move_to_previous_tabstop(&mut self) {
- let mut previous_tabstop = None;
- for tabstop in self.horizontal_tabstops.iter() {
- if *tabstop >= self.cursor.x {
- break;
- }
- previous_tabstop = Some(tabstop);
- }
- match previous_tabstop {
- Some(tabstop) => {
- self.cursor.x = *tabstop;
- }
- None => {
- self.cursor.x = 0;
- }
- }
- }
- pub fn cursor_shape(&self) -> CursorShape {
- self.cursor.get_shape()
- }
- fn set_horizontal_tabstop(&mut self) {
- self.horizontal_tabstops.insert(self.cursor.x);
- }
- fn clear_tabstop(&mut self, position: usize) {
- self.horizontal_tabstops.remove(&position);
- }
- fn clear_all_tabstops(&mut self) {
- self.horizontal_tabstops.clear();
- }
- fn save_cursor_position(&mut self) {
- self.saved_cursor_position = Some(self.cursor.clone());
- }
- fn restore_cursor_position(&mut self) {
- if let Some(saved_cursor_position) = self.saved_cursor_position.as_ref() {
- self.cursor = saved_cursor_position.clone();
- }
- }
- fn configure_charset(&mut self, charset: StandardCharset, index: CharsetIndex) {
- self.cursor.charsets[index] = charset;
- }
- fn set_active_charset(&mut self, index: CharsetIndex) {
- self.active_charset = index;
- }
- fn cursor_canonical_line_index(&self) -> usize {
- let mut cursor_canonical_line_index = 0;
- let mut canonical_lines_traversed = 0;
- for (i, line) in self.viewport.iter().enumerate() {
- if line.is_canonical {
- cursor_canonical_line_index = canonical_lines_traversed;
- canonical_lines_traversed += 1;
- }
- if i == self.cursor.y {
- break;
- }
- }
- cursor_canonical_line_index
- }
- // TODO: merge these two funtions
- fn cursor_index_in_canonical_line(&self) -> usize {
- let mut cursor_canonical_line_index = 0;
- let mut cursor_index_in_canonical_line = 0;
- for (i, line) in self.viewport.iter().enumerate() {
- if line.is_canonical {
- cursor_canonical_line_index = i;
- }
- if i == self.cursor.y {
- let line_wrap_position_in_line = self.cursor.y - cursor_canonical_line_index;
- cursor_index_in_canonical_line = line_wrap_position_in_line + self.cursor.x;
- break;
- }
- }
- cursor_index_in_canonical_line
- }
- fn canonical_line_y_coordinates(&self, canonical_line_index: usize) -> usize {
- let mut canonical_lines_traversed = 0;
- let mut y_coordinates = 0;
- for (i, line) in self.viewport.iter().enumerate() {
- if line.is_canonical {
- canonical_lines_traversed += 1;
- if canonical_lines_traversed == canonical_line_index + 1 {
- y_coordinates = i;
- break;
- }
- }
- }
- y_coordinates
- }
- pub fn scroll_up_one_line(&mut self) {
- if !self.lines_above.is_empty() && self.viewport.len() == self.height {
- let line_to_push_down = self.viewport.pop().unwrap();
- self.lines_below.insert(0, line_to_push_down);
- let line_to_insert_at_viewport_top = self.lines_above.pop_back().unwrap();
- self.viewport.insert(0, line_to_insert_at_viewport_top);
- }
- }
- pub fn scroll_down_one_line(&mut self) {
- if !self.lines_below.is_empty() && self.viewport.len() == self.height {
- let mut line_to_push_up = self.viewport.remove(0);
- if line_to_push_up.is_canonical {
- bounded_push(&mut self.lines_above, line_to_push_up);
- } else {
- let mut last_line_above = self.lines_above.pop_back().unwrap();
- last_line_above.append(&mut line_to_push_up.columns);
- bounded_push(&mut self.lines_above, last_line_above);
- }
- let line_to_insert_at_viewport_bottom = self.lines_below.remove(0);
- self.viewport.push(line_to_insert_at_viewport_bottom);
- }
- }
- pub fn change_size(&mut self, new_rows: usize, new_columns: usize) {
- if new_columns != self.width {
- let mut cursor_canonical_line_index = self.cursor_canonical_line_index();
- let cursor_index_in_canonical_line = self.cursor_index_in_canonical_line();
- let mut viewport_canonical_lines = vec![];
- for mut row in self.viewport.drain(..) {
- if !row.is_canonical
- && viewport_canonical_lines.is_empty()
- && !self.lines_above.is_empty()
- {
- let mut first_line_above = self.lines_above.pop_back().unwrap();
- first_line_above.append(&mut row.columns);
- viewport_canonical_lines.push(first_line_above);
- cursor_canonical_line_index += 1;
- } else if row.is_canonical {
- viewport_canonical_lines.push(row);
- } else {
- match viewport_canonical_lines.last_mut() {
- Some(last_line) => {
- last_line.append(&mut row.columns);
- }
- None => {
- // the state is corrupted somehow
- // this is a bug and I'm not yet sure why it happens
- // usually it fixes itself and is a result of some race
- // TODO: investigate why this happens and solve it
- return;
- }
- }
- }
- }
- let mut new_viewport_rows = vec![];
- for mut canonical_line in viewport_canonical_lines {
- let mut canonical_line_parts: Vec<Row> = vec![];
- if canonical_line.columns.is_empty() {
- canonical_line_parts.push(Row::new().canonical());
- }
- while !canonical_line.columns.is_empty() {
- let next_wrap = if canonical_line.len() > new_columns {
- canonical_line.columns.drain(..new_columns)
- } else {
- canonical_line.columns.drain(..)
- };
- let row = Row::from_columns(next_wrap.collect());
- // if there are no more parts, this row is canonical as long as it originally
- // was canonical (it might not have been for example if it's the first row in
- // the viewport, and the actual canonical row is above it in the scrollback)
- let row = if canonical_line_parts.is_empty() && canonical_line.is_canonical {
- row.canonical()
- } else {
- row
- };
- canonical_line_parts.push(row);
- }
- new_viewport_rows.append(&mut canonical_line_parts);
- }
- self.viewport = new_viewport_rows;
-
- let mut new_cursor_y = self.canonical_line_y_coordinates(cursor_canonical_line_index);
- let new_cursor_x = (cursor_index_in_canonical_line / new_columns)
- + (cursor_index_in_canonical_line % new_columns);
- let current_viewport_row_count = self.viewport.len();
- match current_viewport_row_count.cmp(&self.height) {
- Ordering::Less => {
- let row_count_to_transfer = self.height - current_viewport_row_count;
- transfer_rows_down(
- &mut self.lines_above,
- &mut self.viewport,
- row_count_to_transfer,
- None,
- Some(new_columns),
- );
- let rows_pulled = self.viewport.len() - current_viewport_row_count;
- new_cursor_y += rows_pulled;
- }
- Ordering::Greater => {
- let row_count_to_transfer = current_viewport_row_count - self.height;
- if row_count_to_transfer > new_cursor_y {
- new_cursor_y = 0;
- } else {
- new_cursor_y -= row_count_to_transfer;
- }
- transfer_rows_up(
- &mut self.viewport,
- &mut self.lines_above,
- row_count_to_transfer,
- Some(new_columns),
- None,
- );
- }
- Ordering::Equal => {}
- }
- self.cursor.y = new_cursor_y;
- self.cursor.x = new_cursor_x;
- }
- if new_rows != self.height {
- let current_viewport_row_count = self.viewport.len();
- match current_viewport_row_count.cmp(&new_rows) {
- Ordering::Less => {
- let row_count_to_transfer = new_rows - current_viewport_row_count;
- transfer_rows_down(
- &mut self.lines_above,
- &mut self.viewport,
- row_count_to_transfer,
- None,
- Some(new_columns),
- );
- let rows_pulled = self.viewport.len() - current_viewport_row_count;
- self.cursor.y += rows_pulled;
- }
- Ordering::Greater => {
- let row_count_to_transfer = current_viewport_row_count - new_rows;
- if row_count_to_transfer > self.cursor.y {
- self.cursor.y = 0;
- } else {
- self.cursor.y -= row_count_to_transfer;
- }
- transfer_rows_up(
- &mut self.viewport,
- &mut self.lines_above,
- row_count_to_transfer,
- Some(new_columns),
- None,
- );
- }
- Ordering::Equal => {}
- }
- }
- self.height = new_rows;
- self.width = new_columns;
- if self.scroll_region.is_some() {
- self.set_scroll_region_to_viewport_size();
- }
- }
- pub fn as_character_lines(&self) -> Vec<Vec<TerminalCharacter>> {
- let mut lines: Vec<Vec<TerminalCharacter>> = self
- .viewport
- .iter()
- .map(|r| {
- let mut line: Vec<TerminalCharacter> = r.columns.iter().copied().collect();
- // pad line
- line.resize(self.width, EMPTY_TERMINAL_CHARACTER);
- line
- })
- .collect();
- let empty_row = vec![EMPTY_TERMINAL_CHARACTER; self.width];
- for _ in lines.len()..self.height {
- lines.push(empty_row.clone());
- }
- lines
- }
- pub fn cursor_coordinates(&self) -> Option<(usize, usize)> {
- if self.cursor.is_hidden {
- None
- } else {
- Some((self.cursor.x, self.cursor.y))
- }
- }
- pub fn move_viewport_up(&mut self, count: usize) {
- for _ in 0..count {
- self.scroll_up_one_line();
- }
- }
- pub fn move_viewport_down(&mut self, count: usize) {
- for _ in 0..count {
- self.scroll_down_one_line();
- }
- }
- pub fn reset_viewport(&mut self) {
- let row_count_below = self.lines_below.len();
- for _ in 0..row_count_below {
- self.scroll_down_one_line();
- }
- }
- pub fn rotate_scroll_region_up(&mut self, count: usize) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- for _ in 0..count {
- let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
- if scroll_region_bottom < self.viewport.len() {
- self.viewport.remove(scroll_region_bottom);
- }
- if scroll_region_top < self.viewport.len() {
- self.viewport
- .insert(scroll_region_top, Row::from_columns(columns).canonical());
- }
- }
- }
- }
- pub fn rotate_scroll_region_down(&mut self, count: usize) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- for _ in 0..count {
- let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
- self.viewport.remove(scroll_region_top);
- if self.viewport.len() > scroll_region_top {
- self.viewport
- .insert(scroll_region_bottom, Row::from_columns(columns).canonical());
- } else {
- self.viewport.push(Row::from_columns(columns).canonical());
- }
- }
- }
- }
- pub fn fill_viewport(&mut self, character: TerminalCharacter) {
- self.viewport.clear();
- for _ in 0..self.height {
- let columns = vec![character; self.width];
- self.viewport.push(Row::from_columns(columns).canonical());
- }
- }
- pub fn add_canonical_line(&mut self) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- if self.cursor.y == scroll_region_bottom {
- // end of scroll region
- // when we have a scroll region set and we're at its bottom
- // we need to delete its first line, thus shifting all lines in it upwards
- // then we add an empty line at its end which will be filled by the application
- // controlling the scroll region (presumably filled by whatever comes next in the
- // scroll buffer, but that's not something we control)
- if scroll_region_top >= self.viewport.len() {
- // the state is corrupted
- return;
- }
- self.viewport.remove(scroll_region_top);
- let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
- if self.viewport.len() >= scroll_region_bottom {
- self.viewport
- .insert(scroll_region_bottom, Row::from_columns(columns).canonical());
- } else {
- self.viewport.push(Row::from_columns(columns).canonical());
- }
- return;
- }
- }
- if self.viewport.len() <= self.cursor.y + 1 {
- // FIXME: this should add an empty line with the pad_character
- // but for some reason this breaks rendering in various situations
- // it needs to be investigated and fixed
- let new_row = Row::new().canonical();
- self.viewport.push(new_row);
- }
- if self.cursor.y == self.height - 1 {
- let row_count_to_transfer = 1;
- transfer_rows_up(
- &mut self.viewport,
- &mut self.lines_above,
- row_count_to_transfer,
- Some(self.width),
- None,
- );
- } else {
- self.cursor.y += 1;
- }
- }
- pub fn move_cursor_to_beginning_of_line(&mut self) {
- self.cursor.x = 0;
- }
- pub fn insert_character_at_cursor_position(&mut self, terminal_character: TerminalCharacter) {
- match self.viewport.get_mut(self.cursor.y) {
- Some(row) => {
- row.insert_character_at(terminal_character, self.cursor.x);
- if row.len() > self.width {
- row.truncate(self.width);
- }
- }
- None => {
- // pad lines until cursor if they do not exist
- for _ in self.viewport.len()..self.cursor.y {
- self.viewport.push(Row::new().canonical());
- }
- self.viewport
- .push(Row::new().with_character(terminal_character).canonical());
- }
- }
- }
- pub fn add_character_at_cursor_position(&mut self, terminal_character: TerminalCharacter) {
- match self.viewport.get_mut(self.cursor.y) {
- Some(row) => {
- if self.insert_mode {
- row.insert_character_at(terminal_character, self.cursor.x);
- } else {
- row.add_character_at(terminal_character, self.cursor.x);
- }
- }
- None => {
- // pad lines until cursor if they do not exist
- for _ in self.viewport.len()..self.cursor.y {
- self.viewport.push(Row::new().canonical());
- }
- self.viewport
- .push(Row::new().with_character(terminal_character).canonical());
- }
- }
- }
- pub fn add_character(&mut self, terminal_character: TerminalCharacter) {
- // TODO: try to separate adding characters from moving the cursors in this function
- if self.cursor.x >= self.width {
- if self.disable_linewrap {
- return;
- }
- // line wrap
- self.cursor.x = 0;
- if self.cursor.y == self.height - 1 {
- let row_count_to_transfer = 1;
- transfer_rows_up(
- &mut self.viewport,
- &mut self.lines_above,
- row_count_to_transfer,
- Some(self.width),
- None,
- );
- let wrapped_row = Row::new();
- self.viewport.push(wrapped_row);
- } else {
- self.cursor.y += 1;
- if self.viewport.len() <= self.cursor.y {
- let line_wrapped_row = Row::new();
- self.viewport.push(line_wrapped_row);
- }
- }
- }
- self.add_character_at_cursor_position(terminal_character);
- self.move_cursor_forward_until_edge(1);
- }
- pub fn move_cursor_forward_until_edge(&mut self, count: usize) {
- let count_to_move = std::cmp::min(count, self.width - (self.cursor.x));
- self.cursor.x += count_to_move;
- }
- pub fn replace_characters_in_line_after_cursor(&mut self, replace_with: TerminalCharacter) {
- self.viewport
- .get_mut(self.cursor.y)
- .unwrap()
- .truncate(self.cursor.x);
- if self.cursor.x < self.width - 1 {
- let mut characters_to_append = vec![replace_with; self.width - self.cursor.x];
- self.viewport
- .get_mut(self.cursor.y)
- .unwrap()
- .append(&mut characters_to_append);
- }
- }
- pub fn replace_characters_in_line_before_cursor(&mut self, replace_with: TerminalCharacter) {
- let line_part = vec![replace_with; self.cursor.x + 1];
- let row = self.viewport.get_mut(self.cursor.y).unwrap();
- row.replace_beginning_with(line_part);
- }
- pub fn clear_all_after_cursor(&mut self, replace_with: TerminalCharacter) {
- if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y) {
- cursor_row.truncate(self.cursor.x);
- let replace_with_columns = vec![replace_with; self.width];
- self.replace_characters_in_line_after_cursor(replace_with);
- for row in self.viewport.iter_mut().skip(self.cursor.y + 1) {
- row.replace_columns(replace_with_columns.clone());
- }
- }
- }
- pub fn clear_all_before_cursor(&mut self, replace_with: TerminalCharacter) {
- if self.viewport.get(self.cursor.y).is_some() {
- self.replace_characters_in_line_before_cursor(replace_with);
- let replace_with_columns = vec![replace_with; self.width];
- for row in self.viewport.iter_mut().take(self.cursor.y) {
- row.replace_columns(replace_with_columns.clone());
- }
- }
- }
- pub fn clear_cursor_line(&mut self) {
- self.viewport.get_mut(self.cursor.y).unwrap().truncate(0);
- }
- pub fn clear_all(&mut self, replace_with: TerminalCharacter) {
- let replace_with_columns = vec![replace_with; self.width];
- self.replace_characters_in_line_after_cursor(replace_with);
- for row in self.viewport.iter_mut() {
- row.replace_columns(replace_with_columns.clone());
- }
- }
- fn pad_current_line_until(&mut self, position: usize) {
- let current_row = self.viewport.get_mut(self.cursor.y).unwrap();
- for _ in current_row.len()..position {
- current_row.push(EMPTY_TERMINAL_CHARACTER);
- }
- }
- fn pad_lines_until(&mut self, position: usize, pad_character: TerminalCharacter) {
- for _ in self.viewport.len()..=position {
- let columns = vec![pad_character; self.width];
- self.viewport.push(Row::from_columns(columns).canonical());
- }
- }
- pub fn move_cursor_to(&mut self, x: usize, y: usize, pad_character: TerminalCharacter) {
- match self.scroll_region {
- Some((scroll_region_top, scroll_region_bottom)) => {
- self.cursor.x = std::cmp::min(self.width - 1, x);
- let y_offset = if self.erasure_mode {
- scroll_region_top
- } else {
- 0
- };
- self.cursor.y = std::cmp::min(scroll_region_bottom, y + y_offset);
- self.pad_lines_until(self.cursor.y, pad_character);
- self.pad_current_line_until(self.cursor.x);
- }
- None => {
- self.cursor.x = std::cmp::min(self.width - 1, x);
- self.cursor.y = std::cmp::min(self.height - 1, y);
- self.pad_lines_until(self.cursor.y, pad_character);
- self.pad_current_line_until(self.cursor.x);
- }
- }
- }
- pub fn move_cursor_up(&mut self, count: usize) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- if self.cursor.y >= scroll_region_top && self.cursor.y <= scroll_region_bottom {
- self.cursor.y =
- std::cmp::max(self.cursor.y.saturating_sub(count), scroll_region_top);
- return;
- }
- }
- self.cursor.y = if self.cursor.y < count {
- 0
- } else {
- self.cursor.y - count
- };
- }
- pub fn move_cursor_up_with_scrolling(&mut self, count: usize) {
- let (scroll_region_top, scroll_region_bottom) =
- self.scroll_region.unwrap_or((0, self.height - 1));
- for _ in 0..count {
- let current_line_index = self.cursor.y;
- if current_line_index == scroll_region_top {
- // if we're at the top line, we create a new line and remove the last line that
- // would otherwise overflow
- if scroll_region_bottom < self.viewport.len() {
- self.viewport.remove(scroll_region_bottom);
- }
- self.viewport.insert(current_line_index, Row::new()); // TODO: .canonical() ?
- } else if current_line_index > scroll_region_top
- && current_line_index <= scroll_region_bottom
- {
- self.move_cursor_up(count);
- }
- }
- }
- pub fn move_cursor_down(&mut self, count: usize, pad_character: TerminalCharacter) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- if self.cursor.y >= scroll_region_top && self.cursor.y <= scroll_region_bottom {
- self.cursor.y = std::cmp::min(self.cursor.y + count, scroll_region_bottom);
- return;
- }
- }
- let lines_to_add = if self.cursor.y + count > self.height - 1 {
- (self.cursor.y + count) - (self.height - 1)
- } else {
- 0
- };
- self.cursor.y = if self.cursor.y + count > self.height - 1 {
- self.height - 1
- } else {
- self.cursor.y + count
- };
- for _ in 0..lines_to_add {
- self.add_canonical_line();
- }
- self.pad_lines_until(self.cursor.y, pad_character);
- }
- pub fn move_cursor_back(&mut self, count: usize) {
- if self.cursor.x == self.width {
- // on the rightmost screen edge, backspace skips one character
- self.cursor.x -= 1;
- }
- if self.cursor.x < count {
- self.cursor.x = 0;
- } else {
- self.cursor.x -= count;
- }
- }
- pub fn hide_cursor(&mut self) {
- self.cursor.is_hidden = true;
- }
- pub fn show_cursor(&mut self) {
- self.cursor.is_hidden = false;
- }
- pub fn set_scroll_region(&mut self, top_line_index: usize, bottom_line_index: Option<usize>) {
- let bottom_line_index = bottom_line_index.unwrap_or(self.height);
- self.scroll_region = Some((top_line_index, bottom_line_index));
- }
- pub fn clear_scroll_region(&mut self) {
- self.scroll_region = None;
- }
- pub fn set_scroll_region_to_viewport_size(&mut self) {
- self.scroll_region = Some((0, self.height - 1));
- }
- pub fn delete_lines_in_scroll_region(
- &mut self,
- count: usize,
- pad_character: TerminalCharacter,
- ) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- let current_line_index = self.cursor.y;
- if current_line_index >= scroll_region_top && current_line_index <= scroll_region_bottom
- {
- // when deleting lines inside the scroll region, we must make sure it stays the
- // same size (and that other lines below it aren't shifted inside it)
- // so we delete the current line(s) and add an empty line at the end of the scroll
- // region
- for _ in 0..count {
- self.viewport.remove(current_line_index);
- let columns = vec![pad_character; self.width];
- if self.viewport.len() > scroll_region_bottom {
- self.viewport
- .insert(scroll_region_bottom, Row::from_columns(columns).canonical());
- } else {
- self.viewport.push(Row::from_columns(columns).canonical());
- }
- }
- }
- }
- }
- pub fn add_empty_lines_in_scroll_region(
- &mut self,
- count: usize,
- pad_character: TerminalCharacter,
- ) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- let current_line_index = self.cursor.y;
- if current_line_index >= scroll_region_top && current_line_index <= scroll_region_bottom
- {
- // when adding empty lines inside the scroll region, we must make sure it stays the
- // same size and that lines don't "leak" outside of it
- // so we add an empty line where the cursor currently is, and delete the last line
- // of the scroll region
- for _ in 0..count {
- if scroll_region_bottom < self.viewport.len() {
- self.viewport.remove(scroll_region_bottom);
- }
- let columns = vec![pad_character; self.width];
- self.viewport
- .insert(current_line_index, Row::from_columns(columns).canonical());
- }
- }
- }
- }
- pub fn move_cursor_to_column(&mut self, column: usize) {
- self.cursor.x = column;
- self.pad_current_line_until(self.cursor.x);
- }
- pub fn move_cursor_to_line(&mut self, line: usize, pad_character: TerminalCharacter) {
- self.cursor.y = std::cmp::min(self.height - 1, line);
- self.pad_lines_until(self.cursor.y, pad_character);
- self.pad_current_line_until(self.cursor.x);
- }
- pub fn replace_with_empty_chars(&mut self, count: usize, empty_char_style: CharacterStyles) {
- let mut empty_character = EMPTY_TERMINAL_CHARACTER;
- empty_character.styles = empty_char_style;
- let pad_until = std::cmp::min(self.width, self.cursor.x + count);
- self.pad_current_line_until(pad_until);
- let current_row = self.viewport.get_mut(self.cursor.y).unwrap();
- for i in 0..count {
- current_row.replace_character_at(empty_character, self.cursor.x + i);
- }
- }
- pub fn erase_characters(&mut self, count: usize, empty_char_style: CharacterStyles) {
- let mut empty_character = EMPTY_TERMINAL_CHARACTER;
- empty_character.styles = empty_char_style;
- let current_row = self.viewport.get_mut(self.cursor.y).unwrap();
- for _ in 0..count {
- current_row.delete_character(self.cursor.x);
- }
- let mut empty_space_to_append = vec![empty_character; count];
- self.viewport
- .get_mut(self.cursor.y)
- .unwrap()
- .append(&mut empty_space_to_append);
- }
- fn add_newline(&mut self) {
- self.add_canonical_line();
- self.mark_for_rerender();
- }
- pub fn mark_for_rerender(&mut self) {
- self.should_render = true;
- }
- fn reset_terminal_state(&mut self) {
- self.lines_above = VecDeque::with_capacity(SCROLL_BACK);
- self.lines_below = vec![];
- self.viewport = vec![Row::new().canonical()];
- self.alternative_lines_above_viewport_and_cursor = None;
- self.cursor_key_mode = false;
- self.scroll_region = None;
- self.clear_viewport_before_rendering = true;
- self.cursor = Cursor::new(0, 0);
- self.saved_cursor_position = None;
- self.active_charset = Default::default();
- self.erasure_mode = false;
- self.disable_linewrap = false;
- self.cursor.change_shape(CursorShape::Block);
- }
- fn set_preceding_character(&mut self, terminal_character: TerminalCharacter) {
- self.preceding_char = Some(terminal_character);
- }
-}
-
-impl Perform for Grid {
- fn print(&mut self, c: char) {
- let c = self.cursor.charsets[self.active_charset].map(c);
- // apparently, building TerminalCharacter like this without a "new" method
- // is a little faster
- let terminal_character = TerminalCharacter {
- character: c,
- styles: self.cursor.pending_styles,
- };
- self.set_preceding_character(terminal_character);
- self.add_character(terminal_character);
- }
-
- fn execute(&mut self, byte: u8) {
- match byte {
- 8 => {
- // backspace
- self.move_cursor_back(1);
- }
- 9 => {
- // tab
- self.advance_to_next_tabstop(self.cursor.pending_styles);
- }
- 10 | 11 | 12 => {
- // 0a, newline
- // 0b, vertical tabulation
- // 0c, form feed
- self.add_newline();
- }
- 13 => {
- // 0d, carriage return
- self.move_cursor_to_beginning_of_line();
- }
- 14 => {
- self.set_active_charset(CharsetIndex::G1);
- }
- 15 => {
- self.set_active_charset(CharsetIndex::G0);
- }
- _ => {}
- }
- }
-
- fn hook(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _c: char) {
- // TBD
- }
-
- fn put(&mut self, _byte: u8) {
- // TBD
- }
-
- fn unhook(&mut self) {
- // TBD
- }
-
- fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) {
- // TBD
- }
-
- fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], _ignore: bool, c: char) {
- let mut params_iter = params.iter();
- let mut next_param_or = |default: u16| {
- params_iter
- .next()
- .map(|param| param[0])
- .filter(|&param| param != 0)
- .unwrap_or(default) as usize
- };
- if c == 'm' {
- self.cursor
- .pending_styles
- .add_style_from_ansi_params(&mut params_iter);
- } else if c == 'C' || c == 'a' {
- // move cursor forward
- let move_by = next_param_or(1);
- self.move_cursor_forward_until_edge(move_by);
- } else if c == 'K' {
- // clear line (0 => right, 1 => left, 2 => all)
- if let Some(clear_type) = params_iter.next().map(|param| param[0]) {
- if clear_type == 0 {
- let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
- char_to_replace.styles = self.cursor.pending_styles;
- self.replace_characters_in_line_after_cursor(char_to_replace);
- } else if clear_type == 1 {
- let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
- char_to_replace.styles = self.cursor.pending_styles;
- self.replace_characters_in_line_before_cursor(char_to_replace);
- } else if clear_type == 2 {
- self.clear_cursor_line();
- }
- };
- } else if c == 'J' {
- // clear all (0 => below, 1 => above, 2 => all, 3 => saved)
- let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
- char_to_replace.styles = self.cursor.pending_styles;
-
- if let Some(clear_type) = params_iter.next().map(|param| param[0]) {
- if clear_type == 0 {
- self.clear_all_after_cursor(char_to_replace);
- } else if clear_type == 1 {
- self.clear_all_before_cursor(char_to_replace);
- } else if clear_type == 2 {
- self.fill_viewport(char_to_replace);
- }
- };
- } else if c == 'H' || c == 'f' {
- // goto row/col
- // we subtract 1 from the row/column because these are 1 indexed
- let row = next_param_or(1).saturating_sub(1);
- let col = next_param_or(1).saturating_sub(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.move_cursor_to(col, row, pad_character);
- } else if c == 'A' {
- // move cursor up until edge of screen
- let move_up_count = next_param_or(1);
- self.move_cursor_up(move_up_count as usize);
- } else if c == 'B' || c == 'e' {
- // move cursor down until edge of screen
- let move_down_count = next_param_or(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.move_cursor_down(move_down_count as usize, pad_character);
- } else if c == 'D' {
- let move_back_count = next_param_or(1);
- self.move_cursor_back(move_back_count);
- } else if c == 'l' {
- let first_intermediate_is_questionmark = match intermediates.get(0) {
- Some(b'?') => true,
- None => false,
- _ => false,
- };
- if first_intermediate_is_questionmark {
- match params_iter.next().map(|param| param[0]) {
- Some(1049) => {
- if let Some((
- alternative_lines_above,
- alternative_viewport,
- alternative_cursor,
- )) = self.alternative_lines_above_viewport_and_cursor.as_mut()
- {
- std::mem::swap(&mut self.lines_above, alternative_lines_above);
- std::mem::swap(&mut self.viewport, alternative_viewport);
- std::mem::swap(&mut self.cursor, alternative_cursor);
- }
- self.alternative_lines_above_viewport_and_cursor = None;
- self.clear_viewport_before_rendering = true;
- self.change_size(self.height, self.width); // the alternative_viewport might have been of a different size...
- self.mark_for_rerender();
- }
- Some(25) => {
- self.hide_cursor();
- self.mark_for_rerender();
- }
- Some(1) => {
- self.cursor_key_mode = false;
- }
- Some(3) => {
- // DECCOLM - only side effects
- self.scroll_region = None;
- self.clear_all(EMPTY_TERMINAL_CHARACTER);
- self.cursor.x = 0;
- self.cursor.y = 0;
- }
- Some(6) => {
- self.erasure_mode = false;
- }
- Some(7) => {
- self.disable_linewrap = true;
- }
- _ => {}
- };
- } else if let Some(4) = params_iter.next().map(|param| param[0]) {
- self.insert_mode = false;
- }
- } else if c == 'h' {
- let first_intermediate_is_questionmark = match intermediates.get(0) {
- Some(b'?') => true,
- None => false,
- _ => false,
- };
- if first_intermediate_is_questionmark {
- match params_iter.next().map(|param| param[0]) {
- Some(25) => {
- self.show_cursor();
- self.mark_for_rerender();
- }
- Some(1049) => {
- let current_lines_above = std::mem::replace(
- &mut self.lines_above,
- VecDeque::with_capacity(SCROLL_BACK),
- );
- let current_viewport =
- std::mem::replace(&mut self.viewport, vec![Row::new().canonical()]);
- let current_cursor = std::mem::replace(&mut self.cursor, Cursor::new(0, 0));
- self.alternative_lines_above_viewport_and_cursor =
- Some((current_lines_above, current_viewport, current_cursor));
- self.clear_viewport_before_rendering = true;
- }
- Some(1) => {
- self.cursor_key_mode = true;
- }
- Some(3) => {
- // DECCOLM - only side effects
- self.scroll_region = None;
- self.clear_all(EMPTY_TERMINAL_CHARACTER);
- self.cursor.x = 0;
- self.cursor.y = 0;
- }
- Some(6) => {
- self.erasure_mode = true;
- }
- Some(7) => {
- self.disable_linewrap = false;
- }
- _ => {}
- };
- } else if let Some(4) = params_iter.next().map(|param| param[0]) {
- self.insert_mode = true;
- }
- } else if c == 'r' {
- if params.len() > 1 {
- let top = (next_param_or(1) as usize).saturating_sub(1);
- let bottom = params_iter
- .next()
- .map(|param| param[0] as usize)
- .filter(|&param| param != 0)
- .map(|bottom| bottom.saturating_sub(1));
- self.set_scroll_region(top, bottom);
- if self.erasure_mode {
- self.move_cursor_to_line(top, EMPTY_TERMINAL_CHARACTER);
- self.move_cursor_to_beginning_of_line();
- }
- } else {
- self.clear_scroll_region();
- }
- } else if c == 'M' {
- // delete lines if currently inside scroll region
- let line_count_to_delete = next_param_or(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.delete_lines_in_scroll_region(line_count_to_delete, pad_character);
- } else if c == 'L' {
- // insert blank lines if inside scroll region
- let line_count_to_add = next_param_or(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.add_empty_lines_in_scroll_region(line_count_to_add, pad_character);
- } else if c == 'G' || c == '`' {
- let column = next_param_or(1).saturating_sub(1);
- self.move_cursor_to_column(column);
- } else if c == 'g' {
- let clear_type = next_param_or(0);
- if clear_type == 0 {
- self.clear_tabstop(self.cursor.x);
- } else if clear_type == 3 {
- self.clear_all_tabstops();
- }
- } else if c == 'd' {
- // goto line
- let line = next_param_or(1).saturating_sub(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.move_cursor_to_line(line, pad_character);
- } else if c == 'P' {
- // erase characters
- let count = next_param_or(1);
- self.erase_characters(count, self.cursor.pending_styles);
- } else if c == 'X' {
- // erase characters and replace with empty characters of current style
- let count = next_param_or(1);
- self.replace_with_empty_chars(count, self.cursor.pending_styles);
- } else if c == 'T' {
- /*
- * 124 54 T SD
- * Scroll down, new lines inserted at top of screen
- * [4T = Scroll down 4, bring previous lines back into view
- */
- let line_count = next_param_or(1);
- self.rotate_scroll_region_up(line_count as usize);
- } else if c == 'S' {
- // move scroll up
- let count = next_param_or(1);
- self.rotate_scroll_region_down(count);
- } else if c == 's' {
- self.save_cursor_position();
- } else if c == 'u' {
- self.restore_cursor_position();
- } else if c == '@' {
- let count = next_param_or(1);
- for _ in 0..count {
- // TODO: should this be styled?
- self.insert_character_at_cursor_position(EMPTY_TERMINAL_CHARACTER);
- }
- } else if c == 'b' {
- if let Some(c) = self.preceding_char {
- for _ in 0..next_param_or(1) {
- self.add_character(c);
- }
- }
- } else if c == 'E' {
- let count = next_param_or(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.move_cursor_down(count, pad_character);
- } else if c == 'F' {
- let count = next_param_or(1);
- self.move_cursor_up(count);
- self.move_cursor_to_beginning_of_line();
- } else if c == 'I' {
- for _ in 0..next_param_or(1) {
- self.advance_to_next_tabstop(self.cursor.pending_styles);
- }
- } else if c == 'q' {
- let first_intermediate_is_space = matches!(intermediates.get(0), Some(b' '));
- if first_intermediate_is_space {
- // DECSCUSR (CSI Ps SP q) -- Set Cursor Style.
- let cursor_style_id = next_param_or(0);
- let shape = match cursor_style_id {
- 0 | 2 => Some(CursorShape::Block),
- 1 => Some(CursorShape::BlinkingBlock),
- 3 => Some(CursorShape::BlinkingUnderline),
- 4 => Some(CursorShape::Underline),
- 5 => Some(CursorShape::BlinkingBeam),
- 6 => Some(CursorShape::Beam),
- _ => None,
- };
- if let Some(cursor_shape) = shape {
- self.cursor.change_shape(cursor_shape);
- }
- }
- } else if c == 'Z' {
- for _ in 0..next_param_or(1) {
- self.move_to_previous_tabstop();
- }
- } else if c == 'c' {
- // identify terminal
- // https://vt100.net/docs/vt510-rm/DA1.html
- match intermediates.get(0) {
- None | Some(0) => {
- // primary device attributes
- let terminal_capabilities = "\u{1b}[?6c";
- self.pending_messages_to_pty
- .push(terminal_capabilities.as_bytes().to_vec());
- }
- Some(b'>') => {
- // secondary device attributes
- let version = version_number(VERSION);
- let text = format!("\u{1b}[>0;{};1c", version);
- self.pending_messages_to_pty.push(text.as_bytes().to_vec());
- }
- _ => {}
- }
- } else if c == 'n' {
- // DSR - device status report
- // https://vt100.net/docs/vt510-rm/DSR.html
- match next_param_or(0) {
- 5 => {
- // report terminal status
- let all_good = "\u{1b}[0n";
- self.pending_messages_to_pty
- .push(all_good.as_bytes().to_vec());
- }
- 6 => {
- // CPR - cursor position report
- let position_report =
- format!("\x1b[{};{}R", self.cursor.y + 1, self.cursor.x + 1);
- self.pending_messages_to_pty
- .push(position_report.as_bytes().to_vec());
- }
- _ => {}
- }
- } else if c == 't' {
- match next_param_or(1) as usize {
- 14 => {
- // TODO: report text area size in pixels, currently unimplemented
- // to solve this we probably need to query the user's terminal for the cursor
- // size and then use it as a multiplier
- }
- 18 => {
- // report text area
- let text_area_report = format!("\x1b[8;{};{}t", self.height, self.width);
- self.pending_messages_to_pty
- .push(text_area_report.as_bytes().to_vec());
- }
- 22 => {
- // TODO: push title
- }
- 23 => {
- // TODO: pop title
- }
- _ => {}
- }
- } else {
- let result = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params));
- #[cfg(not(test))]
- result.unwrap();
- }
- }
-
- fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) {
- match (byte, intermediates.get(0)) {
- (b'B', charset_index_symbol) => {
- let charset_index: CharsetIndex = match charset_index_symbol {
- Some(b'(') => CharsetIndex::G0,
- Some(b')') => CharsetIndex::G1,
- Some(b'*') => CharsetIndex::G2,
- Some(b'+') => CharsetIndex::G3,
- _ => {
- // invalid, silently do nothing
- return;
- }
- };
- self.configure_charset(StandardCharset::Ascii, charset_index);
- }
- (b'0', charset_index_symbol) => {
- let charset_index: CharsetIndex = match charset_index_symbol {
- Some(b'(') => CharsetIndex::G0,
- Some(b')') => CharsetIndex::G1,
- Some(b'*') => CharsetIndex::G2,
- Some(b'+') => CharsetIndex::G3,
- _ => {
- // invalid, silently do nothing
- return;
- }
- };
- self.configure_charset(
- StandardCharset::SpecialCharacterAndLineDrawing,
- charset_index,
- );
- }
- (b'D', None) => {
- self.add_newline();
- }
- (b'E', None) => {
- self.add_newline();
- self.move_cursor_to_beginning_of_line();
- }
- (b'M', None) => {
- // TODO: if cursor is at the top, it should go down one
- self.move_cursor_up_with_scrolling(1);
- }
- (b'c', None) => {
- self.reset_terminal_state();
- }
- (b'H', None) => {
- self.set_horizontal_tabstop();
- }
- (b'7', None) => {
- self.save_cursor_position();
- }
- (b'Z', None) => {
- let terminal_capabilities = "\u{1b}[?6c";
- self.pending_messages_to_pty
- .push(terminal_capabilities.as_bytes().to_vec());
- }
- (b'8', None) => {
- self.restore_cursor_position();
- }
- (b'8', Some(b'#')) => {
- let mut fill_character = EMPTY_TERMINAL_CHARACTER;
- fill_character.character = 'E';
- self.fill_viewport(fill_character);
- }
- _ => {}
- }
- }
-}
-
-#[derive(Clone)]
-pub struct Row {
- pub columns: Vec<TerminalCharacter>,
- pub is_canonical: bool,
-}
-
-impl Debug for Row {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- for character in &self.columns {
- write!(f, "{:?}", character)?;
- }
- Ok(())
- }
-}
-
-impl Row {
- pub fn new() -> Self {
- Row {
- columns: vec![],
- is_canonical: false,
- }
- }
- pub fn from_columns(columns: Vec<TerminalCharacter>) -> Self {
- Row {
- columns,
- is_canonical: false,
- }
- }
- pub fn from_rows(mut rows: Vec<Row>) -> Self {
- if rows.is_empty() {
- Row::new()
- } else {
- let mut first_row = rows.remove(0);
- for row in rows.iter_mut() {
- first_row.append(&mut row.columns);
- }
- first_row
- }
- }
- pub fn with_character(mut self, terminal_character: TerminalCharacter) -> Self {
- self.columns.push(terminal_character);
- self
- }
- pub fn canonical(mut self) -> Self {
- self.is_canonical = true;
- self
- }
- pub fn contains_widechar(&self) -> bool {
- self.columns.iter().any(|c| c.is_widechar())
- }
- pub fn add_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
- match self.columns.len().cmp(&x) {
- Ordering::Equal => self.columns.push(terminal_character),
- Ordering::Less => {
- self.columns.resize(x, EMPTY_TERMINAL_CHARACTER);
- self.columns.push(terminal_character);
- }
- Ordering::Greater => {
- // this is much more performant than remove/insert
- self.columns.push(terminal_character);
- self.columns.swap_remove(x);
- }
- }
- }
- pub fn insert_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
- match self.columns.len().cmp(&x) {
- Ordering::Equal => self.columns.push(terminal_character),
- Ordering::Less => {
- self.columns.resize(x, EMPTY_TERMINAL_CHARACTER);
- self.columns.push(terminal_character);
- }
- Ordering::Greater => {
- self.columns.insert(x, terminal_character);
- }
- }
- }
- pub fn replace_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
- // this is much more performant than remove/insert
- if x < self.columns.len() {
- self.columns.push(terminal_character);
- self.columns.swap_remove(x);
- }
- }
- pub fn replace_columns(&mut self, columns: Vec<TerminalCharacter>) {
- self.columns = columns;
- }
- pub fn push(&mut self, terminal_character: TerminalCharacter) {
- self.columns.push(terminal_character);
- }
- pub fn truncate(&mut self, x: usize) {
- self.columns.truncate(x);
- }
- pub fn append(&mut self, to_append: &mut Vec<TerminalCharacter>) {
- self.columns.append(to_append);
- }
- pub fn replace_beginning_with(&mut self, mut line_part: Vec<TerminalCharacter>) {
- if line_part.len() > self.columns.len() {
- self.columns.clear();
- } else {
- drop(self.columns.drain(0..line_part.len()));
- }
- line_part.append(&mut self.columns);
- self.columns = line_part;
- }
- pub fn len(&self) -> usize {
- self.columns.len()
- }
- pub fn delete_character(&mut self, x: usize) {
- if x < self.columns.len() {
- self.columns.remove(x);
- }
- }
- pub fn split_to_rows_of_length(&mut self, max_row_length: usize) -> Vec<Row> {
- let mut parts: Vec<Row> = vec![];
- let mut current_part: Vec<TerminalCharacter> = vec![];
- for character in self.columns.drain(..) {
- if current_part.len() == max_row_length {
- parts.push(Row::from_columns(current_part));
- current_part = vec![];
- }
- current_part.push(character);
- }
- if !current_part.is_empty() {
- parts.push(Row::from_columns(current_part))
- };
- if !parts.is_empty() && self.is_canonical {
- parts.get_mut(0).unwrap().is_canonical = true;
- }
- parts
- }
-}
-
-#[cfg(test)]
-#[path = "./unit/grid_tests.rs"]
-mod grid_tests;
diff --git a/src/client/panes/mod.rs b/src/client/panes/mod.rs
deleted file mode 100644
index 4e7d6e80..00000000
--- a/src/client/panes/mod.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-mod grid;
-mod plugin_pane;
-mod terminal_character;
-mod terminal_pane;
-
-pub use grid::*;
-pub use plugin_pane::*;
-pub use terminal_character::*;
-pub use terminal_pane::*;
diff --git a/src/client/panes/plugin_pane.rs b/src/client/panes/plugin_pane.rs
deleted file mode 100644
index dde3cd9b..00000000
--- a/src/client/panes/plugin_pane.rs
+++ /dev/null
@@ -1,227 +0,0 @@
-use std::sync::mpsc::channel;
-use std::time::Instant;
-use std::unimplemented;
-
-use crate::common::thread_bus::SenderWithContext;
-use crate::panes::{PaneId, PositionAndSize};
-use crate::pty::VteBytes;
-use crate::tab::Pane;
-use crate::wasm_vm::PluginInstruction;
-
-pub struct PluginPane {
- pub pid: u32,
- pub should_render: bool,
- pub selectable: bool,
- pub invisible_borders: bool,
- pub position_and_size: PositionAndSize,
- pub position_and_size_override: Option<PositionAndSize>,
- pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
- pub max_height: Option<usize>,
- pub max_width: Option<usize>,
- pub active_at: Instant,
-}
-
-impl PluginPane {
- pub fn new(
- pid: u32,
- position_and_size: PositionAndSize,
- send_plugin_instructions: SenderWithContext<PluginInstruction>,
- ) -> Self {
- Self {
- pid,
- should_render: true,
- selectable: true,
- invisible_borders: false,
- position_and_size,
- position_and_size_override: None,
- send_plugin_instructions,
- max_height: None,
- max_width: None,
- active_at: Instant::now(),
- }
- }
-}
-
-impl Pane for PluginPane {
- // FIXME: These position and size things should all be moved to default trait implementations,
- // with something like a get_pos_and_sz() method underpinning all of them. Alternatively and
- // preferably, just use an enum and not a trait object
- fn x(&self) -> usize {
- self.position_and_size_override
- .unwrap_or(self.position_and_size)
- .x
- }
- fn y(&self) -> usize {
- self.position_and_size_override
- .unwrap_or(self.position_and_size)
- .y
- }
- fn rows(&self) -> usize {
- self.position_and_size_override
- .unwrap_or(self.position_and_size)
- .rows
- }
- fn columns(&self) -> usize {
- self.position_and_size_override
- .unwrap_or(self.position_and_size)
- .columns
- }
- fn reset_size_and_position_override(&mut self) {
- self.position_and_size_override = None;
- self.should_render = true;
- }
- fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize) {
- self.position_and_size = *position_and_size;
- self.should_render = true;
- }
- // FIXME: This is obviously a bit outdated and needs the x and y moved into `size`
- fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) {
- let position_and_size_override = PositionAndSize {
- x,
- y,
- rows: size.rows,
- columns: size.columns,
- ..Default::default()
- };
- self.position_and_size_override = Some(position_and_size_override);
- self.should_render = true;
- }
- fn handle_pty_bytes(&mut self, _event: VteBytes) {
- unimplemented!()
- }
- fn cursor_coordinates(&self) -> Option<(usize, usize)> {
- None
- }
- fn adjust_input_to_terminal(&self, _input_bytes: Vec<u8>) -> Vec<u8> {
- unimplemented!() // FIXME: Shouldn't need this implmented?
- }
-
- fn position_and_size_override(&self) -> Option<PositionAndSize> {
- self.position_and_size_override
- }
- fn contains_widechar(&self) -> bool {
- false
- }
- fn should_render(&self) -> bool {
- self.should_render
- }
- fn set_should_render(&mut self, should_render: bool) {
- self.should_render = should_render;
- }
- fn selectable(&self) -> bool {
- self.selectable
- }
- fn set_selectable(&mut self, selectable: bool) {
- self.selectable = selectable;
- }
- fn set_invisible_borders(&mut self, invisible_borders: bool) {
- self.invisible_borders = invisible_borders;
- }
- fn set_max_height(&mut self, max_height: usize) {
- self.max_height = Some(max_height);
- }
- fn set_max_width(&mut self, max_width: usize) {
- self.max_width = Some(max_width);
- }
- fn render(&mut self) -> Option<String> {
- // if self.should_render {
- if true {
- // while checking should_render rather than rendering each pane every time
- // is more performant, it causes some problems when the pane to the left should be
- // rendered and has wide characters (eg. Chinese characters or emoji)
- // as a (hopefully) temporary hack, we render all panes until we find a better solution
- let (buf_tx, buf_rx) = channel();
-
- self.send_plugin_instructions
- .send(PluginInstruction::Render(
- buf_tx,
- self.pid,
- self.rows(),
- self.columns(),
- ))
- .unwrap();
-
- self.should_render = false;
- Some(buf_rx.recv().unwrap())
- } else {
- None
- }
- }
- fn pid(&self) -> PaneId {
- PaneId::Plugin(self.pid)
- }
- fn reduce_height_down(&mut self, count: usize) {
- self.position_and_size.y += count;
- self.position_and_size.rows -= count;
- self.should_render = true;
- }
- fn increase_height_down(&mut self, count: usize) {
- self.position_and_size.rows += count;
- self.should_render = true;
- }
- fn increase_height_up(&mut self, count: usize) {
- self.position_and_size.y -= count;
- self.position_and_size.rows += count;
- self.should_render = true;
- }
- fn reduce_height_up(&mut self, count: usize) {
- self.position_and_size.rows -= count;
- self.should_render = true;
- }
- fn reduce_width_right(&mut self, count: usize) {
- self.position_and_size.x += count;
- self.position_and_size.columns -= count;
- self.should_render = true;
- }
- fn reduce_width_left(&mut self, count: usize) {
- self.position_and_size.columns -= count;
- self.should_render = true;
- }
- fn increase_width_left(&mut self, count: usize) {
- self.position_and_size.x -= count;
- self.position_and_size.columns += count;
- self.should_render = true;
- }
- fn increase_width_right(&mut self, count: usize) {
- self.position_and_size.columns += count;
- self.should_render = true;
- }
- fn push_down(&mut self, count: usize) {
- self.position_and_size.y += count;
- }
- fn push_right(&mut self, count: usize) {
- self.position_and_size.x += count;
- }
- fn pull_left(&mut self, count: usize) {
- self.position_and_size.x -= count;
- }
- fn pull_up(&mut self, count: usize) {
- self.position_and_size.y -= count;
- }
- fn scroll_up(&mut self, _count: usize) {
- unimplemented!()
- }
- fn scroll_down(&mut self, _count: usize) {
- unimplemented!()
- }
- fn clear_scroll(&mut self) {
- unimplemented!()
- }
- fn max_height(&self) -> Option<usize> {
- self.max_height
- }
- fn max_width(&self) -> Option<usize> {
- self.max_width
- }
- fn invisible_borders(&self) -> bool {
- self.invisible_borders
- }
-
- fn active_at(&self) -> Instant {
- self.active_at
- }
-
- fn set_active_at(&mut self, time: Instant) {
- self.active_at = time;
- }
-}
diff --git a/src/client/panes/terminal_character.rs b/src/client/panes/terminal_character.rs
deleted file mode 100644
index d2340e2e..00000000
--- a/src/client/panes/terminal_character.rs
+++ /dev/null
@@ -1,775 +0,0 @@
-use unicode_width::UnicodeWidthChar;
-
-use crate::utils::logging::debug_log_to_file;
-use std::convert::TryFrom;
-use std::fmt::{self, Debug, Display, Formatter};
-use std::ops::{Index, IndexMut};
-use vte::ParamsIter;
-
-pub const EMPTY_TERMINAL_CHARACTER: TerminalCharacter = TerminalCharacter {
- character: ' ',
- styles: CharacterStyles {
- foreground: Some(AnsiCode::Reset),
- background: Some(AnsiCode::Reset),
- strike: Some(AnsiCode::Reset),
- hidden: Some(AnsiCode::Reset),
- reverse: Some(AnsiCode::Reset),
- slow_blink: Some(AnsiCode::Reset),
- fast_blink: Some(AnsiCode::Reset),
- underline: Some(AnsiCode::Reset),
- bold: Some(AnsiCode::Reset),
- dim: Some(AnsiCode::Reset),
- italic: Some(AnsiCode::Reset),
- },
-};
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum AnsiCode {
- On,
- Reset,
- NamedColor(NamedColor),
- RgbCode((u8, u8, u8)),
- ColorIndex(u8),
-}
-
-#[derive(Clone, Copy, Eq, PartialEq, Debug)]
-pub enum NamedColor {
- Black,
- Red,
- Green,
- Yellow,
- Blue,
- Magenta,
- Cyan,
- White,
- BrightBlack,
- BrightRed,
- BrightGreen,
- BrightYellow,
- BrightBlue,
- BrightMagenta,
- BrightCyan,
- BrightWhite,
-}
-
-impl NamedColor {
- fn to_foreground_ansi_code(self) -> String {
- match self {
- NamedColor::Black => format!("{}", 30),
- NamedColor::Red => format!("{}", 31),
- NamedColor::Green => format!("{}", 32),
- NamedColor::Yellow => format!("{}", 33),
- NamedColor::Blue => format!("{}", 34),
- NamedColor::Magenta => format!("{}", 35),
- NamedColor::Cyan => format!("{}", 36),
- NamedColor::White => format!("{}", 37),
- NamedColor::BrightBlack => format!("{}", 90),
- NamedColor::BrightRed => format!("{}", 91),
- NamedColor::BrightGreen => format!("{}", 92),
- NamedColor::BrightYellow => format!("{}", 93),
- NamedColor::BrightBlue => format!("{}", 94),
- NamedColor::BrightMagenta => format!("{}", 95),
- NamedColor::BrightCyan => format!("{}", 96),
- NamedColor::BrightWhite => format!("{}", 97),
- }
- }
- fn to_background_ansi_code(self) -> String {
- match self {
- NamedColor::Black => format!("{}", 40),
- NamedColor::Red => format!("{}", 41),
- NamedColor::Green => format!("{}", 42),
- NamedColor::Yellow => format!("{}", 43),
- NamedColor::Blue => format!("{}", 44),
- NamedColor::Magenta => format!("{}", 45),
- NamedColor::Cyan => format!("{}", 46),
- NamedColor::White => format!("{}", 47),
- NamedColor::BrightBlack => format!("{}", 100),
- NamedColor::BrightRed => format!("{}", 101),
- NamedColor::BrightGreen => format!("{}", 102),
- NamedColor::BrightYellow => format!("{}", 103),
- NamedColor::BrightBlue => format!("{}", 104),
- NamedColor::BrightMagenta => format!("{}", 105),
- NamedColor::BrightCyan => format!("{}", 106),
- NamedColor::BrightWhite => format!("{}", 107),
- }
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct CharacterStyles {
- pub foreground: Option<AnsiCode>,
- pub background: Option<AnsiCode>,
- pub strike: Option<AnsiCode>,
- pub hidden: Option<AnsiCode>,
- pub reverse: Option<AnsiCode>,
- pub slow_blink: Option<AnsiCode>,
- pub fast_blink: Option<AnsiCode>,
- pub underline: Option<AnsiCode>,
- pub bold: Option<AnsiCode>,
- pub dim: Option<AnsiCode>,
- pub italic: Option<AnsiCode>,
-}
-
-impl CharacterStyles {
- pub fn new() -> Self {
- CharacterStyles {
- foreground: None,
- background: None,
- strike: None,
- hidden: None,
- reverse: None,
- slow_blink: None,
- fast_blink: None,
- underline: None,
- bold: None,
- dim: None,
- italic: None,
- }
- }
- pub fn foreground(mut self, foreground_code: Option<AnsiCode>) -> Self {
- self.foreground = foreground_code;
- self
- }
- pub fn background(mut self, background_code: Option<AnsiCode>) -> Self {
- self.background = background_code;
- self
- }
- pub fn bold(mut self, bold_code: Option<AnsiCode>) -> Self {
- self.bold = bold_code;
- self
- }
- pub fn dim(mut self, dim_code: Option<AnsiCode>) -> Self {
- self.dim = dim_code;
- self
- }
- pub fn italic(mut self, italic_code: Option<AnsiCode>) -> Self {
- self.italic = italic_code;
- self
- }
- pub fn underline(mut self, underline_code: Option<AnsiCode>) -> Self {
- self.underline = underline_code;
- self
- }
- pub fn blink_slow(mut self, slow_blink_code: Option<AnsiCode>) -> Self {
- self.slow_blink = slow_blink_code;
- self
- }
- pub fn blink_fast(mut self, fast_blink_code: Option<AnsiCode>) -> Self {
- self.fast_blink = fast_blink_code;
- self
- }
- pub fn reverse(mut self, reverse_code: Option<AnsiCode>) -> Self {
- self.reverse = reverse_code;
- self
- }
- pub fn hidden(mut self, hidden_code: Option<AnsiCode>) -> Self {
- self.hidden = hidden_code;
- self
- }
- pub fn strike(mut self, strike_code: Option<AnsiCode>) -> Self {
- self.strike = strike_code;
- self
- }
- pub fn clear(&mut self) {
- self.foreground = None;
- self.background = None;
- self.strike = None;
- self.hidden = None;
- self.reverse = None;
- self.slow_blink = None;
- self.fast_blink = None;
- self.underline = None;
- self.bold = None;
- self.dim = None;
- self.italic = None;
- }
- pub fn update_and_return_diff(
- &mut self,
- new_styles: &CharacterStyles,
- ) -> Option<CharacterStyles> {
- let mut diff: Option<CharacterStyles> = None;
-
- if new_styles.foreground == Some(AnsiCode::Reset)
- && new_styles.background == Some(AnsiCode::Reset)
- && new_styles.strike == Some(AnsiCode::Reset)
- && new_styles.hidden == Some(AnsiCode::Reset)
- && new_styles.reverse == Some(AnsiCode::Reset)
- && new_styles.fast_blink == Some(AnsiCode::Reset)
- && new_styles.slow_blink == Some(AnsiCode::Reset)
- && new_styles.underline == Some(AnsiCode::Reset)
- && new_styles.bold == Some(AnsiCode::Reset)
- && new_styles.dim == Some(AnsiCode::Reset)
- && new_styles.italic == Some(AnsiCode::Reset)
- {
- self.foreground = Some(AnsiCode::Reset);
- self.background = Some(AnsiCode::Reset);
- self.strike = Some(AnsiCode::Reset);
- self.hidden = Some(AnsiCode::Reset);
- self.reverse = Some(AnsiCode::Reset);
- self.fast_blink = Some(AnsiCode::Reset);
- self.slow_blink = Some(AnsiCode::Reset);
- self.underline = Some(AnsiCode::Reset);
- self.bold = Some(AnsiCode::Reset);
- self.dim = Some(AnsiCode::Reset);
- self.italic = Some(AnsiCode::Reset);
- return Some(*new_styles);
- };
-
- if self.foreground != new_styles.foreground {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.foreground(new_styles.foreground));
- self.foreground = new_styles.foreground;
- } else {
- diff = Some(CharacterStyles::new().foreground(new_styles.foreground));
- self.foreground = new_styles.foreground;
- }
- }
- if self.background != new_styles.background {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.background(new_styles.background));
- self.background = new_styles.background;
- } else {
- diff = Some(CharacterStyles::new().background(new_styles.background));
- self.background = new_styles.background;
- }
- }
- if self.strike != new_styles.strike {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.strike(new_styles.strike));
- self.strike = new_styles.strike;
- } else {
- diff = Some(CharacterStyles::new().strike(new_styles.strike));
- self.strike = new_styles.strike;
- }
- }
- if self.hidden != new_styles.hidden {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.hidden(new_styles.hidden));
- self.hidden = new_styles.hidden;
- } else {
- diff = Some(CharacterStyles::new().hidden(new_styles.hidden));
- self.hidden = new_styles.hidden;
- }
- }
- if self.reverse != new_styles.reverse {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.reverse(new_styles.reverse));
- self.reverse = new_styles.reverse;
- } else {
- diff = Some(CharacterStyles::new().reverse(new_styles.reverse));
- self.reverse = new_styles.reverse;
- }
- }
- if self.slow_blink != new_styles.slow_blink {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.blink_slow(new_styles.slow_blink));
- self.slow_blink = new_styles.slow_blink;
- } else {
- diff = Some(CharacterStyles::new().blink_slow(new_styles.slow_blink));
- self.slow_blink = new_styles.slow_blink;
- }
- }
- if self.fast_blink != new_styles.fast_blink {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.blink_fast(new_styles.fast_blink));
- self.fast_blink = new_styles.fast_blink;
- } else {
- diff = Some(CharacterStyles::new().blink_fast(new_styles.fast_blink));
- self.fast_blink = new_styles.fast_blink;
- }
- }
- if self.underline != new_styles.underline {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.underline(new_styles.underline));
- self.underline = new_styles.underline;
- } else {
- diff = Some(CharacterStyles::new().underline(new_styles.underline));
- self.underline = new_styles.underline;
- }
- }
- if self.bold != new_styles.bold {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.bold(new_styles.bold));
- self.bold = new_styles.bold;
- } else {
- diff = Some(CharacterStyles::new().bold(new_styles.bold));
- self.bold = new_styles.bold;
- }
- }
- if self.dim != new_styles.dim {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.dim(new_styles.dim));
- self.dim = new_styles.dim;
- } else {
- diff = Some(CharacterStyles::new().dim(new_styles.dim));
- self.dim = new_styles.dim;
- }
- }
- if self.italic != new_styles.italic {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.italic(new_styles.italic));
- self.italic = new_styles.italic;
- } else {
- diff = Some(CharacterStyles::new().italic(new_styles.italic));
- self.italic = new_styles.italic;
- }
- }
- diff
- }
- pub fn reset_all(&mut self) {
- self.foreground = Some(AnsiCode::Reset);
- self.background = Some(AnsiCode::Reset);
- self.bold = Some(AnsiCode::Reset);
- self.dim = Some(AnsiCode::Reset);
- self.italic = Some(AnsiCode::Reset);
- self.underline = Some(AnsiCode::Reset);
- self.slow_blink = Some(AnsiCode::Reset);
- self.fast_blink = Some(AnsiCode::Reset);
- self.reverse = Some(AnsiCode::Reset);
- self.hidden = Some(AnsiCode::Reset);
- self.strike = Some(AnsiCode::Reset);
- }
- pub fn add_style_from_ansi_params(&mut self, params: &mut ParamsIter) {
- while let Some(param) = params.next() {
- match param {
- [] | [0] => self.reset_all(),
- [1] => *self = self.bold(Some(AnsiCode::On)),
- [2] => *self = self.dim(Some(AnsiCode::On)),
- [3] => *self = self.italic(Some(AnsiCode::On)),
- [4] => *self = self.underline(Some(AnsiCode::On)),
- [5] => *self = self.blink_slow(Some(AnsiCode::On)),
- [6] => *self = self.blink_fast(Some(AnsiCode::On)),
- [7] => *self = self.reverse(Some(AnsiCode::On)),
- [8] => *self = self.hidden(Some(AnsiCode::On)),
- [9] => *self = self.strike(Some(AnsiCode::On)),
- [21] => *self = self.bold(Some(AnsiCode::Reset)),
- [22] => {
- *self = self.bold(Some(AnsiCode::Reset));
- *self = self.dim(Some(AnsiCode::Reset));
- }
- [23] => *self = self.italic(Some(AnsiCode::Reset)),
- [24] => *self = self.underline(Some(AnsiCode::Reset)),
- [25] => {
- *self = self.blink_slow(Some(AnsiCode::Reset));
- *self = self.blink_fast(Some(AnsiCode::Reset));
- }
- [27] => *self = self.reverse(Some(AnsiCode::Reset)),
- [28] => *self = self.hidden(Some(AnsiCode::Reset)),
- [29] => *self = self.strike(Some(AnsiCode::Reset)),
- [30] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Black))),
- [31] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Red))),
- [32] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Green))),
- [33] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Yellow))),
- [34] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Blue))),
- [35] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Magenta))),
- [36] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Cyan))),
- [37] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::White))),
- [38] => {
- let mut iter = params.map(|param| param[0]);
- if let Some(ansi_code) = parse_sgr_color(&mut iter) {
- *self = self.foreground(Some(ansi_code));
- }
- }
- [38, params @ ..] => {
- let rgb_start = if params.len() > 4 { 2 } else { 1 };
- let rgb_iter = params[rgb_start..].iter().copied();
- let mut iter = std::iter::once(params[0]).chain(rgb_iter);
- if let Some(ansi_code) = parse_sgr_color(&mut iter) {
- *self = self.foreground(Some(ansi_code));
- }
- }
- [39] => *self = self.foreground(Some(AnsiCode::Reset)),
- [40] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Black))),
- [41] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Red))),
- [42] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Green))),
- [43] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Yellow))),
- [44] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Blue))),
- [45] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Magenta))),
- [46] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Cyan))),
- [47] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::White))),
- [48] => {
- let mut iter = params.map(|param| param[0]);
- if let Some(ansi_code) = parse_sgr_color(&mut iter) {
- *self = self.background(Some(ansi_code));
- }
- }
- [48, params @ ..] => {
- let rgb_start = if params.len() > 4 { 2 } else { 1 };
- let rgb_iter = params[rgb_start..].iter().copied();
- let mut iter = std::iter::once(params[0]).chain(rgb_iter);
- if let Some(ansi_code) = parse_sgr_color(&mut iter) {
- *self = self.background(Some(ansi_code));
- }
- }
- [49] => *self = self.background(Some(AnsiCode::Reset)),
- [90] => {
- *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlack)))
- }
- [91] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightRed))),
- [92] => {
- *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightGreen)))
- }
- [93] => {
- *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightYellow)))
- }
- [94] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlue))),
- [95] => {
- *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta)))
- }
- [96] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightCyan))),
- [97] => {
- *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightWhite)))
- }
- [100] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlack)))
- }
- [101] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightRed))),
- [102] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightGreen)))
- }
- [103] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightYellow)))
- }
- [104] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlue)))
- }
- [105] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta)))
- }
- [106] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightCyan)))
- }
- [107] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightWhite)))
- }
- _ => {
- let _ = debug_log_to_file(format!("unhandled csi m code {:?}", param));
- return;
- }
- }
- }
- }
-}
-
-impl Display for CharacterStyles {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- if self.foreground == Some(AnsiCode::Reset)
- && self.background == Some(AnsiCode::Reset)
- && self.strike == Some(AnsiCode::Reset)
- && self.hidden == Some(AnsiCode::Reset)
- && self.reverse == Some(AnsiCode::Reset)
- && self.fast_blink == Some(AnsiCode::Reset)
- && self.slow_blink == Some(AnsiCode::Reset)
- && self.underline == Some(AnsiCode::Reset)
- && self.bold == Some(AnsiCode::Reset)
- && self.dim == Some(AnsiCode::Reset)
- && self.italic == Some(AnsiCode::Reset)
- {
- write!(f, "\u{1b}[m")?; // reset all
- return Ok(());
- }
- if let Some(ansi_code) = self.foreground {
- match ansi_code {
- AnsiCode::RgbCode((r, g, b)) => {
- write!(f, "\u{1b}[38;2;{};{};{}m", r, g, b)?;
- }
- AnsiCode::ColorIndex(color_index) => {
- write!(f, "\u{1b}[38;5;{}m", color_index)?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[39m")?;
- }
- AnsiCode::NamedColor(named_color) => {
- write!(f, "\u{1b}[{}m", named_color.to_foreground_ansi_code())?;
- }
- _ => {}
- }
- };
- if let Some(ansi_code) = self.background {
- match ansi_code {
- AnsiCode::RgbCode((r, g, b)) => {
- write!(f, "\u{1b}[48;2;{};{};{}m", r, g, b)?;
- }
- AnsiCode::ColorIndex(color_index) => {
- write!(f, "\u{1b}[48;5;{}m", color_index)?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[49m")?;
- }
- AnsiCode::NamedColor(named_color) => {
- write!(f, "\u{1b}[{}m", named_color.to_background_ansi_code())?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.strike {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[9m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[29m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.hidden {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[8m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[28m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.reverse {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[7m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[27m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.fast_blink {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[6m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[25m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.slow_blink {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[5m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[25m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.bold {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[1m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[22m\u{1b}[24m")?;
- // TODO: this cancels bold + underline, if this behaviour is indeed correct, we
- // need to properly handle it in the struct methods etc like dim
- }
- _ => {}
- }
- }
- // notice the order is important here, bold must be before underline
- // because the bold reset also resets underline, and would override it
- // otherwise
- if let Some(ansi_code) = self.underline {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[4m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[24m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.dim {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[2m")?;
- }
- AnsiCode::Reset => {
- if let Some(AnsiCode::Reset) = self.bold {
- // we only reset dim if both dim and bold should be reset
- write!(f, "\u{1b}[22m")?;
- }
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.italic {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[3m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[23m")?;
- }
- _ => {}
- }
- };
- Ok(())
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum CharsetIndex {
- G0,
- G1,
- G2,
- G3,
-}
-
-impl Default for CharsetIndex {
- fn default() -> Self {
- CharsetIndex::G0
- }
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum StandardCharset {
- Ascii,
- SpecialCharacterAndLineDrawing,
-}
-
-impl Default for StandardCharset {
- fn default() -> Self {
- StandardCharset::Ascii
- }
-}
-
-impl StandardCharset {
- /// Switch/Map character to the active charset. Ascii is the common case and
- /// for that we want to do as little as possible.
- #[inline]
- pub fn map(self, c: char) -> char {
- match self {
- StandardCharset::Ascii => c,
- StandardCharset::SpecialCharacterAndLineDrawing => match c {
- '`' => '◆',
- 'a' => '▒',
- 'b' => '␉',
- 'c' => '␌',
- 'd' => '␍',
- 'e' => '␊',
- 'f' => '°',
- 'g' => '±',
- 'h' => '␤',
- 'i' => '␋',
- 'j' => '┘',
- 'k' => '┐',
- 'l' => '┌',
- 'm' => '└',
- 'n' => '┼',
- 'o' => '⎺',
- 'p' => '⎻',
- 'q' => '─',
- 'r' => '⎼',
- 's' => '⎽',
- 't' => '├',
- 'u' => '┤',
- 'v' => '┴',
- 'w' => '┬',
- 'x' => '│',
- 'y' => '≤',
- 'z' => '≥',
- '{' => 'π',
- '|' => '≠',
- '}' => '£',
- '~' => '·',
- _ => c,
- },
- }
- }
-}
-
-#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
-pub struct Charsets([StandardCharset; 4]);
-
-impl Index<CharsetIndex> for Charsets {
- type Output = StandardCharset;
-
- fn index(&self, index: CharsetIndex) -> &StandardCharset {
- &self.0[index as usize]
- }
-}
-
-impl IndexMut<CharsetIndex> for Charsets {
- fn index_mut(&mut self, index: CharsetIndex) -> &mut StandardCharset {
- &mut self.0[index as usize]
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum CursorShape {
- Block,
- BlinkingBlock,
- Underline,
- BlinkingUnderline,
- Beam,
- BlinkingBeam,
-}
-
-#[derive(Clone, Debug)]
-pub struct Cursor {
- pub x: usize,
- pub y: usize,
- pub is_hidden: bool,
- pub pending_styles: CharacterStyles,
- pub charsets: Charsets,
- shape: CursorShape,
-}
-
-impl Cursor {
- pub fn new(x: usize, y: usize) -> Self {
- Cursor {
- x,
- y,
- is_hidden: false,
- pending_styles: CharacterStyles::new(),
- charsets: Default::default(),
- shape: CursorShape::Block,
- }
- }
- pub fn change_shape(&mut self, shape: CursorShape) {
- self.shape = shape;
- }
- pub fn get_shape(&self) -> CursorShape {
- self.shape
- }
-}
-
-#[derive(Clone, Copy)]
-pub struct TerminalCharacter {
- pub character: char,
- pub styles: CharacterStyles,
-}
-
-impl ::std::fmt::Debug for TerminalCharacter {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.character)
- }
-}
-
-impl TerminalCharacter {
- pub fn width(&self) -> usize {
- self.character.width().unwrap_or(0)
- }
-
- pub fn is_widechar(&self) -> bool {
- self.width() > 1
- }
-}
-
-fn parse_sgr_color(params: &mut dyn Iterator<Item = u16>) -> Option<AnsiCode> {
- match params.next() {
- Some(2) => Some(AnsiCode::RgbCode((
- u8::try_from(params.next()?).ok()?,
- u8::try_from(params.next()?).ok()?,
- u8::try_from(params.next()?).ok()?,
- ))),
- Some(5) => Some(AnsiCode::ColorIndex(u8::try_from(params.next()?).ok()?)),
- _ => None,
- }
-}
diff --git a/src/client/panes/terminal_pane.rs b/src/client/panes/terminal_pane.rs
deleted file mode 100644
index 5c4f55c8..00000000
--- a/src/client/panes/terminal_pane.rs
+++ /dev/null
@@ -1,362 +0,0 @@
-use std::fmt::Debug;
-use std::os::unix::io::RawFd;
-use std::time::Instant;
-
-use nix::pty::Winsize;
-use serde::{Deserialize, Serialize};
-
-use crate::panes::grid::Grid;
-use crate::panes::terminal_character::{
- CharacterStyles, CursorShape, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
-};
-use crate::pty::VteBytes;
-use crate::tab::Pane;
-
-#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum PaneId {
- Terminal(RawFd),
- Plugin(u32), // FIXME: Drop the trait object, make this a wrapper for the struct?
-}
-
-/// Contains the position and size of a [`Pane`], or more generally of any terminal, measured
-/// in character rows and columns.
-#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
-pub struct PositionAndSize {
- pub x: usize,
- pub y: usize,
- pub rows: usize,
- pub columns: usize,
- pub max_rows: Option<usize>,
- pub max_columns: Option<usize>,
-}
-
-impl From<Winsize> for PositionAndSize {
- fn from(winsize: Winsize) -> PositionAndSize {
- PositionAndSize {
- columns: winsize.ws_col as usize,
- rows: winsize.ws_row as usize,
- ..Default::default()
- }
- }
-}
-
-pub struct TerminalPane {
- pub grid: Grid,
- pub pid: RawFd,
- pub selectable: bool,
- pub position_and_size: PositionAndSize,
- pub position_and_size_override: Option<PositionAndSize>,
- pub max_height: Option<usize>,
- pub max_width: Option<usize>,
- pub active_at: Instant,
- vte_parser: vte::Parser,
-}
-
-impl Pane for TerminalPane {
- fn x(&self) -> usize {
- self.get_x()
- }
- fn y(&self) -> usize {
- self.get_y()
- }
- fn rows(&self) -> usize {
- self.get_rows()
- }
- fn columns(&self) -> usize {
- self.get_columns()
- }
- fn reset_size_and_position_override(&mut self) {
- self.position_and_size_override = None;
- self.reflow_lines();
- }
- fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize) {
- self.position_and_size.columns = position_and_size.columns;
- self.position_and_size.rows = position_and_size.rows;
- self.reflow_lines();
- }
- fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) {
- let position_and_size_override = PositionAndSize {
- x,
- y,
- rows: size.rows,
- columns: size.columns,
- ..Default::default()
- };
- self.position_and_size_override = Some(position_and_size_override);
- self.reflow_lines();
- }
- fn handle_pty_bytes(&mut self, bytes: VteBytes) {
- for byte in bytes.iter() {
- self.vte_parser.advance(&mut self.grid, *byte);
- }
- self.set_should_render(true);
- }
- fn cursor_coordinates(&self) -> Option<(usize, usize)> {
- // (x, y)
- self.grid.cursor_coordinates()
- }
- fn adjust_input_to_terminal(&self, input_bytes: Vec<u8>) -> Vec<u8> {
- // there are some cases in which the terminal state means that input sent to it
- // needs to be adjusted.
- // here we match against those cases - if need be, we adjust the input and if not
- // we send back the original input
- match input_bytes.as_slice() {
- [27, 91, 68] => {
- // left arrow
- if self.grid.cursor_key_mode {
- // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
- // some editors will not show this
- return "OD".as_bytes().to_vec();
- }
- }
- [27, 91, 67] => {
- // right arrow
- if self.grid.cursor_key_mode {
- // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
- // some editors will not show this
- return "OC".as_bytes().to_vec();
- }
- }
- [27, 91, 65] => {
- // up arrow
- if self.grid.cursor_key_mode {
- // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
- // some editors will not show this
- return "OA".as_bytes().to_vec();
- }
- }
- [27, 91, 66] => {
- // down arrow
- if self.grid.cursor_key_mode {
- // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
- // some editors will not show this
- return "OB".as_bytes().to_vec();
- }
- }
- _ => {}
- };
- input_bytes
- }
-
- fn position_and_size_override(&self) -> Option<PositionAndSize> {
- self.position_and_size_override
- }
- fn contains_widechar(&self) -> bool {
- self.grid.contains_widechar()
- }
- fn should_render(&self) -> bool {
- self.grid.should_render
- }
- fn set_should_render(&mut self, should_render: bool) {
- self.grid.should_render = should_render;
- }
- fn selectable(&self) -> bool {
- self.selectable
- }
- fn set_selectable(&mut self, selectable: bool) {
- self.selectable = selectable;
- }
- fn set_max_height(&mut self, max_height: usize) {
- self.max_height = Some(max_height);
- }
- fn set_max_width(&mut self, max_width: usize) {
- self.max_width = Some(max_width);
- }
- fn set_invisible_borders(&mut self, _invisible_borders: bool) {
- unimplemented!();
- }
- fn max_height(&self) -> Option<usize> {
- self.max_height
- }
- fn max_width(&self) -> Option<usize> {
- self.max_width
- }
- fn render(&mut self) -> Option<String> {
- if self.should_render() {
- let mut vte_output = String::new();
- let buffer_lines = &self.read_buffer_as_lines();
- let display_cols = self.get_columns();
- let mut character_styles = CharacterStyles::new();
- if self.grid.clear_viewport_before_rendering {
- for line_index in 0..self.grid.height {
- let x = self.get_x();
- let y = self.get_y();
- vte_output.push_str(&format!(
- "\u{1b}[{};{}H\u{1b}[m",
- y + line_index + 1,
- x + 1
- )); // goto row/col and reset styles
- for _col_index in 0..self.grid.width {
- vte_output.push(EMPTY_TERMINAL_CHARACTER.character);
- }
- }
- self.grid.clear_viewport_before_rendering = false;
- }
- for (row, line) in buffer_lines.iter().enumerate() {
- let x = self.get_x();
- let y = self.get_y();
- vte_output.push_str(&format!("\u{1b}[{};{}H\u{1b}[m", y + row + 1, x + 1)); // goto row/col and reset styles
- for (col, t_character) in line.iter().enumerate() {
- if col < display_cols {
- // in some cases (eg. while resizing) some characters will spill over
- // before they are corrected by the shell (for the prompt) or by reflowing
- // lines
- if let Some(new_styles) =
- character_styles.update_and_return_diff(&t_character.styles)
- {
- // the terminal keeps the previous styles as long as we're in the same
- // line, so we only want to update the new styles here (this also
- // includes resetting previous styles as needed)
- vte_output.push_str(&new_styles.to_string());
- }
- vte_output.push(t_character.character);
- }
- }
- character_styles.clear();
- }
- self.set_should_render(false);
- Some(vte_output)
- } else {
- None
- }
- }
- fn pid(&self) -> PaneId {
- PaneId::Terminal(self.pid)
- }
- fn reduce_height_down(&mut self, count: usize) {
- self.position_and_size.y += count;
- self.position_and_size.rows -= count;
- self.reflow_lines();
- }
- fn increase_height_down(&mut self, count: usize) {
- self.position_and_size.rows += count;
- self.reflow_lines();
- }
- fn increase_height_up(&mut self, count: usize) {
- self.position_and_size.y -= count;
- self.position_and_size.rows += count;
- self.reflow_lines();
- }
- fn reduce_height_up(&mut self, count: usize) {
- self.position_and_size.rows -= count;
- self.reflow_lines();
- }
- fn reduce_width_right(&mut self, count: usize) {
- self.position_and_size.x += count;
- self.position_and_size.columns -= count;
- self.reflow_lines();
- }
- fn reduce_width_left(&mut self, count: usize) {
- self.position_and_size.columns -= count;
- self.reflow_lines();
- }
- fn increase_width_left(&mut self, count: usize) {
- self.position_and_size.x -= count;
- self.position_and_size.columns += count;
- self.reflow_lines();
- }
- fn increase_width_right(&mut self, count: usize) {
- self.position_and_size.columns += count;
- self.reflow_lines();
- }
- fn push_down(&mut self, count: usize) {
- self.position_and_size.y += count;
- }
- fn push_right(&mut self, count: usize) {
- self.position_and_size.x += count;
- }
- fn pull_left(&mut self, count: usize) {
- self.position_and_size.x -= count;
- }
- fn pull_up(&mut self, count: usize) {
- self.position_and_size.y -= count;
- }
- fn scroll_up(&mut self, count: usize) {
- self.grid.move_viewport_up(count);
- self.set_should_render(true);
- }
- fn scroll_down(&mut self, count: usize) {
- self.grid.move_viewport_down(count);
- self.set_should_render(true);
- }
- fn clear_scroll(&mut self) {
- self.grid.reset_viewport();
- self.set_should_render(true);
- }
-
- fn active_at(&self) -> Instant {
- self.active_at
- }
-
- fn set_active_at(&mut self, time: Instant) {
- self.active_at = time;
- }
- fn cursor_shape_csi(&self) -> String {
- match self.grid.cursor_shape() {
- CursorShape::Block => "\u{1b}[0 q".to_string(),
- CursorShape::BlinkingBlock => "\u{1b}[1 q".to_string(),
- CursorShape::Underline => "\u{1b}[4 q".to_string(),
- CursorShape::BlinkingUnderline => "\u{1b}[3 q".to_string(),
- CursorShape::Beam => "\u{1b}[6 q".to_string(),
- CursorShape::BlinkingBeam => "\u{1b}[5 q".to_string(),
- }
- }
- fn drain_messages_to_pty(&mut self) -> Vec<Vec<u8>> {
- self.grid.pending_messages_to_pty.drain(..).collect()
- }
-}
-
-impl TerminalPane {
- pub fn new(pid: RawFd, position_and_size: PositionAndSize) -> TerminalPane {
- let grid = Grid::new(position_and_size.rows, position_and_size.columns);
- TerminalPane {
- pid,
- grid,
- selectable: true,
- position_and_size,
- position_and_size_override: None,
- max_height: None,
- max_width: None,
- vte_parser: vte::Parser::new(),
- active_at: Instant::now(),
- }
- }
- pub fn get_x(&self) -> usize {
- match self.position_and_size_override {
- Some(position_and_size_override) => position_and_size_override.x,
- None => self.position_and_size.x as usize,
- }
- }
- pub fn get_y(&self) -> usize {
- match self.position_and_size_override {
- Some(position_and_size_override) => position_and_size_override.y,
- None => self.position_and_size.y as usize,
- }
- }
- pub fn get_columns(&self) -> usize {
- match &self.position_and_size_override.as_ref() {
- Some(position_and_size_override) => position_and_size_override.columns,
- None => self.position_and_size.columns as usize,
- }
- }
- pub fn get_rows(&self) -> usize {
- match &self.position_and_size_override.as_ref() {
- Some(position_and_size_override) => position_and_size_override.rows,
- None => self.position_and_size.rows as usize,
- }
- }
- fn reflow_lines(&mut self) {
- let rows = self.get_rows();
- let columns = self.get_columns();
- self.grid.change_size(rows, columns);
- self.set_should_render(true);
- }
- pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
- self.grid.as_character_lines()
- }
- #[cfg(test)]
- pub fn cursor_coordinates(&self) -> Option<(usize, usize)> {
- // (x, y)
- self.grid.cursor_coordinates()
- }
-}
diff --git a/src/client/panes/unit/grid_tests.rs b/src/client/panes/unit/grid_tests.rs
deleted file mode 100644
index 8517a975..00000000
--- a/src/client/panes/unit/grid_tests.rs
+++ /dev/null
@@ -1,397 +0,0 @@
-use super::super::Grid;
-use ::insta::assert_snapshot;
-
-fn read_fixture(fixture_name: &str) -> Vec<u8> {
- let mut path_to_file = std::path::PathBuf::new();
- path_to_file.push("src");
- path_to_file.push("tests");
- path_to_file.push("fixtures");
- path_to_file.push(fixture_name);
- let content = std::fs::read(path_to_file)
- .unwrap_or_else(|_| panic!("could not read fixture {:?}", &fixture_name));
- content
-}
-
-#[test]
-fn vttest1_0() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-0";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest1_1() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-1";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest1_2() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-2";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest1_3() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-3";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest1_4() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-4";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest1_5() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-5";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_0() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-0";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_1() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-1";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_2() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-2";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_3() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-3";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_4() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-4";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_5() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-5";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_6() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-6";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_7() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-7";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_8() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-8";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_9() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-9";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_10() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-10";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_11() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-11";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_12() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-12";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_13() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-13";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_14() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-14";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest3_0() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest3-0";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_0() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-0";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_1() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-1";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_2() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-2";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_3() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-3";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_4() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-4";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_5() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-5";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn csi_b() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "csi-b";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn csi_capital_i() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "csi-capital-i";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn csi_capital_z() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "csi-capital-z";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn terminal_reports() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "terminal_reports";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid.pending_messages_to_pty));
-}
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_b.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_b.snap
deleted file mode 100644
index 1d0c34e5..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_b.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): ffffff
-01 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_i.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_i.snap
deleted file mode 100644
index 11f7b8f8..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_i.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): foo
-01 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_z.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_z.snap
deleted file mode 100644
index 04f492b8..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_z.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): 12345678foo234567890
-01 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__terminal_reports.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__terminal_reports.snap
deleted file mode 100644
index b2dca273..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__terminal_reports.snap
+++ /dev/null
@@ -1,6 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid . pending_messages_to_pty)"
-
----
-[[27, 91, 63, 54, 99], [27, 91, 56, 59, 53, 49, 59, 57, 55, 116], [27, 91, 63, 54, 99], [27, 91, 48, 110], [27, 91, 49, 59, 49, 82]]
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap
deleted file mode 100644
index 8641098c..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): ********************************************************************************
-01 (C): *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
-02 (C): *+ +*
-03 (C): *+ +*
-04 (C): *+ +*
-05 (C): *+ +*
-06 (C): *+ +*
-07 (C): *+ +*
-08 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
-09 (C): *+ E E +*
-10 (C): *+ E The screen should be cleared, and have an unbroken bor- E +*
-11 (C): *+ E der of *'s and +'s around the edge, and exactly in the E +*
-12 (C): *+ E middle there should be a frame of E's around this text E +*
-13 (C): *+ E with one (1) free position around it. Push <RETURN> E +*
-14 (C): *+ E E +*
-15 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
-16 (C): *+ +*
-17 (C): *+ +*
-18 (C): *+ +*
-19 (C): *+ +*
-20 (C): *+ +*
-21 (C): *+ +*
-22 (C): *+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-23 (C): ********************************************************************************
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap
deleted file mode 100644
index 09663484..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): **************************************************************************************************************
-01 (C): *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
-02 (C): *+ +*
-03 (C): *+ +*
-04 (C): *+ +*
-05 (C): *+ +*
-06 (C): *+ +*
-07 (C): *+ +*
-08 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
-09 (C): *+ E E +*
-10 (C): *+ E The screen should be cleared, and have an unbroken bor- E +*
-11 (C): *+ E der of *'s and +'s around the edge, and exactly in the E +*
-12 (C): *+ E middle there should be a frame of E's around this text E +*
-13 (C): *+ E with one (1) free position around it. Push <RETURN> E +*
-14 (C): *+ E E +*
-15 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
-16 (C): *+ +*
-17 (C): *+ +*
-18 (C): *+ +*
-19 (C): *+ +*
-20 (C): *+ +*
-21 (C): *+ +*
-22 (C): ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ *
-23 (C): **************************************************************************************************************
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap
deleted file mode 100644
index 05d80b1d..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Test of autowrap, mixing control and print characters.
-01 (C): The left/right margins should have letters in order:
-02 (C): I iJ
-03 (C): j
-04 (C): K K k
-05 (C): L l
-06 (C): M mN
-07 (C): n
-08 (C): O O o
-09 (C): P p
-10 (C): Q qR
-11 (C): r
-12 (C): S S s
-13 (C): T t
-14 (C): U uV
-15 (C): v
-16 (C): W W w
-17 (C): X x
-18 (C): Y yZ
-19 (C): z
-20 (C):
-21 (C): Push <RETURN>
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap
deleted file mode 100644
index 64c3e7b9..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Test of autowrap, mixing control and print characters.
-01 (C): The left/right margins should have letters in order:
-02 (C): I i
-03 (C): J j
-04 (C): K k
-05 (C): L l
-06 (C): M m
-07 (C): N n
-08 (C): O o
-09 (C): P p
-10 (C): Q q
-11 (C): R r
-12 (C): S s
-13 (C): T t
-14 (C): U u
-15 (C): V v
-16 (C): W w
-17 (C): X x
-18 (C): Y y
-19 (C): Z z
-20 (C):
-21 (C): Push <RETURN>
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap
deleted file mode 100644
index dcb417ae..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Test of cursor-control characters inside ESC sequences.
-01 (C): Below should be four identical lines:
-02 (C):
-03 (C): A B C D E F G H I
-04 (C): A B C D E F G H I
-05 (C): A B C D E F G H I
-06 (C): A B C D E F G H I
-07 (C):
-08 (C): Push <RETURN>
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap
deleted file mode 100644
index d044691c..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Test of leading zeros in ESC sequences.
-01 (C): Two lines below you should see the sentence "This is a correct sentence".
-02 (C):
-03 (C): This is a correct sentence
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C): Push <RETURN>
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_0.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_0.snap
deleted file mode 100644
index f5bd11a5..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_0.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): **************************************************************************************************************
-01 (C): **************************************************
-02 (C): **************************************************************************************************************
-03 (C):
-04 (C): This should be three identical lines of *'s completely filling
-05 (C): the top of the screen without any empty lines between.
-06 (C): (Test of WRAP AROUND mode setting.)
-07 (C): Push <RETURN>
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_1.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_1.snap
deleted file mode 100644
index 148b8732..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_1.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): * * * * * * * * * * * * *
-01 (C): * * * * * * * * * * * * *
-02 (C):
-03 (C): Test of TAB setting/resetting. These two lines
-04 (C): should look the same. Push <RETURN>
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_10.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_10.snap
deleted file mode 100644
index 7cbb8545..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_10.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C):
-01 (C):
-02 (C):
-03 (C):
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C): This line should be the one above the bottom of the screen. Push <RETURN>
-23 (C): Origin mode test. This line should be at the bottom of the screen.
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_11.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_11.snap
deleted file mode 100644
index 1201290b..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_11.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): This line should be at the top of the screen. Push <RETURN>
-01 (C):
-02 (C):
-03 (C):
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C): Origin mode test. This line should be at the bottom of the screen.
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_12.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_12.snap
deleted file mode 100644
index fc15da63..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_12.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Graphic rendition test pattern:
-01 (C):
-02 (C):
-03 (C): vanilla bold
-04 (C):
-05 (C): underline bold underline
-06 (C):
-07 (C): blink bold blink
-08 (C):
-09 (C): underline blink bold underline blink
-10 (C):
-11 (C): negative bold negative
-12 (C):
-13 (C): underline negative bold underline negative
-14 (C):
-15 (C): blink negative bold blink negative
-16 (C):
-17 (C): underline blink negative bold underline blink negative
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C): Dark background. Push <RETURN>
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_13.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_13.snap
deleted file mode 100644
index ee8e30fb..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_13.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Graphic rendition test pattern:
-01 (C):
-02 (C):
-03 (C): vanilla bold
-04 (C):
-05 (C): underline bold underline
-06 (C):
-07 (C): blink bold blink
-08 (C):
-09 (C): underline blink bold underline blink
-10 (C):
-11 (C): negative bold negative
-12 (C):
-13 (C): underline negative bold underline negative
-14 (C):
-15 (C): blink negative bold blink negative
-16 (C):
-17 (C): underline blink negative bold underline blink negative
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C): Light background. Push <RETURN>
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_14.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_14.snap
deleted file mode 100644
index 2bdf1956..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_14.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): AAAAA
-01 (C): AAAAA
-02 (C): AAAAA
-03 (C): AAAAA
-04 (C):
-05 (C):
-06 (C):
-07 (C): normal bold underscored blinking reversed
-08 (C):
-09 (C): stars: ********** ********** ********** ********** **********
-10 (C):
-11 (C): line: ────────── ────────── ────────── ────────── ──────────
-12 (C):
-13 (C): x'es: xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx
-14 (C):
-15 (C): diamonds: ◆◆◆◆◆◆◆◆◆◆ ◆◆◆◆◆◆◆◆◆◆ ◆◆◆◆◆◆◆◆◆◆ ◆◆◆◆◆◆◆◆◆◆ ◆◆◆◆◆◆◆◆◆◆
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C): Test of the SAVE/RESTORE CURSOR feature. There should
-21 (C): be ten characters of each flavour, and a rectangle
-22 (C): of 5 x 4 A's filling the top left of the screen.
-23 (C): Push <RETURN>
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_2.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_2.snap
deleted file mode 100644
index 91a9a2e6..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_2.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
-01 (C): 123456789012345678901
-02 (C): This is 132 column mode, light background.
-03 (C): This is 132 column mode, light background.
-04 (C): This is 132 column mode, light background.
-05 (C): This is 132 column mode, light background.
-06 (C): This is 132 column mode, light background.
-07 (C): This is 132 column mode, light background.
-08 (C): This is 132 column mode, light background.
-09 (C): This is 132 column mode, light background.
-10 (C): This is 132 column mode, light background.
-11 (C): This is 132 column mode, light background.
-12 (C): This is 132 column mode, light background.
-13 (C): This is 132 column mode, light background.
-14 (C): This is 132 column mode, light background.
-15 (C): This is 132 column mode, light background.
-16 (C): This is 132 column mode, light background.
-17 (C): This is 132 column mode, light background.
-18 (C): This is 132 column mode, light background.
-19 (C): This is 132 column mode, light background.Push <RETURN>
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_3.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_3.snap
deleted file mode 100644
index c3881efb..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_3.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): 1234567890123456789012345678901234567890123456789012345678901234567890123456789
-01 (C):
-02 (C): This is 80 column mode, light background.
-03 (C): This is 80 column mode, light background.
-04 (C): This is 80 column mode, light background.
-05 (C): This is 80 column mode, light background.
-06 (C): This is 80 column mode, light background.
-07 (C): This is 80 column mode, light background.
-08 (C): This is 80 column mode, light background.
-09 (C): This is 80 column mode, light background.
-10 (C): This is 80 column mode, light background.
-11 (C): This is 80 column mode, light background.
-12 (C): This is 80 column mode, light background.
-13 (C): This is 80 column mode, light background.
-14 (C): This is 80 column mode, light background.
-15 (C): This is 80 column mode, light background.
-16 (C): This is 80 column mode, light background.
-17 (C): This is 80 column mode, light background.
-18 (C): This is 80 column mode, light background.
-19 (C): This is 80 column mode, light background.Push <RETURN>
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_4.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_4.snap
deleted file mode 100644
index 089f3d7d..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_4.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
-01 (C): 123456789012345678901
-02 (C): This is 132 column mode, dark background.
-03 (C): This is 132 column mode, dark background.
-04 (C): This is 132 column mode, dark background.
-05 (C): This is 132 column mode, dark background.
-06 (C): This is 132 column mode, dark background.
-07 (C): This is 132 column mode, dark background.
-08 (C): This is 132 column mode, dark background.
-09 (C): This is 132 column mode, dark background.
-10 (C): This is 132 column mode, dark background.
-11 (C): This is 132 column mode, dark background.
-12 (C): This is 132 column mode, dark background.
-13 (C): This is 132 column mode, dark background.
-14 (C): This is 132 column mode, dark background.
-15 (C): This is 132 column mode, dark background.
-16 (C): This is 132 column mode, dark background.
-17 (C): This is 132 column mode, dark background.
-18 (C): This is 132 column mode, dark background.
-19 (C): This is 132 column mode, dark background.Push <RETURN>
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_5.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_5.snap
deleted file mode 100644
index 6cf729c1..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_5.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): 1234567890123456789012345678901234567890123456789012345678901234567890123456789
-01 (C):
-02 (C): This is 80 column mode, dark background.
-03 (C): This is 80 column mode, dark background.
-04 (C): This is 80 column mode, dark background.
-05 (C): This is 80 column mode, dark background.
-06 (C): This is 80 column mode, dark background.
-07 (C): This is 80 column mode, dark background.
-08 (C): This is 80 column mode, dark background.
-09 (C): This is 80 column mode, dark background.
-10 (C): This is 80 column mode, dark background.
-11 (C): This is 80 column mode, dark background.
-12 (C): This is 80 column mode, dark background.
-13 (C): This is 80 column mode, dark background.
-14 (C): This is 80 column mode, dark background.
-15 (C): This is 80 column mode, dark background.
-16 (C): This is 80 column mode, dark background.
-17 (C): This is 80 column mode, dark background.
-18 (C): This is 80 column mode, dark background.
-19 (C): This is 80 column mode, dark background.Push <RETURN>
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_6.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_6.snap
deleted file mode 100644
index 3c6a1c77..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_6.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C):
-01 (C):
-02 (C):
-03 (C):
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (W): Push <RETURN>
-12 (W): Soft scroll down region [12..13] size 2 Line 29
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_7.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_7.snap
deleted file mode 100644
index 0c4c5cac..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_7.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (W): Push <RETURN>
-01 (W): Soft scroll down region [1..24] size 24 Line 29
-02 (W): Soft scroll down region [1..24] size 24 Line 28
-03 (W): Soft scroll down region [1..24] size 24 Line 27
-04 (W): Soft scroll down region [1..24] size 24 Line 26
-05 (W): Soft scroll down region [1..24] size 24 Line 25
-06 (W): Soft scroll down region [1..24] size 24 Line 24
-07 (W): Soft scroll down region [1..24] size 24 Line 23
-08 (W): Soft scroll down region [1..24] size 24 Line 22
-09 (W): Soft scroll down region [1..24] size 24 Line 21
-10 (W): Soft scroll down region [1..24] size 24 Line 20
-11 (W): Soft scroll down region [1..24] size 24 Line 19
-12 (W): Soft scroll down region [1..24] size 24 Line 18
-13 (W): Soft scroll down region [1..24] size 24 Line 17
-14 (W): Soft scroll down region [1..24] size 24 Line 16
-15 (W): Soft scroll down region [1..24] size 24 Line 15
-16 (W): Soft scroll down region [1..24] size 24 Line 14
-17 (W): Soft scroll down region [1..24] size 24 Line 13
-18 (W): Soft scroll down region [1..24] size 24 Line 12
-19 (W): Soft scroll down region [1..24] size 24 Line 11
-20 (W): Soft scroll down region [1..24] size 24 Line 10
-21 (W): Soft scroll down region [1..24] size 24 Line 9
-22 (W): Soft scroll down region [1..24] size 24 Line 8
-23 (W): Soft scroll down region [1..24] size 24 Line 7
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_8.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_8.snap
deleted file mode 100644
index 9e9ac891..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_8.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C):
-01 (C):
-02 (C):
-03 (C):
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (W): Push <RETURN>
-12 (W): Jump scroll down region [12..13] size 2 Line 29
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_9.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_9.snap
deleted file mode 100644
index 852203c8..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_9.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (W): Push <RETURN>
-01 (W): Jump scroll down region [1..24] size 24 Line 29
-02 (W): Jump scroll down region [1..24] size 24 Line 28
-03 (W): Jump scroll down region [1..24] size 24 Line 27
-04 (W): Jump scroll down region [1..24] size 24 Line 26
-05 (W): Jump scroll down region [1..24] size 24 Line 25
-06 (W): Jump scroll down region [1..24] size 24 Line 24
-07 (W): Jump scroll down region [1..24] size 24 Line 23
-08 (W): Jump scroll down region [1..24] size 24 Line 22
-09 (W): Jump scroll down region [1..24] size 24 Line 21
-10 (W): Jump scroll down region [1..24] size 24 Line 20
-11 (W): Jump scroll down region [1..24] size 24 Line 19
-12 (W): Jump scroll down region [1..24] size 24 Line 18
-13 (W): Jump scroll down region [1..24] size 24 Line 17
-14 (W): Jump scroll down region [1..24] size 24 Line 16
-15 (W): Jump scroll down region [1..24] size 24 Line 15
-16 (W): Jump scroll down region [1..24] size 24 Line 14
-17 (W): Jump scroll down region [1..24] size 24 Line 13
-18 (W): Jump scroll down region [1..24] size 24 Line 12
-19 (W): Jump scroll down region [1..24] size 24 Line 11
-20 (W): Jump scroll down region [1..24] size 24 Line 10
-21 (W): Jump scroll down region [1..24] size 24 Line 9
-22 (W): Jump scroll down region [1..24] size 24 Line 8
-23 (W): Jump scroll down region [1..24] size 24 Line 7
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest3_0.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest3_0.snap
deleted file mode 100644
index 6902cf25..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest3_0.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Selected as G0 (with SI) Selected as G1 (with SO)
-01 (C):
-02 (C): Character set B (US ASCII)
-03 (C): !"#$%&'()*+,-./0123456789:;<=>? !"#$%&'()*+,-./0123456789:;<=>?
-04 (C): @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
-05 (C): `abcdefghijklmnopqrstuvwxyz{|}~ `abcdefghijklmnopqrstuvwxyz{|}~
-06 (C): Character set A (British)
-07 (C): !"#$%&'()*+,-./0123456789:;<=>? !"#$%&'()*+,-./0123456789:;<=>?
-08 (C): @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
-09 (C): `abcdefghijklmnopqrstuvwxyz{|}~ `abcdefghijklmnopqrstuvwxyz{|}~
-10 (C): Character set 0 (DEC Special graphics and line drawing)
-11 (C): !"#$%&'()*+,-./0123456789:;<=>? !"#$%&'()*+,-./0123456789:;<=>?
-12 (C): @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
-13 (C): ◆▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π≠£· ◆▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π≠£·
-14 (C): Character set 1 (DEC Alternate character ROM standard characters)
-15 (C): !"#$%&'()*+,-./0123456789:;<=>? !"#$%&'()*+,-./0123456789:;<=>?
-16 (C): @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
-17 (C): `abcdefghijklmnopqrstuvwxyz{|}~ `abcdefghijklmnopqrstuvwxyz{|}~
-18 (C): Character set 2 (DEC Alternate character ROM special graphics)
-19 (C): !"#$%&'()*+,-./0123456789:;<=>? !"#$%&'()*+,-./0123456789:;<=>?
-20 (C): @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
-21 (C): `abcdefghijklmnopqrstuvwxyz{|}~ `abcdefghijklmnopqrstuvwxyz{|}~
-22 (C):
-23 (C): These are the installed character sets. Push <RETURN>
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_0.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_0.snap
deleted file mode 100644
index 18cdd01b..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_0.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-01 (C): Top line: A's, bottom line: X's, this line, nothing more. Push <RETURN>
-02 (C):
-03 (C):
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C): XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_1.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_1.snap
deleted file mode 100644
index 7618e82b..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_1.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): A******************************************************************************BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-01 (C):
-02 (C):
-03 (C): Test of 'Insert Mode'. The top line should be 'A*** ... ***B'. Push <RETURN>
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_2.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_2.snap
deleted file mode 100644
index 0873ec23..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_2.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-01 (C):
-02 (C):
-03 (C): Test of 'Delete Character'. The top line should be 'AB'. Push <RETURN>
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_3.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_3.snap
deleted file mode 100644
index 049f5083..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_3.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-01 (C): BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
-02 (C): CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
-03 (C): The right column should be staggered DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
-04 (C): by one. Push <RETURN>EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
-05 (C): FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-06 (C): GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
-07 (C): HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
-08 (C): IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
-09 (C): JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
-10 (C): KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
-11 (C): LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
-12 (C): MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
-13 (C): NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
-14 (C): OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
-15 (C): PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
-16 (C): QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
-17 (C): RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
-18 (C): SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
-19 (C): TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
-20 (C): UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
-21 (C): VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
-22 (C): WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
-23 (C): XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_4.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_4.snap
deleted file mode 100644
index e7eb978f..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_4.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-01 (C): BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
-02 (C): CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
-03 (C): The right column should be staggered
-04 (C): by one. Push <RETURN>EEEEEEEEEEEEE
-05 (C): FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-06 (C): GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
-07 (C): HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
-08 (C): IIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
-09 (C): JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
-10 (C): KKKKKKKKKKKKKKKKKKKKKKKKKKKKK
-11 (C): LLLLLLLLLLLLLLLLLLLLLLLLLLLL
-12 (C): MMMMMMMMMMMMMMMMMMMMMMMMMMM
-13 (C): NNNNNNNNNNNNNNNNNNNNNNNNNN
-14 (C): OOOOOOOOOOOOOOOOOOOOOOOOO
-15 (C): PPPPPPPPPPPPPPPPPPPPPPPP
-16 (C): QQQQQQQQQQQQQQQQQQQQQQQ
-17 (C): RRRRRRRRRRRRRRRRRRRRRR
-18 (C): SSSSSSSSSSSSSSSSSSSSS
-19 (C): TTTTTTTTTTTTTTTTTTTT
-20 (C): UUUUUUUUUUUUUUUUUUU
-21 (C): VVVVVVVVVVVVVVVVVV
-22 (C): WWWWWWWWWWWWWWWWW
-23 (C): XXXXXXXXXXXXXXXX
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_5.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_5.snap
deleted file mode 100644
index 9b54cbc8..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_5.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): If your terminal has the ANSI 'Insert Character' function
-01 (C): (the VT102 does not), then you should see a line like this
-02 (C): A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-03 (C): below:
-04 (C):
-05 (C): A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-06 (C):
-07 (C):
-08 (C):
-09 (C): Push <RETURN>
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/tab.rs b/src/client/tab.rs
deleted file mode 100644
index 29a41530..00000000
--- a/src/client/tab.rs
+++ /dev/null
@@ -1,2274 +0,0 @@
-//! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size,
-//! as well as how they should be resized
-
-use crate::client::pane_resizer::PaneResizer;
-use crate::common::input::handler::parse_keys;
-use crate::common::thread_bus::ThreadSenders;
-use crate::layout::Layout;
-use crate::os_input_output::ServerOsApi;
-use crate::panes::{PaneId, PositionAndSize, TerminalPane};
-use crate::pty::{PtyInstruction, VteBytes};
-use crate::server::ServerInstruction;
-use crate::utils::shared::adjust_to_size;
-use crate::wasm_vm::PluginInstruction;
-use crate::{boundaries::Boundaries, panes::PluginPane};
-use serde::{Deserialize, Serialize};
-use std::os::unix::io::RawFd;
-use std::sync::mpsc::channel;
-use std::time::Instant;
-use std::{
- cmp::Reverse,
- collections::{BTreeMap, HashSet},
-};
-use zellij_tile::data::{Event, InputMode, ModeInfo, Palette};
-
-const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this
-
-// MIN_TERMINAL_HEIGHT here must be larger than the height of any of the status bars
-// this is a dirty hack until we implement fixed panes
-const MIN_TERMINAL_HEIGHT: usize = 3;
-const MIN_TERMINAL_WIDTH: usize = 4;
-
-type BorderAndPaneIds = (usize, Vec<PaneId>);
-
-fn split_vertically_with_gap(rect: &PositionAndSize) -> (PositionAndSize, PositionAndSize) {
- let width_of_each_half = (rect.columns - 1) / 2;
- let mut first_rect = *rect;
- let mut second_rect = *rect;
- if rect.columns % 2 == 0 {
- first_rect.columns = width_of_each_half + 1;
- } else {
- first_rect.columns = width_of_each_half;
- }
- second_rect.x = first_rect.x + first_rect.columns + 1;
- second_rect.columns = width_of_each_half;
- (first_rect, second_rect)
-}
-
-fn split_horizontally_with_gap(rect: &PositionAndSize) -> (PositionAndSize, PositionAndSize) {
- let height_of_each_half = (rect.rows - 1) / 2;
- let mut first_rect = *rect;
- let mut second_rect = *rect;
- if rect.rows % 2 == 0 {
- first_rect.rows = height_of_each_half + 1;
- } else {
- first_rect.rows = height_of_each_half;
- }
- second_rect.y = first_rect.y + first_rect.rows + 1;
- second_rect.rows = height_of_each_half;
- (first_rect, second_rect)
-}
-
-pub struct Tab {
- pub index: usize,
- pub position: usize,
- pub name: String,
- panes: BTreeMap<PaneId, Box<dyn Pane>>,
- panes_to_hide: HashSet<PaneId>,
- active_terminal: Option<PaneId>,
- max_panes: Option<usize>,
- full_screen_ws: PositionAndSize,
- fullscreen_is_active: bool,
- os_api: Box<dyn ServerOsApi>,
- pub senders: ThreadSenders,
- synchronize_is_active: bool,
- should_clear_display_before_rendering: bool,
- pub mode_info: ModeInfo,
- pub input_mode: InputMode,
- pub colors: Palette,
-}
-
-#[derive(Clone, Debug, Default, Serialize, Deserialize)]
-pub struct TabData {
- /* subset of fields to publish to plugins */
- pub position: usize,
- pub name: String,
- pub active: bool,
- pub mode_info: ModeInfo,
- pub input_mode: InputMode,
- pub colors: Palette,
-}
-
-// FIXME: Use a struct that has a pane_type enum, to reduce all of the duplication
-pub trait Pane {
- fn x(&self) -> usize;
- fn y(&self) -> usize;
- fn rows(&self) -> usize;
- fn columns(&self) -> usize;
- fn reset_size_and_position_override(&mut self);
- fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize);
- fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize);
- fn handle_pty_bytes(&mut self, bytes: VteBytes);
- fn cursor_coordinates(&self) -> Option<(usize, usize)>;
- fn adjust_input_to_terminal(&self, input_bytes: Vec<u8>) -> Vec<u8>;
-
- fn position_and_size_override(&self) -> Option<PositionAndSize>;
- fn should_render(&self) -> bool;
- fn set_should_render(&mut self, should_render: bool);
- // FIXME: this method is used to trigger a force render to hide widechar problem
- // it should be removed when we can handle widechars
- fn contains_widechar(&self) -> bool;
- fn selectable(&self) -> bool;
- fn set_selectable(&mut self, selectable: bool);
- fn set_invisible_borders(&mut self, invisible_borders: bool);
- fn set_max_height(&mut self, max_height: usize);
- fn set_max_width(&mut self, max_width: usize);
- fn render(&mut self) -> Option<String>;
- fn pid(&self) -> PaneId;
- fn reduce_height_down(&mut self, count: usize);
- fn increase_height_down(&mut self, count: usize);
- fn increase_height_up(&mut self, count: usize);
- fn reduce_height_up(&mut self, count: usize);
- fn increase_width_right(&mut self, count: usize);
- fn reduce_width_right(&mut self, count: usize);
- fn reduce_width_left(&mut self, count: usize);
- fn increase_width_left(&mut self, count: usize);
- fn push_down(&mut self, count: usize);
- fn push_right(&mut self, count: usize);
- fn pull_left(&mut self, count: usize);
- fn pull_up(&mut self, count: usize);
- fn scroll_up(&mut self, count: usize);
- fn scroll_down(&mut self, count: usize);
- fn clear_scroll(&mut self);
- fn active_at(&self) -> Instant;
- fn set_active_at(&mut self, instant: Instant);
- fn cursor_shape_csi(&self) -> String {
- "\u{1b}[0 q".to_string() // default to non blinking block
- }
-
- fn right_boundary_x_coords(&self) -> usize {
- self.x() + self.columns()
- }
- fn bottom_boundary_y_coords(&self) -> usize {
- self.y() + self.rows()
- }
- fn is_directly_right_of(&self, other: &dyn Pane) -> bool {
- self.x() == other.x() + other.columns() + 1
- }
- fn is_directly_left_of(&self, other: &dyn Pane) -> bool {
- self.x() + self.columns() + 1 == other.x()
- }
- fn is_directly_below(&self, other: &dyn Pane) -> bool {
- self.y() == other.y() + other.rows() + 1
- }
- fn is_directly_above(&self, other: &dyn Pane) -> bool {
- self.y() + self.rows() + 1 == other.y()
- }
- fn horizontally_overlaps_with(&self, other: &dyn Pane) -> bool {
- (self.y() >= other.y() && self.y() <= (other.y() + other.rows()))
- || ((self.y() + self.rows()) <= (other.y() + other.rows())
- && (self.y() + self.rows()) > other.y())
- || (self.y() <= other.y() && (self.y() + self.rows() >= (other.y() + other.rows())))
- || (other.y() <= self.y() && (other.y() + other.rows() >= (self.y() + self.rows())))
- }
- fn get_horizontal_overlap_with(&self, other: &dyn Pane) -> usize {
- std::cmp::min(self.y() + self.rows(), other.y() + other.rows())
- - std::cmp::max(self.y(), other.y())
- }
- fn vertically_overlaps_with(&self, other: &dyn Pane) -> bool {
- (self.x() >= other.x() && self.x() <= (other.x() + other.columns()))
- || ((self.x() + self.columns()) <= (other.x() + other.columns())
- && (self.x() + self.columns()) > other.x())
- || (self.x() <= other.x()
- && (self.x() + self.columns() >= (other.x() + other.columns())))
- || (other.x() <= self.x()
- && (other.x() + other.columns() >= (self.x() + self.columns())))
- }
- fn get_vertical_overlap_with(&self, other: &dyn Pane) -> usize {
- std::cmp::min(self.x() + self.columns(), other.x() + other.columns())
- - std::cmp::max(self.x(), other.x())
- }
- fn position_and_size(&self) -> PositionAndSize {
- PositionAndSize {
- x: self.x(),
- y: self.y(),
- columns: self.columns(),
- rows: self.rows(),
- ..Default::default()
- }
- }
- fn can_increase_height_by(&self, increase_by: usize) -> bool {
- self.max_height()
- .map(|max_height| self.rows() + increase_by <= max_height)
- .unwrap_or(true)
- }
- fn can_increase_width_by(&self, increase_by: usize) -> bool {
- self.max_width()
- .map(|max_width| self.columns() + increase_by <= max_width)
- .unwrap_or(true)
- }
- fn can_reduce_height_by(&self, reduce_by: usize) -> bool {
- self.rows() > reduce_by && self.rows() - reduce_by >= self.min_height()
- }
- fn can_reduce_width_by(&self, reduce_by: usize) -> bool {
- self.columns() > reduce_by && self.columns() - reduce_by >= self.min_width()
- }
- fn min_width(&self) -> usize {
- MIN_TERMINAL_WIDTH
- }
- fn min_height(&self) -> usize {
- MIN_TERMINAL_HEIGHT
- }
- fn max_width(&self) -> Option<usize> {
- None
- }
- fn max_height(&self) -> Option<usize> {
- None
- }
- fn invisible_borders(&self) -> bool {
- false
- }
- fn drain_messages_to_pty(&mut self) -> Vec<Vec<u8>> {
- // TODO: this is only relevant to terminal panes
- // we should probably refactor away from this trait at some point
- vec![]
- }
-}
-
-impl Tab {
- // FIXME: Still too many arguments for clippy to be happy...
- #[allow(clippy::too_many_arguments)]
- pub fn new(
- index: usize,
- position: usize,
- name: String,
- full_screen_ws: &PositionAndSize,
- mut os_api: Box<dyn ServerOsApi>,
- senders: ThreadSenders,
- max_panes: Option<usize>,
- pane_id: Option<PaneId>,
- mode_info: ModeInfo,
- input_mode: InputMode,
- colors: Palette,
- ) -> Self {
- let panes = if let Some(PaneId::Terminal(pid)) = pane_id {
- let new_terminal = TerminalPane::new(pid, *full_screen_ws);
- os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- );
- let mut panes: BTreeMap<PaneId, Box<dyn Pane>> = BTreeMap::new();
- panes.insert(PaneId::Terminal(pid), Box::new(new_terminal));
- panes
- } else {
- BTreeMap::new()
- };
- Tab {
- index,
- position,
- panes,
- name,
- max_panes,
- panes_to_hide: HashSet::new(),
- active_terminal: pane_id,
- full_screen_ws: *full_screen_ws,
- fullscreen_is_active: false,
- synchronize_is_active: false,
- os_api,
- senders,
- should_clear_display_before_rendering: false,
- mode_info,
- input_mode,
- colors,
- }
- }
-
- pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>) {
- // TODO: this should be an attribute on Screen instead of full_screen_ws
- let free_space = PositionAndSize {
- x: 0,
- y: 0,
- rows: self.full_screen_ws.rows,
- columns: self.full_screen_ws.columns,
- ..Default::default()
- };
- self.panes_to_hide.clear();
- let positions_in_layout = layout.position_panes_in_space(&free_space);
- let mut positions_and_size = positions_in_layout.iter();
- for (pane_kind, terminal_pane) in self.panes.iter_mut() {
- // for now the layout only supports terminal panes
- if let PaneId::Terminal(pid) = pane_kind {
- match positions_and_size.next() {
- Some((_, position_and_size)) => {
- terminal_pane.reset_size_and_position_override();
- if let Some(max_rows) = position_and_size.max_rows {
- terminal_pane.set_max_height(max_rows);
- }
- if let Some(max_columns) = position_and_size.max_columns {
- terminal_pane.set_max_width(max_columns);
- }
- terminal_pane.change_pos_and_size(&position_and_size);
- self.os_api.set_terminal_size_using_fd(
- *pid,
- position_and_size.columns as u16,
- position_and_size.rows as u16,
- );
- }
- None => {
- // we filled the entire layout, no room for this pane
- // TODO: handle active terminal
- self.panes_to_hide.insert(PaneId::Terminal(*pid));
- }
- }
- }
- }
- let mut new_pids = new_pids.iter();
- for (layout, position_and_size) in positions_and_size {
- // Just a regular terminal
- if let Some(plugin) = &layout.plugin {
- let (pid_tx, pid_rx) = channel();
- self.senders
- .send_to_plugin(PluginInstruction::Load(pid_tx, plugin.clone()))
- .unwrap();
- let pid = pid_rx.recv().unwrap();
- let mut new_plugin = PluginPane::new(
- pid,
- *position_and_size,
- self.senders.to_plugin.as_ref().unwrap().clone(),
- );
- if let Some(max_rows) = position_and_size.max_rows {
- new_plugin.set_max_height(max_rows);
- }
- if let Some(max_columns) = position_and_size.max_columns {
- new_plugin.set_max_width(max_columns);
- }
- self.panes.insert(PaneId::Plugin(pid), Box::new(new_plugin));
- // Send an initial mode update to the newly loaded plugin only!
- self.senders
- .send_to_plugin(PluginInstruction::Update(
- Some(pid),
- Event::ModeUpdate(self.mode_info.clone()),
- ))
- .unwrap();
- } else {
- // there are still panes left to fill, use the pids we received in this method
- let pid = new_pids.next().unwrap(); // if this crashes it means we got less pids than there are panes in this layout
- let new_terminal = TerminalPane::new(*pid, *position_and_size);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- );
- self.panes
- .insert(PaneId::Terminal(*pid), Box::new(new_terminal));
- }
- }
- for unused_pid in new_pids {
- // this is a bit of a hack and happens because we don't have any central location that
- // can query the screen as to how many panes it needs to create a layout
- // fixing this will require a bit of an architecture change
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(*unused_pid)))
- .unwrap();
- }
- self.active_terminal = self.panes.iter().map(|(id, _)| id.to_owned()).next();
- self.render();
- }
- pub fn new_pane(&mut self, pid: PaneId) {
- self.close_down_to_max_terminals();
- if self.fullscreen_is_active {
- self.toggle_active_pane_fullscreen();
- }
- if !self.has_panes() {
- if let PaneId::Terminal(term_pid) = pid {
- let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- );
- self.panes.insert(pid, Box::new(new_terminal));
- self.active_terminal = Some(pid);
- }
- } else {
- // TODO: check minimum size of active terminal
-
- let (_largest_terminal_size, terminal_id_to_split) = self.get_panes().fold(
- (0, None),
- |(current_largest_terminal_size, current_terminal_id_to_split),
- id_and_terminal_to_check| {
- let (id_of_terminal_to_check, terminal_to_check) = id_and_terminal_to_check;
- let terminal_size = (terminal_to_check.rows() * CURSOR_HEIGHT_WIDTH_RATIO)
- * terminal_to_check.columns();
- let terminal_can_be_split = terminal_to_check.columns() >= MIN_TERMINAL_WIDTH
- && terminal_to_check.rows() >= MIN_TERMINAL_HEIGHT
- && ((terminal_to_check.columns() > terminal_to_check.min_width() * 2)
- || (terminal_to_check.rows() > terminal_to_check.min_height() * 2));
- if terminal_can_be_split && terminal_size > current_largest_terminal_size {
- (terminal_size, Some(*id_of_terminal_to_check))
- } else {
- (current_largest_terminal_size, current_terminal_id_to_split)
- }
- },
- );
- if terminal_id_to_split.is_none() {
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty
- .unwrap();
- return; // likely no terminal large enough to split
- }
- let terminal_id_to_split = terminal_id_to_split.unwrap();
- let terminal_to_split = self.panes.get_mut(&terminal_id_to_split).unwrap();
- let terminal_ws = PositionAndSize {
- rows: terminal_to_split.rows(),
- columns: terminal_to_split.columns(),
- x: terminal_to_split.x(),
- y: terminal_to_split.y(),
- ..Default::default()
- };
- if terminal_to_split.rows() * CURSOR_HEIGHT_WIDTH_RATIO > terminal_to_split.columns()
- && terminal_to_split.rows() > terminal_to_split.min_height() * 2
- {
- if let PaneId::Terminal(term_pid) = pid {
- let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws);
- let new_terminal = TerminalPane::new(term_pid, bottom_winsize);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- bottom_winsize.columns as u16,
- bottom_winsize.rows as u16,
- );
- terminal_to_split.change_pos_and_size(&top_winsize);
- self.panes.insert(pid, Box::new(new_terminal));
- if let PaneId::Terminal(terminal_id_to_split) = terminal_id_to_split {
- self.os_api.set_terminal_size_using_fd(
- terminal_id_to_split,
- top_winsize.columns as u16,
- top_winsize.rows as u16,
- );
- }
- self.active_terminal = Some(pid);
- }
- } else if terminal_to_split.columns() > terminal_to_split.min_width() * 2 {
- if let PaneId::Terminal(term_pid) = pid {
- let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws);
- let new_terminal = TerminalPane::new(term_pid, right_winsize);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- right_winsize.columns as u16,
- right_winsize.rows as u16,
- );
- terminal_to_split.change_pos_and_size(&left_winsize);
- self.panes.insert(pid, Box::new(new_terminal));
- if let PaneId::Terminal(terminal_id_to_split) = terminal_id_to_split {
- self.os_api.set_terminal_size_using_fd(
- terminal_id_to_split,
- left_winsize.columns as u16,
- left_winsize.rows as u16,
- );
- }
- }
- }
- self.active_terminal = Some(pid);
- self.render();
- }
- }
- pub fn horizontal_split(&mut self, pid: PaneId) {
- self.close_down_to_max_terminals();
- if self.fullscreen_is_active {
- self.toggle_active_pane_fullscreen();
- }
- if !self.has_panes() {
- if let PaneId::Terminal(term_pid) = pid {
- let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- );
- self.panes.insert(pid, Box::new(new_terminal));
- self.active_terminal = Some(pid);
- }
- } else if let PaneId::Terminal(term_pid) = pid {
- // TODO: check minimum size of active terminal
- let active_pane_id = &self.get_active_pane_id().unwrap();
- let active_pane = self.panes.get_mut(active_pane_id).unwrap();
- if active_pane.rows() < MIN_TERMINAL_HEIGHT * 2 + 1 {
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty
- .unwrap();
- return;
- }
- let terminal_ws = PositionAndSize {
- x: active_pane.x(),
- y: active_pane.y(),
- rows: active_pane.rows(),
- columns: active_pane.columns(),
- ..Default::default()
- };
- let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws);
-
- active_pane.change_pos_and_size(&top_winsize);
-
- let new_terminal = TerminalPane::new(term_pid, bottom_winsize);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- bottom_winsize.columns as u16,
- bottom_winsize.rows as u16,
- );
- self.panes.insert(pid, Box::new(new_terminal));
-
- if let PaneId::Terminal(active_terminal_pid) = active_pane_id {
- self.os_api.set_terminal_size_using_fd(
- *active_terminal_pid,
- top_winsize.columns as u16,
- top_winsize.rows as u16,
- );
- }
-
- self.active_terminal = Some(pid);
- self.render();
- }
- }
- pub fn vertical_split(&mut self, pid: PaneId) {
- self.close_down_to_max_terminals();
- if self.fullscreen_is_active {
- self.toggle_active_pane_fullscreen();
- }
- if !self.has_panes() {
- if let PaneId::Terminal(term_pid) = pid {
- let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- );
- self.panes.insert(pid, Box::new(new_terminal));
- self.active_terminal = Some(pid);
- }
- } else if let PaneId::Terminal(term_pid) = pid {
- // TODO: check minimum size of active terminal
- let active_pane_id = &self.get_active_pane_id().unwrap();
- let active_pane = self.panes.get_mut(active_pane_id).unwrap();
- if active_pane.columns() < MIN_TERMINAL_WIDTH * 2 + 1 {
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty
- .unwrap();
- return;
- }
- let terminal_ws = PositionAndSize {
- x: active_pane.x(),
- y: active_pane.y(),
- rows: active_pane.rows(),
- columns: active_pane.columns(),
- ..Default::default()
- };
- let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws);
-
- active_pane.change_pos_and_size(&left_winsize);
-
- let new_terminal = TerminalPane::new(term_pid, right_winsize);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- right_winsize.columns as u16,
- right_winsize.rows as u16,
- );
- self.panes.insert(pid, Box::new(new_terminal));
-
- if let PaneId::Terminal(active_terminal_pid) = active_pane_id {
- self.os_api.set_terminal_size_using_fd(
- *active_terminal_pid,
- left_winsize.columns as u16,
- left_winsize.rows as u16,
- );
- }
-
- self.active_terminal = Some(pid);
- self.render();
- }
- }
- pub fn get_active_pane(&self) -> Option<&dyn Pane> {
- // FIXME: Could use Option::map() here
- match self.get_active_pane_id() {
- Some(active_pane) => self.panes.get(&active_pane).map(Box::as_ref),
- None => None,
- }
- }
- fn get_active_pane_id(&self) -> Option<PaneId> {
- self.active_terminal
- }
- fn get_active_terminal_id(&self) -> Option<RawFd> {
- // FIXME: Is there a better way to do this?
- if let Some(PaneId::Terminal(pid)) = self.active_terminal {
- Some(pid)
- } else {
- None
- }
- }
- pub fn has_terminal_pid(&self, pid: RawFd) -> bool {
- self.panes.contains_key(&PaneId::Terminal(pid))
- }
- pub fn handle_pty_bytes(&mut self, pid: RawFd, bytes: VteBytes) {
- // if we don't have the terminal in self.terminals it's probably because
- // of a race condition where the terminal was created in pty but has not
- // yet been created in Screen. These events are currently not buffered, so
- // if you're debugging seemingly randomly missing stdout data, this is
- // the reason
- if let Some(terminal_output) = self.panes.get_mut(&PaneId::Terminal(pid)) {
- terminal_output.handle_pty_bytes(bytes);
- let messages_to_pty = terminal_output.drain_messages_to_pty();
- for message in messages_to_pty {
- self.write_to_pane_id(message, PaneId::Terminal(pid));
- }
- }
- }
- pub fn write_to_terminals_on_current_tab(&mut self, input_bytes: Vec<u8>) {
- let pane_ids = self.get_pane_ids();
- pane_ids.iter().for_each(|&pane_id| {
- self.write_to_pane_id(input_bytes.clone(), pane_id);
- });
- }
- pub fn write_to_active_terminal(&mut self, input_bytes: Vec<u8>) {
- self.write_to_pane_id(input_bytes, self.get_active_pane_id().unwrap());
- }
- pub fn write_to_pane_id(&mut self, input_bytes: Vec<u8>, pane_id: PaneId) {
- match pane_id {
- PaneId::Terminal(active_terminal_id) => {
- let active_terminal = self.panes.get(&pane_id).unwrap();
- let mut adjusted_input = active_terminal.adjust_input_to_terminal(input_bytes);
- self.os_api
- .write_to_tty_stdin(active_terminal_id, &mut adjusted_input)
- .expect("failed to write to terminal");
- self.os_api
- .tcdrain(active_terminal_id)
- .expect("failed to drain terminal");
- }
- PaneId::Plugin(pid) => {
- for key in parse_keys(&input_bytes) {
- self.senders
- .send_to_plugin(PluginInstruction::Update(Some(pid), Event::KeyPress(key)))
- .unwrap()
- }
- }
- }
- }
- pub fn get_active_terminal_cursor_position(&self) -> Option<(usize, usize)> {
- // (x, y)
- let active_terminal = &self.get_active_pane()?;
- active_terminal
- .cursor_coordinates()
- .map(|(x_in_terminal, y_in_terminal)| {
- let x = active_terminal.x() + x_in_terminal;
- let y = active_terminal.y() + y_in_terminal;
- (x, y)
- })
- }
- pub fn toggle_active_pane_fullscreen(&mut self) {
- if let Some(active_pane_id) = self.get_active_pane_id() {
- if self.fullscreen_is_active {
- for terminal_id in self.panes_to_hide.iter() {
- self.panes
- .get_mut(terminal_id)
- .unwrap()
- .set_should_render(true);
- }
- self.panes_to_hide.clear();
- let active_terminal = self.panes.get_mut(&active_pane_id).unwrap();
- active_terminal.reset_size_and_position_override();
- } else {
- let panes = self.get_panes();
- let pane_ids_to_hide =
- panes.filter_map(
- |(&id, _)| {
- if id != active_pane_id {
- Some(id)
- } else {
- None
- }
- },
- );
- self.panes_to_hide = pane_ids_to_hide.collect();
- if self.panes_to_hide.is_empty() {
- // nothing to do, pane is already as fullscreen as it can be, let's bail
- return;
- } else {
- let active_terminal = self.panes.get_mut(&active_pane_id).unwrap();
- active_terminal.override_size_and_position(
- self.full_screen_ws.x,
- self.full_screen_ws.y,
- &self.full_screen_ws,
- );
- }
- }
- let active_terminal = self.panes.get(&active_pane_id).unwrap();
- if let PaneId::Terminal(active_pid) = active_pane_id {
- self.os_api.set_terminal_size_using_fd(
- active_pid,
- active_terminal.columns() as u16,
- active_terminal.rows() as u16,
- );
- }
- self.render();
- self.toggle_fullscreen_is_active();
- }
- }
- pub fn toggle_fullscreen_is_active(&mut self) {
- self.fullscreen_is_active = !self.fullscreen_is_active;
- }
- pub fn set_force_render(&mut self) {
- for pane in self.panes.values_mut() {
- pane.set_should_render(true);
- }
- }
- pub fn is_sync_panes_active(&self) -> bool {
- self.synchronize_is_active
- }
- pub fn toggle_sync_panes_is_active(&mut self) {
- self.synchronize_is_active = !self.synchronize_is_active;
- }
- pub fn panes_contain_widechar(&self) -> bool {
- self.panes.iter().any(|(_, p)| p.contains_widechar())
- }
- pub fn render(&mut self) {
- if self.active_terminal.is_none() {
- // we might not have an active terminal if we closed the last pane
- // in that case, we should not render as the app is exiting
- return;
- }
- // if any pane contain widechar, all pane in the same row will messup. We should render them every time
- // FIXME: remove this when we can handle widechars correctly
- if self.panes_contain_widechar() {
- self.set_force_render()
- }
- let mut output = String::new();
- let mut boundaries = Boundaries::new(
- self.full_screen_ws.columns as u16,
- self.full_screen_ws.rows as u16,
- );
- let hide_cursor = "\u{1b}[?25l";
- output.push_str(hide_cursor);
- if self.should_clear_display_before_rendering {
- let clear_display = "\u{1b}[2J";
- output.push_str(clear_display);
- self.should_clear_display_before_rendering = false;
- }
- for (kind, pane) in self.panes.iter_mut() {
- if !self.panes_to_hide.contains(&pane.pid()) {
- match self.active_terminal.unwrap() == pane.pid() {
- true => {
- pane.set_active_at(Instant::now());
- boundaries.add_rect(pane.as_ref(), self.mode_info.mode, Some(self.colors))
- }
- false => boundaries.add_rect(pane.as_ref(), self.mode_info.mode, None),
- }
- if let Some(vte_output) = pane.render() {
- let vte_output = if let PaneId::Terminal(_) = kind {
- vte_output
- } else {
- adjust_to_size(&vte_output, pane.rows(), pane.columns())
- };
- // FIXME: Use Termion for cursor and style clearing?
- output.push_str(&format!(
- "\u{1b}[{};{}H\u{1b}[m{}",
- pane.y() + 1,
- pane.x() + 1,
- vte_output
- ));
- }
- }
- }
-
- // TODO: only render (and calculate) boundaries if there was a resize
- output.push_str(&boundaries.vte_output());
-
- match self.get_active_terminal_cursor_position() {
- Some((cursor_position_x, cursor_position_y)) => {
- let show_cursor = "\u{1b}[?25h";
- let change_cursor_shape = self.get_active_pane().unwrap().cursor_shape_csi();
- let goto_cursor_position = &format!(
- "\u{1b}[{};{}H\u{1b}[m{}",
- cursor_position_y + 1,
- cursor_position_x + 1,
- change_cursor_shape
- ); // goto row/col
- output.push_str(show_cursor);
- output.push_str(goto_cursor_position);
- }
- None => {
- let hide_cursor = "\u{1b}[?25l";
- output.push_str(hide_cursor);
- }
- }
-
- self.senders
- .send_to_server(ServerInstruction::Render(Some(output)))
- .unwrap();
- }
- fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
- self.panes.iter()
- }
- // FIXME: This is some shameful duplication...
- fn get_selectable_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
- self.panes.iter().filter(|(_, p)| p.selectable())
- }
- fn has_panes(&self) -> bool {
- let mut all_terminals = self.get_panes();
- all_terminals.next().is_some()
- }
- fn has_selectable_panes(&self) -> bool {
- let mut all_terminals = self.get_selectable_panes();
- all_terminals.next().is_some()
- }
- fn next_active_pane(&self, panes: Vec<PaneId>) -> Option<PaneId> {
- panes
- .into_iter()
- .rev()
- .find(|pid| self.panes.get(pid).unwrap().selectable())
- }
- fn pane_ids_directly_left_of(&self, id: &PaneId) -> Option<Vec<PaneId>> {
- let mut ids = vec![];
- let terminal_to_check = self.panes.get(id).unwrap();
- if terminal_to_check.x() == 0 {
- return None;
- }
- for (&pid, terminal) in self.get_panes() {
- if terminal.x() + terminal.columns() == terminal_to_check.x() - 1 {
- ids.push(pid);
- }
- }
- if ids.is_empty() {
- None
- } else {
- Some(ids)
- }
- }
- fn pane_ids_directly_right_of(&self, id: &PaneId) -> Option<Vec<PaneId>> {
- let mut ids = vec![];
- let terminal_to_check = self.panes.get(id).unwrap();
- for (&pid, terminal) in self.get_panes() {
- if terminal.x() == terminal_to_check.x() + terminal_to_check.columns() + 1 {
- ids.push(pid);
- }
- }
- if ids.is_empty() {
- None
- } else {
- Some(ids)
- }
- }
- fn pane_ids_directly_below(&self, id: &PaneId) -> Option<Vec<PaneId>> {
- let mut ids = vec![];
- let terminal_to_check = self.panes.get(id).unwrap();
- for (&pid, terminal) in self.get_panes() {
- if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() + 1 {
- ids.push(pid);
- }
- }
- if ids.is_empty() {
- None
- } else {
- Some(ids)
- }
- }
- fn pane_ids_directly_above(&self, id: &PaneId) -> Option<Vec<PaneId>> {
- let mut ids = vec![];
- let terminal_to_check = self.panes.get(id).unwrap();
- for (&pid, terminal) in self.get_panes() {
- if terminal.y() + terminal.rows() + 1 == terminal_to_check.y() {
- ids.push(pid);
- }
- }
- if ids.is_empty() {
- None
- } else {
- Some(ids)
- }
- }
- fn panes_top_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> {
- self.panes
- .keys()
- .map(|t_id| self.panes.get(&t_id).unwrap().as_ref())
- .filter(|terminal| terminal.pid() != pane.pid() && terminal.y() == pane.y())
- .collect()
- }
- fn panes_bottom_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> {
- self.panes
- .keys()
- .map(|t_id| self.panes.get(&t_id).unwrap().as_ref())
- .filter(|terminal| {
- terminal.pid() != pane.pid()
- && terminal.y() + terminal.rows() == pane.y() + pane.rows()
- })
- .collect()
- }
- fn panes_right_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> {
- self.panes
- .keys()
- .map(|t_id| self.panes.get(&t_id).unwrap().as_ref())
- .filter(|terminal| {
- terminal.pid() != pane.pid()
- && terminal.x() + terminal.columns() == pane.x() + pane.columns()
- })
- .collect()
- }
- fn panes_left_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> {
- self.panes
- .keys()
- .map(|t_id| self.panes.get(&t_id).unwrap().as_ref())
- .filter(|terminal| terminal.pid() != pane.pid() && terminal.x() == pane.x())
- .collect()
- }
- fn right_aligned_contiguous_panes_above(
- &self,
- id: &PaneId,
- terminal_borders_to_the_right: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self
- .panes
- .get(id)
- .expect("terminal id does not exist")
- .as_ref();
- let mut right_aligned_terminals = self.panes_right_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- right_aligned_terminals.sort_by_key(|a| Reverse(a.y()));
- for terminal in right_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.y() + terminal.rows() + 1 == terminal_to_check.y() {
- terminals.push(terminal);
- }
- }
- // top-most border aligned with a pane border to the right
- let mut top_resize_border = 0;
- for terminal in &terminals {
- let bottom_terminal_boundary = terminal.y() + terminal.rows();
- if terminal_borders_to_the_right
- .get(&(bottom_terminal_boundary + 1))
- .is_some()
- && top_resize_border < bottom_terminal_boundary
- {
- top_resize_border = bottom_terminal_boundary + 1;
- }
- }
- terminals.retain(|terminal| terminal.y() >= top_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let top_resize_border = if terminals.is_empty() {
- terminal_to_check.y()
- } else {
- top_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (top_resize_border, terminal_ids)
- }
- fn right_aligned_contiguous_panes_below(
- &self,
- id: &PaneId,
- terminal_borders_to_the_right: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self
- .panes
- .get(id)
- .expect("terminal id does not exist")
- .as_ref();
- let mut right_aligned_terminals = self.panes_right_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- right_aligned_terminals.sort_by_key(|a| a.y());
- for terminal in right_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() + 1 {
- terminals.push(terminal);
- }
- }
- // bottom-most border aligned with a pane border to the right
- let mut bottom_resize_border = self.full_screen_ws.rows;
- for terminal in &terminals {
- let top_terminal_boundary = terminal.y();
- if terminal_borders_to_the_right
- .get(&(top_terminal_boundary))
- .is_some()
- && top_terminal_boundary < bottom_resize_border
- {
- bottom_resize_border = top_terminal_boundary;
- }
- }
- terminals.retain(|terminal| terminal.y() + terminal.rows() <= bottom_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let bottom_resize_border = if terminals.is_empty() {
- terminal_to_check.y() + terminal_to_check.rows()
- } else {
- bottom_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (bottom_resize_border, terminal_ids)
- }
- fn left_aligned_contiguous_panes_above(
- &self,
- id: &PaneId,
- terminal_borders_to_the_left: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self
- .panes
- .get(id)
- .expect("terminal id does not exist")
- .as_ref();
- let mut left_aligned_terminals = self.panes_left_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- left_aligned_terminals.sort_by_key(|a| Reverse(a.y()));
- for terminal in left_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.y() + terminal.rows() + 1 == terminal_to_check.y() {
- terminals.push(terminal);
- }
- }
- // top-most border aligned with a pane border to the right
- let mut top_resize_border = 0;
- for terminal in &terminals {
- let bottom_terminal_boundary = terminal.y() + terminal.rows();
- if terminal_borders_to_the_left
- .get(&(bottom_terminal_boundary + 1))
- .is_some()
- && top_resize_border < bottom_terminal_boundary
- {
- top_resize_border = bottom_terminal_boundary + 1;
- }
- }
- terminals.retain(|terminal| terminal.y() >= top_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let top_resize_border = if terminals.is_empty() {
- terminal_to_check.y()
- } else {
- top_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (top_resize_border, terminal_ids)
- }
- fn left_aligned_contiguous_panes_below(
- &self,
- id: &PaneId,
- terminal_borders_to_the_left: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self
- .panes
- .get(id)
- .expect("terminal id does not exist")
- .as_ref();
- let mut left_aligned_terminals = self.panes_left_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- left_aligned_terminals.sort_by_key(|a| a.y());
- for terminal in left_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() + 1 {
- terminals.push(terminal);
- }
- }
- // bottom-most border aligned with a pane border to the left
- let mut bottom_resize_border = self.full_screen_ws.rows;
- for terminal in &terminals {
- let top_terminal_boundary = terminal.y();
- if terminal_borders_to_the_left
- .get(&(top_terminal_boundary))
- .is_some()
- && top_terminal_boundary < bottom_resize_border
- {
- bottom_resize_border = top_terminal_boundary;
- }
- }
- terminals.retain(|terminal| {
- // terminal.y() + terminal.rows() < bottom_resize_border
- terminal.y() + terminal.rows() <= bottom_resize_border
- });
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let bottom_resize_border = if terminals.is_empty() {
- terminal_to_check.y() + terminal_to_check.rows()
- } else {
- bottom_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (bottom_resize_border, terminal_ids)
- }
- fn top_aligned_contiguous_panes_to_the_left(
- &self,
- id: &PaneId,
- terminal_borders_above: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self
- .panes
- .get(id)
- .expect("terminal id does not exist")
- .as_ref();
- let mut top_aligned_terminals = self.panes_top_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- top_aligned_terminals.sort_by_key(|a| Reverse(a.x()));
- for terminal in top_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.x() + terminal.columns() + 1 == terminal_to_check.x() {
- terminals.push(terminal);
- }
- }
- // leftmost border aligned with a pane border above
- let mut left_resize_border = 0;
- for terminal in &terminals {
- let right_terminal_boundary = terminal.x() + terminal.columns();
- if terminal_borders_above
- .get(&(right_terminal_boundary + 1))
- .is_some()
- && left_resize_border < right_terminal_boundary
- {
- left_resize_border = right_terminal_boundary + 1;
- }
- }
- terminals.retain(|terminal| terminal.x() >= left_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let left_resize_border = if terminals.is_empty() {
- terminal_to_check.x()
- } else {
- left_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (left_resize_border, terminal_ids)
- }
- fn top_aligned_contiguous_panes_to_the_right(
- &self,
- id: &PaneId,
- terminal_borders_above: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self.panes.get(id).unwrap().as_ref();
- let mut top_aligned_terminals = self.panes_top_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- top_aligned_terminals.sort_by_key(|a| a.x());
- for terminal in top_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.x() == terminal_to_check.x() + terminal_to_check.columns() + 1 {
- terminals.push(terminal);
- }
- }
- // rightmost border aligned with a pane border above
- let mut right_resize_border = self.full_screen_ws.columns;
- for terminal in &terminals {
- let left_terminal_boundary = terminal.x();
- if terminal_borders_above
- .get(&left_terminal_boundary)
- .is_some()
- && right_resize_border > left_terminal_boundary
- {
- right_resize_border = left_terminal_boundary;
- }
- }
- terminals.retain(|terminal| terminal.x() + terminal.columns() <= right_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let right_resize_border = if terminals.is_empty() {
- terminal_to_check.x() + terminal_to_check.columns()
- } else {
- right_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (right_resize_border, terminal_ids)
- }
- fn bottom_aligned_contiguous_panes_to_the_left(
- &self,
- id: &PaneId,
- terminal_borders_below: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self.panes.get(id).unwrap().as_ref();
- let mut bottom_aligned_terminals = self.panes_bottom_aligned_with_pane(terminal_to_check);
- bottom_aligned_terminals.sort_by_key(|a| Reverse(a.x()));
- // terminals that are next to each other up to current
- for terminal in bottom_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.x() + terminal.columns() + 1 == terminal_to_check.x() {
- terminals.push(terminal);
- }
- }
- // leftmost border aligned with a pane border above
- let mut left_resize_border = 0;
- for terminal in &terminals {
- let right_terminal_boundary = terminal.x() + terminal.columns();
- if terminal_borders_below
- .get(&(right_terminal_boundary + 1))
- .is_some()
- && left_resize_border < right_terminal_boundary
- {
- left_resize_border = right_terminal_boundary + 1;
- }
- }
- terminals.retain(|terminal| terminal.x() >= left_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let left_resize_border = if terminals.is_empty() {
- terminal_to_check.x()
- } else {
- left_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (left_resize_border, terminal_ids)
- }
- fn bottom_aligned_contiguous_panes_to_the_right(
- &self,
- id: &PaneId,
- terminal_borders_below: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self.panes.get(id).unwrap().as_ref();
- let mut bottom_aligned_terminals = self.panes_bottom_aligned_with_pane(terminal_to_check);
- bottom_aligned_terminals.sort_by_key(|a| a.x());
- // terminals that are next to each other up to current
- for terminal in bottom_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.x() == terminal_to_check.x() + terminal_to_check.columns() + 1 {
- terminals.push(terminal);
- }
- }
- // leftmost border aligned with a pane border above
- let mut right_resize_border = self.full_screen_ws.columns;
- for terminal in &terminals {
- let left_terminal_boundary = terminal.x();
- if terminal_borders_below
- .get(&left_terminal_boundary)
- .is_some()
- && right_resize_border > left_terminal_boundary
- {
- right_resize_border = left_terminal_boundary;
- }
- }
- terminals.retain(|terminal| terminal.x() + terminal.columns() <= right_resize_border);
- let right_resize_border = if terminals.is_empty() {
- terminal_to_check.x() + terminal_to_check.columns()
- } else {
- right_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (right_resize_border, terminal_ids)
- }
- fn reduce_pane_height_down(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.reduce_height_down(count);
- if let PaneId::Terminal(pid) = id {
- self.os_api.set_terminal_size_using_fd(
- *pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn reduce_pane_height_up(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.reduce_height_up(count);
- if let PaneId::Terminal(pid) = id {
- self.os_api.set_terminal_size_using_fd(
- *pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn increase_pane_height_down(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.increase_height_down(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn increase_pane_height_up(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.increase_height_up(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn increase_pane_width_right(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.increase_width_right(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn increase_pane_width_left(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.increase_width_left(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn reduce_pane_width_right(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.reduce_width_right(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn reduce_pane_width_left(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.reduce_width_left(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn pane_is_between_vertical_borders(
- &self,
- id: &PaneId,
- left_border_x: usize,
- right_border_x: usize,
- ) -> bool {
- let terminal = self
- .panes
- .get(id)
- .expect("could not find terminal to check between borders");
- terminal.x() >= left_border_x && terminal.x() + terminal.columns() <= right_border_x
- }
- fn pane_is_between_horizontal_borders(
- &self,
- id: &PaneId,
- top_border_y: usize,
- bottom_border_y: usize,
- ) -> bool {
- let terminal = self
- .panes
- .get(id)
- .expect("could not find terminal to check between borders");
- terminal.y() >= top_border_y && terminal.y() + terminal.rows() <= bottom_border_y
- }
- fn reduce_pane_and_surroundings_up(&mut self, id: &PaneId, count: usize) {
- let mut terminals_below = self
- .pane_ids_directly_below(&id)
- .expect("can't reduce pane size up if there are no terminals below");
- let terminal_borders_below: HashSet<usize> = terminals_below
- .iter()
- .map(|t| self.panes.get(t).unwrap().x())
- .collect();
- let (left_resize_border, terminals_to_the_left) =
- self.bottom_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_below);
- let (right_resize_border, terminals_to_the_right) =
- self.bottom_aligned_contiguous_panes_to_the_right(&id, &terminal_borders_below);
- terminals_below.retain(|t| {
- self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border)
- });
- self.reduce_pane_height_up(&id, count);
- for terminal_id in terminals_below {
- self.increase_pane_height_up(&terminal_id, count);
- }
- for terminal_id in terminals_to_the_left
- .iter()
- .chain(terminals_to_the_right.iter())
- {
- self.reduce_pane_height_up(&terminal_id, count);
- }
- }
- fn reduce_pane_and_surroundings_down(&mut self, id: &PaneId, count: usize) {
- let mut terminals_above = self
- .pane_ids_directly_above(&id)
- .expect("can't reduce pane size down if there are no terminals above");
- let terminal_borders_above: HashSet<usize> = terminals_above
- .iter()
- .map(|t| self.panes.get(t).unwrap().x())
- .collect();
- let (left_resize_border, terminals_to_the_left) =
- self.top_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_above);
- let (right_resize_border, terminals_to_the_right) =
- self.top_aligned_contiguous_panes_to_the_right(&id, &terminal_borders_above);
- terminals_above.retain(|t| {
- self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border)
- });
- self.reduce_pane_height_down(&id, count);
- for terminal_id in terminals_above {
- self.increase_pane_height_down(&terminal_id, count);
- }
- for terminal_id in terminals_to_the_left
- .iter()
- .chain(terminals_to_the_right.iter())
- {
- self.reduce_pane_height_down(&terminal_id, count);
- }
- }
- fn reduce_pane_and_surroundings_right(&mut self, id: &PaneId, count: usize) {
- let mut terminals_to_the_left = self
- .pane_ids_directly_left_of(&id)
- .expect("can't reduce pane size right if there are no terminals to the left");
- let terminal_borders_to_the_left: HashSet<usize> = terminals_to_the_left
- .iter()
- .map(|t| self.panes.get(t).unwrap().y())
- .collect();
- let (top_resize_border, terminals_above) =
- self.left_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_left);
- let (bottom_resize_border, terminals_below) =
- self.left_aligned_contiguous_panes_below(&id, &terminal_borders_to_the_left);
- terminals_to_the_left.retain(|t| {
- self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border)
- });
- self.reduce_pane_width_right(&id, count);
- for terminal_id in terminals_to_the_left {
- self.increase_pane_width_right(&terminal_id, count);
- }
- for terminal_id in terminals_above.iter().chain(terminals_below.iter()) {
- self.reduce_pane_width_right(&terminal_id, count);
- }
- }
- fn reduce_pane_and_surroundings_left(&mut self, id: &PaneId, count: usize) {
- let mut terminals_to_the_right = self
- .pane_ids_directly_right_of(&id)
- .expect("can't reduce pane size left if there are no terminals to the right");
- let terminal_borders_to_the_right: HashSet<usize> = terminals_to_the_right
- .iter()
- .map(|t| self.panes.get(t).unwrap().y())
- .collect();
- let (top_resize_border, terminals_above) =
- self.right_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_right);
- let (bottom_resize_border, terminals_below) =
- self.right_aligned_contiguous_panes_below(&id, &terminal_borders_to_the_right);
- terminals_to_the_right.retain(|t| {
- self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border)
- });
- self.reduce_pane_width_left(&id, count);
- for terminal_id in terminals_to_the_right {
- self.increase_pane_width_left(&terminal_id, count);
- }
- for terminal_id in terminals_above.iter().chain(terminals_below.iter()) {
- self.reduce_pane_width_left(&terminal_id, count);
- }
- }
- fn increase_pane_and_surroundings_up(&mut self, id: &PaneId, count: usize) {
- let mut terminals_above = self
- .pane_ids_directly_above(&id)
- .expect("can't increase pane size up if there are no terminals above");
- let terminal_borders_above: HashSet<usize> = terminals_above
- .iter()
- .map(|t| self.panes.get(t).unwrap().x())
- .collect();
- let (left_resize_border, terminals_to_the_left) =
- self.top_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_above);
- let (right_resize_border, terminals_to_the_right) =
- self.top_aligned_contiguous_panes_to_the_right(&id, &terminal_borders_above);
- terminals_above.retain(|t| {
- self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border)
- });
- self.increase_pane_height_up(&id, count);
- for terminal_id in terminals_above {
- self.reduce_pane_height_up(&terminal_id, count);
- }
- for terminal_id in terminals_to_the_left
- .iter()
- .chain(terminals_to_the_right.iter())
- {
- self.increase_pane_height_up(&terminal_id, count);
- }
- }
- fn increase_pane_and_surroundings_down(&mut self, id: &PaneId, count: usize) {
- let mut terminals_below = self
- .pane_ids_directly_below(&id)
- .expect("can't increase pane size down if there are no terminals below");
- let terminal_borders_below: HashSet<usize> = terminals_below
- .iter()
- .map(|t| self.panes.get(t).unwrap().x())
- .collect();
- let (left_resize_border, terminals_to_the_left) =
- self.bottom_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_below);
- let (right_resize_border, terminals_to_the_right) =
- self.bottom_aligned_contiguous_panes_to_the_right(&id, &terminal_borders_below);
- terminals_below.retain(|t| {
- self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border)
- });
- self.increase_pane_height_down(&id, count);
- for terminal_id in terminals_below {
- self.reduce_pane_height_down(&terminal_id, count);
- }
- for terminal_id in terminals_to_the_left
- .iter()
- .chain(terminals_to_the_right.iter())
- {
- self.increase_pane_height_down(&terminal_id, count);
- }
- }
- fn increase_pane_and_surroundings_right(&mut self, id: &PaneId, count: usize) {
- let mut terminals_to_the_right = self
- .pane_ids_directly_right_of(&id)
- .expect("can't increase pane size right if there are no terminals to the right");
- let terminal_borders_to_the_right: HashSet<usize> = terminals_to_the_right
- .iter()
- .map(|t| self.panes.get(t).unwrap().y())
- .collect();
- let (top_resize_border, terminals_above) =
- self.right_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_right);
- let (bottom_resize_border, terminals_below) =
- self.right_aligned_contiguous_panes_below(&id, &terminal_borders_to_the_right);
- terminals_to_the_right.retain(|t| {
- self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border)
- });
- self.increase_pane_width_right(&id, count);
- for terminal_id in terminals_to_the_right {
- self.reduce_pane_width_right(&terminal_id, count);
- }
- for terminal_id in terminals_above.iter().chain(terminals_below.iter()) {
- self.increase_pane_width_right(&terminal_id, count);
- }
- }
- fn increase_pane_and_surroundings_left(&mut self, id: &PaneId, count: usize) {
- let mut terminals_to_the_left = self
- .pane_ids_directly_left_of(&id)
- .expect("can't increase pane size right if there are no terminals to the right");
- let terminal_borders_to_the_left: HashSet<usize> = terminals_to_the_left
- .iter()
- .map(|t| self.panes.get(t).unwrap().y())
- .collect();
- let (top_resize_border, terminals_above) =
- self.left_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_left);
- let (bottom_resize_border, terminals_below) =
- self.left_aligned_contiguous_panes_below(&id, &terminal_borders_to_the_left);
- terminals_to_the_left.retain(|t| {
- self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border)
- });
- self.increase_pane_width_left(&id, count);
- for terminal_id in terminals_to_the_left {
- self.reduce_pane_width_left(&terminal_id, count);
- }
- for terminal_id in terminals_above.iter().chain(terminals_below.iter()) {
- self.increase_pane_width_left(&terminal_id, count);
- }
- }
- fn can_increase_pane_and_surroundings_right(
- &self,
- pane_id: &PaneId,
- increase_by: usize,
- ) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let can_increase_pane_size = pane
- .max_width()
- .map(|max_width| pane.columns() + increase_by <= max_width)
- .unwrap_or(true); // no max width, increase to your heart's content
- if !can_increase_pane_size {
- return false;
- }
- let mut new_pos_and_size_for_pane = pane.position_and_size();
- new_pos_and_size_for_pane.columns += increase_by;
-
- if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(&pane_id) {
- return panes_to_the_right.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.columns() > increase_by && p.columns() - increase_by >= p.min_width()
- });
- } else {
- false
- }
- }
- fn can_increase_pane_and_surroundings_left(
- &self,
- pane_id: &PaneId,
- increase_by: usize,
- ) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let can_increase_pane_size = pane
- .max_width()
- .map(|max_width| pane.columns() + increase_by <= max_width)
- .unwrap_or(true); // no max width, increase to your heart's content
- if !can_increase_pane_size {
- return false;
- }
- let mut new_pos_and_size_for_pane = pane.position_and_size();
- new_pos_and_size_for_pane.x = new_pos_and_size_for_pane.x.saturating_sub(increase_by);
-
- if let Some(panes_to_the_left) = self.pane_ids_directly_left_of(&pane_id) {
- return panes_to_the_left.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.columns() > increase_by && p.columns() - increase_by >= p.min_width()
- });
- } else {
- false
- }
- }
- fn can_increase_pane_and_surroundings_down(
- &self,
- pane_id: &PaneId,
- increase_by: usize,
- ) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let can_increase_pane_size = pane
- .max_height()
- .map(|max_height| pane.rows() + increase_by <= max_height)
- .unwrap_or(true); // no max width, increase to your heart's content
- if !can_increase_pane_size {
- return false;
- }
- let mut new_pos_and_size_for_pane = pane.position_and_size();
- new_pos_and_size_for_pane.rows += increase_by;
-
- if let Some(panes_below) = self.pane_ids_directly_below(&pane_id) {
- return panes_below.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.rows() > increase_by && p.rows() - increase_by >= p.min_height()
- });
- } else {
- false
- }
- }
- fn can_increase_pane_and_surroundings_up(&self, pane_id: &PaneId, increase_by: usize) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let can_increase_pane_size = pane
- .max_height()
- .map(|max_height| pane.rows() + increase_by <= max_height)
- .unwrap_or(true); // no max width, increase to your heart's content
- if !can_increase_pane_size {
- return false;
- }
- let mut new_pos_and_size_for_pane = pane.position_and_size();
- new_pos_and_size_for_pane.y = new_pos_and_size_for_pane.y.saturating_sub(increase_by);
-
- if let Some(panes_above) = self.pane_ids_directly_above(&pane_id) {
- return panes_above.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.rows() > increase_by && p.rows() - increase_by >= p.min_height()
- });
- } else {
- false
- }
- }
- fn can_reduce_pane_and_surroundings_right(&self, pane_id: &PaneId, reduce_by: usize) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let pane_columns = pane.columns();
- let can_reduce_pane_size =
- pane_columns > reduce_by && pane_columns - reduce_by >= pane.min_width();
- if !can_reduce_pane_size {
- return false;
- }
- if let Some(panes_to_the_left) = self.pane_ids_directly_left_of(&pane_id) {
- return panes_to_the_left.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.max_width()
- .map(|max_width| p.columns() + reduce_by <= max_width)
- .unwrap_or(true) // no max width, increase to your heart's content
- });
- } else {
- false
- }
- }
- fn can_reduce_pane_and_surroundings_left(&self, pane_id: &PaneId, reduce_by: usize) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let pane_columns = pane.columns();
- let can_reduce_pane_size =
- pane_columns > reduce_by && pane_columns - reduce_by >= pane.min_width();
- if !can_reduce_pane_size {
- return false;
- }
- if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(&pane_id) {
- return panes_to_the_right.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.max_width()
- .map(|max_width| p.columns() + reduce_by <= max_width)
- .unwrap_or(true) // no max width, increase to your heart's content
- });
- } else {
- false
- }
- }
- fn can_reduce_pane_and_surroundings_down(&self, pane_id: &PaneId, reduce_by: usize) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let pane_rows = pane.rows();
- let can_reduce_pane_size =
- pane_rows > reduce_by && pane_rows - reduce_by >= pane.min_height();
- if !can_reduce_pane_size {
- return false;
- }
- if let Some(panes_above) = self.pane_ids_directly_above(&pane_id) {
- return panes_above.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.max_height()
- .map(|max_height| p.rows() + reduce_by <= max_height)
- .unwrap_or(true) // no max height, increase to your heart's content
- });
- } else {
- false
- }
- }
- fn can_reduce_pane_and_surroundings_up(&self, pane_id: &PaneId, reduce_by: usize) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let pane_rows = pane.rows();
- let can_reduce_pane_size =
- pane_rows > reduce_by && pane_rows - reduce_by >= pane.min_height();
- if !can_reduce_pane_size {
- return false;
- }
- if let Some(panes_below) = self.pane_ids_directly_below(&pane_id) {
- return panes_below.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.max_height()
- .map(|max_height| p.rows() + reduce_by <= max_height)
- .unwrap_or(true) // no max height, increase to your heart's content
- });
- } else {
- false
- }
- }
- pub fn resize_whole_tab(&mut self, new_screen_size: PositionAndSize) {
- if self.fullscreen_is_active {
- // this is not ideal, we can improve this
- self.toggle_active_pane_fullscreen();
- }
- if let Some((column_difference, row_difference)) =
- PaneResizer::new(&mut self.panes, &mut self.os_api)
- .resize(self.full_screen_ws, new_screen_size)
- {
- self.should_clear_display_before_rendering = true;
- self.full_screen_ws.columns =
- (self.full_screen_ws.columns as isize + column_difference) as usize;
- self.full_screen_ws.rows =
- (self.full_screen_ws.rows as isize + row_difference) as usize;
- };
- }
- pub fn resize_left(&mut self) {
- // TODO: find out by how much we actually reduced and only reduce by that much
- let count = 10;
- if let Some(active_pane_id) = self.get_active_pane_id() {
- if self.can_increase_pane_and_surroundings_left(&active_pane_id, count) {
- self.increase_pane_and_surroundings_left(&active_pane_id, count);
- } else if self.can_reduce_pane_and_surroundings_left(&active_pane_id, count) {
- self.reduce_pane_and_surroundings_left(&active_pane_id, count);
- }
- }
- self.render();
- }
- pub fn resize_right(&mut self) {
- // TODO: find out by how much we actually reduced and only reduce by that much
- let count = 10;
- if let Some(active_pane_id) = self.get_active_pane_id() {
- if self.can_increase_pane_and_surroundings_right(&active_pane_id, count) {
- self.increase_pane_and_surroundings_right(&active_pane_id, count);
- } else if self.can_reduce_pane_and_surroundings_right(&active_pane_id, count) {
- self.reduce_pane_and_surroundings_right(&active_pane_id, count);
- }
- }
- self.render();
- }
- pub fn resize_down(&mut self) {
- // TODO: find out by how much we actually reduced and only reduce by that much
- let count = 2;
- if let Some(active_pane_id) = self.get_active_pane_id() {
- if self.can_increase_pane_and_surroundings_down(&active_pane_id, count) {
- self.increase_pane_and_surroundings_down(&active_pane_id, count);
- } else if self.can_reduce_pane_and_surroundings_down(&active_pane_id, count) {
- self.reduce_pane_and_surroundings_down(&active_pane_id, count);
- }
- }
- self.render();
- }
- pub fn resize_up(&mut self) {
- // TODO: find out by how much we actually reduced and only reduce by that much
- let count = 2;
- if let Some(active_pane_id) = self.get_active_pane_id() {
- if self.can_increase_pane_and_surroundings_up(&active_pane_id, count) {
- self.increase_pane_and_surroundings_up(&active_pane_id, count);
- } else if self.can_reduce_pane_and_surroundings_up(&active_pane_id, count) {
- self.reduce_pane_and_surroundings_up(&active_pane_id, count);
- }
- }
- self.render();
- }
- pub fn move_focus(&mut self) {
- if !self.has_selectable_panes() {
- return;
- }
- if self.fullscreen_is_active {
- return;
- }
- let active_terminal_id = self.get_active_pane_id().unwrap();
- let terminal_ids: Vec<PaneId> = self.get_selectable_panes().map(|(&pid, _)| pid).collect(); // TODO: better, no allocations
- let first_terminal = terminal_ids.get(0).unwrap();
- let active_terminal_id_position = terminal_ids
- .iter()
- .position(|id| id == &active_terminal_id)
- .unwrap();
- if let Some(next_terminal) = terminal_ids.get(active_terminal_id_position + 1) {
- self.active_terminal = Some(*next_terminal);
- } else {
- self.active_terminal = Some(*first_terminal);
- }
- self.render();
- }
- pub fn focus_next_pane(&mut self) {
- if !self.has_selectable_panes() {
- return;
- }
- if self.fullscreen_is_active {
- return;
- }
- let active_pane_id = self.get_active_pane_id().unwrap();
- let mut panes: Vec<(&PaneId, &Box<dyn Pane>)> = self.get_selectable_panes().collect();
- panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| {
- if a_pane.y() == b_pane.y() {
- a_pane.x().cmp(&b_pane.x())
- } else {
- a_pane.y().cmp(&b_pane.y())
- }
- });
- let first_pane = panes.get(0).unwrap();
- let active_pane_position = panes
- .iter()
- .position(|(id, _)| *id == &active_pane_id) // TODO: better
- .unwrap();
- if let Some(next_pane) = panes.get(active_pane_position + 1) {
- self.active_terminal = Some(*next_pane.0);
- } else {
- self.active_terminal = Some(*first_pane.0);
- }
- self.render();
- }
- pub fn focus_previous_pane(&mut self) {
- if !self.has_selectable_panes() {
- return;
- }
- if self.fullscreen_is_active {
- return;
- }
- let active_pane_id = self.get_active_pane_id().unwrap();
- let mut panes: Vec<(&PaneId, &Box<dyn Pane>)> = self.get_selectable_panes().collect();
- panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| {
- if a_pane.y() == b_pane.y() {
- a_pane.x().cmp(&b_pane.x())
- } else {
- a_pane.y().cmp(&b_pane.y())
- }
- });
- let last_pane = panes.last().unwrap();
- let active_pane_position = panes
- .iter()
- .position(|(id, _)| *id == &active_pane_id) // TODO: better
- .unwrap();
- if active_pane_position == 0 {
- self.active_terminal = Some(*last_pane.0);
- } else {
- self.active_terminal = Some(*panes.get(active_pane_position - 1).unwrap().0);
- }
- self.render();
- }
- // returns a boolean that indicates whether the focus moved
- pub fn move_focus_left(&mut self) -> bool {
- if !self.has_selectable_panes() {
- return false;
- }
- if self.fullscreen_is_active {
- return false;
- }
- let active_terminal = self.get_active_pane();
- if let Some(active) = active_terminal {
- let terminals = self.get_selectable_panes();
- let next_index = terminals
- .enumerate()
- .filter(|(_, (_, c))| {
- c.is_directly_left_of(active) && c.horizontally_overlaps_with(active)
- })
- .max_by_key(|(_, (_, c))| c.active_at())
- .map(|(_, (pid, _))| pid);
- match next_index {
- Some(&p) => {
- self.active_terminal = Some(p);
- self.render();
- return true;
- }
- None => {
- self.active_terminal = Some(active.pid());
- }
- }
- } else {
- self.active_terminal = Some(active_terminal.unwrap().pid());
- }
- false
- }
- pub fn move_focus_down(&mut self) {
- if !self.has_selectable_panes() {
- return;
- }
- if self.fullscreen_is_active {
- return;
- }
- let active_terminal = self.get_active_pane();
- if let Some(active) = active_terminal {
- let terminals = self.get_selectable_panes();
- let next_index = terminals
- .enumerate()
- .filter(|(_, (_, c))| {
- c.is_directly_below(active) && c.vertically_overlaps_with(active)
- })
- .max_by_key(|(_, (_, c))| c.active_at())
- .map(|(_, (pid, _))| pid);
- match next_index {
- Some(&p) => {
- self.active_terminal = Some(p);
- }
- None => {
- self.active_terminal = Some(active.pid());
- }
- }
- } else {
- self.active_terminal = Some(active_terminal.unwrap().pid());
- }
- self.render();
- }
- pub fn move_focus_up(&mut self) {
- if !self.has_selectable_panes() {
- return;
- }
- if self.fullscreen_is_active {
- return;
- }
- let active_terminal = self.get_active_pane();
- if let Some(active) = active_terminal {
- let terminals = self.get_selectable_panes();
- let next_index = terminals
- .enumerate()
- .filter(|(_, (_, c))| {
- c.is_directly_above(active) && c.vertically_overlaps_with(active)
- })
- .max_by_key(|(_, (_, c))| c.active_at())
- .map(|(_, (pid, _))| pid);
- match next_index {
- Some(&p) => {
- self.active_terminal = Some(p);
- }
- None => {
- self.active_terminal = Some(active.pid());
- }
- }
- } else {
- self.active_terminal = Some(active_terminal.unwrap().pid());
- }
- self.render();
- }
- // returns a boolean that indicates whether the focus moved
- pub fn move_focus_right(&mut self) -> bool {
- if !self.has_selectable_panes() {
- return false;
- }
- if self.fullscreen_is_active {
- return false;
- }
- let active_terminal = self.get_active_pane();
- if let Some(active) = active_terminal {
- let terminals = self.get_selectable_panes();
- let next_index = terminals
- .enumerate()
- .filter(|(_, (_, c))| {
- c.is_directly_right_of(active) && c.horizontally_overlaps_with(active)
- })
- .max_by_key(|(_, (_, c))| c.active_at())
- .map(|(_, (pid, _))| pid);
- match next_index {
- Some(&p) => {
- self.active_terminal = Some(p);
- self.render();
- return true;
- }
- None => {
- self.active_terminal = Some(active.pid());
- }
- }
- } else {
- self.active_terminal = Some(active_terminal.unwrap().pid());
- }
- false
- }
- fn horizontal_borders(&self, terminals: &[PaneId]) -> HashSet<usize> {
- terminals.iter().fold(HashSet::new(), |mut borders, t| {
- let terminal = self.panes.get(t).unwrap();
- borders.insert(terminal.y());
- borders.insert(terminal.y() + terminal.rows() + 1); // 1 for the border width
- borders
- })
- }
- fn vertical_borders(&self, terminals: &[PaneId]) -> HashSet<usize> {
- terminals.iter().fold(HashSet::new(), |mut borders, t| {
- let terminal = self.panes.get(t).unwrap();
- borders.insert(terminal.x());
- borders.insert(terminal.x() + terminal.columns() + 1); // 1 for the border width
- borders
- })
- }
- fn panes_to_the_left_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
- if let Some(terminal) = self.panes.get(&id) {
- let upper_close_border = terminal.y();
- let lower_close_border = terminal.y() + terminal.rows() + 1;
-
- if let Some(mut terminals_to_the_left) = self.pane_ids_directly_left_of(&id) {
- let terminal_borders_to_the_left = self.horizontal_borders(&terminals_to_the_left);
- if terminal_borders_to_the_left.contains(&upper_close_border)
- && terminal_borders_to_the_left.contains(&lower_close_border)
- {
- terminals_to_the_left.retain(|t| {
- self.pane_is_between_horizontal_borders(
- t,
- upper_close_border,
- lower_close_border,
- )
- });
- return Some(terminals_to_the_left);
- }
- }
- }
- None
- }
- fn panes_to_the_right_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
- if let Some(terminal) = self.panes.get(&id) {
- let upper_close_border = terminal.y();
- let lower_close_border = terminal.y() + terminal.rows() + 1;
-
- if let Some(mut terminals_to_the_right) = self.pane_ids_directly_right_of(&id) {
- let terminal_borders_to_the_right =
- self.horizontal_borders(&terminals_to_the_right);
- if terminal_borders_to_the_right.contains(&upper_close_border)
- && terminal_borders_to_the_right.contains(&lower_close_border)
- {
- terminals_to_the_right.retain(|t| {
- self.pane_is_between_horizontal_borders(
- t,
- upper_close_border,
- lower_close_border,
- )
- });
- return Some(terminals_to_the_right);
- }
- }
- }
- None
- }
- fn panes_above_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
- if let Some(terminal) = self.panes.get(&id) {
- let left_close_border = terminal.x();
- let right_close_border = terminal.x() + terminal.columns() + 1;
-
- if let Some(mut terminals_above) = self.pane_ids_directly_above(&id) {
- let terminal_borders_above = self.vertical_borders(&terminals_above);
- if terminal_borders_above.contains(&left_close_border)
- && terminal_borders_above.contains(&right_close_border)
- {
- terminals_above.retain(|t| {
- self.pane_is_between_vertical_borders(
- t,
- left_close_border,
- right_close_border,
- )
- });
- return Some(terminals_above);
- }
- }
- }
- None
- }
- fn panes_below_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
- if let Some(terminal) = self.panes.get(&id) {
- let left_close_border = terminal.x();
- let right_close_border = terminal.x() + terminal.columns() + 1;
-
- if let Some(mut terminals_below) = self.pane_ids_directly_below(&id) {
- let terminal_borders_below = self.vertical_borders(&terminals_below);
- if terminal_borders_below.contains(&left_close_border)
- && terminal_borders_below.contains(&right_close_border)
- {
- terminals_below.retain(|t| {
- self.pane_is_between_vertical_borders(
- t,
- left_close_border,
- right_close_border,
- )
- });
- return Some(terminals_below);
- }
- }
- }
- None
- }
- fn close_down_to_max_terminals(&mut self) {
- if let Some(max_panes) = self.max_panes {
- let terminals = self.get_pane_ids();
- for &pid in terminals.iter().skip(max_panes - 1) {
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(pid))
- .unwrap();
- self.close_pane_without_rerender(pid);
- }
- }
- }
- pub fn get_pane_ids(&self) -> Vec<PaneId> {
- self.get_panes().map(|(&pid, _)| pid).collect()
- }
- pub fn set_pane_selectable(&mut self, id: PaneId, selectable: bool) {
- if let Some(pane) = self.panes.get_mut(&id) {
- pane.set_selectable(selectable);
- if self.get_active_pane_id() == Some(id) && !selectable {
- self.active_terminal = self.next_active_pane(self.get_pane_ids())
- }
- }
- }
- pub fn set_pane_invisible_borders(&mut self, id: PaneId, invisible_borders: bool) {
- if let Some(pane) = self.panes.get_mut(&id) {
- pane.set_invisible_borders(invisible_borders);
- }
- }
- pub fn set_pane_max_height(&mut self, id: PaneId, max_height: usize) {
- if let Some(pane) = self.panes.get_mut(&id) {
- pane.set_max_height(max_height);
- }
- }
- pub fn close_pane(&mut self, id: PaneId) {
- if self.panes.get(&id).is_some() {
- self.close_pane_without_rerender(id);
- }
- }
- pub fn close_pane_without_rerender(&mut self, id: PaneId) {
- if self.fullscreen_is_active {
- self.toggle_active_pane_fullscreen();
- }
- if let Some(pane_to_close) = self.panes.get(&id) {
- let pane_to_close_width = pane_to_close.columns();
- let pane_to_close_height = pane_to_close.rows();
- if let Some(panes) = self.panes_to_the_left_between_aligning_borders(id) {
- if panes.iter().all(|p| {
- let pane = self.panes.get(p).unwrap();
- pane.can_increase_width_by(pane_to_close_width + 1)
- }) {
- for pane_id in panes.iter() {
- self.increase_pane_width_right(&pane_id, pane_to_close_width + 1);
- // 1 for the border
- }
- self.panes.remove(&id);
- if self.active_terminal == Some(id) {
- self.active_terminal = self.next_active_pane(panes);
- }
- return;
- }
- }
- if let Some(panes) = self.panes_to_the_right_between_aligning_borders(id) {
- if panes.iter().all(|p| {
- let pane = self.panes.get(p).unwrap();
- pane.can_increase_width_by(pane_to_close_width + 1)
- }) {
- for pane_id in panes.iter() {
- self.increase_pane_width_left(&pane_id, pane_to_close_width + 1);
- // 1 for the border
- }
- self.panes.remove(&id);
- if self.active_terminal == Some(id) {
- self.active_terminal = self.next_active_pane(panes);
- }
- return;
- }
- }
- if let Some(panes) = self.panes_above_between_aligning_borders(id) {
- if panes.iter().all(|p| {
- let pane = self.panes.get(p).unwrap();
- pane.can_increase_height_by(pane_to_close_height + 1)
- }) {
- for pane_id in panes.iter() {
- self.increase_pane_height_down(&pane_id, pane_to_close_height + 1);
- // 1 for the border
- }
- self.panes.remove(&id);
- if self.active_terminal == Some(id) {
- self.active_terminal = self.next_active_pane(panes);
- }
- return;
- }
- }
- if let Some(panes) = self.panes_below_between_aligning_borders(id) {
- if panes.iter().all(|p| {
- let pane = self.panes.get(p).unwrap();
- pane.can_increase_height_by(pane_to_close_height + 1)
- }) {
- for pane_id in panes.iter() {
- self.increase_pane_height_up(&pane_id, pane_to_close_height + 1);
- // 1 for the border
- }
- self.panes.remove(&id);
- if self.active_terminal == Some(id) {
- self.active_terminal = self.next_active_pane(panes);
- }
- return;
- }
- }
- // if we reached here, this is either the last pane or there's some sort of
- // configuration error (eg. we're trying to close a pane surrounded by fixed panes)
- self.panes.remove(&id);
- }
- }
- pub fn close_focused_pane(&mut self) {
- if let Some(active_pane_id) = self.get_active_pane_id() {
- self.close_pane(active_pane_id);
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(active_pane_id))
- .unwrap();
- }
- }
- pub fn scroll_active_terminal_up(&mut self) {
- if let Some(active_terminal_id) = self.get_active_terminal_id() {
- let active_terminal = self
- .panes
- .get_mut(&PaneId::Terminal(active_terminal_id))
- .unwrap();
- active_terminal.scroll_up(1);
- self.render();
- }
- }
- pub fn scroll_active_terminal_down(&mut self) {
- if let Some(active_terminal_id) = self.get_active_terminal_id() {
- let active_terminal = self
- .panes
- .get_mut(&PaneId::Terminal(active_terminal_id))
- .unwrap();
- active_terminal.scroll_down(1);
- self.render();
- }
- }
- pub fn scroll_active_terminal_up_page(&mut self) {
- if let Some(active_terminal_id) = self.get_active_terminal_id() {
- let active_terminal = self
- .panes
- .get_mut(&PaneId::Terminal(active_terminal_id))
- .unwrap();
- // prevent overflow when row == 0
- let scroll_columns = active_terminal.rows().max(1) - 1;
- active_terminal.scroll_up(scroll_columns);
- self.render();
- }
- }
- pub fn scroll_active_terminal_down_page(&mut self) {
- if let Some(active_terminal_id) = self.get_active_terminal_id() {
- let active_terminal = self
- .panes
- .get_mut(&PaneId::Terminal(active_terminal_id))
- .unwrap();
- // prevent overflow when row == 0
- let scroll_columns = active_terminal.rows().max(1) - 1;
- active_terminal.scroll_down(scroll_columns);
- self.render();
- }
- }
- pub fn clear_active_terminal_scroll(&mut self) {
- if let Some(active_terminal_id) = self.get_active_terminal_id() {
- let active_terminal = self
- .panes
- .get_mut(&PaneId::Terminal(active_terminal_id))
- .unwrap();
- active_terminal.clear_scroll();
- }
- }
-}