diff options
| author | Kunal Mohan <kunalmohan99@gmail.com> | 2021-05-16 16:42:50 +0530 |
|---|---|---|
| committer | Kunal Mohan <kunalmohan99@gmail.com> | 2021-05-16 21:41:56 +0530 |
| commit | 2038947a14d32d89da2bdbcaa1381c3015ac9c7a (patch) | |
| tree | 5a332470e32b380816098594a89d3c3db1f6eb53 /src/client | |
| parent | handle error on termios initialization (#511) (diff) | |
| download | sl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.tar.gz sl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.tar.xz sl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.zip | |
Big refactor: separate crates for client, server and utilities
Diffstat (limited to 'src/client')
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(¤t_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(¤t_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. "[D") - 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(|¶m| 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(|¶m| 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(); - } - } -} |
