aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authora-kenji <aks.kenji@protonmail.com>2021-05-18 10:05:15 +0200
committera-kenji <aks.kenji@protonmail.com>2021-05-18 10:05:15 +0200
commitbcbde9fbb5bffe4e1334acc1270314517938cac5 (patch)
tree8a18820a32392fb4a8a132a27740299ac4849f84 /src
parentFix Clippy Suggestions (diff)
parentMerge pull request #510 from zellij-org/speedup-ci (diff)
downloadsl0ppij-bcbde9fbb5bffe4e1334acc1270314517938cac5.tar.gz
sl0ppij-bcbde9fbb5bffe4e1334acc1270314517938cac5.tar.xz
sl0ppij-bcbde9fbb5bffe4e1334acc1270314517938cac5.zip
Merge branch 'main' of https://github.com/zellij-org/zellij into layout-path-506
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs55
-rw-r--r--src/client/boundaries.rs630
-rw-r--r--src/client/layout.rs230
-rw-r--r--src/client/mod.rs244
-rw-r--r--src/client/pane_resizer.rs532
-rw-r--r--src/client/panes/grid.rs1558
-rw-r--r--src/client/panes/mod.rs9
-rw-r--r--src/client/panes/plugin_pane.rs227
-rw-r--r--src/client/panes/terminal_character.rs775
-rw-r--r--src/client/panes/terminal_pane.rs362
-rw-r--r--src/client/panes/unit/grid_tests.rs397
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_b.snap8
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_i.snap8
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_z.snap8
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__terminal_reports.snap6
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_0.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_1.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_10.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_11.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_12.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_13.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_14.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_2.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_3.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_4.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_5.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_6.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_7.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_8.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_9.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest3_0.snap47
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_0.snap57
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_1.snap57
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_2.snap57
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_3.snap57
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_4.snap57
-rw-r--r--src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_5.snap57
-rw-r--r--src/client/tab.rs2274
-rw-r--r--src/common/command_is_executing.rs33
-rw-r--r--src/common/errors.rs393
-rw-r--r--src/common/input/actions.rs64
-rw-r--r--src/common/input/config.rs218
-rw-r--r--src/common/input/handler.rs268
-rw-r--r--src/common/input/keybinds.rs301
-rw-r--r--src/common/input/mod.rs7
-rw-r--r--src/common/input/options.rs45
-rw-r--r--src/common/input/unit/keybinds_test.rs801
-rw-r--r--src/common/ipc.rs131
-rw-r--r--src/common/mod.rs14
-rw-r--r--src/common/os_input_output.rs414
-rw-r--r--src/common/pty.rs293
-rw-r--r--src/common/screen.rs598
-rw-r--r--src/common/setup.rs240
-rw-r--r--src/common/thread_bus.rs142
-rw-r--r--src/common/utils/consts.rs54
-rw-r--r--src/common/utils/logging.rs85
-rw-r--r--src/common/utils/mod.rs5
-rw-r--r--src/common/utils/shared.rs108
-rw-r--r--src/common/wasm_vm.rs267
-rw-r--r--src/main.rs41
-rw-r--r--src/server/mod.rs291
-rw-r--r--src/server/route.rs215
-rw-r--r--src/tests/fakes.rs23
-rw-r--r--src/tests/integration/basic.rs4
-rw-r--r--src/tests/integration/close_pane.rs4
-rw-r--r--src/tests/integration/compatibility.rs4
-rw-r--r--src/tests/integration/layouts.rs4
-rw-r--r--src/tests/integration/move_focus_down.rs4
-rw-r--r--src/tests/integration/move_focus_left.rs4
-rw-r--r--src/tests/integration/move_focus_right.rs4
-rw-r--r--src/tests/integration/move_focus_up.rs4
-rw-r--r--src/tests/integration/resize_down.rs4
-rw-r--r--src/tests/integration/resize_left.rs4
-rw-r--r--src/tests/integration/resize_right.rs4
-rw-r--r--src/tests/integration/resize_up.rs4
-rw-r--r--src/tests/integration/tabs.rs5
-rw-r--r--src/tests/integration/terminal_window_resize.rs4
-rw-r--r--src/tests/integration/toggle_fullscreen.rs4
-rw-r--r--src/tests/mod.rs8
-rw-r--r--src/tests/utils.rs7
86 files changed, 74 insertions, 13752 deletions
diff --git a/src/cli.rs b/src/cli.rs
deleted file mode 100644
index b8a61344..00000000
--- a/src/cli.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-use super::common::utils::consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV};
-use crate::common::input::options::Options;
-use crate::common::setup::Setup;
-use serde::{Deserialize, Serialize};
-use std::path::PathBuf;
-use structopt::StructOpt;
-
-#[derive(StructOpt, Default, Debug, Clone, Serialize, Deserialize)]
-#[structopt(name = "zellij")]
-pub struct CliArgs {
- /// Maximum panes on screen, caution: opening more panes will close old ones
- #[structopt(long)]
- pub max_panes: Option<usize>,
-
- /// Change where zellij looks for layouts and plugins
- #[structopt(long, parse(from_os_str))]
- pub data_dir: Option<PathBuf>,
-
- /// Run server listening at the specified socket path
- #[structopt(long, parse(from_os_str))]
- pub server: Option<PathBuf>,
-
- /// Path to a layout yaml file
- #[structopt(short, long, parse(from_os_str))]
- pub layout: Option<PathBuf>,
-
- /// Path to a layout yaml file
- #[structopt(long, parse(from_os_str))]
- pub layout_path: Option<PathBuf>,
-
- /// Change where zellij looks for the configuration
- #[structopt(short, long, env=ZELLIJ_CONFIG_FILE_ENV, parse(from_os_str))]
- pub config: Option<PathBuf>,
-
- /// Change where zellij looks for the configuration
- #[structopt(long, env=ZELLIJ_CONFIG_DIR_ENV, parse(from_os_str))]
- pub config_dir: Option<PathBuf>,
-
- #[structopt(subcommand)]
- pub option: Option<ConfigCli>,
-
- #[structopt(short, long)]
- pub debug: bool,
-}
-
-#[derive(Debug, StructOpt, Clone, Serialize, Deserialize)]
-pub enum ConfigCli {
- /// Change the behaviour of zellij
- #[structopt(name = "options")]
- Options(Options),
-
- /// Setup zellij and check its configuration
- #[structopt(name = "setup")]
- Setup(Setup),
-}
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 592481dc..00000000
--- a/src/client/layout.rs
+++ /dev/null
@@ -1,230 +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) -> Self {
- let mut layout_file = File::open(&layout_path)
- .or_else(|_| File::open(&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_dir(layout: &PathBuf, data_dir: &Path) -> Self {
- Self::new(&data_dir.join("layouts/").join(layout))
- }
-
- 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 f148a50a..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,
- Box::new(opts),
- Box::new(config_options),
- ));
- os_input.set_raw_mode(0);
- let _ = os_input
- .get_stdout_writer()
- .write(bracketed_paste.as_bytes())
- .unwrap();
-
- let (send_client_instructions, receive_client_instructions): SyncChannelWithContext<
- ClientInstruction,
- > = mpsc::sync_channel(50);
- let send_client_instructions =
- SenderWithContext::new(SenderType::SyncSender(send_client_instructions));
-
- #[cfg(not(test))]
- std::panic::set_hook({
- use crate::errors::handle_panic;
- let send_client_instructions = send_client_instructions.clone();
- Box::new(move |info| {
- handle_panic(info, &send_client_instructions);
- })
- });
-
- let _stdin_thread = thread::Builder::new()
- .name("stdin_handler".to_string())
- .spawn({
- let send_client_instructions = send_client_instructions.clone();
- let command_is_executing = command_is_executing.clone();
- let os_input = os_input.clone();
- move || {
- input_loop(
- os_input,
- config,
- command_is_executing,
- send_client_instructions,
- )
- }
- });
-
- let _signal_thread = thread::Builder::new()
- .name("signal_listener".to_string())
- .spawn({
- let os_input = os_input.clone();
- let send_client_instructions = send_client_instructions.clone();
- move || {
- os_input.handle_signals(
- Box::new({
- let os_api = os_input.clone();
- move || {
- os_api.send_to_server(ClientToServerMsg::TerminalResize(
- os_api.get_terminal_size_using_fd(0),
- ));
- }
- }),
- Box::new({
- let send_client_instructions = send_client_instructions.clone();
- move || {
- send_client_instructions
- .send(ClientInstruction::Exit)
- .unwrap()
- }
- }),
- );
- }
- })
- .unwrap();
-
- let router_thread = thread::Builder::new()
- .name("router".to_string())
- .spawn({
- let os_input = os_input.clone();
- let mut should_break = false;
- move || loop {
- let (instruction, err_ctx) = os_input.recv_from_server();
- err_ctx.update_thread_ctx();
- match instruction {
- ServerToClientMsg::Exit | ServerToClientMsg::ServerError(_) => {
- should_break = true;
- }
- _ => {}
- }
- send_client_instructions.send(instruction.into()).unwrap();
- if should_break {
- break;
- }
- }
- })
- .unwrap();
-
- let handle_error = |backtrace: String| {
- os_input.unset_raw_mode(0);
- let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
- let restore_snapshot = "\u{1b}[?1049l";
- let error = format!(
- "{}\n{}{}",
- goto_start_of_last_line, restore_snapshot, backtrace
- );
- let _ = os_input
- .get_stdout_writer()
- .write(error.as_bytes())
- .unwrap();
- std::process::exit(1);
- };
-
- loop {
- let (client_instruction, mut err_ctx) = receive_client_instructions
- .recv()
- .expect("failed to receive app instruction on channel");
-
- err_ctx.add_call(ContextType::Client((&client_instruction).into()));
- match client_instruction {
- ClientInstruction::Exit => break,
- ClientInstruction::Error(backtrace) => {
- let _ = os_input.send_to_server(ClientToServerMsg::ClientExit);
- handle_error(backtrace);
- }
- ClientInstruction::ServerError(backtrace) => {
- handle_error(backtrace);
- }
- ClientInstruction::Render(output) => {
- if output.is_none() {
- break;
- }
- let mut stdout = os_input.get_stdout_writer();
- stdout
- .write_all(&output.unwrap().as_bytes())
- .expect("cannot write to stdout");
- stdout.flush().expect("could not flush");
- }
- ClientInstruction::UnblockInputThread => {
- command_is_executing.unblock_input_thread();
- }
- }
- }
-
- let _ = os_input.send_to_server(ClientToServerMsg::ClientExit);
- router_thread.join().unwrap();
-
- // cleanup();
- let reset_style = "\u{1b}[m";
- let show_cursor = "\u{1b}[?25h";
- let restore_snapshot = "\u{1b}[?1049l";
- let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
- let goodbye_message = format!(
- "{}\n{}{}{}Bye from Zellij!\n",
- goto_start_of_last_line, restore_snapshot, reset_style, show_cursor
- );
-
- os_input.unset_raw_mode(0);
- let mut stdout = os_input.get_stdout_writer();
- let _ = stdout.write(goodbye_message.as_bytes()).unwrap();
- stdout.flush().unwrap();
-}
diff --git a/src/client/pane_resizer.rs b/src/client/pane_resizer.rs
deleted file mode 100644
index 73678499..00000000
--- a/src/client/pane_resizer.rs
+++ /dev/null
@@ -1,532 +0,0 @@
-use crate::os_input_output::ServerOsApi;
-use crate::panes::{PaneId, PositionAndSize};
-use crate::tab::Pane;
-use std::{
- cmp::Ordering,
- collections::{BTreeMap, HashSet},
-};
-
-pub struct PaneResizer<'a> {
- panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
- os_api: &'a mut Box<dyn ServerOsApi>,
-}
-
-// TODO: currently there are some functions here duplicated with Tab
-// all resizing functions should move here
-
-impl<'a> PaneResizer<'a> {
- pub fn new(
- panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
- os_api: &'a mut Box<dyn ServerOsApi>,
- ) -> Self {
- PaneResizer { panes, os_api }
- }
- pub fn resize(
- &mut self,
- mut current_size: PositionAndSize,
- new_size: PositionAndSize,
- ) -> Option<(isize, isize)> {
- // (column_difference, row_difference)
- let mut successfully_resized = false;
- let mut column_difference: isize = 0;
- let mut row_difference: isize = 0;
- match new_size.columns.cmp(&current_size.columns) {
- Ordering::Greater => {
- let increase_by = new_size.columns - current_size.columns;
- if let Some(panes_to_resize) = find_increasable_vertical_chain(
- &self.panes,
- increase_by,
- current_size.columns,
- current_size.rows,
- ) {
- self.increase_panes_right_and_push_adjacents_right(
- panes_to_resize,
- increase_by,
- );
- column_difference = new_size.columns as isize - current_size.columns as isize;
- current_size.columns =
- (current_size.columns as isize + column_difference) as usize;
- successfully_resized = true;
- };
- }
- Ordering::Less => {
- let reduce_by = current_size.columns - new_size.columns;
- if let Some(panes_to_resize) = find_reducible_vertical_chain(
- &self.panes,
- reduce_by,
- current_size.columns,
- current_size.rows,
- ) {
- self.reduce_panes_left_and_pull_adjacents_left(panes_to_resize, reduce_by);
- column_difference = new_size.columns as isize - current_size.columns as isize;
- current_size.columns =
- (current_size.columns as isize + column_difference) as usize;
- successfully_resized = true;
- };
- }
- Ordering::Equal => (),
- }
- match new_size.rows.cmp(&current_size.rows) {
- Ordering::Greater => {
- let increase_by = new_size.rows - current_size.rows;
- if let Some(panes_to_resize) = find_increasable_horizontal_chain(
- &self.panes,
- increase_by,
- current_size.columns,
- current_size.rows,
- ) {
- self.increase_panes_down_and_push_down_adjacents(panes_to_resize, increase_by);
- row_difference = new_size.rows as isize - current_size.rows as isize;
- current_size.rows = (current_size.rows as isize + row_difference) as usize;
- successfully_resized = true;
- };
- }
- Ordering::Less => {
- let reduce_by = current_size.rows - new_size.rows;
- if let Some(panes_to_resize) = find_reducible_horizontal_chain(
- &self.panes,
- reduce_by,
- current_size.columns,
- current_size.rows,
- ) {
- self.reduce_panes_up_and_pull_adjacents_up(panes_to_resize, reduce_by);
- row_difference = new_size.rows as isize - current_size.rows as isize;
- current_size.rows = (current_size.rows as isize + row_difference) as usize;
- successfully_resized = true;
- };
- }
- Ordering::Equal => (),
- }
- if successfully_resized {
- Some((column_difference, row_difference))
- } else {
- None
- }
- }
- fn reduce_panes_left_and_pull_adjacents_left(
- &mut self,
- panes_to_reduce: Vec<PaneId>,
- reduce_by: usize,
- ) {
- let mut pulled_panes: HashSet<PaneId> = HashSet::new();
- for pane_id in panes_to_reduce {
- let (pane_x, pane_y, pane_columns, pane_rows) = {
- let pane = self.panes.get(&pane_id).unwrap();
- (pane.x(), pane.y(), pane.columns(), pane.rows())
- };
- let panes_to_pull = self.panes.values_mut().filter(|p| {
- p.x() > pane_x + pane_columns
- && (p.y() <= pane_y && p.y() + p.rows() >= pane_y
- || p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows)
- });
- for pane in panes_to_pull {
- if !pulled_panes.contains(&pane.pid()) {
- pane.pull_left(reduce_by);
- pulled_panes.insert(pane.pid());
- }
- }
- self.reduce_pane_width_left(&pane_id, reduce_by);
- }
- }
- fn reduce_panes_up_and_pull_adjacents_up(
- &mut self,
- panes_to_reduce: Vec<PaneId>,
- reduce_by: usize,
- ) {
- let mut pulled_panes: HashSet<PaneId> = HashSet::new();
- for pane_id in panes_to_reduce {
- let (pane_x, pane_y, pane_columns, pane_rows) = {
- let pane = self.panes.get(&pane_id).unwrap();
- (pane.x(), pane.y(), pane.columns(), pane.rows())
- };
- let panes_to_pull = self.panes.values_mut().filter(|p| {
- p.y() > pane_y + pane_rows
- && (p.x() <= pane_x && p.x() + p.columns() >= pane_x
- || p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns)
- });
- for pane in panes_to_pull {
- if !pulled_panes.contains(&pane.pid()) {
- pane.pull_up(reduce_by);
- pulled_panes.insert(pane.pid());
- }
- }
- self.reduce_pane_height_up(&pane_id, reduce_by);
- }
- }
- fn increase_panes_down_and_push_down_adjacents(
- &mut self,
- panes_to_increase: Vec<PaneId>,
- increase_by: usize,
- ) {
- let mut pushed_panes: HashSet<PaneId> = HashSet::new();
- for pane_id in panes_to_increase {
- let (pane_x, pane_y, pane_columns, pane_rows) = {
- let pane = self.panes.get(&pane_id).unwrap();
- (pane.x(), pane.y(), pane.columns(), pane.rows())
- };
- let panes_to_push = self.panes.values_mut().filter(|p| {
- p.y() > pane_y + pane_rows
- && (p.x() <= pane_x && p.x() + p.columns() >= pane_x
- || p.x() >= pane_x && p.x() + p.columns() <= pane_x + pane_columns)
- });
- for pane in panes_to_push {
- if !pushed_panes.contains(&pane.pid()) {
- pane.push_down(increase_by);
- pushed_panes.insert(pane.pid());
- }
- }
- self.increase_pane_height_down(&pane_id, increase_by);
- }
- }
- fn increase_panes_right_and_push_adjacents_right(
- &mut self,
- panes_to_increase: Vec<PaneId>,
- increase_by: usize,
- ) {
- let mut pushed_panes: HashSet<PaneId> = HashSet::new();
- for pane_id in panes_to_increase {
- let (pane_x, pane_y, pane_columns, pane_rows) = {
- let pane = self.panes.get(&pane_id).unwrap();
- (pane.x(), pane.y(), pane.columns(), pane.rows())
- };
- let panes_to_push = self.panes.values_mut().filter(|p| {
- p.x() > pane_x + pane_columns
- && (p.y() <= pane_y && p.y() + p.rows() >= pane_y
- || p.y() >= pane_y && p.y() + p.rows() <= pane_y + pane_rows)
- });
- for pane in panes_to_push {
- if !pushed_panes.contains(&pane.pid()) {
- pane.push_right(increase_by);
- pushed_panes.insert(pane.pid());
- }
- }
- self.increase_pane_width_right(&pane_id, increase_by);
- }
- }
- fn reduce_pane_height_up(&mut self, id: &PaneId, count: usize) {
- let pane = self.panes.get_mut(id).unwrap();
- pane.reduce_height_up(count);
- if let PaneId::Terminal(pid) = id {
- self.os_api
- .set_terminal_size_using_fd(*pid, pane.columns() as u16, pane.rows() as u16);
- }
- }
- fn increase_pane_height_down(&mut self, id: &PaneId, count: usize) {
- let pane = self.panes.get_mut(id).unwrap();
- pane.increase_height_down(count);
- if let PaneId::Terminal(pid) = pane.pid() {
- self.os_api
- .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16);
- }
- }
- fn increase_pane_width_right(&mut self, id: &PaneId, count: usize) {
- let pane = self.panes.get_mut(id).unwrap();
- pane.increase_width_right(count);
- if let PaneId::Terminal(pid) = pane.pid() {
- self.os_api
- .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16);
- }
- }
- fn reduce_pane_width_left(&mut self, id: &PaneId, count: usize) {
- let pane = self.panes.get_mut(id).unwrap();
- pane.reduce_width_left(count);
- if let PaneId::Terminal(pid) = pane.pid() {
- self.os_api
- .set_terminal_size_using_fd(pid, pane.columns() as u16, pane.rows() as u16);
- }
- }
-}
-
-fn find_next_increasable_horizontal_pane(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- right_of: &dyn Pane,
- increase_by: usize,
-) -> Option<PaneId> {
- let next_pane_candidates = panes.values().filter(
- |p| {
- p.x() == right_of.x() + right_of.columns() + 1 && p.horizontally_overlaps_with(right_of)
- }, // TODO: the name here is wrong, it should be vertically_overlaps_with
- );
- let resizable_candidates =
- next_pane_candidates.filter(|p| p.can_increase_height_by(increase_by));
- resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
- Some(next_pane) => {
- let next_pane = panes.get(&next_pane).unwrap();
- if next_pane.y() < p.y() {
- next_pane_id
- } else {
- Some(p.pid())
- }
- }
- None => Some(p.pid()),
- })
-}
-
-fn find_next_increasable_vertical_pane(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- below: &dyn Pane,
- increase_by: usize,
-) -> Option<PaneId> {
- let next_pane_candidates = panes.values().filter(
- |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with
- );
- let resizable_candidates =
- next_pane_candidates.filter(|p| p.can_increase_width_by(increase_by));
- resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
- Some(next_pane) => {
- let next_pane = panes.get(&next_pane).unwrap();
- if next_pane.x() < p.x() {
- next_pane_id
- } else {
- Some(p.pid())
- }
- }
- None => Some(p.pid()),
- })
-}
-
-fn find_next_reducible_vertical_pane(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- below: &dyn Pane,
- reduce_by: usize,
-) -> Option<PaneId> {
- let next_pane_candidates = panes.values().filter(
- |p| p.y() == below.y() + below.rows() + 1 && p.vertically_overlaps_with(below), // TODO: the name here is wrong, it should be horizontally_overlaps_with
- );
- let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_width_by(reduce_by));
- resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
- Some(next_pane) => {
- let next_pane = panes.get(&next_pane).unwrap();
- if next_pane.x() < p.x() {
- next_pane_id
- } else {
- Some(p.pid())
- }
- }
- None => Some(p.pid()),
- })
-}
-
-fn find_next_reducible_horizontal_pane(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- right_of: &dyn Pane,
- reduce_by: usize,
-) -> Option<PaneId> {
- let next_pane_candidates = panes.values().filter(
- |p| {
- p.x() == right_of.x() + right_of.columns() + 1 && p.horizontally_overlaps_with(right_of)
- }, // TODO: the name here is wrong, it should be vertically_overlaps_with
- );
- let resizable_candidates = next_pane_candidates.filter(|p| p.can_reduce_height_by(reduce_by));
- resizable_candidates.fold(None, |next_pane_id, p| match next_pane_id {
- Some(next_pane) => {
- let next_pane = panes.get(&next_pane).unwrap();
- if next_pane.y() < p.y() {
- next_pane_id
- } else {
- Some(p.pid())
- }
- }
- None => Some(p.pid()),
- })
-}
-
-fn find_increasable_horizontal_chain(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- increase_by: usize,
- screen_width: usize,
- screen_height: usize, // TODO: this is the previous size (make this clearer)
-) -> Option<Vec<PaneId>> {
- let mut horizontal_coordinate = 0;
- loop {
- if horizontal_coordinate == screen_height {
- return None;
- }
-
- match panes
- .values()
- .find(|p| p.x() == 0 && p.y() == horizontal_coordinate)
- {
- Some(leftmost_pane) => {
- if !leftmost_pane.can_increase_height_by(increase_by) {
- horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1;
- continue;
- }
- let mut panes_to_resize = vec![];
- let mut current_pane = leftmost_pane;
- loop {
- panes_to_resize.push(current_pane.pid());
- if current_pane.x() + current_pane.columns() == screen_width {
- return Some(panes_to_resize);
- }
- match find_next_increasable_horizontal_pane(
- panes,
- current_pane.as_ref(),
- increase_by,
- ) {
- Some(next_pane_id) => {
- current_pane = panes.get(&next_pane_id).unwrap();
- }
- None => {
- horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1;
- break;
- }
- };
- }
- }
- None => {
- return None;
- }
- }
- }
-}
-
-fn find_increasable_vertical_chain(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- increase_by: usize,
- screen_width: usize,
- screen_height: usize, // TODO: this is the previous size (make this clearer)
-) -> Option<Vec<PaneId>> {
- let mut vertical_coordinate = 0;
- loop {
- if vertical_coordinate == screen_width {
- return None;
- }
-
- match panes
- .values()
- .find(|p| p.y() == 0 && p.x() == vertical_coordinate)
- {
- Some(topmost_pane) => {
- if !topmost_pane.can_increase_width_by(increase_by) {
- vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1;
- continue;
- }
- let mut panes_to_resize = vec![];
- let mut current_pane = topmost_pane;
- loop {
- panes_to_resize.push(current_pane.pid());
- if current_pane.y() + current_pane.rows() == screen_height {
- return Some(panes_to_resize);
- }
- match find_next_increasable_vertical_pane(
- panes,
- current_pane.as_ref(),
- increase_by,
- ) {
- Some(next_pane_id) => {
- current_pane = panes.get(&next_pane_id).unwrap();
- }
- None => {
- vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1;
- break;
- }
- };
- }
- }
- None => {
- return None;
- }
- }
- }
-}
-
-fn find_reducible_horizontal_chain(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- reduce_by: usize,
- screen_width: usize,
- screen_height: usize, // TODO: this is the previous size (make this clearer)
-) -> Option<Vec<PaneId>> {
- let mut horizontal_coordinate = 0;
- loop {
- if horizontal_coordinate == screen_height {
- return None;
- }
-
- match panes
- .values()
- .find(|p| p.x() == 0 && p.y() == horizontal_coordinate)
- {
- Some(leftmost_pane) => {
- if !leftmost_pane.can_reduce_height_by(reduce_by) {
- horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1;
- continue;
- }
- let mut panes_to_resize = vec![];
- let mut current_pane = leftmost_pane;
- loop {
- panes_to_resize.push(current_pane.pid());
- if current_pane.x() + current_pane.columns() == screen_width {
- return Some(panes_to_resize);
- }
- match find_next_reducible_horizontal_pane(
- panes,
- current_pane.as_ref(),
- reduce_by,
- ) {
- Some(next_pane_id) => {
- current_pane = panes.get(&next_pane_id).unwrap();
- }
- None => {
- horizontal_coordinate = leftmost_pane.y() + leftmost_pane.rows() + 1;
- break;
- }
- };
- }
- }
- None => {
- return None;
- }
- }
- }
-}
-
-fn find_reducible_vertical_chain(
- panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- increase_by: usize,
- screen_width: usize,
- screen_height: usize, // TODO: this is the previous size (make this clearer)
-) -> Option<Vec<PaneId>> {
- let mut vertical_coordinate = 0;
- loop {
- if vertical_coordinate == screen_width {
- return None;
- }
-
- match panes
- .values()
- .find(|p| p.y() == 0 && p.x() == vertical_coordinate)
- {
- Some(topmost_pane) => {
- if !topmost_pane.can_reduce_width_by(increase_by) {
- vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1;
- continue;
- }
- let mut panes_to_resize = vec![];
- let mut current_pane = topmost_pane;
- loop {
- panes_to_resize.push(current_pane.pid());
- if current_pane.y() + current_pane.rows() == screen_height {
- return Some(panes_to_resize);
- }
- match find_next_reducible_vertical_pane(
- panes,
- current_pane.as_ref(),
- increase_by,
- ) {
- Some(next_pane_id) => {
- current_pane = panes.get(&next_pane_id).unwrap();
- }
- None => {
- vertical_coordinate = topmost_pane.x() + topmost_pane.columns() + 1;
- break;
- }
- };
- }
- }
- None => {
- return None;
- }
- }
- }
-}
diff --git a/src/client/panes/grid.rs b/src/client/panes/grid.rs
deleted file mode 100644
index e51b1ed5..00000000
--- a/src/client/panes/grid.rs
+++ /dev/null
@@ -1,1558 +0,0 @@
-use std::{
- cmp::Ordering,
- collections::{BTreeSet, VecDeque},
- fmt::{self, Debug, Formatter},
-};
-
-use vte::{Params, Perform};
-
-const TABSTOP_WIDTH: usize = 8; // TODO: is this always right?
-const SCROLL_BACK: usize = 10_000;
-
-use crate::utils::consts::VERSION;
-use crate::utils::logging::debug_log_to_file;
-use crate::utils::shared::version_number;
-
-use crate::panes::terminal_character::{
- CharacterStyles, CharsetIndex, Cursor, CursorShape, StandardCharset, TerminalCharacter,
- EMPTY_TERMINAL_CHARACTER,
-};
-
-fn get_top_non_canonical_rows(rows: &mut Vec<Row>) -> Vec<Row> {
- let mut index_of_last_non_canonical_row = None;
- for (i, row) in rows.iter().enumerate() {
- if row.is_canonical {
- break;
- } else {
- index_of_last_non_canonical_row = Some(i);
- }
- }
- match index_of_last_non_canonical_row {
- Some(index_of_last_non_canonical_row) => {
- rows.drain(..=index_of_last_non_canonical_row).collect()
- }
- None => vec![],
- }
-}
-
-fn get_bottom_canonical_row_and_wraps(rows: &mut VecDeque<Row>) -> Vec<Row> {
- let mut index_of_last_non_canonical_row = None;
- for (i, row) in rows.iter().enumerate().rev() {
- index_of_last_non_canonical_row = Some(i);
- if row.is_canonical {
- break;
- }
- }
- match index_of_last_non_canonical_row {
- Some(index_of_last_non_canonical_row) => {
- rows.drain(index_of_last_non_canonical_row..).collect()
- }
- None => vec![],
- }
-}
-
-fn transfer_rows_down(
- source: &mut VecDeque<Row>,
- destination: &mut Vec<Row>,
- count: usize,
- max_src_width: Option<usize>,
- max_dst_width: Option<usize>,
-) {
- let mut next_lines: Vec<Row> = vec![];
- let mut lines_added_to_destination: isize = 0;
- loop {
- if lines_added_to_destination as usize == count {
- break;
- }
- if next_lines.is_empty() {
- match source.pop_back() {
- Some(next_line) => {
- let mut top_non_canonical_rows_in_dst = get_top_non_canonical_rows(destination);
- lines_added_to_destination -= top_non_canonical_rows_in_dst.len() as isize;
- next_lines.push(next_line);
- next_lines.append(&mut top_non_canonical_rows_in_dst);
- next_lines = match max_dst_width {
- Some(max_row_width) => {
- Row::from_rows(next_lines).split_to_rows_of_length(max_row_width)
- }
- None => vec![Row::from_rows(next_lines)],
- };
- if next_lines.is_empty() {
- // no more lines at source, the line we popped was probably empty
- break;
- }
- }
- None => break, // no more rows
- }
- }
- destination.insert(0, next_lines.pop().unwrap());
- lines_added_to_destination += 1;
- }
- if !next_lines.is_empty() {
- match max_src_width {
- Some(max_row_width) => {
- let excess_rows = Row::from_rows(next_lines).split_to_rows_of_length(max_row_width);
- source.extend(excess_rows);
- }
- None => {
- let excess_row = Row::from_rows(next_lines);
- bounded_push(source, excess_row);
- }
- }
- }
-}
-
-fn transfer_rows_up(
- source: &mut Vec<Row>,
- destination: &mut VecDeque<Row>,
- count: usize,
- max_src_width: Option<usize>,
- max_dst_width: Option<usize>,
-) {
- let mut next_lines: Vec<Row> = vec![];
- for _ in 0..count {
- if next_lines.is_empty() {
- if !source.is_empty() {
- let next_line = source.remove(0);
- if !next_line.is_canonical {
- let mut bottom_canonical_row_and_wraps_in_dst =
- get_bottom_canonical_row_and_wraps(destination);
- next_lines.append(&mut bottom_canonical_row_and_wraps_in_dst);
- }
- next_lines.push(next_line);
- next_lines = match max_dst_width {
- Some(max_row_width) => {
- Row::from_rows(next_lines).split_to_rows_of_length(max_row_width)
- }
- None => vec![Row::from_rows(next_lines)],
- };
- } else {
- break; // no more rows
- }
- }
- bounded_push(destination, next_lines.remove(0));
- }
- if !next_lines.is_empty() {
- match max_src_width {
- Some(max_row_width) => {
- let excess_rows = Row::from_rows(next_lines).split_to_rows_of_length(max_row_width);
- for row in excess_rows {
- source.insert(0, row);
- }
- }
- None => {
- let excess_row = Row::from_rows(next_lines);
- source.insert(0, excess_row);
- }
- }
- }
-}
-
-fn bounded_push(vec: &mut VecDeque<Row>, value: Row) {
- if vec.len() >= SCROLL_BACK {
- vec.pop_front();
- }
- vec.push_back(value)
-}
-
-pub fn create_horizontal_tabstops(columns: usize) -> BTreeSet<usize> {
- let mut i = TABSTOP_WIDTH;
- let mut horizontal_tabstops = BTreeSet::new();
- loop {
- if i > columns {
- break;
- }
- horizontal_tabstops.insert(i);
- i += TABSTOP_WIDTH;
- }
- horizontal_tabstops
-}
-
-#[derive(Clone)]
-pub struct Grid {
- lines_above: VecDeque<Row>,
- viewport: Vec<Row>,
- lines_below: Vec<Row>,
- horizontal_tabstops: BTreeSet<usize>,
- alternative_lines_above_viewport_and_cursor: Option<(VecDeque<Row>, Vec<Row>, Cursor)>,
- cursor: Cursor,
- saved_cursor_position: Option<Cursor>,
- scroll_region: Option<(usize, usize)>,
- active_charset: CharsetIndex,
- preceding_char: Option<TerminalCharacter>,
- pub should_render: bool,
- pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "")
- pub erasure_mode: bool, // ERM
- pub insert_mode: bool,
- pub disable_linewrap: bool,
- pub clear_viewport_before_rendering: bool,
- pub width: usize,
- pub height: usize,
- pub pending_messages_to_pty: Vec<Vec<u8>>,
-}
-
-impl Debug for Grid {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- for (i, row) in self.viewport.iter().enumerate() {
- if row.is_canonical {
- writeln!(f, "{:02?} (C): {:?}", i, row)?;
- } else {
- writeln!(f, "{:02?} (W): {:?}", i, row)?;
- }
- }
- Ok(())
- }
-}
-
-impl Grid {
- pub fn new(rows: usize, columns: usize) -> Self {
- Grid {
- lines_above: VecDeque::with_capacity(SCROLL_BACK),
- viewport: vec![Row::new().canonical()],
- lines_below: vec![],
- horizontal_tabstops: create_horizontal_tabstops(columns),
- cursor: Cursor::new(0, 0),
- saved_cursor_position: None,
- scroll_region: None,
- preceding_char: None,
- width: columns,
- height: rows,
- should_render: true,
- cursor_key_mode: false,
- erasure_mode: false,
- insert_mode: false,
- disable_linewrap: false,
- alternative_lines_above_viewport_and_cursor: None,
- clear_viewport_before_rendering: false,
- active_charset: Default::default(),
- pending_messages_to_pty: vec![],
- }
- }
- pub fn contains_widechar(&self) -> bool {
- self.viewport.iter().any(|c| c.contains_widechar())
- }
- pub fn advance_to_next_tabstop(&mut self, styles: CharacterStyles) {
- let mut next_tabstop = None;
- for tabstop in self.horizontal_tabstops.iter() {
- if *tabstop > self.cursor.x {
- next_tabstop = Some(tabstop);
- break;
- }
- }
- match next_tabstop {
- Some(tabstop) => {
- self.cursor.x = *tabstop;
- }
- None => {
- self.cursor.x = self.width.saturating_sub(1);
- }
- }
- let mut empty_character = EMPTY_TERMINAL_CHARACTER;
- empty_character.styles = styles;
- self.pad_current_line_until(self.cursor.x);
- }
- pub fn move_to_previous_tabstop(&mut self) {
- let mut previous_tabstop = None;
- for tabstop in self.horizontal_tabstops.iter() {
- if *tabstop >= self.cursor.x {
- break;
- }
- previous_tabstop = Some(tabstop);
- }
- match previous_tabstop {
- Some(tabstop) => {
- self.cursor.x = *tabstop;
- }
- None => {
- self.cursor.x = 0;
- }
- }
- }
- pub fn cursor_shape(&self) -> CursorShape {
- self.cursor.get_shape()
- }
- fn set_horizontal_tabstop(&mut self) {
- self.horizontal_tabstops.insert(self.cursor.x);
- }
- fn clear_tabstop(&mut self, position: usize) {
- self.horizontal_tabstops.remove(&position);
- }
- fn clear_all_tabstops(&mut self) {
- self.horizontal_tabstops.clear();
- }
- fn save_cursor_position(&mut self) {
- self.saved_cursor_position = Some(self.cursor.clone());
- }
- fn restore_cursor_position(&mut self) {
- if let Some(saved_cursor_position) = self.saved_cursor_position.as_ref() {
- self.cursor = saved_cursor_position.clone();
- }
- }
- fn configure_charset(&mut self, charset: StandardCharset, index: CharsetIndex) {
- self.cursor.charsets[index] = charset;
- }
- fn set_active_charset(&mut self, index: CharsetIndex) {
- self.active_charset = index;
- }
- fn cursor_canonical_line_index(&self) -> usize {
- let mut cursor_canonical_line_index = 0;
- let mut canonical_lines_traversed = 0;
- for (i, line) in self.viewport.iter().enumerate() {
- if line.is_canonical {
- cursor_canonical_line_index = canonical_lines_traversed;
- canonical_lines_traversed += 1;
- }
- if i == self.cursor.y {
- break;
- }
- }
- cursor_canonical_line_index
- }
- // TODO: merge these two funtions
- fn cursor_index_in_canonical_line(&self) -> usize {
- let mut cursor_canonical_line_index = 0;
- let mut cursor_index_in_canonical_line = 0;
- for (i, line) in self.viewport.iter().enumerate() {
- if line.is_canonical {
- cursor_canonical_line_index = i;
- }
- if i == self.cursor.y {
- let line_wrap_position_in_line = self.cursor.y - cursor_canonical_line_index;
- cursor_index_in_canonical_line = line_wrap_position_in_line + self.cursor.x;
- break;
- }
- }
- cursor_index_in_canonical_line
- }
- fn canonical_line_y_coordinates(&self, canonical_line_index: usize) -> usize {
- let mut canonical_lines_traversed = 0;
- let mut y_coordinates = 0;
- for (i, line) in self.viewport.iter().enumerate() {
- if line.is_canonical {
- canonical_lines_traversed += 1;
- if canonical_lines_traversed == canonical_line_index + 1 {
- y_coordinates = i;
- break;
- }
- }
- }
- y_coordinates
- }
- pub fn scroll_up_one_line(&mut self) {
- if !self.lines_above.is_empty() && self.viewport.len() == self.height {
- let line_to_push_down = self.viewport.pop().unwrap();
- self.lines_below.insert(0, line_to_push_down);
- let line_to_insert_at_viewport_top = self.lines_above.pop_back().unwrap();
- self.viewport.insert(0, line_to_insert_at_viewport_top);
- }
- }
- pub fn scroll_down_one_line(&mut self) {
- if !self.lines_below.is_empty() && self.viewport.len() == self.height {
- let mut line_to_push_up = self.viewport.remove(0);
- if line_to_push_up.is_canonical {
- bounded_push(&mut self.lines_above, line_to_push_up);
- } else {
- let mut last_line_above = self.lines_above.pop_back().unwrap();
- last_line_above.append(&mut line_to_push_up.columns);
- bounded_push(&mut self.lines_above, last_line_above);
- }
- let line_to_insert_at_viewport_bottom = self.lines_below.remove(0);
- self.viewport.push(line_to_insert_at_viewport_bottom);
- }
- }
- pub fn change_size(&mut self, new_rows: usize, new_columns: usize) {
- if new_columns != self.width {
- let mut cursor_canonical_line_index = self.cursor_canonical_line_index();
- let cursor_index_in_canonical_line = self.cursor_index_in_canonical_line();
- let mut viewport_canonical_lines = vec![];
- for mut row in self.viewport.drain(..) {
- if !row.is_canonical
- && viewport_canonical_lines.is_empty()
- && !self.lines_above.is_empty()
- {
- let mut first_line_above = self.lines_above.pop_back().unwrap();
- first_line_above.append(&mut row.columns);
- viewport_canonical_lines.push(first_line_above);
- cursor_canonical_line_index += 1;
- } else if row.is_canonical {
- viewport_canonical_lines.push(row);
- } else {
- match viewport_canonical_lines.last_mut() {
- Some(last_line) => {
- last_line.append(&mut row.columns);
- }
- None => {
- // the state is corrupted somehow
- // this is a bug and I'm not yet sure why it happens
- // usually it fixes itself and is a result of some race
- // TODO: investigate why this happens and solve it
- return;
- }
- }
- }
- }
- let mut new_viewport_rows = vec![];
- for mut canonical_line in viewport_canonical_lines {
- let mut canonical_line_parts: Vec<Row> = vec![];
- if canonical_line.columns.is_empty() {
- canonical_line_parts.push(Row::new().canonical());
- }
- while !canonical_line.columns.is_empty() {
- let next_wrap = if canonical_line.len() > new_columns {
- canonical_line.columns.drain(..new_columns)
- } else {
- canonical_line.columns.drain(..)
- };
- let row = Row::from_columns(next_wrap.collect());
- // if there are no more parts, this row is canonical as long as it originally
- // was canonical (it might not have been for example if it's the first row in
- // the viewport, and the actual canonical row is above it in the scrollback)
- let row = if canonical_line_parts.is_empty() && canonical_line.is_canonical {
- row.canonical()
- } else {
- row
- };
- canonical_line_parts.push(row);
- }
- new_viewport_rows.append(&mut canonical_line_parts);
- }
- self.viewport = new_viewport_rows;
-
- let mut new_cursor_y = self.canonical_line_y_coordinates(cursor_canonical_line_index);
- let new_cursor_x = (cursor_index_in_canonical_line / new_columns)
- + (cursor_index_in_canonical_line % new_columns);
- let current_viewport_row_count = self.viewport.len();
- match current_viewport_row_count.cmp(&self.height) {
- Ordering::Less => {
- let row_count_to_transfer = self.height - current_viewport_row_count;
- transfer_rows_down(
- &mut self.lines_above,
- &mut self.viewport,
- row_count_to_transfer,
- None,
- Some(new_columns),
- );
- let rows_pulled = self.viewport.len() - current_viewport_row_count;
- new_cursor_y += rows_pulled;
- }
- Ordering::Greater => {
- let row_count_to_transfer = current_viewport_row_count - self.height;
- if row_count_to_transfer > new_cursor_y {
- new_cursor_y = 0;
- } else {
- new_cursor_y -= row_count_to_transfer;
- }
- transfer_rows_up(
- &mut self.viewport,
- &mut self.lines_above,
- row_count_to_transfer,
- Some(new_columns),
- None,
- );
- }
- Ordering::Equal => {}
- }
- self.cursor.y = new_cursor_y;
- self.cursor.x = new_cursor_x;
- }
- if new_rows != self.height {
- let current_viewport_row_count = self.viewport.len();
- match current_viewport_row_count.cmp(&new_rows) {
- Ordering::Less => {
- let row_count_to_transfer = new_rows - current_viewport_row_count;
- transfer_rows_down(
- &mut self.lines_above,
- &mut self.viewport,
- row_count_to_transfer,
- None,
- Some(new_columns),
- );
- let rows_pulled = self.viewport.len() - current_viewport_row_count;
- self.cursor.y += rows_pulled;
- }
- Ordering::Greater => {
- let row_count_to_transfer = current_viewport_row_count - new_rows;
- if row_count_to_transfer > self.cursor.y {
- self.cursor.y = 0;
- } else {
- self.cursor.y -= row_count_to_transfer;
- }
- transfer_rows_up(
- &mut self.viewport,
- &mut self.lines_above,
- row_count_to_transfer,
- Some(new_columns),
- None,
- );
- }
- Ordering::Equal => {}
- }
- }
- self.height = new_rows;
- self.width = new_columns;
- if self.scroll_region.is_some() {
- self.set_scroll_region_to_viewport_size();
- }
- }
- pub fn as_character_lines(&self) -> Vec<Vec<TerminalCharacter>> {
- let mut lines: Vec<Vec<TerminalCharacter>> = self
- .viewport
- .iter()
- .map(|r| {
- let mut line: Vec<TerminalCharacter> = r.columns.iter().copied().collect();
- // pad line
- line.resize(self.width, EMPTY_TERMINAL_CHARACTER);
- line
- })
- .collect();
- let empty_row = vec![EMPTY_TERMINAL_CHARACTER; self.width];
- for _ in lines.len()..self.height {
- lines.push(empty_row.clone());
- }
- lines
- }
- pub fn cursor_coordinates(&self) -> Option<(usize, usize)> {
- if self.cursor.is_hidden {
- None
- } else {
- Some((self.cursor.x, self.cursor.y))
- }
- }
- pub fn move_viewport_up(&mut self, count: usize) {
- for _ in 0..count {
- self.scroll_up_one_line();
- }
- }
- pub fn move_viewport_down(&mut self, count: usize) {
- for _ in 0..count {
- self.scroll_down_one_line();
- }
- }
- pub fn reset_viewport(&mut self) {
- let row_count_below = self.lines_below.len();
- for _ in 0..row_count_below {
- self.scroll_down_one_line();
- }
- }
- pub fn rotate_scroll_region_up(&mut self, count: usize) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- for _ in 0..count {
- let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
- if scroll_region_bottom < self.viewport.len() {
- self.viewport.remove(scroll_region_bottom);
- }
- if scroll_region_top < self.viewport.len() {
- self.viewport
- .insert(scroll_region_top, Row::from_columns(columns).canonical());
- }
- }
- }
- }
- pub fn rotate_scroll_region_down(&mut self, count: usize) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- for _ in 0..count {
- let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
- self.viewport.remove(scroll_region_top);
- if self.viewport.len() > scroll_region_top {
- self.viewport
- .insert(scroll_region_bottom, Row::from_columns(columns).canonical());
- } else {
- self.viewport.push(Row::from_columns(columns).canonical());
- }
- }
- }
- }
- pub fn fill_viewport(&mut self, character: TerminalCharacter) {
- self.viewport.clear();
- for _ in 0..self.height {
- let columns = vec![character; self.width];
- self.viewport.push(Row::from_columns(columns).canonical());
- }
- }
- pub fn add_canonical_line(&mut self) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- if self.cursor.y == scroll_region_bottom {
- // end of scroll region
- // when we have a scroll region set and we're at its bottom
- // we need to delete its first line, thus shifting all lines in it upwards
- // then we add an empty line at its end which will be filled by the application
- // controlling the scroll region (presumably filled by whatever comes next in the
- // scroll buffer, but that's not something we control)
- if scroll_region_top >= self.viewport.len() {
- // the state is corrupted
- return;
- }
- self.viewport.remove(scroll_region_top);
- let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
- if self.viewport.len() >= scroll_region_bottom {
- self.viewport
- .insert(scroll_region_bottom, Row::from_columns(columns).canonical());
- } else {
- self.viewport.push(Row::from_columns(columns).canonical());
- }
- return;
- }
- }
- if self.viewport.len() <= self.cursor.y + 1 {
- // FIXME: this should add an empty line with the pad_character
- // but for some reason this breaks rendering in various situations
- // it needs to be investigated and fixed
- let new_row = Row::new().canonical();
- self.viewport.push(new_row);
- }
- if self.cursor.y == self.height - 1 {
- let row_count_to_transfer = 1;
- transfer_rows_up(
- &mut self.viewport,
- &mut self.lines_above,
- row_count_to_transfer,
- Some(self.width),
- None,
- );
- } else {
- self.cursor.y += 1;
- }
- }
- pub fn move_cursor_to_beginning_of_line(&mut self) {
- self.cursor.x = 0;
- }
- pub fn insert_character_at_cursor_position(&mut self, terminal_character: TerminalCharacter) {
- match self.viewport.get_mut(self.cursor.y) {
- Some(row) => {
- row.insert_character_at(terminal_character, self.cursor.x);
- if row.len() > self.width {
- row.truncate(self.width);
- }
- }
- None => {
- // pad lines until cursor if they do not exist
- for _ in self.viewport.len()..self.cursor.y {
- self.viewport.push(Row::new().canonical());
- }
- self.viewport
- .push(Row::new().with_character(terminal_character).canonical());
- }
- }
- }
- pub fn add_character_at_cursor_position(&mut self, terminal_character: TerminalCharacter) {
- match self.viewport.get_mut(self.cursor.y) {
- Some(row) => {
- if self.insert_mode {
- row.insert_character_at(terminal_character, self.cursor.x);
- } else {
- row.add_character_at(terminal_character, self.cursor.x);
- }
- }
- None => {
- // pad lines until cursor if they do not exist
- for _ in self.viewport.len()..self.cursor.y {
- self.viewport.push(Row::new().canonical());
- }
- self.viewport
- .push(Row::new().with_character(terminal_character).canonical());
- }
- }
- }
- pub fn add_character(&mut self, terminal_character: TerminalCharacter) {
- // TODO: try to separate adding characters from moving the cursors in this function
- if self.cursor.x >= self.width {
- if self.disable_linewrap {
- return;
- }
- // line wrap
- self.cursor.x = 0;
- if self.cursor.y == self.height - 1 {
- let row_count_to_transfer = 1;
- transfer_rows_up(
- &mut self.viewport,
- &mut self.lines_above,
- row_count_to_transfer,
- Some(self.width),
- None,
- );
- let wrapped_row = Row::new();
- self.viewport.push(wrapped_row);
- } else {
- self.cursor.y += 1;
- if self.viewport.len() <= self.cursor.y {
- let line_wrapped_row = Row::new();
- self.viewport.push(line_wrapped_row);
- }
- }
- }
- self.add_character_at_cursor_position(terminal_character);
- self.move_cursor_forward_until_edge(1);
- }
- pub fn move_cursor_forward_until_edge(&mut self, count: usize) {
- let count_to_move = std::cmp::min(count, self.width - (self.cursor.x));
- self.cursor.x += count_to_move;
- }
- pub fn replace_characters_in_line_after_cursor(&mut self, replace_with: TerminalCharacter) {
- self.viewport
- .get_mut(self.cursor.y)
- .unwrap()
- .truncate(self.cursor.x);
- if self.cursor.x < self.width - 1 {
- let mut characters_to_append = vec![replace_with; self.width - self.cursor.x];
- self.viewport
- .get_mut(self.cursor.y)
- .unwrap()
- .append(&mut characters_to_append);
- }
- }
- pub fn replace_characters_in_line_before_cursor(&mut self, replace_with: TerminalCharacter) {
- let line_part = vec![replace_with; self.cursor.x + 1];
- let row = self.viewport.get_mut(self.cursor.y).unwrap();
- row.replace_beginning_with(line_part);
- }
- pub fn clear_all_after_cursor(&mut self, replace_with: TerminalCharacter) {
- if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y) {
- cursor_row.truncate(self.cursor.x);
- let replace_with_columns = vec![replace_with; self.width];
- self.replace_characters_in_line_after_cursor(replace_with);
- for row in self.viewport.iter_mut().skip(self.cursor.y + 1) {
- row.replace_columns(replace_with_columns.clone());
- }
- }
- }
- pub fn clear_all_before_cursor(&mut self, replace_with: TerminalCharacter) {
- if self.viewport.get(self.cursor.y).is_some() {
- self.replace_characters_in_line_before_cursor(replace_with);
- let replace_with_columns = vec![replace_with; self.width];
- for row in self.viewport.iter_mut().take(self.cursor.y) {
- row.replace_columns(replace_with_columns.clone());
- }
- }
- }
- pub fn clear_cursor_line(&mut self) {
- self.viewport.get_mut(self.cursor.y).unwrap().truncate(0);
- }
- pub fn clear_all(&mut self, replace_with: TerminalCharacter) {
- let replace_with_columns = vec![replace_with; self.width];
- self.replace_characters_in_line_after_cursor(replace_with);
- for row in self.viewport.iter_mut() {
- row.replace_columns(replace_with_columns.clone());
- }
- }
- fn pad_current_line_until(&mut self, position: usize) {
- let current_row = self.viewport.get_mut(self.cursor.y).unwrap();
- for _ in current_row.len()..position {
- current_row.push(EMPTY_TERMINAL_CHARACTER);
- }
- }
- fn pad_lines_until(&mut self, position: usize, pad_character: TerminalCharacter) {
- for _ in self.viewport.len()..=position {
- let columns = vec![pad_character; self.width];
- self.viewport.push(Row::from_columns(columns).canonical());
- }
- }
- pub fn move_cursor_to(&mut self, x: usize, y: usize, pad_character: TerminalCharacter) {
- match self.scroll_region {
- Some((scroll_region_top, scroll_region_bottom)) => {
- self.cursor.x = std::cmp::min(self.width - 1, x);
- let y_offset = if self.erasure_mode {
- scroll_region_top
- } else {
- 0
- };
- self.cursor.y = std::cmp::min(scroll_region_bottom, y + y_offset);
- self.pad_lines_until(self.cursor.y, pad_character);
- self.pad_current_line_until(self.cursor.x);
- }
- None => {
- self.cursor.x = std::cmp::min(self.width - 1, x);
- self.cursor.y = std::cmp::min(self.height - 1, y);
- self.pad_lines_until(self.cursor.y, pad_character);
- self.pad_current_line_until(self.cursor.x);
- }
- }
- }
- pub fn move_cursor_up(&mut self, count: usize) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- if self.cursor.y >= scroll_region_top && self.cursor.y <= scroll_region_bottom {
- self.cursor.y =
- std::cmp::max(self.cursor.y.saturating_sub(count), scroll_region_top);
- return;
- }
- }
- self.cursor.y = if self.cursor.y < count {
- 0
- } else {
- self.cursor.y - count
- };
- }
- pub fn move_cursor_up_with_scrolling(&mut self, count: usize) {
- let (scroll_region_top, scroll_region_bottom) =
- self.scroll_region.unwrap_or((0, self.height - 1));
- for _ in 0..count {
- let current_line_index = self.cursor.y;
- if current_line_index == scroll_region_top {
- // if we're at the top line, we create a new line and remove the last line that
- // would otherwise overflow
- if scroll_region_bottom < self.viewport.len() {
- self.viewport.remove(scroll_region_bottom);
- }
- self.viewport.insert(current_line_index, Row::new()); // TODO: .canonical() ?
- } else if current_line_index > scroll_region_top
- && current_line_index <= scroll_region_bottom
- {
- self.move_cursor_up(count);
- }
- }
- }
- pub fn move_cursor_down(&mut self, count: usize, pad_character: TerminalCharacter) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- if self.cursor.y >= scroll_region_top && self.cursor.y <= scroll_region_bottom {
- self.cursor.y = std::cmp::min(self.cursor.y + count, scroll_region_bottom);
- return;
- }
- }
- let lines_to_add = if self.cursor.y + count > self.height - 1 {
- (self.cursor.y + count) - (self.height - 1)
- } else {
- 0
- };
- self.cursor.y = if self.cursor.y + count > self.height - 1 {
- self.height - 1
- } else {
- self.cursor.y + count
- };
- for _ in 0..lines_to_add {
- self.add_canonical_line();
- }
- self.pad_lines_until(self.cursor.y, pad_character);
- }
- pub fn move_cursor_back(&mut self, count: usize) {
- if self.cursor.x == self.width {
- // on the rightmost screen edge, backspace skips one character
- self.cursor.x -= 1;
- }
- if self.cursor.x < count {
- self.cursor.x = 0;
- } else {
- self.cursor.x -= count;
- }
- }
- pub fn hide_cursor(&mut self) {
- self.cursor.is_hidden = true;
- }
- pub fn show_cursor(&mut self) {
- self.cursor.is_hidden = false;
- }
- pub fn set_scroll_region(&mut self, top_line_index: usize, bottom_line_index: Option<usize>) {
- let bottom_line_index = bottom_line_index.unwrap_or(self.height);
- self.scroll_region = Some((top_line_index, bottom_line_index));
- }
- pub fn clear_scroll_region(&mut self) {
- self.scroll_region = None;
- }
- pub fn set_scroll_region_to_viewport_size(&mut self) {
- self.scroll_region = Some((0, self.height - 1));
- }
- pub fn delete_lines_in_scroll_region(
- &mut self,
- count: usize,
- pad_character: TerminalCharacter,
- ) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- let current_line_index = self.cursor.y;
- if current_line_index >= scroll_region_top && current_line_index <= scroll_region_bottom
- {
- // when deleting lines inside the scroll region, we must make sure it stays the
- // same size (and that other lines below it aren't shifted inside it)
- // so we delete the current line(s) and add an empty line at the end of the scroll
- // region
- for _ in 0..count {
- self.viewport.remove(current_line_index);
- let columns = vec![pad_character; self.width];
- if self.viewport.len() > scroll_region_bottom {
- self.viewport
- .insert(scroll_region_bottom, Row::from_columns(columns).canonical());
- } else {
- self.viewport.push(Row::from_columns(columns).canonical());
- }
- }
- }
- }
- }
- pub fn add_empty_lines_in_scroll_region(
- &mut self,
- count: usize,
- pad_character: TerminalCharacter,
- ) {
- if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
- let current_line_index = self.cursor.y;
- if current_line_index >= scroll_region_top && current_line_index <= scroll_region_bottom
- {
- // when adding empty lines inside the scroll region, we must make sure it stays the
- // same size and that lines don't "leak" outside of it
- // so we add an empty line where the cursor currently is, and delete the last line
- // of the scroll region
- for _ in 0..count {
- if scroll_region_bottom < self.viewport.len() {
- self.viewport.remove(scroll_region_bottom);
- }
- let columns = vec![pad_character; self.width];
- self.viewport
- .insert(current_line_index, Row::from_columns(columns).canonical());
- }
- }
- }
- }
- pub fn move_cursor_to_column(&mut self, column: usize) {
- self.cursor.x = column;
- self.pad_current_line_until(self.cursor.x);
- }
- pub fn move_cursor_to_line(&mut self, line: usize, pad_character: TerminalCharacter) {
- self.cursor.y = std::cmp::min(self.height - 1, line);
- self.pad_lines_until(self.cursor.y, pad_character);
- self.pad_current_line_until(self.cursor.x);
- }
- pub fn replace_with_empty_chars(&mut self, count: usize, empty_char_style: CharacterStyles) {
- let mut empty_character = EMPTY_TERMINAL_CHARACTER;
- empty_character.styles = empty_char_style;
- let pad_until = std::cmp::min(self.width, self.cursor.x + count);
- self.pad_current_line_until(pad_until);
- let current_row = self.viewport.get_mut(self.cursor.y).unwrap();
- for i in 0..count {
- current_row.replace_character_at(empty_character, self.cursor.x + i);
- }
- }
- pub fn erase_characters(&mut self, count: usize, empty_char_style: CharacterStyles) {
- let mut empty_character = EMPTY_TERMINAL_CHARACTER;
- empty_character.styles = empty_char_style;
- let current_row = self.viewport.get_mut(self.cursor.y).unwrap();
- for _ in 0..count {
- current_row.delete_character(self.cursor.x);
- }
- let mut empty_space_to_append = vec![empty_character; count];
- self.viewport
- .get_mut(self.cursor.y)
- .unwrap()
- .append(&mut empty_space_to_append);
- }
- fn add_newline(&mut self) {
- self.add_canonical_line();
- self.mark_for_rerender();
- }
- pub fn mark_for_rerender(&mut self) {
- self.should_render = true;
- }
- fn reset_terminal_state(&mut self) {
- self.lines_above = VecDeque::with_capacity(SCROLL_BACK);
- self.lines_below = vec![];
- self.viewport = vec![Row::new().canonical()];
- self.alternative_lines_above_viewport_and_cursor = None;
- self.cursor_key_mode = false;
- self.scroll_region = None;
- self.clear_viewport_before_rendering = true;
- self.cursor = Cursor::new(0, 0);
- self.saved_cursor_position = None;
- self.active_charset = Default::default();
- self.erasure_mode = false;
- self.disable_linewrap = false;
- self.cursor.change_shape(CursorShape::Block);
- }
- fn set_preceding_character(&mut self, terminal_character: TerminalCharacter) {
- self.preceding_char = Some(terminal_character);
- }
-}
-
-impl Perform for Grid {
- fn print(&mut self, c: char) {
- let c = self.cursor.charsets[self.active_charset].map(c);
- // apparently, building TerminalCharacter like this without a "new" method
- // is a little faster
- let terminal_character = TerminalCharacter {
- character: c,
- styles: self.cursor.pending_styles,
- };
- self.set_preceding_character(terminal_character);
- self.add_character(terminal_character);
- }
-
- fn execute(&mut self, byte: u8) {
- match byte {
- 8 => {
- // backspace
- self.move_cursor_back(1);
- }
- 9 => {
- // tab
- self.advance_to_next_tabstop(self.cursor.pending_styles);
- }
- 10 | 11 | 12 => {
- // 0a, newline
- // 0b, vertical tabulation
- // 0c, form feed
- self.add_newline();
- }
- 13 => {
- // 0d, carriage return
- self.move_cursor_to_beginning_of_line();
- }
- 14 => {
- self.set_active_charset(CharsetIndex::G1);
- }
- 15 => {
- self.set_active_charset(CharsetIndex::G0);
- }
- _ => {}
- }
- }
-
- fn hook(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _c: char) {
- // TBD
- }
-
- fn put(&mut self, _byte: u8) {
- // TBD
- }
-
- fn unhook(&mut self) {
- // TBD
- }
-
- fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) {
- // TBD
- }
-
- fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], _ignore: bool, c: char) {
- let mut params_iter = params.iter();
- let mut next_param_or = |default: u16| {
- params_iter
- .next()
- .map(|param| param[0])
- .filter(|&param| param != 0)
- .unwrap_or(default) as usize
- };
- if c == 'm' {
- self.cursor
- .pending_styles
- .add_style_from_ansi_params(&mut params_iter);
- } else if c == 'C' || c == 'a' {
- // move cursor forward
- let move_by = next_param_or(1);
- self.move_cursor_forward_until_edge(move_by);
- } else if c == 'K' {
- // clear line (0 => right, 1 => left, 2 => all)
- if let Some(clear_type) = params_iter.next().map(|param| param[0]) {
- if clear_type == 0 {
- let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
- char_to_replace.styles = self.cursor.pending_styles;
- self.replace_characters_in_line_after_cursor(char_to_replace);
- } else if clear_type == 1 {
- let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
- char_to_replace.styles = self.cursor.pending_styles;
- self.replace_characters_in_line_before_cursor(char_to_replace);
- } else if clear_type == 2 {
- self.clear_cursor_line();
- }
- };
- } else if c == 'J' {
- // clear all (0 => below, 1 => above, 2 => all, 3 => saved)
- let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
- char_to_replace.styles = self.cursor.pending_styles;
-
- if let Some(clear_type) = params_iter.next().map(|param| param[0]) {
- if clear_type == 0 {
- self.clear_all_after_cursor(char_to_replace);
- } else if clear_type == 1 {
- self.clear_all_before_cursor(char_to_replace);
- } else if clear_type == 2 {
- self.fill_viewport(char_to_replace);
- }
- };
- } else if c == 'H' || c == 'f' {
- // goto row/col
- // we subtract 1 from the row/column because these are 1 indexed
- let row = next_param_or(1).saturating_sub(1);
- let col = next_param_or(1).saturating_sub(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.move_cursor_to(col, row, pad_character);
- } else if c == 'A' {
- // move cursor up until edge of screen
- let move_up_count = next_param_or(1);
- self.move_cursor_up(move_up_count as usize);
- } else if c == 'B' || c == 'e' {
- // move cursor down until edge of screen
- let move_down_count = next_param_or(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.move_cursor_down(move_down_count as usize, pad_character);
- } else if c == 'D' {
- let move_back_count = next_param_or(1);
- self.move_cursor_back(move_back_count);
- } else if c == 'l' {
- let first_intermediate_is_questionmark = match intermediates.get(0) {
- Some(b'?') => true,
- None => false,
- _ => false,
- };
- if first_intermediate_is_questionmark {
- match params_iter.next().map(|param| param[0]) {
- Some(1049) => {
- if let Some((
- alternative_lines_above,
- alternative_viewport,
- alternative_cursor,
- )) = self.alternative_lines_above_viewport_and_cursor.as_mut()
- {
- std::mem::swap(&mut self.lines_above, alternative_lines_above);
- std::mem::swap(&mut self.viewport, alternative_viewport);
- std::mem::swap(&mut self.cursor, alternative_cursor);
- }
- self.alternative_lines_above_viewport_and_cursor = None;
- self.clear_viewport_before_rendering = true;
- self.change_size(self.height, self.width); // the alternative_viewport might have been of a different size...
- self.mark_for_rerender();
- }
- Some(25) => {
- self.hide_cursor();
- self.mark_for_rerender();
- }
- Some(1) => {
- self.cursor_key_mode = false;
- }
- Some(3) => {
- // DECCOLM - only side effects
- self.scroll_region = None;
- self.clear_all(EMPTY_TERMINAL_CHARACTER);
- self.cursor.x = 0;
- self.cursor.y = 0;
- }
- Some(6) => {
- self.erasure_mode = false;
- }
- Some(7) => {
- self.disable_linewrap = true;
- }
- _ => {}
- };
- } else if let Some(4) = params_iter.next().map(|param| param[0]) {
- self.insert_mode = false;
- }
- } else if c == 'h' {
- let first_intermediate_is_questionmark = match intermediates.get(0) {
- Some(b'?') => true,
- None => false,
- _ => false,
- };
- if first_intermediate_is_questionmark {
- match params_iter.next().map(|param| param[0]) {
- Some(25) => {
- self.show_cursor();
- self.mark_for_rerender();
- }
- Some(1049) => {
- let current_lines_above = std::mem::replace(
- &mut self.lines_above,
- VecDeque::with_capacity(SCROLL_BACK),
- );
- let current_viewport =
- std::mem::replace(&mut self.viewport, vec![Row::new().canonical()]);
- let current_cursor = std::mem::replace(&mut self.cursor, Cursor::new(0, 0));
- self.alternative_lines_above_viewport_and_cursor =
- Some((current_lines_above, current_viewport, current_cursor));
- self.clear_viewport_before_rendering = true;
- }
- Some(1) => {
- self.cursor_key_mode = true;
- }
- Some(3) => {
- // DECCOLM - only side effects
- self.scroll_region = None;
- self.clear_all(EMPTY_TERMINAL_CHARACTER);
- self.cursor.x = 0;
- self.cursor.y = 0;
- }
- Some(6) => {
- self.erasure_mode = true;
- }
- Some(7) => {
- self.disable_linewrap = false;
- }
- _ => {}
- };
- } else if let Some(4) = params_iter.next().map(|param| param[0]) {
- self.insert_mode = true;
- }
- } else if c == 'r' {
- if params.len() > 1 {
- let top = (next_param_or(1) as usize).saturating_sub(1);
- let bottom = params_iter
- .next()
- .map(|param| param[0] as usize)
- .filter(|&param| param != 0)
- .map(|bottom| bottom.saturating_sub(1));
- self.set_scroll_region(top, bottom);
- if self.erasure_mode {
- self.move_cursor_to_line(top, EMPTY_TERMINAL_CHARACTER);
- self.move_cursor_to_beginning_of_line();
- }
- } else {
- self.clear_scroll_region();
- }
- } else if c == 'M' {
- // delete lines if currently inside scroll region
- let line_count_to_delete = next_param_or(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.delete_lines_in_scroll_region(line_count_to_delete, pad_character);
- } else if c == 'L' {
- // insert blank lines if inside scroll region
- let line_count_to_add = next_param_or(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.add_empty_lines_in_scroll_region(line_count_to_add, pad_character);
- } else if c == 'G' || c == '`' {
- let column = next_param_or(1).saturating_sub(1);
- self.move_cursor_to_column(column);
- } else if c == 'g' {
- let clear_type = next_param_or(0);
- if clear_type == 0 {
- self.clear_tabstop(self.cursor.x);
- } else if clear_type == 3 {
- self.clear_all_tabstops();
- }
- } else if c == 'd' {
- // goto line
- let line = next_param_or(1).saturating_sub(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.move_cursor_to_line(line, pad_character);
- } else if c == 'P' {
- // erase characters
- let count = next_param_or(1);
- self.erase_characters(count, self.cursor.pending_styles);
- } else if c == 'X' {
- // erase characters and replace with empty characters of current style
- let count = next_param_or(1);
- self.replace_with_empty_chars(count, self.cursor.pending_styles);
- } else if c == 'T' {
- /*
- * 124 54 T SD
- * Scroll down, new lines inserted at top of screen
- * [4T = Scroll down 4, bring previous lines back into view
- */
- let line_count = next_param_or(1);
- self.rotate_scroll_region_up(line_count as usize);
- } else if c == 'S' {
- // move scroll up
- let count = next_param_or(1);
- self.rotate_scroll_region_down(count);
- } else if c == 's' {
- self.save_cursor_position();
- } else if c == 'u' {
- self.restore_cursor_position();
- } else if c == '@' {
- let count = next_param_or(1);
- for _ in 0..count {
- // TODO: should this be styled?
- self.insert_character_at_cursor_position(EMPTY_TERMINAL_CHARACTER);
- }
- } else if c == 'b' {
- if let Some(c) = self.preceding_char {
- for _ in 0..next_param_or(1) {
- self.add_character(c);
- }
- }
- } else if c == 'E' {
- let count = next_param_or(1);
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.move_cursor_down(count, pad_character);
- } else if c == 'F' {
- let count = next_param_or(1);
- self.move_cursor_up(count);
- self.move_cursor_to_beginning_of_line();
- } else if c == 'I' {
- for _ in 0..next_param_or(1) {
- self.advance_to_next_tabstop(self.cursor.pending_styles);
- }
- } else if c == 'q' {
- let first_intermediate_is_space = matches!(intermediates.get(0), Some(b' '));
- if first_intermediate_is_space {
- // DECSCUSR (CSI Ps SP q) -- Set Cursor Style.
- let cursor_style_id = next_param_or(0);
- let shape = match cursor_style_id {
- 0 | 2 => Some(CursorShape::Block),
- 1 => Some(CursorShape::BlinkingBlock),
- 3 => Some(CursorShape::BlinkingUnderline),
- 4 => Some(CursorShape::Underline),
- 5 => Some(CursorShape::BlinkingBeam),
- 6 => Some(CursorShape::Beam),
- _ => None,
- };
- if let Some(cursor_shape) = shape {
- self.cursor.change_shape(cursor_shape);
- }
- }
- } else if c == 'Z' {
- for _ in 0..next_param_or(1) {
- self.move_to_previous_tabstop();
- }
- } else if c == 'c' {
- // identify terminal
- // https://vt100.net/docs/vt510-rm/DA1.html
- match intermediates.get(0) {
- None | Some(0) => {
- // primary device attributes
- let terminal_capabilities = "\u{1b}[?6c";
- self.pending_messages_to_pty
- .push(terminal_capabilities.as_bytes().to_vec());
- }
- Some(b'>') => {
- // secondary device attributes
- let version = version_number(VERSION);
- let text = format!("\u{1b}[>0;{};1c", version);
- self.pending_messages_to_pty.push(text.as_bytes().to_vec());
- }
- _ => {}
- }
- } else if c == 'n' {
- // DSR - device status report
- // https://vt100.net/docs/vt510-rm/DSR.html
- match next_param_or(0) {
- 5 => {
- // report terminal status
- let all_good = "\u{1b}[0n";
- self.pending_messages_to_pty
- .push(all_good.as_bytes().to_vec());
- }
- 6 => {
- // CPR - cursor position report
- let position_report =
- format!("\x1b[{};{}R", self.cursor.y + 1, self.cursor.x + 1);
- self.pending_messages_to_pty
- .push(position_report.as_bytes().to_vec());
- }
- _ => {}
- }
- } else if c == 't' {
- match next_param_or(1) as usize {
- 14 => {
- // TODO: report text area size in pixels, currently unimplemented
- // to solve this we probably need to query the user's terminal for the cursor
- // size and then use it as a multiplier
- }
- 18 => {
- // report text area
- let text_area_report = format!("\x1b[8;{};{}t", self.height, self.width);
- self.pending_messages_to_pty
- .push(text_area_report.as_bytes().to_vec());
- }
- 22 => {
- // TODO: push title
- }
- 23 => {
- // TODO: pop title
- }
- _ => {}
- }
- } else {
- let result = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params));
- #[cfg(not(test))]
- result.unwrap();
- }
- }
-
- fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) {
- match (byte, intermediates.get(0)) {
- (b'B', charset_index_symbol) => {
- let charset_index: CharsetIndex = match charset_index_symbol {
- Some(b'(') => CharsetIndex::G0,
- Some(b')') => CharsetIndex::G1,
- Some(b'*') => CharsetIndex::G2,
- Some(b'+') => CharsetIndex::G3,
- _ => {
- // invalid, silently do nothing
- return;
- }
- };
- self.configure_charset(StandardCharset::Ascii, charset_index);
- }
- (b'0', charset_index_symbol) => {
- let charset_index: CharsetIndex = match charset_index_symbol {
- Some(b'(') => CharsetIndex::G0,
- Some(b')') => CharsetIndex::G1,
- Some(b'*') => CharsetIndex::G2,
- Some(b'+') => CharsetIndex::G3,
- _ => {
- // invalid, silently do nothing
- return;
- }
- };
- self.configure_charset(
- StandardCharset::SpecialCharacterAndLineDrawing,
- charset_index,
- );
- }
- (b'D', None) => {
- self.add_newline();
- }
- (b'E', None) => {
- self.add_newline();
- self.move_cursor_to_beginning_of_line();
- }
- (b'M', None) => {
- // TODO: if cursor is at the top, it should go down one
- self.move_cursor_up_with_scrolling(1);
- }
- (b'c', None) => {
- self.reset_terminal_state();
- }
- (b'H', None) => {
- self.set_horizontal_tabstop();
- }
- (b'7', None) => {
- self.save_cursor_position();
- }
- (b'Z', None) => {
- let terminal_capabilities = "\u{1b}[?6c";
- self.pending_messages_to_pty
- .push(terminal_capabilities.as_bytes().to_vec());
- }
- (b'8', None) => {
- self.restore_cursor_position();
- }
- (b'8', Some(b'#')) => {
- let mut fill_character = EMPTY_TERMINAL_CHARACTER;
- fill_character.character = 'E';
- self.fill_viewport(fill_character);
- }
- _ => {}
- }
- }
-}
-
-#[derive(Clone)]
-pub struct Row {
- pub columns: Vec<TerminalCharacter>,
- pub is_canonical: bool,
-}
-
-impl Debug for Row {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- for character in &self.columns {
- write!(f, "{:?}", character)?;
- }
- Ok(())
- }
-}
-
-impl Row {
- pub fn new() -> Self {
- Row {
- columns: vec![],
- is_canonical: false,
- }
- }
- pub fn from_columns(columns: Vec<TerminalCharacter>) -> Self {
- Row {
- columns,
- is_canonical: false,
- }
- }
- pub fn from_rows(mut rows: Vec<Row>) -> Self {
- if rows.is_empty() {
- Row::new()
- } else {
- let mut first_row = rows.remove(0);
- for row in rows.iter_mut() {
- first_row.append(&mut row.columns);
- }
- first_row
- }
- }
- pub fn with_character(mut self, terminal_character: TerminalCharacter) -> Self {
- self.columns.push(terminal_character);
- self
- }
- pub fn canonical(mut self) -> Self {
- self.is_canonical = true;
- self
- }
- pub fn contains_widechar(&self) -> bool {
- self.columns.iter().any(|c| c.is_widechar())
- }
- pub fn add_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
- match self.columns.len().cmp(&x) {
- Ordering::Equal => self.columns.push(terminal_character),
- Ordering::Less => {
- self.columns.resize(x, EMPTY_TERMINAL_CHARACTER);
- self.columns.push(terminal_character);
- }
- Ordering::Greater => {
- // this is much more performant than remove/insert
- self.columns.push(terminal_character);
- self.columns.swap_remove(x);
- }
- }
- }
- pub fn insert_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
- match self.columns.len().cmp(&x) {
- Ordering::Equal => self.columns.push(terminal_character),
- Ordering::Less => {
- self.columns.resize(x, EMPTY_TERMINAL_CHARACTER);
- self.columns.push(terminal_character);
- }
- Ordering::Greater => {
- self.columns.insert(x, terminal_character);
- }
- }
- }
- pub fn replace_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
- // this is much more performant than remove/insert
- if x < self.columns.len() {
- self.columns.push(terminal_character);
- self.columns.swap_remove(x);
- }
- }
- pub fn replace_columns(&mut self, columns: Vec<TerminalCharacter>) {
- self.columns = columns;
- }
- pub fn push(&mut self, terminal_character: TerminalCharacter) {
- self.columns.push(terminal_character);
- }
- pub fn truncate(&mut self, x: usize) {
- self.columns.truncate(x);
- }
- pub fn append(&mut self, to_append: &mut Vec<TerminalCharacter>) {
- self.columns.append(to_append);
- }
- pub fn replace_beginning_with(&mut self, mut line_part: Vec<TerminalCharacter>) {
- if line_part.len() > self.columns.len() {
- self.columns.clear();
- } else {
- drop(self.columns.drain(0..line_part.len()));
- }
- line_part.append(&mut self.columns);
- self.columns = line_part;
- }
- pub fn len(&self) -> usize {
- self.columns.len()
- }
- pub fn delete_character(&mut self, x: usize) {
- if x < self.columns.len() {
- self.columns.remove(x);
- }
- }
- pub fn split_to_rows_of_length(&mut self, max_row_length: usize) -> Vec<Row> {
- let mut parts: Vec<Row> = vec![];
- let mut current_part: Vec<TerminalCharacter> = vec![];
- for character in self.columns.drain(..) {
- if current_part.len() == max_row_length {
- parts.push(Row::from_columns(current_part));
- current_part = vec![];
- }
- current_part.push(character);
- }
- if !current_part.is_empty() {
- parts.push(Row::from_columns(current_part))
- };
- if !parts.is_empty() && self.is_canonical {
- parts.get_mut(0).unwrap().is_canonical = true;
- }
- parts
- }
-}
-
-#[cfg(test)]
-#[path = "./unit/grid_tests.rs"]
-mod grid_tests;
diff --git a/src/client/panes/mod.rs b/src/client/panes/mod.rs
deleted file mode 100644
index 4e7d6e80..00000000
--- a/src/client/panes/mod.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-mod grid;
-mod plugin_pane;
-mod terminal_character;
-mod terminal_pane;
-
-pub use grid::*;
-pub use plugin_pane::*;
-pub use terminal_character::*;
-pub use terminal_pane::*;
diff --git a/src/client/panes/plugin_pane.rs b/src/client/panes/plugin_pane.rs
deleted file mode 100644
index dde3cd9b..00000000
--- a/src/client/panes/plugin_pane.rs
+++ /dev/null
@@ -1,227 +0,0 @@
-use std::sync::mpsc::channel;
-use std::time::Instant;
-use std::unimplemented;
-
-use crate::common::thread_bus::SenderWithContext;
-use crate::panes::{PaneId, PositionAndSize};
-use crate::pty::VteBytes;
-use crate::tab::Pane;
-use crate::wasm_vm::PluginInstruction;
-
-pub struct PluginPane {
- pub pid: u32,
- pub should_render: bool,
- pub selectable: bool,
- pub invisible_borders: bool,
- pub position_and_size: PositionAndSize,
- pub position_and_size_override: Option<PositionAndSize>,
- pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
- pub max_height: Option<usize>,
- pub max_width: Option<usize>,
- pub active_at: Instant,
-}
-
-impl PluginPane {
- pub fn new(
- pid: u32,
- position_and_size: PositionAndSize,
- send_plugin_instructions: SenderWithContext<PluginInstruction>,
- ) -> Self {
- Self {
- pid,
- should_render: true,
- selectable: true,
- invisible_borders: false,
- position_and_size,
- position_and_size_override: None,
- send_plugin_instructions,
- max_height: None,
- max_width: None,
- active_at: Instant::now(),
- }
- }
-}
-
-impl Pane for PluginPane {
- // FIXME: These position and size things should all be moved to default trait implementations,
- // with something like a get_pos_and_sz() method underpinning all of them. Alternatively and
- // preferably, just use an enum and not a trait object
- fn x(&self) -> usize {
- self.position_and_size_override
- .unwrap_or(self.position_and_size)
- .x
- }
- fn y(&self) -> usize {
- self.position_and_size_override
- .unwrap_or(self.position_and_size)
- .y
- }
- fn rows(&self) -> usize {
- self.position_and_size_override
- .unwrap_or(self.position_and_size)
- .rows
- }
- fn columns(&self) -> usize {
- self.position_and_size_override
- .unwrap_or(self.position_and_size)
- .columns
- }
- fn reset_size_and_position_override(&mut self) {
- self.position_and_size_override = None;
- self.should_render = true;
- }
- fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize) {
- self.position_and_size = *position_and_size;
- self.should_render = true;
- }
- // FIXME: This is obviously a bit outdated and needs the x and y moved into `size`
- fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) {
- let position_and_size_override = PositionAndSize {
- x,
- y,
- rows: size.rows,
- columns: size.columns,
- ..Default::default()
- };
- self.position_and_size_override = Some(position_and_size_override);
- self.should_render = true;
- }
- fn handle_pty_bytes(&mut self, _event: VteBytes) {
- unimplemented!()
- }
- fn cursor_coordinates(&self) -> Option<(usize, usize)> {
- None
- }
- fn adjust_input_to_terminal(&self, _input_bytes: Vec<u8>) -> Vec<u8> {
- unimplemented!() // FIXME: Shouldn't need this implmented?
- }
-
- fn position_and_size_override(&self) -> Option<PositionAndSize> {
- self.position_and_size_override
- }
- fn contains_widechar(&self) -> bool {
- false
- }
- fn should_render(&self) -> bool {
- self.should_render
- }
- fn set_should_render(&mut self, should_render: bool) {
- self.should_render = should_render;
- }
- fn selectable(&self) -> bool {
- self.selectable
- }
- fn set_selectable(&mut self, selectable: bool) {
- self.selectable = selectable;
- }
- fn set_invisible_borders(&mut self, invisible_borders: bool) {
- self.invisible_borders = invisible_borders;
- }
- fn set_max_height(&mut self, max_height: usize) {
- self.max_height = Some(max_height);
- }
- fn set_max_width(&mut self, max_width: usize) {
- self.max_width = Some(max_width);
- }
- fn render(&mut self) -> Option<String> {
- // if self.should_render {
- if true {
- // while checking should_render rather than rendering each pane every time
- // is more performant, it causes some problems when the pane to the left should be
- // rendered and has wide characters (eg. Chinese characters or emoji)
- // as a (hopefully) temporary hack, we render all panes until we find a better solution
- let (buf_tx, buf_rx) = channel();
-
- self.send_plugin_instructions
- .send(PluginInstruction::Render(
- buf_tx,
- self.pid,
- self.rows(),
- self.columns(),
- ))
- .unwrap();
-
- self.should_render = false;
- Some(buf_rx.recv().unwrap())
- } else {
- None
- }
- }
- fn pid(&self) -> PaneId {
- PaneId::Plugin(self.pid)
- }
- fn reduce_height_down(&mut self, count: usize) {
- self.position_and_size.y += count;
- self.position_and_size.rows -= count;
- self.should_render = true;
- }
- fn increase_height_down(&mut self, count: usize) {
- self.position_and_size.rows += count;
- self.should_render = true;
- }
- fn increase_height_up(&mut self, count: usize) {
- self.position_and_size.y -= count;
- self.position_and_size.rows += count;
- self.should_render = true;
- }
- fn reduce_height_up(&mut self, count: usize) {
- self.position_and_size.rows -= count;
- self.should_render = true;
- }
- fn reduce_width_right(&mut self, count: usize) {
- self.position_and_size.x += count;
- self.position_and_size.columns -= count;
- self.should_render = true;
- }
- fn reduce_width_left(&mut self, count: usize) {
- self.position_and_size.columns -= count;
- self.should_render = true;
- }
- fn increase_width_left(&mut self, count: usize) {
- self.position_and_size.x -= count;
- self.position_and_size.columns += count;
- self.should_render = true;
- }
- fn increase_width_right(&mut self, count: usize) {
- self.position_and_size.columns += count;
- self.should_render = true;
- }
- fn push_down(&mut self, count: usize) {
- self.position_and_size.y += count;
- }
- fn push_right(&mut self, count: usize) {
- self.position_and_size.x += count;
- }
- fn pull_left(&mut self, count: usize) {
- self.position_and_size.x -= count;
- }
- fn pull_up(&mut self, count: usize) {
- self.position_and_size.y -= count;
- }
- fn scroll_up(&mut self, _count: usize) {
- unimplemented!()
- }
- fn scroll_down(&mut self, _count: usize) {
- unimplemented!()
- }
- fn clear_scroll(&mut self) {
- unimplemented!()
- }
- fn max_height(&self) -> Option<usize> {
- self.max_height
- }
- fn max_width(&self) -> Option<usize> {
- self.max_width
- }
- fn invisible_borders(&self) -> bool {
- self.invisible_borders
- }
-
- fn active_at(&self) -> Instant {
- self.active_at
- }
-
- fn set_active_at(&mut self, time: Instant) {
- self.active_at = time;
- }
-}
diff --git a/src/client/panes/terminal_character.rs b/src/client/panes/terminal_character.rs
deleted file mode 100644
index d2340e2e..00000000
--- a/src/client/panes/terminal_character.rs
+++ /dev/null
@@ -1,775 +0,0 @@
-use unicode_width::UnicodeWidthChar;
-
-use crate::utils::logging::debug_log_to_file;
-use std::convert::TryFrom;
-use std::fmt::{self, Debug, Display, Formatter};
-use std::ops::{Index, IndexMut};
-use vte::ParamsIter;
-
-pub const EMPTY_TERMINAL_CHARACTER: TerminalCharacter = TerminalCharacter {
- character: ' ',
- styles: CharacterStyles {
- foreground: Some(AnsiCode::Reset),
- background: Some(AnsiCode::Reset),
- strike: Some(AnsiCode::Reset),
- hidden: Some(AnsiCode::Reset),
- reverse: Some(AnsiCode::Reset),
- slow_blink: Some(AnsiCode::Reset),
- fast_blink: Some(AnsiCode::Reset),
- underline: Some(AnsiCode::Reset),
- bold: Some(AnsiCode::Reset),
- dim: Some(AnsiCode::Reset),
- italic: Some(AnsiCode::Reset),
- },
-};
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum AnsiCode {
- On,
- Reset,
- NamedColor(NamedColor),
- RgbCode((u8, u8, u8)),
- ColorIndex(u8),
-}
-
-#[derive(Clone, Copy, Eq, PartialEq, Debug)]
-pub enum NamedColor {
- Black,
- Red,
- Green,
- Yellow,
- Blue,
- Magenta,
- Cyan,
- White,
- BrightBlack,
- BrightRed,
- BrightGreen,
- BrightYellow,
- BrightBlue,
- BrightMagenta,
- BrightCyan,
- BrightWhite,
-}
-
-impl NamedColor {
- fn to_foreground_ansi_code(self) -> String {
- match self {
- NamedColor::Black => format!("{}", 30),
- NamedColor::Red => format!("{}", 31),
- NamedColor::Green => format!("{}", 32),
- NamedColor::Yellow => format!("{}", 33),
- NamedColor::Blue => format!("{}", 34),
- NamedColor::Magenta => format!("{}", 35),
- NamedColor::Cyan => format!("{}", 36),
- NamedColor::White => format!("{}", 37),
- NamedColor::BrightBlack => format!("{}", 90),
- NamedColor::BrightRed => format!("{}", 91),
- NamedColor::BrightGreen => format!("{}", 92),
- NamedColor::BrightYellow => format!("{}", 93),
- NamedColor::BrightBlue => format!("{}", 94),
- NamedColor::BrightMagenta => format!("{}", 95),
- NamedColor::BrightCyan => format!("{}", 96),
- NamedColor::BrightWhite => format!("{}", 97),
- }
- }
- fn to_background_ansi_code(self) -> String {
- match self {
- NamedColor::Black => format!("{}", 40),
- NamedColor::Red => format!("{}", 41),
- NamedColor::Green => format!("{}", 42),
- NamedColor::Yellow => format!("{}", 43),
- NamedColor::Blue => format!("{}", 44),
- NamedColor::Magenta => format!("{}", 45),
- NamedColor::Cyan => format!("{}", 46),
- NamedColor::White => format!("{}", 47),
- NamedColor::BrightBlack => format!("{}", 100),
- NamedColor::BrightRed => format!("{}", 101),
- NamedColor::BrightGreen => format!("{}", 102),
- NamedColor::BrightYellow => format!("{}", 103),
- NamedColor::BrightBlue => format!("{}", 104),
- NamedColor::BrightMagenta => format!("{}", 105),
- NamedColor::BrightCyan => format!("{}", 106),
- NamedColor::BrightWhite => format!("{}", 107),
- }
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct CharacterStyles {
- pub foreground: Option<AnsiCode>,
- pub background: Option<AnsiCode>,
- pub strike: Option<AnsiCode>,
- pub hidden: Option<AnsiCode>,
- pub reverse: Option<AnsiCode>,
- pub slow_blink: Option<AnsiCode>,
- pub fast_blink: Option<AnsiCode>,
- pub underline: Option<AnsiCode>,
- pub bold: Option<AnsiCode>,
- pub dim: Option<AnsiCode>,
- pub italic: Option<AnsiCode>,
-}
-
-impl CharacterStyles {
- pub fn new() -> Self {
- CharacterStyles {
- foreground: None,
- background: None,
- strike: None,
- hidden: None,
- reverse: None,
- slow_blink: None,
- fast_blink: None,
- underline: None,
- bold: None,
- dim: None,
- italic: None,
- }
- }
- pub fn foreground(mut self, foreground_code: Option<AnsiCode>) -> Self {
- self.foreground = foreground_code;
- self
- }
- pub fn background(mut self, background_code: Option<AnsiCode>) -> Self {
- self.background = background_code;
- self
- }
- pub fn bold(mut self, bold_code: Option<AnsiCode>) -> Self {
- self.bold = bold_code;
- self
- }
- pub fn dim(mut self, dim_code: Option<AnsiCode>) -> Self {
- self.dim = dim_code;
- self
- }
- pub fn italic(mut self, italic_code: Option<AnsiCode>) -> Self {
- self.italic = italic_code;
- self
- }
- pub fn underline(mut self, underline_code: Option<AnsiCode>) -> Self {
- self.underline = underline_code;
- self
- }
- pub fn blink_slow(mut self, slow_blink_code: Option<AnsiCode>) -> Self {
- self.slow_blink = slow_blink_code;
- self
- }
- pub fn blink_fast(mut self, fast_blink_code: Option<AnsiCode>) -> Self {
- self.fast_blink = fast_blink_code;
- self
- }
- pub fn reverse(mut self, reverse_code: Option<AnsiCode>) -> Self {
- self.reverse = reverse_code;
- self
- }
- pub fn hidden(mut self, hidden_code: Option<AnsiCode>) -> Self {
- self.hidden = hidden_code;
- self
- }
- pub fn strike(mut self, strike_code: Option<AnsiCode>) -> Self {
- self.strike = strike_code;
- self
- }
- pub fn clear(&mut self) {
- self.foreground = None;
- self.background = None;
- self.strike = None;
- self.hidden = None;
- self.reverse = None;
- self.slow_blink = None;
- self.fast_blink = None;
- self.underline = None;
- self.bold = None;
- self.dim = None;
- self.italic = None;
- }
- pub fn update_and_return_diff(
- &mut self,
- new_styles: &CharacterStyles,
- ) -> Option<CharacterStyles> {
- let mut diff: Option<CharacterStyles> = None;
-
- if new_styles.foreground == Some(AnsiCode::Reset)
- && new_styles.background == Some(AnsiCode::Reset)
- && new_styles.strike == Some(AnsiCode::Reset)
- && new_styles.hidden == Some(AnsiCode::Reset)
- && new_styles.reverse == Some(AnsiCode::Reset)
- && new_styles.fast_blink == Some(AnsiCode::Reset)
- && new_styles.slow_blink == Some(AnsiCode::Reset)
- && new_styles.underline == Some(AnsiCode::Reset)
- && new_styles.bold == Some(AnsiCode::Reset)
- && new_styles.dim == Some(AnsiCode::Reset)
- && new_styles.italic == Some(AnsiCode::Reset)
- {
- self.foreground = Some(AnsiCode::Reset);
- self.background = Some(AnsiCode::Reset);
- self.strike = Some(AnsiCode::Reset);
- self.hidden = Some(AnsiCode::Reset);
- self.reverse = Some(AnsiCode::Reset);
- self.fast_blink = Some(AnsiCode::Reset);
- self.slow_blink = Some(AnsiCode::Reset);
- self.underline = Some(AnsiCode::Reset);
- self.bold = Some(AnsiCode::Reset);
- self.dim = Some(AnsiCode::Reset);
- self.italic = Some(AnsiCode::Reset);
- return Some(*new_styles);
- };
-
- if self.foreground != new_styles.foreground {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.foreground(new_styles.foreground));
- self.foreground = new_styles.foreground;
- } else {
- diff = Some(CharacterStyles::new().foreground(new_styles.foreground));
- self.foreground = new_styles.foreground;
- }
- }
- if self.background != new_styles.background {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.background(new_styles.background));
- self.background = new_styles.background;
- } else {
- diff = Some(CharacterStyles::new().background(new_styles.background));
- self.background = new_styles.background;
- }
- }
- if self.strike != new_styles.strike {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.strike(new_styles.strike));
- self.strike = new_styles.strike;
- } else {
- diff = Some(CharacterStyles::new().strike(new_styles.strike));
- self.strike = new_styles.strike;
- }
- }
- if self.hidden != new_styles.hidden {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.hidden(new_styles.hidden));
- self.hidden = new_styles.hidden;
- } else {
- diff = Some(CharacterStyles::new().hidden(new_styles.hidden));
- self.hidden = new_styles.hidden;
- }
- }
- if self.reverse != new_styles.reverse {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.reverse(new_styles.reverse));
- self.reverse = new_styles.reverse;
- } else {
- diff = Some(CharacterStyles::new().reverse(new_styles.reverse));
- self.reverse = new_styles.reverse;
- }
- }
- if self.slow_blink != new_styles.slow_blink {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.blink_slow(new_styles.slow_blink));
- self.slow_blink = new_styles.slow_blink;
- } else {
- diff = Some(CharacterStyles::new().blink_slow(new_styles.slow_blink));
- self.slow_blink = new_styles.slow_blink;
- }
- }
- if self.fast_blink != new_styles.fast_blink {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.blink_fast(new_styles.fast_blink));
- self.fast_blink = new_styles.fast_blink;
- } else {
- diff = Some(CharacterStyles::new().blink_fast(new_styles.fast_blink));
- self.fast_blink = new_styles.fast_blink;
- }
- }
- if self.underline != new_styles.underline {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.underline(new_styles.underline));
- self.underline = new_styles.underline;
- } else {
- diff = Some(CharacterStyles::new().underline(new_styles.underline));
- self.underline = new_styles.underline;
- }
- }
- if self.bold != new_styles.bold {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.bold(new_styles.bold));
- self.bold = new_styles.bold;
- } else {
- diff = Some(CharacterStyles::new().bold(new_styles.bold));
- self.bold = new_styles.bold;
- }
- }
- if self.dim != new_styles.dim {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.dim(new_styles.dim));
- self.dim = new_styles.dim;
- } else {
- diff = Some(CharacterStyles::new().dim(new_styles.dim));
- self.dim = new_styles.dim;
- }
- }
- if self.italic != new_styles.italic {
- if let Some(new_diff) = diff.as_mut() {
- diff = Some(new_diff.italic(new_styles.italic));
- self.italic = new_styles.italic;
- } else {
- diff = Some(CharacterStyles::new().italic(new_styles.italic));
- self.italic = new_styles.italic;
- }
- }
- diff
- }
- pub fn reset_all(&mut self) {
- self.foreground = Some(AnsiCode::Reset);
- self.background = Some(AnsiCode::Reset);
- self.bold = Some(AnsiCode::Reset);
- self.dim = Some(AnsiCode::Reset);
- self.italic = Some(AnsiCode::Reset);
- self.underline = Some(AnsiCode::Reset);
- self.slow_blink = Some(AnsiCode::Reset);
- self.fast_blink = Some(AnsiCode::Reset);
- self.reverse = Some(AnsiCode::Reset);
- self.hidden = Some(AnsiCode::Reset);
- self.strike = Some(AnsiCode::Reset);
- }
- pub fn add_style_from_ansi_params(&mut self, params: &mut ParamsIter) {
- while let Some(param) = params.next() {
- match param {
- [] | [0] => self.reset_all(),
- [1] => *self = self.bold(Some(AnsiCode::On)),
- [2] => *self = self.dim(Some(AnsiCode::On)),
- [3] => *self = self.italic(Some(AnsiCode::On)),
- [4] => *self = self.underline(Some(AnsiCode::On)),
- [5] => *self = self.blink_slow(Some(AnsiCode::On)),
- [6] => *self = self.blink_fast(Some(AnsiCode::On)),
- [7] => *self = self.reverse(Some(AnsiCode::On)),
- [8] => *self = self.hidden(Some(AnsiCode::On)),
- [9] => *self = self.strike(Some(AnsiCode::On)),
- [21] => *self = self.bold(Some(AnsiCode::Reset)),
- [22] => {
- *self = self.bold(Some(AnsiCode::Reset));
- *self = self.dim(Some(AnsiCode::Reset));
- }
- [23] => *self = self.italic(Some(AnsiCode::Reset)),
- [24] => *self = self.underline(Some(AnsiCode::Reset)),
- [25] => {
- *self = self.blink_slow(Some(AnsiCode::Reset));
- *self = self.blink_fast(Some(AnsiCode::Reset));
- }
- [27] => *self = self.reverse(Some(AnsiCode::Reset)),
- [28] => *self = self.hidden(Some(AnsiCode::Reset)),
- [29] => *self = self.strike(Some(AnsiCode::Reset)),
- [30] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Black))),
- [31] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Red))),
- [32] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Green))),
- [33] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Yellow))),
- [34] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Blue))),
- [35] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Magenta))),
- [36] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::Cyan))),
- [37] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::White))),
- [38] => {
- let mut iter = params.map(|param| param[0]);
- if let Some(ansi_code) = parse_sgr_color(&mut iter) {
- *self = self.foreground(Some(ansi_code));
- }
- }
- [38, params @ ..] => {
- let rgb_start = if params.len() > 4 { 2 } else { 1 };
- let rgb_iter = params[rgb_start..].iter().copied();
- let mut iter = std::iter::once(params[0]).chain(rgb_iter);
- if let Some(ansi_code) = parse_sgr_color(&mut iter) {
- *self = self.foreground(Some(ansi_code));
- }
- }
- [39] => *self = self.foreground(Some(AnsiCode::Reset)),
- [40] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Black))),
- [41] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Red))),
- [42] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Green))),
- [43] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Yellow))),
- [44] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Blue))),
- [45] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Magenta))),
- [46] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::Cyan))),
- [47] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::White))),
- [48] => {
- let mut iter = params.map(|param| param[0]);
- if let Some(ansi_code) = parse_sgr_color(&mut iter) {
- *self = self.background(Some(ansi_code));
- }
- }
- [48, params @ ..] => {
- let rgb_start = if params.len() > 4 { 2 } else { 1 };
- let rgb_iter = params[rgb_start..].iter().copied();
- let mut iter = std::iter::once(params[0]).chain(rgb_iter);
- if let Some(ansi_code) = parse_sgr_color(&mut iter) {
- *self = self.background(Some(ansi_code));
- }
- }
- [49] => *self = self.background(Some(AnsiCode::Reset)),
- [90] => {
- *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlack)))
- }
- [91] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightRed))),
- [92] => {
- *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightGreen)))
- }
- [93] => {
- *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightYellow)))
- }
- [94] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightBlue))),
- [95] => {
- *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta)))
- }
- [96] => *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightCyan))),
- [97] => {
- *self = self.foreground(Some(AnsiCode::NamedColor(NamedColor::BrightWhite)))
- }
- [100] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlack)))
- }
- [101] => *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightRed))),
- [102] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightGreen)))
- }
- [103] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightYellow)))
- }
- [104] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightBlue)))
- }
- [105] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightMagenta)))
- }
- [106] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightCyan)))
- }
- [107] => {
- *self = self.background(Some(AnsiCode::NamedColor(NamedColor::BrightWhite)))
- }
- _ => {
- let _ = debug_log_to_file(format!("unhandled csi m code {:?}", param));
- return;
- }
- }
- }
- }
-}
-
-impl Display for CharacterStyles {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- if self.foreground == Some(AnsiCode::Reset)
- && self.background == Some(AnsiCode::Reset)
- && self.strike == Some(AnsiCode::Reset)
- && self.hidden == Some(AnsiCode::Reset)
- && self.reverse == Some(AnsiCode::Reset)
- && self.fast_blink == Some(AnsiCode::Reset)
- && self.slow_blink == Some(AnsiCode::Reset)
- && self.underline == Some(AnsiCode::Reset)
- && self.bold == Some(AnsiCode::Reset)
- && self.dim == Some(AnsiCode::Reset)
- && self.italic == Some(AnsiCode::Reset)
- {
- write!(f, "\u{1b}[m")?; // reset all
- return Ok(());
- }
- if let Some(ansi_code) = self.foreground {
- match ansi_code {
- AnsiCode::RgbCode((r, g, b)) => {
- write!(f, "\u{1b}[38;2;{};{};{}m", r, g, b)?;
- }
- AnsiCode::ColorIndex(color_index) => {
- write!(f, "\u{1b}[38;5;{}m", color_index)?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[39m")?;
- }
- AnsiCode::NamedColor(named_color) => {
- write!(f, "\u{1b}[{}m", named_color.to_foreground_ansi_code())?;
- }
- _ => {}
- }
- };
- if let Some(ansi_code) = self.background {
- match ansi_code {
- AnsiCode::RgbCode((r, g, b)) => {
- write!(f, "\u{1b}[48;2;{};{};{}m", r, g, b)?;
- }
- AnsiCode::ColorIndex(color_index) => {
- write!(f, "\u{1b}[48;5;{}m", color_index)?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[49m")?;
- }
- AnsiCode::NamedColor(named_color) => {
- write!(f, "\u{1b}[{}m", named_color.to_background_ansi_code())?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.strike {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[9m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[29m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.hidden {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[8m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[28m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.reverse {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[7m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[27m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.fast_blink {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[6m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[25m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.slow_blink {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[5m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[25m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.bold {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[1m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[22m\u{1b}[24m")?;
- // TODO: this cancels bold + underline, if this behaviour is indeed correct, we
- // need to properly handle it in the struct methods etc like dim
- }
- _ => {}
- }
- }
- // notice the order is important here, bold must be before underline
- // because the bold reset also resets underline, and would override it
- // otherwise
- if let Some(ansi_code) = self.underline {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[4m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[24m")?;
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.dim {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[2m")?;
- }
- AnsiCode::Reset => {
- if let Some(AnsiCode::Reset) = self.bold {
- // we only reset dim if both dim and bold should be reset
- write!(f, "\u{1b}[22m")?;
- }
- }
- _ => {}
- }
- }
- if let Some(ansi_code) = self.italic {
- match ansi_code {
- AnsiCode::On => {
- write!(f, "\u{1b}[3m")?;
- }
- AnsiCode::Reset => {
- write!(f, "\u{1b}[23m")?;
- }
- _ => {}
- }
- };
- Ok(())
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum CharsetIndex {
- G0,
- G1,
- G2,
- G3,
-}
-
-impl Default for CharsetIndex {
- fn default() -> Self {
- CharsetIndex::G0
- }
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum StandardCharset {
- Ascii,
- SpecialCharacterAndLineDrawing,
-}
-
-impl Default for StandardCharset {
- fn default() -> Self {
- StandardCharset::Ascii
- }
-}
-
-impl StandardCharset {
- /// Switch/Map character to the active charset. Ascii is the common case and
- /// for that we want to do as little as possible.
- #[inline]
- pub fn map(self, c: char) -> char {
- match self {
- StandardCharset::Ascii => c,
- StandardCharset::SpecialCharacterAndLineDrawing => match c {
- '`' => '◆',
- 'a' => '▒',
- 'b' => '␉',
- 'c' => '␌',
- 'd' => '␍',
- 'e' => '␊',
- 'f' => '°',
- 'g' => '±',
- 'h' => '␤',
- 'i' => '␋',
- 'j' => '┘',
- 'k' => '┐',
- 'l' => '┌',
- 'm' => '└',
- 'n' => '┼',
- 'o' => '⎺',
- 'p' => '⎻',
- 'q' => '─',
- 'r' => '⎼',
- 's' => '⎽',
- 't' => '├',
- 'u' => '┤',
- 'v' => '┴',
- 'w' => '┬',
- 'x' => '│',
- 'y' => '≤',
- 'z' => '≥',
- '{' => 'π',
- '|' => '≠',
- '}' => '£',
- '~' => '·',
- _ => c,
- },
- }
- }
-}
-
-#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
-pub struct Charsets([StandardCharset; 4]);
-
-impl Index<CharsetIndex> for Charsets {
- type Output = StandardCharset;
-
- fn index(&self, index: CharsetIndex) -> &StandardCharset {
- &self.0[index as usize]
- }
-}
-
-impl IndexMut<CharsetIndex> for Charsets {
- fn index_mut(&mut self, index: CharsetIndex) -> &mut StandardCharset {
- &mut self.0[index as usize]
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum CursorShape {
- Block,
- BlinkingBlock,
- Underline,
- BlinkingUnderline,
- Beam,
- BlinkingBeam,
-}
-
-#[derive(Clone, Debug)]
-pub struct Cursor {
- pub x: usize,
- pub y: usize,
- pub is_hidden: bool,
- pub pending_styles: CharacterStyles,
- pub charsets: Charsets,
- shape: CursorShape,
-}
-
-impl Cursor {
- pub fn new(x: usize, y: usize) -> Self {
- Cursor {
- x,
- y,
- is_hidden: false,
- pending_styles: CharacterStyles::new(),
- charsets: Default::default(),
- shape: CursorShape::Block,
- }
- }
- pub fn change_shape(&mut self, shape: CursorShape) {
- self.shape = shape;
- }
- pub fn get_shape(&self) -> CursorShape {
- self.shape
- }
-}
-
-#[derive(Clone, Copy)]
-pub struct TerminalCharacter {
- pub character: char,
- pub styles: CharacterStyles,
-}
-
-impl ::std::fmt::Debug for TerminalCharacter {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.character)
- }
-}
-
-impl TerminalCharacter {
- pub fn width(&self) -> usize {
- self.character.width().unwrap_or(0)
- }
-
- pub fn is_widechar(&self) -> bool {
- self.width() > 1
- }
-}
-
-fn parse_sgr_color(params: &mut dyn Iterator<Item = u16>) -> Option<AnsiCode> {
- match params.next() {
- Some(2) => Some(AnsiCode::RgbCode((
- u8::try_from(params.next()?).ok()?,
- u8::try_from(params.next()?).ok()?,
- u8::try_from(params.next()?).ok()?,
- ))),
- Some(5) => Some(AnsiCode::ColorIndex(u8::try_from(params.next()?).ok()?)),
- _ => None,
- }
-}
diff --git a/src/client/panes/terminal_pane.rs b/src/client/panes/terminal_pane.rs
deleted file mode 100644
index 5c4f55c8..00000000
--- a/src/client/panes/terminal_pane.rs
+++ /dev/null
@@ -1,362 +0,0 @@
-use std::fmt::Debug;
-use std::os::unix::io::RawFd;
-use std::time::Instant;
-
-use nix::pty::Winsize;
-use serde::{Deserialize, Serialize};
-
-use crate::panes::grid::Grid;
-use crate::panes::terminal_character::{
- CharacterStyles, CursorShape, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
-};
-use crate::pty::VteBytes;
-use crate::tab::Pane;
-
-#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum PaneId {
- Terminal(RawFd),
- Plugin(u32), // FIXME: Drop the trait object, make this a wrapper for the struct?
-}
-
-/// Contains the position and size of a [`Pane`], or more generally of any terminal, measured
-/// in character rows and columns.
-#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
-pub struct PositionAndSize {
- pub x: usize,
- pub y: usize,
- pub rows: usize,
- pub columns: usize,
- pub max_rows: Option<usize>,
- pub max_columns: Option<usize>,
-}
-
-impl From<Winsize> for PositionAndSize {
- fn from(winsize: Winsize) -> PositionAndSize {
- PositionAndSize {
- columns: winsize.ws_col as usize,
- rows: winsize.ws_row as usize,
- ..Default::default()
- }
- }
-}
-
-pub struct TerminalPane {
- pub grid: Grid,
- pub pid: RawFd,
- pub selectable: bool,
- pub position_and_size: PositionAndSize,
- pub position_and_size_override: Option<PositionAndSize>,
- pub max_height: Option<usize>,
- pub max_width: Option<usize>,
- pub active_at: Instant,
- vte_parser: vte::Parser,
-}
-
-impl Pane for TerminalPane {
- fn x(&self) -> usize {
- self.get_x()
- }
- fn y(&self) -> usize {
- self.get_y()
- }
- fn rows(&self) -> usize {
- self.get_rows()
- }
- fn columns(&self) -> usize {
- self.get_columns()
- }
- fn reset_size_and_position_override(&mut self) {
- self.position_and_size_override = None;
- self.reflow_lines();
- }
- fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize) {
- self.position_and_size.columns = position_and_size.columns;
- self.position_and_size.rows = position_and_size.rows;
- self.reflow_lines();
- }
- fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize) {
- let position_and_size_override = PositionAndSize {
- x,
- y,
- rows: size.rows,
- columns: size.columns,
- ..Default::default()
- };
- self.position_and_size_override = Some(position_and_size_override);
- self.reflow_lines();
- }
- fn handle_pty_bytes(&mut self, bytes: VteBytes) {
- for byte in bytes.iter() {
- self.vte_parser.advance(&mut self.grid, *byte);
- }
- self.set_should_render(true);
- }
- fn cursor_coordinates(&self) -> Option<(usize, usize)> {
- // (x, y)
- self.grid.cursor_coordinates()
- }
- fn adjust_input_to_terminal(&self, input_bytes: Vec<u8>) -> Vec<u8> {
- // there are some cases in which the terminal state means that input sent to it
- // needs to be adjusted.
- // here we match against those cases - if need be, we adjust the input and if not
- // we send back the original input
- match input_bytes.as_slice() {
- [27, 91, 68] => {
- // left arrow
- if self.grid.cursor_key_mode {
- // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
- // some editors will not show this
- return "OD".as_bytes().to_vec();
- }
- }
- [27, 91, 67] => {
- // right arrow
- if self.grid.cursor_key_mode {
- // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
- // some editors will not show this
- return "OC".as_bytes().to_vec();
- }
- }
- [27, 91, 65] => {
- // up arrow
- if self.grid.cursor_key_mode {
- // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
- // some editors will not show this
- return "OA".as_bytes().to_vec();
- }
- }
- [27, 91, 66] => {
- // down arrow
- if self.grid.cursor_key_mode {
- // please note that in the line below, there is an ANSI escape code (27) at the beginning of the string,
- // some editors will not show this
- return "OB".as_bytes().to_vec();
- }
- }
- _ => {}
- };
- input_bytes
- }
-
- fn position_and_size_override(&self) -> Option<PositionAndSize> {
- self.position_and_size_override
- }
- fn contains_widechar(&self) -> bool {
- self.grid.contains_widechar()
- }
- fn should_render(&self) -> bool {
- self.grid.should_render
- }
- fn set_should_render(&mut self, should_render: bool) {
- self.grid.should_render = should_render;
- }
- fn selectable(&self) -> bool {
- self.selectable
- }
- fn set_selectable(&mut self, selectable: bool) {
- self.selectable = selectable;
- }
- fn set_max_height(&mut self, max_height: usize) {
- self.max_height = Some(max_height);
- }
- fn set_max_width(&mut self, max_width: usize) {
- self.max_width = Some(max_width);
- }
- fn set_invisible_borders(&mut self, _invisible_borders: bool) {
- unimplemented!();
- }
- fn max_height(&self) -> Option<usize> {
- self.max_height
- }
- fn max_width(&self) -> Option<usize> {
- self.max_width
- }
- fn render(&mut self) -> Option<String> {
- if self.should_render() {
- let mut vte_output = String::new();
- let buffer_lines = &self.read_buffer_as_lines();
- let display_cols = self.get_columns();
- let mut character_styles = CharacterStyles::new();
- if self.grid.clear_viewport_before_rendering {
- for line_index in 0..self.grid.height {
- let x = self.get_x();
- let y = self.get_y();
- vte_output.push_str(&format!(
- "\u{1b}[{};{}H\u{1b}[m",
- y + line_index + 1,
- x + 1
- )); // goto row/col and reset styles
- for _col_index in 0..self.grid.width {
- vte_output.push(EMPTY_TERMINAL_CHARACTER.character);
- }
- }
- self.grid.clear_viewport_before_rendering = false;
- }
- for (row, line) in buffer_lines.iter().enumerate() {
- let x = self.get_x();
- let y = self.get_y();
- vte_output.push_str(&format!("\u{1b}[{};{}H\u{1b}[m", y + row + 1, x + 1)); // goto row/col and reset styles
- for (col, t_character) in line.iter().enumerate() {
- if col < display_cols {
- // in some cases (eg. while resizing) some characters will spill over
- // before they are corrected by the shell (for the prompt) or by reflowing
- // lines
- if let Some(new_styles) =
- character_styles.update_and_return_diff(&t_character.styles)
- {
- // the terminal keeps the previous styles as long as we're in the same
- // line, so we only want to update the new styles here (this also
- // includes resetting previous styles as needed)
- vte_output.push_str(&new_styles.to_string());
- }
- vte_output.push(t_character.character);
- }
- }
- character_styles.clear();
- }
- self.set_should_render(false);
- Some(vte_output)
- } else {
- None
- }
- }
- fn pid(&self) -> PaneId {
- PaneId::Terminal(self.pid)
- }
- fn reduce_height_down(&mut self, count: usize) {
- self.position_and_size.y += count;
- self.position_and_size.rows -= count;
- self.reflow_lines();
- }
- fn increase_height_down(&mut self, count: usize) {
- self.position_and_size.rows += count;
- self.reflow_lines();
- }
- fn increase_height_up(&mut self, count: usize) {
- self.position_and_size.y -= count;
- self.position_and_size.rows += count;
- self.reflow_lines();
- }
- fn reduce_height_up(&mut self, count: usize) {
- self.position_and_size.rows -= count;
- self.reflow_lines();
- }
- fn reduce_width_right(&mut self, count: usize) {
- self.position_and_size.x += count;
- self.position_and_size.columns -= count;
- self.reflow_lines();
- }
- fn reduce_width_left(&mut self, count: usize) {
- self.position_and_size.columns -= count;
- self.reflow_lines();
- }
- fn increase_width_left(&mut self, count: usize) {
- self.position_and_size.x -= count;
- self.position_and_size.columns += count;
- self.reflow_lines();
- }
- fn increase_width_right(&mut self, count: usize) {
- self.position_and_size.columns += count;
- self.reflow_lines();
- }
- fn push_down(&mut self, count: usize) {
- self.position_and_size.y += count;
- }
- fn push_right(&mut self, count: usize) {
- self.position_and_size.x += count;
- }
- fn pull_left(&mut self, count: usize) {
- self.position_and_size.x -= count;
- }
- fn pull_up(&mut self, count: usize) {
- self.position_and_size.y -= count;
- }
- fn scroll_up(&mut self, count: usize) {
- self.grid.move_viewport_up(count);
- self.set_should_render(true);
- }
- fn scroll_down(&mut self, count: usize) {
- self.grid.move_viewport_down(count);
- self.set_should_render(true);
- }
- fn clear_scroll(&mut self) {
- self.grid.reset_viewport();
- self.set_should_render(true);
- }
-
- fn active_at(&self) -> Instant {
- self.active_at
- }
-
- fn set_active_at(&mut self, time: Instant) {
- self.active_at = time;
- }
- fn cursor_shape_csi(&self) -> String {
- match self.grid.cursor_shape() {
- CursorShape::Block => "\u{1b}[0 q".to_string(),
- CursorShape::BlinkingBlock => "\u{1b}[1 q".to_string(),
- CursorShape::Underline => "\u{1b}[4 q".to_string(),
- CursorShape::BlinkingUnderline => "\u{1b}[3 q".to_string(),
- CursorShape::Beam => "\u{1b}[6 q".to_string(),
- CursorShape::BlinkingBeam => "\u{1b}[5 q".to_string(),
- }
- }
- fn drain_messages_to_pty(&mut self) -> Vec<Vec<u8>> {
- self.grid.pending_messages_to_pty.drain(..).collect()
- }
-}
-
-impl TerminalPane {
- pub fn new(pid: RawFd, position_and_size: PositionAndSize) -> TerminalPane {
- let grid = Grid::new(position_and_size.rows, position_and_size.columns);
- TerminalPane {
- pid,
- grid,
- selectable: true,
- position_and_size,
- position_and_size_override: None,
- max_height: None,
- max_width: None,
- vte_parser: vte::Parser::new(),
- active_at: Instant::now(),
- }
- }
- pub fn get_x(&self) -> usize {
- match self.position_and_size_override {
- Some(position_and_size_override) => position_and_size_override.x,
- None => self.position_and_size.x as usize,
- }
- }
- pub fn get_y(&self) -> usize {
- match self.position_and_size_override {
- Some(position_and_size_override) => position_and_size_override.y,
- None => self.position_and_size.y as usize,
- }
- }
- pub fn get_columns(&self) -> usize {
- match &self.position_and_size_override.as_ref() {
- Some(position_and_size_override) => position_and_size_override.columns,
- None => self.position_and_size.columns as usize,
- }
- }
- pub fn get_rows(&self) -> usize {
- match &self.position_and_size_override.as_ref() {
- Some(position_and_size_override) => position_and_size_override.rows,
- None => self.position_and_size.rows as usize,
- }
- }
- fn reflow_lines(&mut self) {
- let rows = self.get_rows();
- let columns = self.get_columns();
- self.grid.change_size(rows, columns);
- self.set_should_render(true);
- }
- pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
- self.grid.as_character_lines()
- }
- #[cfg(test)]
- pub fn cursor_coordinates(&self) -> Option<(usize, usize)> {
- // (x, y)
- self.grid.cursor_coordinates()
- }
-}
diff --git a/src/client/panes/unit/grid_tests.rs b/src/client/panes/unit/grid_tests.rs
deleted file mode 100644
index 8517a975..00000000
--- a/src/client/panes/unit/grid_tests.rs
+++ /dev/null
@@ -1,397 +0,0 @@
-use super::super::Grid;
-use ::insta::assert_snapshot;
-
-fn read_fixture(fixture_name: &str) -> Vec<u8> {
- let mut path_to_file = std::path::PathBuf::new();
- path_to_file.push("src");
- path_to_file.push("tests");
- path_to_file.push("fixtures");
- path_to_file.push(fixture_name);
- let content = std::fs::read(path_to_file)
- .unwrap_or_else(|_| panic!("could not read fixture {:?}", &fixture_name));
- content
-}
-
-#[test]
-fn vttest1_0() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-0";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest1_1() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-1";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest1_2() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-2";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest1_3() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-3";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest1_4() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-4";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest1_5() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest1-5";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_0() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-0";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_1() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-1";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_2() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-2";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_3() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-3";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_4() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-4";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_5() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-5";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_6() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-6";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_7() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-7";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_8() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-8";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_9() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-9";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_10() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-10";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_11() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-11";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_12() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-12";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_13() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-13";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest2_14() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest2-14";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest3_0() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(41, 110);
- let fixture_name = "vttest3-0";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_0() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-0";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_1() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-1";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_2() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-2";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_3() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-3";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_4() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-4";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn vttest8_5() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "vttest8-5";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn csi_b() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "csi-b";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn csi_capital_i() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "csi-capital-i";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn csi_capital_z() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "csi-capital-z";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid));
-}
-
-#[test]
-fn terminal_reports() {
- let mut vte_parser = vte::Parser::new();
- let mut grid = Grid::new(51, 97);
- let fixture_name = "terminal_reports";
- let content = read_fixture(fixture_name);
- for byte in content {
- vte_parser.advance(&mut grid, byte);
- }
- assert_snapshot!(format!("{:?}", grid.pending_messages_to_pty));
-}
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_b.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_b.snap
deleted file mode 100644
index 1d0c34e5..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_b.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): ffffff
-01 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_i.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_i.snap
deleted file mode 100644
index 11f7b8f8..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_i.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): foo
-01 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_z.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_z.snap
deleted file mode 100644
index 04f492b8..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__csi_capital_z.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): 12345678foo234567890
-01 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__terminal_reports.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__terminal_reports.snap
deleted file mode 100644
index b2dca273..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__terminal_reports.snap
+++ /dev/null
@@ -1,6 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid . pending_messages_to_pty)"
-
----
-[[27, 91, 63, 54, 99], [27, 91, 56, 59, 53, 49, 59, 57, 55, 116], [27, 91, 63, 54, 99], [27, 91, 48, 110], [27, 91, 49, 59, 49, 82]]
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap
deleted file mode 100644
index 8641098c..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_0.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): ********************************************************************************
-01 (C): *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
-02 (C): *+ +*
-03 (C): *+ +*
-04 (C): *+ +*
-05 (C): *+ +*
-06 (C): *+ +*
-07 (C): *+ +*
-08 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
-09 (C): *+ E E +*
-10 (C): *+ E The screen should be cleared, and have an unbroken bor- E +*
-11 (C): *+ E der of *'s and +'s around the edge, and exactly in the E +*
-12 (C): *+ E middle there should be a frame of E's around this text E +*
-13 (C): *+ E with one (1) free position around it. Push <RETURN> E +*
-14 (C): *+ E E +*
-15 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
-16 (C): *+ +*
-17 (C): *+ +*
-18 (C): *+ +*
-19 (C): *+ +*
-20 (C): *+ +*
-21 (C): *+ +*
-22 (C): *+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-23 (C): ********************************************************************************
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap
deleted file mode 100644
index 09663484..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_1.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): **************************************************************************************************************
-01 (C): *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
-02 (C): *+ +*
-03 (C): *+ +*
-04 (C): *+ +*
-05 (C): *+ +*
-06 (C): *+ +*
-07 (C): *+ +*
-08 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
-09 (C): *+ E E +*
-10 (C): *+ E The screen should be cleared, and have an unbroken bor- E +*
-11 (C): *+ E der of *'s and +'s around the edge, and exactly in the E +*
-12 (C): *+ E middle there should be a frame of E's around this text E +*
-13 (C): *+ E with one (1) free position around it. Push <RETURN> E +*
-14 (C): *+ E E +*
-15 (C): *+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +*
-16 (C): *+ +*
-17 (C): *+ +*
-18 (C): *+ +*
-19 (C): *+ +*
-20 (C): *+ +*
-21 (C): *+ +*
-22 (C): ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ *
-23 (C): **************************************************************************************************************
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap
deleted file mode 100644
index 05d80b1d..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_2.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Test of autowrap, mixing control and print characters.
-01 (C): The left/right margins should have letters in order:
-02 (C): I iJ
-03 (C): j
-04 (C): K K k
-05 (C): L l
-06 (C): M mN
-07 (C): n
-08 (C): O O o
-09 (C): P p
-10 (C): Q qR
-11 (C): r
-12 (C): S S s
-13 (C): T t
-14 (C): U uV
-15 (C): v
-16 (C): W W w
-17 (C): X x
-18 (C): Y yZ
-19 (C): z
-20 (C):
-21 (C): Push <RETURN>
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap
deleted file mode 100644
index 64c3e7b9..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_3.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Test of autowrap, mixing control and print characters.
-01 (C): The left/right margins should have letters in order:
-02 (C): I i
-03 (C): J j
-04 (C): K k
-05 (C): L l
-06 (C): M m
-07 (C): N n
-08 (C): O o
-09 (C): P p
-10 (C): Q q
-11 (C): R r
-12 (C): S s
-13 (C): T t
-14 (C): U u
-15 (C): V v
-16 (C): W w
-17 (C): X x
-18 (C): Y y
-19 (C): Z z
-20 (C):
-21 (C): Push <RETURN>
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap
deleted file mode 100644
index dcb417ae..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_4.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Test of cursor-control characters inside ESC sequences.
-01 (C): Below should be four identical lines:
-02 (C):
-03 (C): A B C D E F G H I
-04 (C): A B C D E F G H I
-05 (C): A B C D E F G H I
-06 (C): A B C D E F G H I
-07 (C):
-08 (C): Push <RETURN>
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap
deleted file mode 100644
index d044691c..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest1_5.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/grid.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Test of leading zeros in ESC sequences.
-01 (C): Two lines below you should see the sentence "This is a correct sentence".
-02 (C):
-03 (C): This is a correct sentence
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C): Push <RETURN>
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_0.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_0.snap
deleted file mode 100644
index f5bd11a5..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_0.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): **************************************************************************************************************
-01 (C): **************************************************
-02 (C): **************************************************************************************************************
-03 (C):
-04 (C): This should be three identical lines of *'s completely filling
-05 (C): the top of the screen without any empty lines between.
-06 (C): (Test of WRAP AROUND mode setting.)
-07 (C): Push <RETURN>
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_1.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_1.snap
deleted file mode 100644
index 148b8732..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_1.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): * * * * * * * * * * * * *
-01 (C): * * * * * * * * * * * * *
-02 (C):
-03 (C): Test of TAB setting/resetting. These two lines
-04 (C): should look the same. Push <RETURN>
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_10.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_10.snap
deleted file mode 100644
index 7cbb8545..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_10.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C):
-01 (C):
-02 (C):
-03 (C):
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C): This line should be the one above the bottom of the screen. Push <RETURN>
-23 (C): Origin mode test. This line should be at the bottom of the screen.
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_11.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_11.snap
deleted file mode 100644
index 1201290b..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_11.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): This line should be at the top of the screen. Push <RETURN>
-01 (C):
-02 (C):
-03 (C):
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C): Origin mode test. This line should be at the bottom of the screen.
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_12.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_12.snap
deleted file mode 100644
index fc15da63..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_12.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Graphic rendition test pattern:
-01 (C):
-02 (C):
-03 (C): vanilla bold
-04 (C):
-05 (C): underline bold underline
-06 (C):
-07 (C): blink bold blink
-08 (C):
-09 (C): underline blink bold underline blink
-10 (C):
-11 (C): negative bold negative
-12 (C):
-13 (C): underline negative bold underline negative
-14 (C):
-15 (C): blink negative bold blink negative
-16 (C):
-17 (C): underline blink negative bold underline blink negative
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C): Dark background. Push <RETURN>
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_13.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_13.snap
deleted file mode 100644
index ee8e30fb..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_13.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Graphic rendition test pattern:
-01 (C):
-02 (C):
-03 (C): vanilla bold
-04 (C):
-05 (C): underline bold underline
-06 (C):
-07 (C): blink bold blink
-08 (C):
-09 (C): underline blink bold underline blink
-10 (C):
-11 (C): negative bold negative
-12 (C):
-13 (C): underline negative bold underline negative
-14 (C):
-15 (C): blink negative bold blink negative
-16 (C):
-17 (C): underline blink negative bold underline blink negative
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C): Light background. Push <RETURN>
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_14.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_14.snap
deleted file mode 100644
index 2bdf1956..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_14.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): AAAAA
-01 (C): AAAAA
-02 (C): AAAAA
-03 (C): AAAAA
-04 (C):
-05 (C):
-06 (C):
-07 (C): normal bold underscored blinking reversed
-08 (C):
-09 (C): stars: ********** ********** ********** ********** **********
-10 (C):
-11 (C): line: ────────── ────────── ────────── ────────── ──────────
-12 (C):
-13 (C): x'es: xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx
-14 (C):
-15 (C): diamonds: ◆◆◆◆◆◆◆◆◆◆ ◆◆◆◆◆◆◆◆◆◆ ◆◆◆◆◆◆◆◆◆◆ ◆◆◆◆◆◆◆◆◆◆ ◆◆◆◆◆◆◆◆◆◆
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C): Test of the SAVE/RESTORE CURSOR feature. There should
-21 (C): be ten characters of each flavour, and a rectangle
-22 (C): of 5 x 4 A's filling the top left of the screen.
-23 (C): Push <RETURN>
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_2.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_2.snap
deleted file mode 100644
index 91a9a2e6..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_2.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
-01 (C): 123456789012345678901
-02 (C): This is 132 column mode, light background.
-03 (C): This is 132 column mode, light background.
-04 (C): This is 132 column mode, light background.
-05 (C): This is 132 column mode, light background.
-06 (C): This is 132 column mode, light background.
-07 (C): This is 132 column mode, light background.
-08 (C): This is 132 column mode, light background.
-09 (C): This is 132 column mode, light background.
-10 (C): This is 132 column mode, light background.
-11 (C): This is 132 column mode, light background.
-12 (C): This is 132 column mode, light background.
-13 (C): This is 132 column mode, light background.
-14 (C): This is 132 column mode, light background.
-15 (C): This is 132 column mode, light background.
-16 (C): This is 132 column mode, light background.
-17 (C): This is 132 column mode, light background.
-18 (C): This is 132 column mode, light background.
-19 (C): This is 132 column mode, light background.Push <RETURN>
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_3.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_3.snap
deleted file mode 100644
index c3881efb..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_3.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): 1234567890123456789012345678901234567890123456789012345678901234567890123456789
-01 (C):
-02 (C): This is 80 column mode, light background.
-03 (C): This is 80 column mode, light background.
-04 (C): This is 80 column mode, light background.
-05 (C): This is 80 column mode, light background.
-06 (C): This is 80 column mode, light background.
-07 (C): This is 80 column mode, light background.
-08 (C): This is 80 column mode, light background.
-09 (C): This is 80 column mode, light background.
-10 (C): This is 80 column mode, light background.
-11 (C): This is 80 column mode, light background.
-12 (C): This is 80 column mode, light background.
-13 (C): This is 80 column mode, light background.
-14 (C): This is 80 column mode, light background.
-15 (C): This is 80 column mode, light background.
-16 (C): This is 80 column mode, light background.
-17 (C): This is 80 column mode, light background.
-18 (C): This is 80 column mode, light background.
-19 (C): This is 80 column mode, light background.Push <RETURN>
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_4.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_4.snap
deleted file mode 100644
index 089f3d7d..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_4.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
-01 (C): 123456789012345678901
-02 (C): This is 132 column mode, dark background.
-03 (C): This is 132 column mode, dark background.
-04 (C): This is 132 column mode, dark background.
-05 (C): This is 132 column mode, dark background.
-06 (C): This is 132 column mode, dark background.
-07 (C): This is 132 column mode, dark background.
-08 (C): This is 132 column mode, dark background.
-09 (C): This is 132 column mode, dark background.
-10 (C): This is 132 column mode, dark background.
-11 (C): This is 132 column mode, dark background.
-12 (C): This is 132 column mode, dark background.
-13 (C): This is 132 column mode, dark background.
-14 (C): This is 132 column mode, dark background.
-15 (C): This is 132 column mode, dark background.
-16 (C): This is 132 column mode, dark background.
-17 (C): This is 132 column mode, dark background.
-18 (C): This is 132 column mode, dark background.
-19 (C): This is 132 column mode, dark background.Push <RETURN>
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_5.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_5.snap
deleted file mode 100644
index 6cf729c1..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_5.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): 1234567890123456789012345678901234567890123456789012345678901234567890123456789
-01 (C):
-02 (C): This is 80 column mode, dark background.
-03 (C): This is 80 column mode, dark background.
-04 (C): This is 80 column mode, dark background.
-05 (C): This is 80 column mode, dark background.
-06 (C): This is 80 column mode, dark background.
-07 (C): This is 80 column mode, dark background.
-08 (C): This is 80 column mode, dark background.
-09 (C): This is 80 column mode, dark background.
-10 (C): This is 80 column mode, dark background.
-11 (C): This is 80 column mode, dark background.
-12 (C): This is 80 column mode, dark background.
-13 (C): This is 80 column mode, dark background.
-14 (C): This is 80 column mode, dark background.
-15 (C): This is 80 column mode, dark background.
-16 (C): This is 80 column mode, dark background.
-17 (C): This is 80 column mode, dark background.
-18 (C): This is 80 column mode, dark background.
-19 (C): This is 80 column mode, dark background.Push <RETURN>
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_6.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_6.snap
deleted file mode 100644
index 3c6a1c77..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_6.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C):
-01 (C):
-02 (C):
-03 (C):
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (W): Push <RETURN>
-12 (W): Soft scroll down region [12..13] size 2 Line 29
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_7.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_7.snap
deleted file mode 100644
index 0c4c5cac..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_7.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (W): Push <RETURN>
-01 (W): Soft scroll down region [1..24] size 24 Line 29
-02 (W): Soft scroll down region [1..24] size 24 Line 28
-03 (W): Soft scroll down region [1..24] size 24 Line 27
-04 (W): Soft scroll down region [1..24] size 24 Line 26
-05 (W): Soft scroll down region [1..24] size 24 Line 25
-06 (W): Soft scroll down region [1..24] size 24 Line 24
-07 (W): Soft scroll down region [1..24] size 24 Line 23
-08 (W): Soft scroll down region [1..24] size 24 Line 22
-09 (W): Soft scroll down region [1..24] size 24 Line 21
-10 (W): Soft scroll down region [1..24] size 24 Line 20
-11 (W): Soft scroll down region [1..24] size 24 Line 19
-12 (W): Soft scroll down region [1..24] size 24 Line 18
-13 (W): Soft scroll down region [1..24] size 24 Line 17
-14 (W): Soft scroll down region [1..24] size 24 Line 16
-15 (W): Soft scroll down region [1..24] size 24 Line 15
-16 (W): Soft scroll down region [1..24] size 24 Line 14
-17 (W): Soft scroll down region [1..24] size 24 Line 13
-18 (W): Soft scroll down region [1..24] size 24 Line 12
-19 (W): Soft scroll down region [1..24] size 24 Line 11
-20 (W): Soft scroll down region [1..24] size 24 Line 10
-21 (W): Soft scroll down region [1..24] size 24 Line 9
-22 (W): Soft scroll down region [1..24] size 24 Line 8
-23 (W): Soft scroll down region [1..24] size 24 Line 7
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_8.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_8.snap
deleted file mode 100644
index 9e9ac891..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_8.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C):
-01 (C):
-02 (C):
-03 (C):
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (W): Push <RETURN>
-12 (W): Jump scroll down region [12..13] size 2 Line 29
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_9.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_9.snap
deleted file mode 100644
index 852203c8..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest2_9.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (W): Push <RETURN>
-01 (W): Jump scroll down region [1..24] size 24 Line 29
-02 (W): Jump scroll down region [1..24] size 24 Line 28
-03 (W): Jump scroll down region [1..24] size 24 Line 27
-04 (W): Jump scroll down region [1..24] size 24 Line 26
-05 (W): Jump scroll down region [1..24] size 24 Line 25
-06 (W): Jump scroll down region [1..24] size 24 Line 24
-07 (W): Jump scroll down region [1..24] size 24 Line 23
-08 (W): Jump scroll down region [1..24] size 24 Line 22
-09 (W): Jump scroll down region [1..24] size 24 Line 21
-10 (W): Jump scroll down region [1..24] size 24 Line 20
-11 (W): Jump scroll down region [1..24] size 24 Line 19
-12 (W): Jump scroll down region [1..24] size 24 Line 18
-13 (W): Jump scroll down region [1..24] size 24 Line 17
-14 (W): Jump scroll down region [1..24] size 24 Line 16
-15 (W): Jump scroll down region [1..24] size 24 Line 15
-16 (W): Jump scroll down region [1..24] size 24 Line 14
-17 (W): Jump scroll down region [1..24] size 24 Line 13
-18 (W): Jump scroll down region [1..24] size 24 Line 12
-19 (W): Jump scroll down region [1..24] size 24 Line 11
-20 (W): Jump scroll down region [1..24] size 24 Line 10
-21 (W): Jump scroll down region [1..24] size 24 Line 9
-22 (W): Jump scroll down region [1..24] size 24 Line 8
-23 (W): Jump scroll down region [1..24] size 24 Line 7
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest3_0.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest3_0.snap
deleted file mode 100644
index 6902cf25..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest3_0.snap
+++ /dev/null
@@ -1,47 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): Selected as G0 (with SI) Selected as G1 (with SO)
-01 (C):
-02 (C): Character set B (US ASCII)
-03 (C): !"#$%&'()*+,-./0123456789:;<=>? !"#$%&'()*+,-./0123456789:;<=>?
-04 (C): @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
-05 (C): `abcdefghijklmnopqrstuvwxyz{|}~ `abcdefghijklmnopqrstuvwxyz{|}~
-06 (C): Character set A (British)
-07 (C): !"#$%&'()*+,-./0123456789:;<=>? !"#$%&'()*+,-./0123456789:;<=>?
-08 (C): @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
-09 (C): `abcdefghijklmnopqrstuvwxyz{|}~ `abcdefghijklmnopqrstuvwxyz{|}~
-10 (C): Character set 0 (DEC Special graphics and line drawing)
-11 (C): !"#$%&'()*+,-./0123456789:;<=>? !"#$%&'()*+,-./0123456789:;<=>?
-12 (C): @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
-13 (C): ◆▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π≠£· ◆▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π≠£·
-14 (C): Character set 1 (DEC Alternate character ROM standard characters)
-15 (C): !"#$%&'()*+,-./0123456789:;<=>? !"#$%&'()*+,-./0123456789:;<=>?
-16 (C): @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
-17 (C): `abcdefghijklmnopqrstuvwxyz{|}~ `abcdefghijklmnopqrstuvwxyz{|}~
-18 (C): Character set 2 (DEC Alternate character ROM special graphics)
-19 (C): !"#$%&'()*+,-./0123456789:;<=>? !"#$%&'()*+,-./0123456789:;<=>?
-20 (C): @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
-21 (C): `abcdefghijklmnopqrstuvwxyz{|}~ `abcdefghijklmnopqrstuvwxyz{|}~
-22 (C):
-23 (C): These are the installed character sets. Push <RETURN>
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_0.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_0.snap
deleted file mode 100644
index 18cdd01b..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_0.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-01 (C): Top line: A's, bottom line: X's, this line, nothing more. Push <RETURN>
-02 (C):
-03 (C):
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C): XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_1.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_1.snap
deleted file mode 100644
index 7618e82b..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_1.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): A******************************************************************************BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-01 (C):
-02 (C):
-03 (C): Test of 'Insert Mode'. The top line should be 'A*** ... ***B'. Push <RETURN>
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_2.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_2.snap
deleted file mode 100644
index 0873ec23..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_2.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-01 (C):
-02 (C):
-03 (C): Test of 'Delete Character'. The top line should be 'AB'. Push <RETURN>
-04 (C):
-05 (C):
-06 (C):
-07 (C):
-08 (C):
-09 (C):
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_3.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_3.snap
deleted file mode 100644
index 049f5083..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_3.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-01 (C): BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
-02 (C): CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
-03 (C): The right column should be staggered DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
-04 (C): by one. Push <RETURN>EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
-05 (C): FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-06 (C): GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
-07 (C): HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
-08 (C): IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
-09 (C): JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
-10 (C): KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
-11 (C): LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
-12 (C): MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
-13 (C): NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
-14 (C): OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
-15 (C): PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
-16 (C): QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
-17 (C): RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
-18 (C): SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
-19 (C): TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
-20 (C): UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
-21 (C): VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
-22 (C): WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
-23 (C): XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_4.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_4.snap
deleted file mode 100644
index e7eb978f..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_4.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-01 (C): BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
-02 (C): CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
-03 (C): The right column should be staggered
-04 (C): by one. Push <RETURN>EEEEEEEEEEEEE
-05 (C): FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-06 (C): GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
-07 (C): HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
-08 (C): IIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
-09 (C): JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
-10 (C): KKKKKKKKKKKKKKKKKKKKKKKKKKKKK
-11 (C): LLLLLLLLLLLLLLLLLLLLLLLLLLLL
-12 (C): MMMMMMMMMMMMMMMMMMMMMMMMMMM
-13 (C): NNNNNNNNNNNNNNNNNNNNNNNNNN
-14 (C): OOOOOOOOOOOOOOOOOOOOOOOOO
-15 (C): PPPPPPPPPPPPPPPPPPPPPPPP
-16 (C): QQQQQQQQQQQQQQQQQQQQQQQ
-17 (C): RRRRRRRRRRRRRRRRRRRRRR
-18 (C): SSSSSSSSSSSSSSSSSSSSS
-19 (C): TTTTTTTTTTTTTTTTTTTT
-20 (C): UUUUUUUUUUUUUUUUUUU
-21 (C): VVVVVVVVVVVVVVVVVV
-22 (C): WWWWWWWWWWWWWWWWW
-23 (C): XXXXXXXXXXXXXXXX
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_5.snap b/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_5.snap
deleted file mode 100644
index 9b54cbc8..00000000
--- a/src/client/panes/unit/snapshots/zellij__client__panes__grid__grid_tests__vttest8_5.snap
+++ /dev/null
@@ -1,57 +0,0 @@
----
-source: src/client/panes/./unit/grid_tests.rs
-expression: "format!(\"{:?}\", grid)"
-
----
-00 (C): If your terminal has the ANSI 'Insert Character' function
-01 (C): (the VT102 does not), then you should see a line like this
-02 (C): A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-03 (C): below:
-04 (C):
-05 (C): A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
-06 (C):
-07 (C):
-08 (C):
-09 (C): Push <RETURN>
-10 (C):
-11 (C):
-12 (C):
-13 (C):
-14 (C):
-15 (C):
-16 (C):
-17 (C):
-18 (C):
-19 (C):
-20 (C):
-21 (C):
-22 (C):
-23 (C):
-24 (C):
-25 (C):
-26 (C):
-27 (C):
-28 (C):
-29 (C):
-30 (C):
-31 (C):
-32 (C):
-33 (C):
-34 (C):
-35 (C):
-36 (C):
-37 (C):
-38 (C):
-39 (C):
-40 (C):
-41 (C):
-42 (C):
-43 (C):
-44 (C):
-45 (C):
-46 (C):
-47 (C):
-48 (C):
-49 (C):
-50 (C):
-
diff --git a/src/client/tab.rs b/src/client/tab.rs
deleted file mode 100644
index 29a41530..00000000
--- a/src/client/tab.rs
+++ /dev/null
@@ -1,2274 +0,0 @@
-//! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size,
-//! as well as how they should be resized
-
-use crate::client::pane_resizer::PaneResizer;
-use crate::common::input::handler::parse_keys;
-use crate::common::thread_bus::ThreadSenders;
-use crate::layout::Layout;
-use crate::os_input_output::ServerOsApi;
-use crate::panes::{PaneId, PositionAndSize, TerminalPane};
-use crate::pty::{PtyInstruction, VteBytes};
-use crate::server::ServerInstruction;
-use crate::utils::shared::adjust_to_size;
-use crate::wasm_vm::PluginInstruction;
-use crate::{boundaries::Boundaries, panes::PluginPane};
-use serde::{Deserialize, Serialize};
-use std::os::unix::io::RawFd;
-use std::sync::mpsc::channel;
-use std::time::Instant;
-use std::{
- cmp::Reverse,
- collections::{BTreeMap, HashSet},
-};
-use zellij_tile::data::{Event, InputMode, ModeInfo, Palette};
-
-const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this
-
-// MIN_TERMINAL_HEIGHT here must be larger than the height of any of the status bars
-// this is a dirty hack until we implement fixed panes
-const MIN_TERMINAL_HEIGHT: usize = 3;
-const MIN_TERMINAL_WIDTH: usize = 4;
-
-type BorderAndPaneIds = (usize, Vec<PaneId>);
-
-fn split_vertically_with_gap(rect: &PositionAndSize) -> (PositionAndSize, PositionAndSize) {
- let width_of_each_half = (rect.columns - 1) / 2;
- let mut first_rect = *rect;
- let mut second_rect = *rect;
- if rect.columns % 2 == 0 {
- first_rect.columns = width_of_each_half + 1;
- } else {
- first_rect.columns = width_of_each_half;
- }
- second_rect.x = first_rect.x + first_rect.columns + 1;
- second_rect.columns = width_of_each_half;
- (first_rect, second_rect)
-}
-
-fn split_horizontally_with_gap(rect: &PositionAndSize) -> (PositionAndSize, PositionAndSize) {
- let height_of_each_half = (rect.rows - 1) / 2;
- let mut first_rect = *rect;
- let mut second_rect = *rect;
- if rect.rows % 2 == 0 {
- first_rect.rows = height_of_each_half + 1;
- } else {
- first_rect.rows = height_of_each_half;
- }
- second_rect.y = first_rect.y + first_rect.rows + 1;
- second_rect.rows = height_of_each_half;
- (first_rect, second_rect)
-}
-
-pub struct Tab {
- pub index: usize,
- pub position: usize,
- pub name: String,
- panes: BTreeMap<PaneId, Box<dyn Pane>>,
- panes_to_hide: HashSet<PaneId>,
- active_terminal: Option<PaneId>,
- max_panes: Option<usize>,
- full_screen_ws: PositionAndSize,
- fullscreen_is_active: bool,
- os_api: Box<dyn ServerOsApi>,
- pub senders: ThreadSenders,
- synchronize_is_active: bool,
- should_clear_display_before_rendering: bool,
- pub mode_info: ModeInfo,
- pub input_mode: InputMode,
- pub colors: Palette,
-}
-
-#[derive(Clone, Debug, Default, Serialize, Deserialize)]
-pub struct TabData {
- /* subset of fields to publish to plugins */
- pub position: usize,
- pub name: String,
- pub active: bool,
- pub mode_info: ModeInfo,
- pub input_mode: InputMode,
- pub colors: Palette,
-}
-
-// FIXME: Use a struct that has a pane_type enum, to reduce all of the duplication
-pub trait Pane {
- fn x(&self) -> usize;
- fn y(&self) -> usize;
- fn rows(&self) -> usize;
- fn columns(&self) -> usize;
- fn reset_size_and_position_override(&mut self);
- fn change_pos_and_size(&mut self, position_and_size: &PositionAndSize);
- fn override_size_and_position(&mut self, x: usize, y: usize, size: &PositionAndSize);
- fn handle_pty_bytes(&mut self, bytes: VteBytes);
- fn cursor_coordinates(&self) -> Option<(usize, usize)>;
- fn adjust_input_to_terminal(&self, input_bytes: Vec<u8>) -> Vec<u8>;
-
- fn position_and_size_override(&self) -> Option<PositionAndSize>;
- fn should_render(&self) -> bool;
- fn set_should_render(&mut self, should_render: bool);
- // FIXME: this method is used to trigger a force render to hide widechar problem
- // it should be removed when we can handle widechars
- fn contains_widechar(&self) -> bool;
- fn selectable(&self) -> bool;
- fn set_selectable(&mut self, selectable: bool);
- fn set_invisible_borders(&mut self, invisible_borders: bool);
- fn set_max_height(&mut self, max_height: usize);
- fn set_max_width(&mut self, max_width: usize);
- fn render(&mut self) -> Option<String>;
- fn pid(&self) -> PaneId;
- fn reduce_height_down(&mut self, count: usize);
- fn increase_height_down(&mut self, count: usize);
- fn increase_height_up(&mut self, count: usize);
- fn reduce_height_up(&mut self, count: usize);
- fn increase_width_right(&mut self, count: usize);
- fn reduce_width_right(&mut self, count: usize);
- fn reduce_width_left(&mut self, count: usize);
- fn increase_width_left(&mut self, count: usize);
- fn push_down(&mut self, count: usize);
- fn push_right(&mut self, count: usize);
- fn pull_left(&mut self, count: usize);
- fn pull_up(&mut self, count: usize);
- fn scroll_up(&mut self, count: usize);
- fn scroll_down(&mut self, count: usize);
- fn clear_scroll(&mut self);
- fn active_at(&self) -> Instant;
- fn set_active_at(&mut self, instant: Instant);
- fn cursor_shape_csi(&self) -> String {
- "\u{1b}[0 q".to_string() // default to non blinking block
- }
-
- fn right_boundary_x_coords(&self) -> usize {
- self.x() + self.columns()
- }
- fn bottom_boundary_y_coords(&self) -> usize {
- self.y() + self.rows()
- }
- fn is_directly_right_of(&self, other: &dyn Pane) -> bool {
- self.x() == other.x() + other.columns() + 1
- }
- fn is_directly_left_of(&self, other: &dyn Pane) -> bool {
- self.x() + self.columns() + 1 == other.x()
- }
- fn is_directly_below(&self, other: &dyn Pane) -> bool {
- self.y() == other.y() + other.rows() + 1
- }
- fn is_directly_above(&self, other: &dyn Pane) -> bool {
- self.y() + self.rows() + 1 == other.y()
- }
- fn horizontally_overlaps_with(&self, other: &dyn Pane) -> bool {
- (self.y() >= other.y() && self.y() <= (other.y() + other.rows()))
- || ((self.y() + self.rows()) <= (other.y() + other.rows())
- && (self.y() + self.rows()) > other.y())
- || (self.y() <= other.y() && (self.y() + self.rows() >= (other.y() + other.rows())))
- || (other.y() <= self.y() && (other.y() + other.rows() >= (self.y() + self.rows())))
- }
- fn get_horizontal_overlap_with(&self, other: &dyn Pane) -> usize {
- std::cmp::min(self.y() + self.rows(), other.y() + other.rows())
- - std::cmp::max(self.y(), other.y())
- }
- fn vertically_overlaps_with(&self, other: &dyn Pane) -> bool {
- (self.x() >= other.x() && self.x() <= (other.x() + other.columns()))
- || ((self.x() + self.columns()) <= (other.x() + other.columns())
- && (self.x() + self.columns()) > other.x())
- || (self.x() <= other.x()
- && (self.x() + self.columns() >= (other.x() + other.columns())))
- || (other.x() <= self.x()
- && (other.x() + other.columns() >= (self.x() + self.columns())))
- }
- fn get_vertical_overlap_with(&self, other: &dyn Pane) -> usize {
- std::cmp::min(self.x() + self.columns(), other.x() + other.columns())
- - std::cmp::max(self.x(), other.x())
- }
- fn position_and_size(&self) -> PositionAndSize {
- PositionAndSize {
- x: self.x(),
- y: self.y(),
- columns: self.columns(),
- rows: self.rows(),
- ..Default::default()
- }
- }
- fn can_increase_height_by(&self, increase_by: usize) -> bool {
- self.max_height()
- .map(|max_height| self.rows() + increase_by <= max_height)
- .unwrap_or(true)
- }
- fn can_increase_width_by(&self, increase_by: usize) -> bool {
- self.max_width()
- .map(|max_width| self.columns() + increase_by <= max_width)
- .unwrap_or(true)
- }
- fn can_reduce_height_by(&self, reduce_by: usize) -> bool {
- self.rows() > reduce_by && self.rows() - reduce_by >= self.min_height()
- }
- fn can_reduce_width_by(&self, reduce_by: usize) -> bool {
- self.columns() > reduce_by && self.columns() - reduce_by >= self.min_width()
- }
- fn min_width(&self) -> usize {
- MIN_TERMINAL_WIDTH
- }
- fn min_height(&self) -> usize {
- MIN_TERMINAL_HEIGHT
- }
- fn max_width(&self) -> Option<usize> {
- None
- }
- fn max_height(&self) -> Option<usize> {
- None
- }
- fn invisible_borders(&self) -> bool {
- false
- }
- fn drain_messages_to_pty(&mut self) -> Vec<Vec<u8>> {
- // TODO: this is only relevant to terminal panes
- // we should probably refactor away from this trait at some point
- vec![]
- }
-}
-
-impl Tab {
- // FIXME: Still too many arguments for clippy to be happy...
- #[allow(clippy::too_many_arguments)]
- pub fn new(
- index: usize,
- position: usize,
- name: String,
- full_screen_ws: &PositionAndSize,
- mut os_api: Box<dyn ServerOsApi>,
- senders: ThreadSenders,
- max_panes: Option<usize>,
- pane_id: Option<PaneId>,
- mode_info: ModeInfo,
- input_mode: InputMode,
- colors: Palette,
- ) -> Self {
- let panes = if let Some(PaneId::Terminal(pid)) = pane_id {
- let new_terminal = TerminalPane::new(pid, *full_screen_ws);
- os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- );
- let mut panes: BTreeMap<PaneId, Box<dyn Pane>> = BTreeMap::new();
- panes.insert(PaneId::Terminal(pid), Box::new(new_terminal));
- panes
- } else {
- BTreeMap::new()
- };
- Tab {
- index,
- position,
- panes,
- name,
- max_panes,
- panes_to_hide: HashSet::new(),
- active_terminal: pane_id,
- full_screen_ws: *full_screen_ws,
- fullscreen_is_active: false,
- synchronize_is_active: false,
- os_api,
- senders,
- should_clear_display_before_rendering: false,
- mode_info,
- input_mode,
- colors,
- }
- }
-
- pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>) {
- // TODO: this should be an attribute on Screen instead of full_screen_ws
- let free_space = PositionAndSize {
- x: 0,
- y: 0,
- rows: self.full_screen_ws.rows,
- columns: self.full_screen_ws.columns,
- ..Default::default()
- };
- self.panes_to_hide.clear();
- let positions_in_layout = layout.position_panes_in_space(&free_space);
- let mut positions_and_size = positions_in_layout.iter();
- for (pane_kind, terminal_pane) in self.panes.iter_mut() {
- // for now the layout only supports terminal panes
- if let PaneId::Terminal(pid) = pane_kind {
- match positions_and_size.next() {
- Some((_, position_and_size)) => {
- terminal_pane.reset_size_and_position_override();
- if let Some(max_rows) = position_and_size.max_rows {
- terminal_pane.set_max_height(max_rows);
- }
- if let Some(max_columns) = position_and_size.max_columns {
- terminal_pane.set_max_width(max_columns);
- }
- terminal_pane.change_pos_and_size(&position_and_size);
- self.os_api.set_terminal_size_using_fd(
- *pid,
- position_and_size.columns as u16,
- position_and_size.rows as u16,
- );
- }
- None => {
- // we filled the entire layout, no room for this pane
- // TODO: handle active terminal
- self.panes_to_hide.insert(PaneId::Terminal(*pid));
- }
- }
- }
- }
- let mut new_pids = new_pids.iter();
- for (layout, position_and_size) in positions_and_size {
- // Just a regular terminal
- if let Some(plugin) = &layout.plugin {
- let (pid_tx, pid_rx) = channel();
- self.senders
- .send_to_plugin(PluginInstruction::Load(pid_tx, plugin.clone()))
- .unwrap();
- let pid = pid_rx.recv().unwrap();
- let mut new_plugin = PluginPane::new(
- pid,
- *position_and_size,
- self.senders.to_plugin.as_ref().unwrap().clone(),
- );
- if let Some(max_rows) = position_and_size.max_rows {
- new_plugin.set_max_height(max_rows);
- }
- if let Some(max_columns) = position_and_size.max_columns {
- new_plugin.set_max_width(max_columns);
- }
- self.panes.insert(PaneId::Plugin(pid), Box::new(new_plugin));
- // Send an initial mode update to the newly loaded plugin only!
- self.senders
- .send_to_plugin(PluginInstruction::Update(
- Some(pid),
- Event::ModeUpdate(self.mode_info.clone()),
- ))
- .unwrap();
- } else {
- // there are still panes left to fill, use the pids we received in this method
- let pid = new_pids.next().unwrap(); // if this crashes it means we got less pids than there are panes in this layout
- let new_terminal = TerminalPane::new(*pid, *position_and_size);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- );
- self.panes
- .insert(PaneId::Terminal(*pid), Box::new(new_terminal));
- }
- }
- for unused_pid in new_pids {
- // this is a bit of a hack and happens because we don't have any central location that
- // can query the screen as to how many panes it needs to create a layout
- // fixing this will require a bit of an architecture change
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(*unused_pid)))
- .unwrap();
- }
- self.active_terminal = self.panes.iter().map(|(id, _)| id.to_owned()).next();
- self.render();
- }
- pub fn new_pane(&mut self, pid: PaneId) {
- self.close_down_to_max_terminals();
- if self.fullscreen_is_active {
- self.toggle_active_pane_fullscreen();
- }
- if !self.has_panes() {
- if let PaneId::Terminal(term_pid) = pid {
- let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- );
- self.panes.insert(pid, Box::new(new_terminal));
- self.active_terminal = Some(pid);
- }
- } else {
- // TODO: check minimum size of active terminal
-
- let (_largest_terminal_size, terminal_id_to_split) = self.get_panes().fold(
- (0, None),
- |(current_largest_terminal_size, current_terminal_id_to_split),
- id_and_terminal_to_check| {
- let (id_of_terminal_to_check, terminal_to_check) = id_and_terminal_to_check;
- let terminal_size = (terminal_to_check.rows() * CURSOR_HEIGHT_WIDTH_RATIO)
- * terminal_to_check.columns();
- let terminal_can_be_split = terminal_to_check.columns() >= MIN_TERMINAL_WIDTH
- && terminal_to_check.rows() >= MIN_TERMINAL_HEIGHT
- && ((terminal_to_check.columns() > terminal_to_check.min_width() * 2)
- || (terminal_to_check.rows() > terminal_to_check.min_height() * 2));
- if terminal_can_be_split && terminal_size > current_largest_terminal_size {
- (terminal_size, Some(*id_of_terminal_to_check))
- } else {
- (current_largest_terminal_size, current_terminal_id_to_split)
- }
- },
- );
- if terminal_id_to_split.is_none() {
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty
- .unwrap();
- return; // likely no terminal large enough to split
- }
- let terminal_id_to_split = terminal_id_to_split.unwrap();
- let terminal_to_split = self.panes.get_mut(&terminal_id_to_split).unwrap();
- let terminal_ws = PositionAndSize {
- rows: terminal_to_split.rows(),
- columns: terminal_to_split.columns(),
- x: terminal_to_split.x(),
- y: terminal_to_split.y(),
- ..Default::default()
- };
- if terminal_to_split.rows() * CURSOR_HEIGHT_WIDTH_RATIO > terminal_to_split.columns()
- && terminal_to_split.rows() > terminal_to_split.min_height() * 2
- {
- if let PaneId::Terminal(term_pid) = pid {
- let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws);
- let new_terminal = TerminalPane::new(term_pid, bottom_winsize);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- bottom_winsize.columns as u16,
- bottom_winsize.rows as u16,
- );
- terminal_to_split.change_pos_and_size(&top_winsize);
- self.panes.insert(pid, Box::new(new_terminal));
- if let PaneId::Terminal(terminal_id_to_split) = terminal_id_to_split {
- self.os_api.set_terminal_size_using_fd(
- terminal_id_to_split,
- top_winsize.columns as u16,
- top_winsize.rows as u16,
- );
- }
- self.active_terminal = Some(pid);
- }
- } else if terminal_to_split.columns() > terminal_to_split.min_width() * 2 {
- if let PaneId::Terminal(term_pid) = pid {
- let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws);
- let new_terminal = TerminalPane::new(term_pid, right_winsize);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- right_winsize.columns as u16,
- right_winsize.rows as u16,
- );
- terminal_to_split.change_pos_and_size(&left_winsize);
- self.panes.insert(pid, Box::new(new_terminal));
- if let PaneId::Terminal(terminal_id_to_split) = terminal_id_to_split {
- self.os_api.set_terminal_size_using_fd(
- terminal_id_to_split,
- left_winsize.columns as u16,
- left_winsize.rows as u16,
- );
- }
- }
- }
- self.active_terminal = Some(pid);
- self.render();
- }
- }
- pub fn horizontal_split(&mut self, pid: PaneId) {
- self.close_down_to_max_terminals();
- if self.fullscreen_is_active {
- self.toggle_active_pane_fullscreen();
- }
- if !self.has_panes() {
- if let PaneId::Terminal(term_pid) = pid {
- let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- );
- self.panes.insert(pid, Box::new(new_terminal));
- self.active_terminal = Some(pid);
- }
- } else if let PaneId::Terminal(term_pid) = pid {
- // TODO: check minimum size of active terminal
- let active_pane_id = &self.get_active_pane_id().unwrap();
- let active_pane = self.panes.get_mut(active_pane_id).unwrap();
- if active_pane.rows() < MIN_TERMINAL_HEIGHT * 2 + 1 {
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty
- .unwrap();
- return;
- }
- let terminal_ws = PositionAndSize {
- x: active_pane.x(),
- y: active_pane.y(),
- rows: active_pane.rows(),
- columns: active_pane.columns(),
- ..Default::default()
- };
- let (top_winsize, bottom_winsize) = split_horizontally_with_gap(&terminal_ws);
-
- active_pane.change_pos_and_size(&top_winsize);
-
- let new_terminal = TerminalPane::new(term_pid, bottom_winsize);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- bottom_winsize.columns as u16,
- bottom_winsize.rows as u16,
- );
- self.panes.insert(pid, Box::new(new_terminal));
-
- if let PaneId::Terminal(active_terminal_pid) = active_pane_id {
- self.os_api.set_terminal_size_using_fd(
- *active_terminal_pid,
- top_winsize.columns as u16,
- top_winsize.rows as u16,
- );
- }
-
- self.active_terminal = Some(pid);
- self.render();
- }
- }
- pub fn vertical_split(&mut self, pid: PaneId) {
- self.close_down_to_max_terminals();
- if self.fullscreen_is_active {
- self.toggle_active_pane_fullscreen();
- }
- if !self.has_panes() {
- if let PaneId::Terminal(term_pid) = pid {
- let new_terminal = TerminalPane::new(term_pid, self.full_screen_ws);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- new_terminal.columns() as u16,
- new_terminal.rows() as u16,
- );
- self.panes.insert(pid, Box::new(new_terminal));
- self.active_terminal = Some(pid);
- }
- } else if let PaneId::Terminal(term_pid) = pid {
- // TODO: check minimum size of active terminal
- let active_pane_id = &self.get_active_pane_id().unwrap();
- let active_pane = self.panes.get_mut(active_pane_id).unwrap();
- if active_pane.columns() < MIN_TERMINAL_WIDTH * 2 + 1 {
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(pid)) // we can't open this pane, close the pty
- .unwrap();
- return;
- }
- let terminal_ws = PositionAndSize {
- x: active_pane.x(),
- y: active_pane.y(),
- rows: active_pane.rows(),
- columns: active_pane.columns(),
- ..Default::default()
- };
- let (left_winsize, right_winsize) = split_vertically_with_gap(&terminal_ws);
-
- active_pane.change_pos_and_size(&left_winsize);
-
- let new_terminal = TerminalPane::new(term_pid, right_winsize);
- self.os_api.set_terminal_size_using_fd(
- new_terminal.pid,
- right_winsize.columns as u16,
- right_winsize.rows as u16,
- );
- self.panes.insert(pid, Box::new(new_terminal));
-
- if let PaneId::Terminal(active_terminal_pid) = active_pane_id {
- self.os_api.set_terminal_size_using_fd(
- *active_terminal_pid,
- left_winsize.columns as u16,
- left_winsize.rows as u16,
- );
- }
-
- self.active_terminal = Some(pid);
- self.render();
- }
- }
- pub fn get_active_pane(&self) -> Option<&dyn Pane> {
- // FIXME: Could use Option::map() here
- match self.get_active_pane_id() {
- Some(active_pane) => self.panes.get(&active_pane).map(Box::as_ref),
- None => None,
- }
- }
- fn get_active_pane_id(&self) -> Option<PaneId> {
- self.active_terminal
- }
- fn get_active_terminal_id(&self) -> Option<RawFd> {
- // FIXME: Is there a better way to do this?
- if let Some(PaneId::Terminal(pid)) = self.active_terminal {
- Some(pid)
- } else {
- None
- }
- }
- pub fn has_terminal_pid(&self, pid: RawFd) -> bool {
- self.panes.contains_key(&PaneId::Terminal(pid))
- }
- pub fn handle_pty_bytes(&mut self, pid: RawFd, bytes: VteBytes) {
- // if we don't have the terminal in self.terminals it's probably because
- // of a race condition where the terminal was created in pty but has not
- // yet been created in Screen. These events are currently not buffered, so
- // if you're debugging seemingly randomly missing stdout data, this is
- // the reason
- if let Some(terminal_output) = self.panes.get_mut(&PaneId::Terminal(pid)) {
- terminal_output.handle_pty_bytes(bytes);
- let messages_to_pty = terminal_output.drain_messages_to_pty();
- for message in messages_to_pty {
- self.write_to_pane_id(message, PaneId::Terminal(pid));
- }
- }
- }
- pub fn write_to_terminals_on_current_tab(&mut self, input_bytes: Vec<u8>) {
- let pane_ids = self.get_pane_ids();
- pane_ids.iter().for_each(|&pane_id| {
- self.write_to_pane_id(input_bytes.clone(), pane_id);
- });
- }
- pub fn write_to_active_terminal(&mut self, input_bytes: Vec<u8>) {
- self.write_to_pane_id(input_bytes, self.get_active_pane_id().unwrap());
- }
- pub fn write_to_pane_id(&mut self, input_bytes: Vec<u8>, pane_id: PaneId) {
- match pane_id {
- PaneId::Terminal(active_terminal_id) => {
- let active_terminal = self.panes.get(&pane_id).unwrap();
- let mut adjusted_input = active_terminal.adjust_input_to_terminal(input_bytes);
- self.os_api
- .write_to_tty_stdin(active_terminal_id, &mut adjusted_input)
- .expect("failed to write to terminal");
- self.os_api
- .tcdrain(active_terminal_id)
- .expect("failed to drain terminal");
- }
- PaneId::Plugin(pid) => {
- for key in parse_keys(&input_bytes) {
- self.senders
- .send_to_plugin(PluginInstruction::Update(Some(pid), Event::KeyPress(key)))
- .unwrap()
- }
- }
- }
- }
- pub fn get_active_terminal_cursor_position(&self) -> Option<(usize, usize)> {
- // (x, y)
- let active_terminal = &self.get_active_pane()?;
- active_terminal
- .cursor_coordinates()
- .map(|(x_in_terminal, y_in_terminal)| {
- let x = active_terminal.x() + x_in_terminal;
- let y = active_terminal.y() + y_in_terminal;
- (x, y)
- })
- }
- pub fn toggle_active_pane_fullscreen(&mut self) {
- if let Some(active_pane_id) = self.get_active_pane_id() {
- if self.fullscreen_is_active {
- for terminal_id in self.panes_to_hide.iter() {
- self.panes
- .get_mut(terminal_id)
- .unwrap()
- .set_should_render(true);
- }
- self.panes_to_hide.clear();
- let active_terminal = self.panes.get_mut(&active_pane_id).unwrap();
- active_terminal.reset_size_and_position_override();
- } else {
- let panes = self.get_panes();
- let pane_ids_to_hide =
- panes.filter_map(
- |(&id, _)| {
- if id != active_pane_id {
- Some(id)
- } else {
- None
- }
- },
- );
- self.panes_to_hide = pane_ids_to_hide.collect();
- if self.panes_to_hide.is_empty() {
- // nothing to do, pane is already as fullscreen as it can be, let's bail
- return;
- } else {
- let active_terminal = self.panes.get_mut(&active_pane_id).unwrap();
- active_terminal.override_size_and_position(
- self.full_screen_ws.x,
- self.full_screen_ws.y,
- &self.full_screen_ws,
- );
- }
- }
- let active_terminal = self.panes.get(&active_pane_id).unwrap();
- if let PaneId::Terminal(active_pid) = active_pane_id {
- self.os_api.set_terminal_size_using_fd(
- active_pid,
- active_terminal.columns() as u16,
- active_terminal.rows() as u16,
- );
- }
- self.render();
- self.toggle_fullscreen_is_active();
- }
- }
- pub fn toggle_fullscreen_is_active(&mut self) {
- self.fullscreen_is_active = !self.fullscreen_is_active;
- }
- pub fn set_force_render(&mut self) {
- for pane in self.panes.values_mut() {
- pane.set_should_render(true);
- }
- }
- pub fn is_sync_panes_active(&self) -> bool {
- self.synchronize_is_active
- }
- pub fn toggle_sync_panes_is_active(&mut self) {
- self.synchronize_is_active = !self.synchronize_is_active;
- }
- pub fn panes_contain_widechar(&self) -> bool {
- self.panes.iter().any(|(_, p)| p.contains_widechar())
- }
- pub fn render(&mut self) {
- if self.active_terminal.is_none() {
- // we might not have an active terminal if we closed the last pane
- // in that case, we should not render as the app is exiting
- return;
- }
- // if any pane contain widechar, all pane in the same row will messup. We should render them every time
- // FIXME: remove this when we can handle widechars correctly
- if self.panes_contain_widechar() {
- self.set_force_render()
- }
- let mut output = String::new();
- let mut boundaries = Boundaries::new(
- self.full_screen_ws.columns as u16,
- self.full_screen_ws.rows as u16,
- );
- let hide_cursor = "\u{1b}[?25l";
- output.push_str(hide_cursor);
- if self.should_clear_display_before_rendering {
- let clear_display = "\u{1b}[2J";
- output.push_str(clear_display);
- self.should_clear_display_before_rendering = false;
- }
- for (kind, pane) in self.panes.iter_mut() {
- if !self.panes_to_hide.contains(&pane.pid()) {
- match self.active_terminal.unwrap() == pane.pid() {
- true => {
- pane.set_active_at(Instant::now());
- boundaries.add_rect(pane.as_ref(), self.mode_info.mode, Some(self.colors))
- }
- false => boundaries.add_rect(pane.as_ref(), self.mode_info.mode, None),
- }
- if let Some(vte_output) = pane.render() {
- let vte_output = if let PaneId::Terminal(_) = kind {
- vte_output
- } else {
- adjust_to_size(&vte_output, pane.rows(), pane.columns())
- };
- // FIXME: Use Termion for cursor and style clearing?
- output.push_str(&format!(
- "\u{1b}[{};{}H\u{1b}[m{}",
- pane.y() + 1,
- pane.x() + 1,
- vte_output
- ));
- }
- }
- }
-
- // TODO: only render (and calculate) boundaries if there was a resize
- output.push_str(&boundaries.vte_output());
-
- match self.get_active_terminal_cursor_position() {
- Some((cursor_position_x, cursor_position_y)) => {
- let show_cursor = "\u{1b}[?25h";
- let change_cursor_shape = self.get_active_pane().unwrap().cursor_shape_csi();
- let goto_cursor_position = &format!(
- "\u{1b}[{};{}H\u{1b}[m{}",
- cursor_position_y + 1,
- cursor_position_x + 1,
- change_cursor_shape
- ); // goto row/col
- output.push_str(show_cursor);
- output.push_str(goto_cursor_position);
- }
- None => {
- let hide_cursor = "\u{1b}[?25l";
- output.push_str(hide_cursor);
- }
- }
-
- self.senders
- .send_to_server(ServerInstruction::Render(Some(output)))
- .unwrap();
- }
- fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
- self.panes.iter()
- }
- // FIXME: This is some shameful duplication...
- fn get_selectable_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
- self.panes.iter().filter(|(_, p)| p.selectable())
- }
- fn has_panes(&self) -> bool {
- let mut all_terminals = self.get_panes();
- all_terminals.next().is_some()
- }
- fn has_selectable_panes(&self) -> bool {
- let mut all_terminals = self.get_selectable_panes();
- all_terminals.next().is_some()
- }
- fn next_active_pane(&self, panes: Vec<PaneId>) -> Option<PaneId> {
- panes
- .into_iter()
- .rev()
- .find(|pid| self.panes.get(pid).unwrap().selectable())
- }
- fn pane_ids_directly_left_of(&self, id: &PaneId) -> Option<Vec<PaneId>> {
- let mut ids = vec![];
- let terminal_to_check = self.panes.get(id).unwrap();
- if terminal_to_check.x() == 0 {
- return None;
- }
- for (&pid, terminal) in self.get_panes() {
- if terminal.x() + terminal.columns() == terminal_to_check.x() - 1 {
- ids.push(pid);
- }
- }
- if ids.is_empty() {
- None
- } else {
- Some(ids)
- }
- }
- fn pane_ids_directly_right_of(&self, id: &PaneId) -> Option<Vec<PaneId>> {
- let mut ids = vec![];
- let terminal_to_check = self.panes.get(id).unwrap();
- for (&pid, terminal) in self.get_panes() {
- if terminal.x() == terminal_to_check.x() + terminal_to_check.columns() + 1 {
- ids.push(pid);
- }
- }
- if ids.is_empty() {
- None
- } else {
- Some(ids)
- }
- }
- fn pane_ids_directly_below(&self, id: &PaneId) -> Option<Vec<PaneId>> {
- let mut ids = vec![];
- let terminal_to_check = self.panes.get(id).unwrap();
- for (&pid, terminal) in self.get_panes() {
- if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() + 1 {
- ids.push(pid);
- }
- }
- if ids.is_empty() {
- None
- } else {
- Some(ids)
- }
- }
- fn pane_ids_directly_above(&self, id: &PaneId) -> Option<Vec<PaneId>> {
- let mut ids = vec![];
- let terminal_to_check = self.panes.get(id).unwrap();
- for (&pid, terminal) in self.get_panes() {
- if terminal.y() + terminal.rows() + 1 == terminal_to_check.y() {
- ids.push(pid);
- }
- }
- if ids.is_empty() {
- None
- } else {
- Some(ids)
- }
- }
- fn panes_top_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> {
- self.panes
- .keys()
- .map(|t_id| self.panes.get(&t_id).unwrap().as_ref())
- .filter(|terminal| terminal.pid() != pane.pid() && terminal.y() == pane.y())
- .collect()
- }
- fn panes_bottom_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> {
- self.panes
- .keys()
- .map(|t_id| self.panes.get(&t_id).unwrap().as_ref())
- .filter(|terminal| {
- terminal.pid() != pane.pid()
- && terminal.y() + terminal.rows() == pane.y() + pane.rows()
- })
- .collect()
- }
- fn panes_right_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> {
- self.panes
- .keys()
- .map(|t_id| self.panes.get(&t_id).unwrap().as_ref())
- .filter(|terminal| {
- terminal.pid() != pane.pid()
- && terminal.x() + terminal.columns() == pane.x() + pane.columns()
- })
- .collect()
- }
- fn panes_left_aligned_with_pane(&self, pane: &dyn Pane) -> Vec<&dyn Pane> {
- self.panes
- .keys()
- .map(|t_id| self.panes.get(&t_id).unwrap().as_ref())
- .filter(|terminal| terminal.pid() != pane.pid() && terminal.x() == pane.x())
- .collect()
- }
- fn right_aligned_contiguous_panes_above(
- &self,
- id: &PaneId,
- terminal_borders_to_the_right: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self
- .panes
- .get(id)
- .expect("terminal id does not exist")
- .as_ref();
- let mut right_aligned_terminals = self.panes_right_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- right_aligned_terminals.sort_by_key(|a| Reverse(a.y()));
- for terminal in right_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.y() + terminal.rows() + 1 == terminal_to_check.y() {
- terminals.push(terminal);
- }
- }
- // top-most border aligned with a pane border to the right
- let mut top_resize_border = 0;
- for terminal in &terminals {
- let bottom_terminal_boundary = terminal.y() + terminal.rows();
- if terminal_borders_to_the_right
- .get(&(bottom_terminal_boundary + 1))
- .is_some()
- && top_resize_border < bottom_terminal_boundary
- {
- top_resize_border = bottom_terminal_boundary + 1;
- }
- }
- terminals.retain(|terminal| terminal.y() >= top_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let top_resize_border = if terminals.is_empty() {
- terminal_to_check.y()
- } else {
- top_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (top_resize_border, terminal_ids)
- }
- fn right_aligned_contiguous_panes_below(
- &self,
- id: &PaneId,
- terminal_borders_to_the_right: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self
- .panes
- .get(id)
- .expect("terminal id does not exist")
- .as_ref();
- let mut right_aligned_terminals = self.panes_right_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- right_aligned_terminals.sort_by_key(|a| a.y());
- for terminal in right_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() + 1 {
- terminals.push(terminal);
- }
- }
- // bottom-most border aligned with a pane border to the right
- let mut bottom_resize_border = self.full_screen_ws.rows;
- for terminal in &terminals {
- let top_terminal_boundary = terminal.y();
- if terminal_borders_to_the_right
- .get(&(top_terminal_boundary))
- .is_some()
- && top_terminal_boundary < bottom_resize_border
- {
- bottom_resize_border = top_terminal_boundary;
- }
- }
- terminals.retain(|terminal| terminal.y() + terminal.rows() <= bottom_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let bottom_resize_border = if terminals.is_empty() {
- terminal_to_check.y() + terminal_to_check.rows()
- } else {
- bottom_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (bottom_resize_border, terminal_ids)
- }
- fn left_aligned_contiguous_panes_above(
- &self,
- id: &PaneId,
- terminal_borders_to_the_left: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self
- .panes
- .get(id)
- .expect("terminal id does not exist")
- .as_ref();
- let mut left_aligned_terminals = self.panes_left_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- left_aligned_terminals.sort_by_key(|a| Reverse(a.y()));
- for terminal in left_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.y() + terminal.rows() + 1 == terminal_to_check.y() {
- terminals.push(terminal);
- }
- }
- // top-most border aligned with a pane border to the right
- let mut top_resize_border = 0;
- for terminal in &terminals {
- let bottom_terminal_boundary = terminal.y() + terminal.rows();
- if terminal_borders_to_the_left
- .get(&(bottom_terminal_boundary + 1))
- .is_some()
- && top_resize_border < bottom_terminal_boundary
- {
- top_resize_border = bottom_terminal_boundary + 1;
- }
- }
- terminals.retain(|terminal| terminal.y() >= top_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let top_resize_border = if terminals.is_empty() {
- terminal_to_check.y()
- } else {
- top_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (top_resize_border, terminal_ids)
- }
- fn left_aligned_contiguous_panes_below(
- &self,
- id: &PaneId,
- terminal_borders_to_the_left: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self
- .panes
- .get(id)
- .expect("terminal id does not exist")
- .as_ref();
- let mut left_aligned_terminals = self.panes_left_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- left_aligned_terminals.sort_by_key(|a| a.y());
- for terminal in left_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.y() == terminal_to_check.y() + terminal_to_check.rows() + 1 {
- terminals.push(terminal);
- }
- }
- // bottom-most border aligned with a pane border to the left
- let mut bottom_resize_border = self.full_screen_ws.rows;
- for terminal in &terminals {
- let top_terminal_boundary = terminal.y();
- if terminal_borders_to_the_left
- .get(&(top_terminal_boundary))
- .is_some()
- && top_terminal_boundary < bottom_resize_border
- {
- bottom_resize_border = top_terminal_boundary;
- }
- }
- terminals.retain(|terminal| {
- // terminal.y() + terminal.rows() < bottom_resize_border
- terminal.y() + terminal.rows() <= bottom_resize_border
- });
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let bottom_resize_border = if terminals.is_empty() {
- terminal_to_check.y() + terminal_to_check.rows()
- } else {
- bottom_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (bottom_resize_border, terminal_ids)
- }
- fn top_aligned_contiguous_panes_to_the_left(
- &self,
- id: &PaneId,
- terminal_borders_above: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self
- .panes
- .get(id)
- .expect("terminal id does not exist")
- .as_ref();
- let mut top_aligned_terminals = self.panes_top_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- top_aligned_terminals.sort_by_key(|a| Reverse(a.x()));
- for terminal in top_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.x() + terminal.columns() + 1 == terminal_to_check.x() {
- terminals.push(terminal);
- }
- }
- // leftmost border aligned with a pane border above
- let mut left_resize_border = 0;
- for terminal in &terminals {
- let right_terminal_boundary = terminal.x() + terminal.columns();
- if terminal_borders_above
- .get(&(right_terminal_boundary + 1))
- .is_some()
- && left_resize_border < right_terminal_boundary
- {
- left_resize_border = right_terminal_boundary + 1;
- }
- }
- terminals.retain(|terminal| terminal.x() >= left_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let left_resize_border = if terminals.is_empty() {
- terminal_to_check.x()
- } else {
- left_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (left_resize_border, terminal_ids)
- }
- fn top_aligned_contiguous_panes_to_the_right(
- &self,
- id: &PaneId,
- terminal_borders_above: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self.panes.get(id).unwrap().as_ref();
- let mut top_aligned_terminals = self.panes_top_aligned_with_pane(terminal_to_check);
- // terminals that are next to each other up to current
- top_aligned_terminals.sort_by_key(|a| a.x());
- for terminal in top_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.x() == terminal_to_check.x() + terminal_to_check.columns() + 1 {
- terminals.push(terminal);
- }
- }
- // rightmost border aligned with a pane border above
- let mut right_resize_border = self.full_screen_ws.columns;
- for terminal in &terminals {
- let left_terminal_boundary = terminal.x();
- if terminal_borders_above
- .get(&left_terminal_boundary)
- .is_some()
- && right_resize_border > left_terminal_boundary
- {
- right_resize_border = left_terminal_boundary;
- }
- }
- terminals.retain(|terminal| terminal.x() + terminal.columns() <= right_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let right_resize_border = if terminals.is_empty() {
- terminal_to_check.x() + terminal_to_check.columns()
- } else {
- right_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (right_resize_border, terminal_ids)
- }
- fn bottom_aligned_contiguous_panes_to_the_left(
- &self,
- id: &PaneId,
- terminal_borders_below: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self.panes.get(id).unwrap().as_ref();
- let mut bottom_aligned_terminals = self.panes_bottom_aligned_with_pane(terminal_to_check);
- bottom_aligned_terminals.sort_by_key(|a| Reverse(a.x()));
- // terminals that are next to each other up to current
- for terminal in bottom_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.x() + terminal.columns() + 1 == terminal_to_check.x() {
- terminals.push(terminal);
- }
- }
- // leftmost border aligned with a pane border above
- let mut left_resize_border = 0;
- for terminal in &terminals {
- let right_terminal_boundary = terminal.x() + terminal.columns();
- if terminal_borders_below
- .get(&(right_terminal_boundary + 1))
- .is_some()
- && left_resize_border < right_terminal_boundary
- {
- left_resize_border = right_terminal_boundary + 1;
- }
- }
- terminals.retain(|terminal| terminal.x() >= left_resize_border);
- // if there are no adjacent panes to resize, we use the border of the main pane we're
- // resizing
- let left_resize_border = if terminals.is_empty() {
- terminal_to_check.x()
- } else {
- left_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (left_resize_border, terminal_ids)
- }
- fn bottom_aligned_contiguous_panes_to_the_right(
- &self,
- id: &PaneId,
- terminal_borders_below: &HashSet<usize>,
- ) -> BorderAndPaneIds {
- let mut terminals = vec![];
- let terminal_to_check = self.panes.get(id).unwrap().as_ref();
- let mut bottom_aligned_terminals = self.panes_bottom_aligned_with_pane(terminal_to_check);
- bottom_aligned_terminals.sort_by_key(|a| a.x());
- // terminals that are next to each other up to current
- for terminal in bottom_aligned_terminals {
- let terminal_to_check = terminals.last().unwrap_or(&terminal_to_check);
- if terminal.x() == terminal_to_check.x() + terminal_to_check.columns() + 1 {
- terminals.push(terminal);
- }
- }
- // leftmost border aligned with a pane border above
- let mut right_resize_border = self.full_screen_ws.columns;
- for terminal in &terminals {
- let left_terminal_boundary = terminal.x();
- if terminal_borders_below
- .get(&left_terminal_boundary)
- .is_some()
- && right_resize_border > left_terminal_boundary
- {
- right_resize_border = left_terminal_boundary;
- }
- }
- terminals.retain(|terminal| terminal.x() + terminal.columns() <= right_resize_border);
- let right_resize_border = if terminals.is_empty() {
- terminal_to_check.x() + terminal_to_check.columns()
- } else {
- right_resize_border
- };
- let terminal_ids: Vec<PaneId> = terminals.iter().map(|t| t.pid()).collect();
- (right_resize_border, terminal_ids)
- }
- fn reduce_pane_height_down(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.reduce_height_down(count);
- if let PaneId::Terminal(pid) = id {
- self.os_api.set_terminal_size_using_fd(
- *pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn reduce_pane_height_up(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.reduce_height_up(count);
- if let PaneId::Terminal(pid) = id {
- self.os_api.set_terminal_size_using_fd(
- *pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn increase_pane_height_down(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.increase_height_down(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn increase_pane_height_up(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.increase_height_up(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn increase_pane_width_right(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.increase_width_right(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn increase_pane_width_left(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.increase_width_left(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn reduce_pane_width_right(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.reduce_width_right(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn reduce_pane_width_left(&mut self, id: &PaneId, count: usize) {
- let terminal = self.panes.get_mut(id).unwrap();
- terminal.reduce_width_left(count);
- if let PaneId::Terminal(pid) = terminal.pid() {
- self.os_api.set_terminal_size_using_fd(
- pid,
- terminal.columns() as u16,
- terminal.rows() as u16,
- );
- }
- }
- fn pane_is_between_vertical_borders(
- &self,
- id: &PaneId,
- left_border_x: usize,
- right_border_x: usize,
- ) -> bool {
- let terminal = self
- .panes
- .get(id)
- .expect("could not find terminal to check between borders");
- terminal.x() >= left_border_x && terminal.x() + terminal.columns() <= right_border_x
- }
- fn pane_is_between_horizontal_borders(
- &self,
- id: &PaneId,
- top_border_y: usize,
- bottom_border_y: usize,
- ) -> bool {
- let terminal = self
- .panes
- .get(id)
- .expect("could not find terminal to check between borders");
- terminal.y() >= top_border_y && terminal.y() + terminal.rows() <= bottom_border_y
- }
- fn reduce_pane_and_surroundings_up(&mut self, id: &PaneId, count: usize) {
- let mut terminals_below = self
- .pane_ids_directly_below(&id)
- .expect("can't reduce pane size up if there are no terminals below");
- let terminal_borders_below: HashSet<usize> = terminals_below
- .iter()
- .map(|t| self.panes.get(t).unwrap().x())
- .collect();
- let (left_resize_border, terminals_to_the_left) =
- self.bottom_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_below);
- let (right_resize_border, terminals_to_the_right) =
- self.bottom_aligned_contiguous_panes_to_the_right(&id, &terminal_borders_below);
- terminals_below.retain(|t| {
- self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border)
- });
- self.reduce_pane_height_up(&id, count);
- for terminal_id in terminals_below {
- self.increase_pane_height_up(&terminal_id, count);
- }
- for terminal_id in terminals_to_the_left
- .iter()
- .chain(terminals_to_the_right.iter())
- {
- self.reduce_pane_height_up(&terminal_id, count);
- }
- }
- fn reduce_pane_and_surroundings_down(&mut self, id: &PaneId, count: usize) {
- let mut terminals_above = self
- .pane_ids_directly_above(&id)
- .expect("can't reduce pane size down if there are no terminals above");
- let terminal_borders_above: HashSet<usize> = terminals_above
- .iter()
- .map(|t| self.panes.get(t).unwrap().x())
- .collect();
- let (left_resize_border, terminals_to_the_left) =
- self.top_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_above);
- let (right_resize_border, terminals_to_the_right) =
- self.top_aligned_contiguous_panes_to_the_right(&id, &terminal_borders_above);
- terminals_above.retain(|t| {
- self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border)
- });
- self.reduce_pane_height_down(&id, count);
- for terminal_id in terminals_above {
- self.increase_pane_height_down(&terminal_id, count);
- }
- for terminal_id in terminals_to_the_left
- .iter()
- .chain(terminals_to_the_right.iter())
- {
- self.reduce_pane_height_down(&terminal_id, count);
- }
- }
- fn reduce_pane_and_surroundings_right(&mut self, id: &PaneId, count: usize) {
- let mut terminals_to_the_left = self
- .pane_ids_directly_left_of(&id)
- .expect("can't reduce pane size right if there are no terminals to the left");
- let terminal_borders_to_the_left: HashSet<usize> = terminals_to_the_left
- .iter()
- .map(|t| self.panes.get(t).unwrap().y())
- .collect();
- let (top_resize_border, terminals_above) =
- self.left_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_left);
- let (bottom_resize_border, terminals_below) =
- self.left_aligned_contiguous_panes_below(&id, &terminal_borders_to_the_left);
- terminals_to_the_left.retain(|t| {
- self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border)
- });
- self.reduce_pane_width_right(&id, count);
- for terminal_id in terminals_to_the_left {
- self.increase_pane_width_right(&terminal_id, count);
- }
- for terminal_id in terminals_above.iter().chain(terminals_below.iter()) {
- self.reduce_pane_width_right(&terminal_id, count);
- }
- }
- fn reduce_pane_and_surroundings_left(&mut self, id: &PaneId, count: usize) {
- let mut terminals_to_the_right = self
- .pane_ids_directly_right_of(&id)
- .expect("can't reduce pane size left if there are no terminals to the right");
- let terminal_borders_to_the_right: HashSet<usize> = terminals_to_the_right
- .iter()
- .map(|t| self.panes.get(t).unwrap().y())
- .collect();
- let (top_resize_border, terminals_above) =
- self.right_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_right);
- let (bottom_resize_border, terminals_below) =
- self.right_aligned_contiguous_panes_below(&id, &terminal_borders_to_the_right);
- terminals_to_the_right.retain(|t| {
- self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border)
- });
- self.reduce_pane_width_left(&id, count);
- for terminal_id in terminals_to_the_right {
- self.increase_pane_width_left(&terminal_id, count);
- }
- for terminal_id in terminals_above.iter().chain(terminals_below.iter()) {
- self.reduce_pane_width_left(&terminal_id, count);
- }
- }
- fn increase_pane_and_surroundings_up(&mut self, id: &PaneId, count: usize) {
- let mut terminals_above = self
- .pane_ids_directly_above(&id)
- .expect("can't increase pane size up if there are no terminals above");
- let terminal_borders_above: HashSet<usize> = terminals_above
- .iter()
- .map(|t| self.panes.get(t).unwrap().x())
- .collect();
- let (left_resize_border, terminals_to_the_left) =
- self.top_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_above);
- let (right_resize_border, terminals_to_the_right) =
- self.top_aligned_contiguous_panes_to_the_right(&id, &terminal_borders_above);
- terminals_above.retain(|t| {
- self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border)
- });
- self.increase_pane_height_up(&id, count);
- for terminal_id in terminals_above {
- self.reduce_pane_height_up(&terminal_id, count);
- }
- for terminal_id in terminals_to_the_left
- .iter()
- .chain(terminals_to_the_right.iter())
- {
- self.increase_pane_height_up(&terminal_id, count);
- }
- }
- fn increase_pane_and_surroundings_down(&mut self, id: &PaneId, count: usize) {
- let mut terminals_below = self
- .pane_ids_directly_below(&id)
- .expect("can't increase pane size down if there are no terminals below");
- let terminal_borders_below: HashSet<usize> = terminals_below
- .iter()
- .map(|t| self.panes.get(t).unwrap().x())
- .collect();
- let (left_resize_border, terminals_to_the_left) =
- self.bottom_aligned_contiguous_panes_to_the_left(&id, &terminal_borders_below);
- let (right_resize_border, terminals_to_the_right) =
- self.bottom_aligned_contiguous_panes_to_the_right(&id, &terminal_borders_below);
- terminals_below.retain(|t| {
- self.pane_is_between_vertical_borders(t, left_resize_border, right_resize_border)
- });
- self.increase_pane_height_down(&id, count);
- for terminal_id in terminals_below {
- self.reduce_pane_height_down(&terminal_id, count);
- }
- for terminal_id in terminals_to_the_left
- .iter()
- .chain(terminals_to_the_right.iter())
- {
- self.increase_pane_height_down(&terminal_id, count);
- }
- }
- fn increase_pane_and_surroundings_right(&mut self, id: &PaneId, count: usize) {
- let mut terminals_to_the_right = self
- .pane_ids_directly_right_of(&id)
- .expect("can't increase pane size right if there are no terminals to the right");
- let terminal_borders_to_the_right: HashSet<usize> = terminals_to_the_right
- .iter()
- .map(|t| self.panes.get(t).unwrap().y())
- .collect();
- let (top_resize_border, terminals_above) =
- self.right_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_right);
- let (bottom_resize_border, terminals_below) =
- self.right_aligned_contiguous_panes_below(&id, &terminal_borders_to_the_right);
- terminals_to_the_right.retain(|t| {
- self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border)
- });
- self.increase_pane_width_right(&id, count);
- for terminal_id in terminals_to_the_right {
- self.reduce_pane_width_right(&terminal_id, count);
- }
- for terminal_id in terminals_above.iter().chain(terminals_below.iter()) {
- self.increase_pane_width_right(&terminal_id, count);
- }
- }
- fn increase_pane_and_surroundings_left(&mut self, id: &PaneId, count: usize) {
- let mut terminals_to_the_left = self
- .pane_ids_directly_left_of(&id)
- .expect("can't increase pane size right if there are no terminals to the right");
- let terminal_borders_to_the_left: HashSet<usize> = terminals_to_the_left
- .iter()
- .map(|t| self.panes.get(t).unwrap().y())
- .collect();
- let (top_resize_border, terminals_above) =
- self.left_aligned_contiguous_panes_above(&id, &terminal_borders_to_the_left);
- let (bottom_resize_border, terminals_below) =
- self.left_aligned_contiguous_panes_below(&id, &terminal_borders_to_the_left);
- terminals_to_the_left.retain(|t| {
- self.pane_is_between_horizontal_borders(t, top_resize_border, bottom_resize_border)
- });
- self.increase_pane_width_left(&id, count);
- for terminal_id in terminals_to_the_left {
- self.reduce_pane_width_left(&terminal_id, count);
- }
- for terminal_id in terminals_above.iter().chain(terminals_below.iter()) {
- self.increase_pane_width_left(&terminal_id, count);
- }
- }
- fn can_increase_pane_and_surroundings_right(
- &self,
- pane_id: &PaneId,
- increase_by: usize,
- ) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let can_increase_pane_size = pane
- .max_width()
- .map(|max_width| pane.columns() + increase_by <= max_width)
- .unwrap_or(true); // no max width, increase to your heart's content
- if !can_increase_pane_size {
- return false;
- }
- let mut new_pos_and_size_for_pane = pane.position_and_size();
- new_pos_and_size_for_pane.columns += increase_by;
-
- if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(&pane_id) {
- return panes_to_the_right.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.columns() > increase_by && p.columns() - increase_by >= p.min_width()
- });
- } else {
- false
- }
- }
- fn can_increase_pane_and_surroundings_left(
- &self,
- pane_id: &PaneId,
- increase_by: usize,
- ) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let can_increase_pane_size = pane
- .max_width()
- .map(|max_width| pane.columns() + increase_by <= max_width)
- .unwrap_or(true); // no max width, increase to your heart's content
- if !can_increase_pane_size {
- return false;
- }
- let mut new_pos_and_size_for_pane = pane.position_and_size();
- new_pos_and_size_for_pane.x = new_pos_and_size_for_pane.x.saturating_sub(increase_by);
-
- if let Some(panes_to_the_left) = self.pane_ids_directly_left_of(&pane_id) {
- return panes_to_the_left.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.columns() > increase_by && p.columns() - increase_by >= p.min_width()
- });
- } else {
- false
- }
- }
- fn can_increase_pane_and_surroundings_down(
- &self,
- pane_id: &PaneId,
- increase_by: usize,
- ) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let can_increase_pane_size = pane
- .max_height()
- .map(|max_height| pane.rows() + increase_by <= max_height)
- .unwrap_or(true); // no max width, increase to your heart's content
- if !can_increase_pane_size {
- return false;
- }
- let mut new_pos_and_size_for_pane = pane.position_and_size();
- new_pos_and_size_for_pane.rows += increase_by;
-
- if let Some(panes_below) = self.pane_ids_directly_below(&pane_id) {
- return panes_below.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.rows() > increase_by && p.rows() - increase_by >= p.min_height()
- });
- } else {
- false
- }
- }
- fn can_increase_pane_and_surroundings_up(&self, pane_id: &PaneId, increase_by: usize) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let can_increase_pane_size = pane
- .max_height()
- .map(|max_height| pane.rows() + increase_by <= max_height)
- .unwrap_or(true); // no max width, increase to your heart's content
- if !can_increase_pane_size {
- return false;
- }
- let mut new_pos_and_size_for_pane = pane.position_and_size();
- new_pos_and_size_for_pane.y = new_pos_and_size_for_pane.y.saturating_sub(increase_by);
-
- if let Some(panes_above) = self.pane_ids_directly_above(&pane_id) {
- return panes_above.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.rows() > increase_by && p.rows() - increase_by >= p.min_height()
- });
- } else {
- false
- }
- }
- fn can_reduce_pane_and_surroundings_right(&self, pane_id: &PaneId, reduce_by: usize) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let pane_columns = pane.columns();
- let can_reduce_pane_size =
- pane_columns > reduce_by && pane_columns - reduce_by >= pane.min_width();
- if !can_reduce_pane_size {
- return false;
- }
- if let Some(panes_to_the_left) = self.pane_ids_directly_left_of(&pane_id) {
- return panes_to_the_left.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.max_width()
- .map(|max_width| p.columns() + reduce_by <= max_width)
- .unwrap_or(true) // no max width, increase to your heart's content
- });
- } else {
- false
- }
- }
- fn can_reduce_pane_and_surroundings_left(&self, pane_id: &PaneId, reduce_by: usize) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let pane_columns = pane.columns();
- let can_reduce_pane_size =
- pane_columns > reduce_by && pane_columns - reduce_by >= pane.min_width();
- if !can_reduce_pane_size {
- return false;
- }
- if let Some(panes_to_the_right) = self.pane_ids_directly_right_of(&pane_id) {
- return panes_to_the_right.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.max_width()
- .map(|max_width| p.columns() + reduce_by <= max_width)
- .unwrap_or(true) // no max width, increase to your heart's content
- });
- } else {
- false
- }
- }
- fn can_reduce_pane_and_surroundings_down(&self, pane_id: &PaneId, reduce_by: usize) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let pane_rows = pane.rows();
- let can_reduce_pane_size =
- pane_rows > reduce_by && pane_rows - reduce_by >= pane.min_height();
- if !can_reduce_pane_size {
- return false;
- }
- if let Some(panes_above) = self.pane_ids_directly_above(&pane_id) {
- return panes_above.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.max_height()
- .map(|max_height| p.rows() + reduce_by <= max_height)
- .unwrap_or(true) // no max height, increase to your heart's content
- });
- } else {
- false
- }
- }
- fn can_reduce_pane_and_surroundings_up(&self, pane_id: &PaneId, reduce_by: usize) -> bool {
- let pane = self.panes.get(pane_id).unwrap();
- let pane_rows = pane.rows();
- let can_reduce_pane_size =
- pane_rows > reduce_by && pane_rows - reduce_by >= pane.min_height();
- if !can_reduce_pane_size {
- return false;
- }
- if let Some(panes_below) = self.pane_ids_directly_below(&pane_id) {
- return panes_below.iter().all(|id| {
- let p = self.panes.get(id).unwrap();
- p.max_height()
- .map(|max_height| p.rows() + reduce_by <= max_height)
- .unwrap_or(true) // no max height, increase to your heart's content
- });
- } else {
- false
- }
- }
- pub fn resize_whole_tab(&mut self, new_screen_size: PositionAndSize) {
- if self.fullscreen_is_active {
- // this is not ideal, we can improve this
- self.toggle_active_pane_fullscreen();
- }
- if let Some((column_difference, row_difference)) =
- PaneResizer::new(&mut self.panes, &mut self.os_api)
- .resize(self.full_screen_ws, new_screen_size)
- {
- self.should_clear_display_before_rendering = true;
- self.full_screen_ws.columns =
- (self.full_screen_ws.columns as isize + column_difference) as usize;
- self.full_screen_ws.rows =
- (self.full_screen_ws.rows as isize + row_difference) as usize;
- };
- }
- pub fn resize_left(&mut self) {
- // TODO: find out by how much we actually reduced and only reduce by that much
- let count = 10;
- if let Some(active_pane_id) = self.get_active_pane_id() {
- if self.can_increase_pane_and_surroundings_left(&active_pane_id, count) {
- self.increase_pane_and_surroundings_left(&active_pane_id, count);
- } else if self.can_reduce_pane_and_surroundings_left(&active_pane_id, count) {
- self.reduce_pane_and_surroundings_left(&active_pane_id, count);
- }
- }
- self.render();
- }
- pub fn resize_right(&mut self) {
- // TODO: find out by how much we actually reduced and only reduce by that much
- let count = 10;
- if let Some(active_pane_id) = self.get_active_pane_id() {
- if self.can_increase_pane_and_surroundings_right(&active_pane_id, count) {
- self.increase_pane_and_surroundings_right(&active_pane_id, count);
- } else if self.can_reduce_pane_and_surroundings_right(&active_pane_id, count) {
- self.reduce_pane_and_surroundings_right(&active_pane_id, count);
- }
- }
- self.render();
- }
- pub fn resize_down(&mut self) {
- // TODO: find out by how much we actually reduced and only reduce by that much
- let count = 2;
- if let Some(active_pane_id) = self.get_active_pane_id() {
- if self.can_increase_pane_and_surroundings_down(&active_pane_id, count) {
- self.increase_pane_and_surroundings_down(&active_pane_id, count);
- } else if self.can_reduce_pane_and_surroundings_down(&active_pane_id, count) {
- self.reduce_pane_and_surroundings_down(&active_pane_id, count);
- }
- }
- self.render();
- }
- pub fn resize_up(&mut self) {
- // TODO: find out by how much we actually reduced and only reduce by that much
- let count = 2;
- if let Some(active_pane_id) = self.get_active_pane_id() {
- if self.can_increase_pane_and_surroundings_up(&active_pane_id, count) {
- self.increase_pane_and_surroundings_up(&active_pane_id, count);
- } else if self.can_reduce_pane_and_surroundings_up(&active_pane_id, count) {
- self.reduce_pane_and_surroundings_up(&active_pane_id, count);
- }
- }
- self.render();
- }
- pub fn move_focus(&mut self) {
- if !self.has_selectable_panes() {
- return;
- }
- if self.fullscreen_is_active {
- return;
- }
- let active_terminal_id = self.get_active_pane_id().unwrap();
- let terminal_ids: Vec<PaneId> = self.get_selectable_panes().map(|(&pid, _)| pid).collect(); // TODO: better, no allocations
- let first_terminal = terminal_ids.get(0).unwrap();
- let active_terminal_id_position = terminal_ids
- .iter()
- .position(|id| id == &active_terminal_id)
- .unwrap();
- if let Some(next_terminal) = terminal_ids.get(active_terminal_id_position + 1) {
- self.active_terminal = Some(*next_terminal);
- } else {
- self.active_terminal = Some(*first_terminal);
- }
- self.render();
- }
- pub fn focus_next_pane(&mut self) {
- if !self.has_selectable_panes() {
- return;
- }
- if self.fullscreen_is_active {
- return;
- }
- let active_pane_id = self.get_active_pane_id().unwrap();
- let mut panes: Vec<(&PaneId, &Box<dyn Pane>)> = self.get_selectable_panes().collect();
- panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| {
- if a_pane.y() == b_pane.y() {
- a_pane.x().cmp(&b_pane.x())
- } else {
- a_pane.y().cmp(&b_pane.y())
- }
- });
- let first_pane = panes.get(0).unwrap();
- let active_pane_position = panes
- .iter()
- .position(|(id, _)| *id == &active_pane_id) // TODO: better
- .unwrap();
- if let Some(next_pane) = panes.get(active_pane_position + 1) {
- self.active_terminal = Some(*next_pane.0);
- } else {
- self.active_terminal = Some(*first_pane.0);
- }
- self.render();
- }
- pub fn focus_previous_pane(&mut self) {
- if !self.has_selectable_panes() {
- return;
- }
- if self.fullscreen_is_active {
- return;
- }
- let active_pane_id = self.get_active_pane_id().unwrap();
- let mut panes: Vec<(&PaneId, &Box<dyn Pane>)> = self.get_selectable_panes().collect();
- panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| {
- if a_pane.y() == b_pane.y() {
- a_pane.x().cmp(&b_pane.x())
- } else {
- a_pane.y().cmp(&b_pane.y())
- }
- });
- let last_pane = panes.last().unwrap();
- let active_pane_position = panes
- .iter()
- .position(|(id, _)| *id == &active_pane_id) // TODO: better
- .unwrap();
- if active_pane_position == 0 {
- self.active_terminal = Some(*last_pane.0);
- } else {
- self.active_terminal = Some(*panes.get(active_pane_position - 1).unwrap().0);
- }
- self.render();
- }
- // returns a boolean that indicates whether the focus moved
- pub fn move_focus_left(&mut self) -> bool {
- if !self.has_selectable_panes() {
- return false;
- }
- if self.fullscreen_is_active {
- return false;
- }
- let active_terminal = self.get_active_pane();
- if let Some(active) = active_terminal {
- let terminals = self.get_selectable_panes();
- let next_index = terminals
- .enumerate()
- .filter(|(_, (_, c))| {
- c.is_directly_left_of(active) && c.horizontally_overlaps_with(active)
- })
- .max_by_key(|(_, (_, c))| c.active_at())
- .map(|(_, (pid, _))| pid);
- match next_index {
- Some(&p) => {
- self.active_terminal = Some(p);
- self.render();
- return true;
- }
- None => {
- self.active_terminal = Some(active.pid());
- }
- }
- } else {
- self.active_terminal = Some(active_terminal.unwrap().pid());
- }
- false
- }
- pub fn move_focus_down(&mut self) {
- if !self.has_selectable_panes() {
- return;
- }
- if self.fullscreen_is_active {
- return;
- }
- let active_terminal = self.get_active_pane();
- if let Some(active) = active_terminal {
- let terminals = self.get_selectable_panes();
- let next_index = terminals
- .enumerate()
- .filter(|(_, (_, c))| {
- c.is_directly_below(active) && c.vertically_overlaps_with(active)
- })
- .max_by_key(|(_, (_, c))| c.active_at())
- .map(|(_, (pid, _))| pid);
- match next_index {
- Some(&p) => {
- self.active_terminal = Some(p);
- }
- None => {
- self.active_terminal = Some(active.pid());
- }
- }
- } else {
- self.active_terminal = Some(active_terminal.unwrap().pid());
- }
- self.render();
- }
- pub fn move_focus_up(&mut self) {
- if !self.has_selectable_panes() {
- return;
- }
- if self.fullscreen_is_active {
- return;
- }
- let active_terminal = self.get_active_pane();
- if let Some(active) = active_terminal {
- let terminals = self.get_selectable_panes();
- let next_index = terminals
- .enumerate()
- .filter(|(_, (_, c))| {
- c.is_directly_above(active) && c.vertically_overlaps_with(active)
- })
- .max_by_key(|(_, (_, c))| c.active_at())
- .map(|(_, (pid, _))| pid);
- match next_index {
- Some(&p) => {
- self.active_terminal = Some(p);
- }
- None => {
- self.active_terminal = Some(active.pid());
- }
- }
- } else {
- self.active_terminal = Some(active_terminal.unwrap().pid());
- }
- self.render();
- }
- // returns a boolean that indicates whether the focus moved
- pub fn move_focus_right(&mut self) -> bool {
- if !self.has_selectable_panes() {
- return false;
- }
- if self.fullscreen_is_active {
- return false;
- }
- let active_terminal = self.get_active_pane();
- if let Some(active) = active_terminal {
- let terminals = self.get_selectable_panes();
- let next_index = terminals
- .enumerate()
- .filter(|(_, (_, c))| {
- c.is_directly_right_of(active) && c.horizontally_overlaps_with(active)
- })
- .max_by_key(|(_, (_, c))| c.active_at())
- .map(|(_, (pid, _))| pid);
- match next_index {
- Some(&p) => {
- self.active_terminal = Some(p);
- self.render();
- return true;
- }
- None => {
- self.active_terminal = Some(active.pid());
- }
- }
- } else {
- self.active_terminal = Some(active_terminal.unwrap().pid());
- }
- false
- }
- fn horizontal_borders(&self, terminals: &[PaneId]) -> HashSet<usize> {
- terminals.iter().fold(HashSet::new(), |mut borders, t| {
- let terminal = self.panes.get(t).unwrap();
- borders.insert(terminal.y());
- borders.insert(terminal.y() + terminal.rows() + 1); // 1 for the border width
- borders
- })
- }
- fn vertical_borders(&self, terminals: &[PaneId]) -> HashSet<usize> {
- terminals.iter().fold(HashSet::new(), |mut borders, t| {
- let terminal = self.panes.get(t).unwrap();
- borders.insert(terminal.x());
- borders.insert(terminal.x() + terminal.columns() + 1); // 1 for the border width
- borders
- })
- }
- fn panes_to_the_left_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
- if let Some(terminal) = self.panes.get(&id) {
- let upper_close_border = terminal.y();
- let lower_close_border = terminal.y() + terminal.rows() + 1;
-
- if let Some(mut terminals_to_the_left) = self.pane_ids_directly_left_of(&id) {
- let terminal_borders_to_the_left = self.horizontal_borders(&terminals_to_the_left);
- if terminal_borders_to_the_left.contains(&upper_close_border)
- && terminal_borders_to_the_left.contains(&lower_close_border)
- {
- terminals_to_the_left.retain(|t| {
- self.pane_is_between_horizontal_borders(
- t,
- upper_close_border,
- lower_close_border,
- )
- });
- return Some(terminals_to_the_left);
- }
- }
- }
- None
- }
- fn panes_to_the_right_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
- if let Some(terminal) = self.panes.get(&id) {
- let upper_close_border = terminal.y();
- let lower_close_border = terminal.y() + terminal.rows() + 1;
-
- if let Some(mut terminals_to_the_right) = self.pane_ids_directly_right_of(&id) {
- let terminal_borders_to_the_right =
- self.horizontal_borders(&terminals_to_the_right);
- if terminal_borders_to_the_right.contains(&upper_close_border)
- && terminal_borders_to_the_right.contains(&lower_close_border)
- {
- terminals_to_the_right.retain(|t| {
- self.pane_is_between_horizontal_borders(
- t,
- upper_close_border,
- lower_close_border,
- )
- });
- return Some(terminals_to_the_right);
- }
- }
- }
- None
- }
- fn panes_above_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
- if let Some(terminal) = self.panes.get(&id) {
- let left_close_border = terminal.x();
- let right_close_border = terminal.x() + terminal.columns() + 1;
-
- if let Some(mut terminals_above) = self.pane_ids_directly_above(&id) {
- let terminal_borders_above = self.vertical_borders(&terminals_above);
- if terminal_borders_above.contains(&left_close_border)
- && terminal_borders_above.contains(&right_close_border)
- {
- terminals_above.retain(|t| {
- self.pane_is_between_vertical_borders(
- t,
- left_close_border,
- right_close_border,
- )
- });
- return Some(terminals_above);
- }
- }
- }
- None
- }
- fn panes_below_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
- if let Some(terminal) = self.panes.get(&id) {
- let left_close_border = terminal.x();
- let right_close_border = terminal.x() + terminal.columns() + 1;
-
- if let Some(mut terminals_below) = self.pane_ids_directly_below(&id) {
- let terminal_borders_below = self.vertical_borders(&terminals_below);
- if terminal_borders_below.contains(&left_close_border)
- && terminal_borders_below.contains(&right_close_border)
- {
- terminals_below.retain(|t| {
- self.pane_is_between_vertical_borders(
- t,
- left_close_border,
- right_close_border,
- )
- });
- return Some(terminals_below);
- }
- }
- }
- None
- }
- fn close_down_to_max_terminals(&mut self) {
- if let Some(max_panes) = self.max_panes {
- let terminals = self.get_pane_ids();
- for &pid in terminals.iter().skip(max_panes - 1) {
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(pid))
- .unwrap();
- self.close_pane_without_rerender(pid);
- }
- }
- }
- pub fn get_pane_ids(&self) -> Vec<PaneId> {
- self.get_panes().map(|(&pid, _)| pid).collect()
- }
- pub fn set_pane_selectable(&mut self, id: PaneId, selectable: bool) {
- if let Some(pane) = self.panes.get_mut(&id) {
- pane.set_selectable(selectable);
- if self.get_active_pane_id() == Some(id) && !selectable {
- self.active_terminal = self.next_active_pane(self.get_pane_ids())
- }
- }
- }
- pub fn set_pane_invisible_borders(&mut self, id: PaneId, invisible_borders: bool) {
- if let Some(pane) = self.panes.get_mut(&id) {
- pane.set_invisible_borders(invisible_borders);
- }
- }
- pub fn set_pane_max_height(&mut self, id: PaneId, max_height: usize) {
- if let Some(pane) = self.panes.get_mut(&id) {
- pane.set_max_height(max_height);
- }
- }
- pub fn close_pane(&mut self, id: PaneId) {
- if self.panes.get(&id).is_some() {
- self.close_pane_without_rerender(id);
- }
- }
- pub fn close_pane_without_rerender(&mut self, id: PaneId) {
- if self.fullscreen_is_active {
- self.toggle_active_pane_fullscreen();
- }
- if let Some(pane_to_close) = self.panes.get(&id) {
- let pane_to_close_width = pane_to_close.columns();
- let pane_to_close_height = pane_to_close.rows();
- if let Some(panes) = self.panes_to_the_left_between_aligning_borders(id) {
- if panes.iter().all(|p| {
- let pane = self.panes.get(p).unwrap();
- pane.can_increase_width_by(pane_to_close_width + 1)
- }) {
- for pane_id in panes.iter() {
- self.increase_pane_width_right(&pane_id, pane_to_close_width + 1);
- // 1 for the border
- }
- self.panes.remove(&id);
- if self.active_terminal == Some(id) {
- self.active_terminal = self.next_active_pane(panes);
- }
- return;
- }
- }
- if let Some(panes) = self.panes_to_the_right_between_aligning_borders(id) {
- if panes.iter().all(|p| {
- let pane = self.panes.get(p).unwrap();
- pane.can_increase_width_by(pane_to_close_width + 1)
- }) {
- for pane_id in panes.iter() {
- self.increase_pane_width_left(&pane_id, pane_to_close_width + 1);
- // 1 for the border
- }
- self.panes.remove(&id);
- if self.active_terminal == Some(id) {
- self.active_terminal = self.next_active_pane(panes);
- }
- return;
- }
- }
- if let Some(panes) = self.panes_above_between_aligning_borders(id) {
- if panes.iter().all(|p| {
- let pane = self.panes.get(p).unwrap();
- pane.can_increase_height_by(pane_to_close_height + 1)
- }) {
- for pane_id in panes.iter() {
- self.increase_pane_height_down(&pane_id, pane_to_close_height + 1);
- // 1 for the border
- }
- self.panes.remove(&id);
- if self.active_terminal == Some(id) {
- self.active_terminal = self.next_active_pane(panes);
- }
- return;
- }
- }
- if let Some(panes) = self.panes_below_between_aligning_borders(id) {
- if panes.iter().all(|p| {
- let pane = self.panes.get(p).unwrap();
- pane.can_increase_height_by(pane_to_close_height + 1)
- }) {
- for pane_id in panes.iter() {
- self.increase_pane_height_up(&pane_id, pane_to_close_height + 1);
- // 1 for the border
- }
- self.panes.remove(&id);
- if self.active_terminal == Some(id) {
- self.active_terminal = self.next_active_pane(panes);
- }
- return;
- }
- }
- // if we reached here, this is either the last pane or there's some sort of
- // configuration error (eg. we're trying to close a pane surrounded by fixed panes)
- self.panes.remove(&id);
- }
- }
- pub fn close_focused_pane(&mut self) {
- if let Some(active_pane_id) = self.get_active_pane_id() {
- self.close_pane(active_pane_id);
- self.senders
- .send_to_pty(PtyInstruction::ClosePane(active_pane_id))
- .unwrap();
- }
- }
- pub fn scroll_active_terminal_up(&mut self) {
- if let Some(active_terminal_id) = self.get_active_terminal_id() {
- let active_terminal = self
- .panes
- .get_mut(&PaneId::Terminal(active_terminal_id))
- .unwrap();
- active_terminal.scroll_up(1);
- self.render();
- }
- }
- pub fn scroll_active_terminal_down(&mut self) {
- if let Some(active_terminal_id) = self.get_active_terminal_id() {
- let active_terminal = self
- .panes
- .get_mut(&PaneId::Terminal(active_terminal_id))
- .unwrap();
- active_terminal.scroll_down(1);
- self.render();
- }
- }
- pub fn scroll_active_terminal_up_page(&mut self) {
- if let Some(active_terminal_id) = self.get_active_terminal_id() {
- let active_terminal = self
- .panes
- .get_mut(&PaneId::Terminal(active_terminal_id))
- .unwrap();
- // prevent overflow when row == 0
- let scroll_columns = active_terminal.rows().max(1) - 1;
- active_terminal.scroll_up(scroll_columns);
- self.render();
- }
- }
- pub fn scroll_active_terminal_down_page(&mut self) {
- if let Some(active_terminal_id) = self.get_active_terminal_id() {
- let active_terminal = self
- .panes
- .get_mut(&PaneId::Terminal(active_terminal_id))
- .unwrap();
- // prevent overflow when row == 0
- let scroll_columns = active_terminal.rows().max(1) - 1;
- active_terminal.scroll_down(scroll_columns);
- self.render();
- }
- }
- pub fn clear_active_terminal_scroll(&mut self) {
- if let Some(active_terminal_id) = self.get_active_terminal_id() {
- let active_terminal = self
- .panes
- .get_mut(&PaneId::Terminal(active_terminal_id))
- .unwrap();
- active_terminal.clear_scroll();
- }
- }
-}
diff --git a/src/common/command_is_executing.rs b/src/common/command_is_executing.rs
deleted file mode 100644
index ad032557..00000000
--- a/src/common/command_is_executing.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-#![allow(clippy::mutex_atomic)]
-use std::sync::{Arc, Condvar, Mutex};
-
-#[derive(Clone)]
-pub struct CommandIsExecuting {
- input_thread: Arc<(Mutex<bool>, Condvar)>,
-}
-
-impl CommandIsExecuting {
- pub fn new() -> Self {
- CommandIsExecuting {
- input_thread: Arc::new((Mutex::new(false), Condvar::new())),
- }
- }
- pub fn blocking_input_thread(&mut self) {
- let (lock, _cvar) = &*self.input_thread;
- let mut input_thread = lock.lock().unwrap();
- *input_thread = true;
- }
- pub fn unblock_input_thread(&mut self) {
- let (lock, cvar) = &*self.input_thread;
- let mut input_thread = lock.lock().unwrap();
- *input_thread = false;
- cvar.notify_all();
- }
- pub fn wait_until_input_thread_is_unblocked(&self) {
- let (lock, cvar) = &*self.input_thread;
- let mut input_thread = lock.lock().unwrap();
- while *input_thread {
- input_thread = cvar.wait(input_thread).unwrap();
- }
- }
-}
diff --git a/src/common/errors.rs b/src/common/errors.rs
deleted file mode 100644
index 262dfd6b..00000000
--- a/src/common/errors.rs
+++ /dev/null
@@ -1,393 +0,0 @@
-//! Error context system based on a thread-local representation of the call stack, itself based on
-//! the instructions that are sent between threads.
-
-use serde::{Deserialize, Serialize};
-
-use std::fmt::{Display, Error, Formatter};
-
-use crate::client::ClientInstruction;
-use crate::common::thread_bus::{ASYNCOPENCALLS, OPENCALLS};
-use crate::pty::PtyInstruction;
-use crate::screen::ScreenInstruction;
-use crate::server::ServerInstruction;
-
-/// The maximum amount of calls an [`ErrorContext`] will keep track
-/// of in its stack representation. This is a per-thread maximum.
-const MAX_THREAD_CALL_STACK: usize = 6;
-
-#[cfg(not(test))]
-use super::thread_bus::SenderWithContext;
-#[cfg(not(test))]
-use std::panic::PanicInfo;
-
-pub trait ErrorInstruction {
- fn error(err: String) -> Self;
-}
-
-impl ErrorInstruction for ClientInstruction {
- fn error(err: String) -> Self {
- ClientInstruction::Error(err)
- }
-}
-
-impl ErrorInstruction for ServerInstruction {
- fn error(err: String) -> Self {
- ServerInstruction::Error(err)
- }
-}
-
-/// Custom panic handler/hook. Prints the [`ErrorContext`].
-#[cfg(not(test))]
-pub fn handle_panic<T>(info: &PanicInfo<'_>, sender: &SenderWithContext<T>)
-where
- T: ErrorInstruction + Clone,
-{
- use backtrace::Backtrace;
- use std::{process, thread};
- let backtrace = Backtrace::new();
- let thread = thread::current();
- let thread = thread.name().unwrap_or("unnamed");
-
- let msg = match info.payload().downcast_ref::<&'static str>() {
- Some(s) => Some(*s),
- None => info.payload().downcast_ref::<String>().map(|s| &**s),
- };
-
- let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
-
- let backtrace = match (info.location(), msg) {
- (Some(location), Some(msg)) => format!(
- "{}\n\u{1b}[0;0mError: \u{1b}[0;31mthread '{}' panicked at '{}': {}:{}\n\u{1b}[0;0m{:?}",
- err_ctx,
- thread,
- msg,
- location.file(),
- location.line(),
- backtrace,
- ),
- (Some(location), None) => format!(
- "{}\n\u{1b}[0;0mError: \u{1b}[0;31mthread '{}' panicked: {}:{}\n\u{1b}[0;0m{:?}",
- err_ctx,
- thread,
- location.file(),
- location.line(),
- backtrace
- ),
- (None, Some(msg)) => format!(
- "{}\n\u{1b}[0;0mError: \u{1b}[0;31mthread '{}' panicked at '{}'\n\u{1b}[0;0m{:?}",
- err_ctx, thread, msg, backtrace
- ),
- (None, None) => format!(
- "{}\n\u{1b}[0;0mError: \u{1b}[0;31mthread '{}' panicked\n\u{1b}[0;0m{:?}",
- err_ctx, thread, backtrace
- ),
- };
-
- if thread == "main" {
- println!("{}", backtrace);
- process::exit(1);
- } else {
- let _ = sender.send(T::error(backtrace));
- }
-}
-
-pub fn get_current_ctx() -> ErrorContext {
- ASYNCOPENCALLS
- .try_with(|ctx| *ctx.borrow())
- .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow()))
-}
-
-/// A representation of the call stack.
-#[derive(Clone, Copy, Serialize, Deserialize)]
-pub struct ErrorContext {
- calls: [ContextType; MAX_THREAD_CALL_STACK],
-}
-
-impl ErrorContext {
- /// Returns a new, blank [`ErrorContext`] containing only [`Empty`](ContextType::Empty)
- /// calls.
- pub fn new() -> Self {
- Self {
- calls: [ContextType::Empty; MAX_THREAD_CALL_STACK],
- }
- }
-
- /// Adds a call to this [`ErrorContext`]'s call stack representation.
- pub fn add_call(&mut self, call: ContextType) {
- for ctx in self.calls.iter_mut() {
- if *ctx == ContextType::Empty {
- *ctx = call;
- break;
- }
- }
- self.update_thread_ctx()
- }
-
- /// Updates the thread local [`ErrorContext`].
- pub fn update_thread_ctx(&self) {
- ASYNCOPENCALLS
- .try_with(|ctx| *ctx.borrow_mut() = *self)
- .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self));
- }
-}
-
-impl Default for ErrorContext {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl Display for ErrorContext {
- fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
- writeln!(f, "Originating Thread(s):")?;
- for (index, ctx) in self.calls.iter().enumerate() {
- if *ctx == ContextType::Empty {
- break;
- }
- writeln!(f, "\u{1b}[0;0m{}. {}", index + 1, ctx)?;
- }
- Ok(())
- }
-}
-
-/// Different types of calls that form an [`ErrorContext`] call stack.
-///
-/// Complex variants store a variant of a related enum, whose variants can be built from
-/// the corresponding Zellij MSPC instruction enum variants ([`ScreenInstruction`],
-/// [`PtyInstruction`], [`ClientInstruction`], etc).
-#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
-pub enum ContextType {
- /// A screen-related call.
- Screen(ScreenContext),
- /// A PTY-related call.
- Pty(PtyContext),
- /// A plugin-related call.
- Plugin(PluginContext),
- /// An app-related call.
- Client(ClientContext),
- /// A server-related call.
- IPCServer(ServerContext),
- StdinHandler,
- AsyncTask,
- /// An empty, placeholder call. This should be thought of as representing no call at all.
- /// A call stack representation filled with these is the representation of an empty call stack.
- Empty,
-}
-
-// TODO use the `colored` crate for color formatting
-impl Display for ContextType {
- fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
- let purple = "\u{1b}[1;35m";
- let green = "\u{1b}[0;32m";
- match *self {
- ContextType::Screen(c) => write!(f, "{}screen_thread: {}{:?}", purple, green, c),
- ContextType::Pty(c) => write!(f, "{}pty_thread: {}{:?}", purple, green, c),
- ContextType::Plugin(c) => write!(f, "{}plugin_thread: {}{:?}", purple, green, c),
- ContextType::Client(c) => write!(f, "{}main_thread: {}{:?}", purple, green, c),
- ContextType::IPCServer(c) => write!(f, "{}ipc_server: {}{:?}", purple, green, c),
- ContextType::StdinHandler => {
- write!(f, "{}stdin_handler_thread: {}AcceptInput", purple, green)
- }
- ContextType::AsyncTask => {
- write!(f, "{}stream_terminal_bytes: {}AsyncTask", purple, green)
- }
- ContextType::Empty => write!(f, ""),
- }
- }
-}
-
-// FIXME: Just deriving EnumDiscriminants from strum will remove the need for any of this!!!
-/// Stack call representations corresponding to the different types of [`ScreenInstruction`]s.
-#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
-pub enum ScreenContext {
- HandlePtyBytes,
- Render,
- NewPane,
- HorizontalSplit,
- VerticalSplit,
- WriteCharacter,
- ResizeLeft,
- ResizeRight,
- ResizeDown,
- ResizeUp,
- SwitchFocus,
- FocusNextPane,
- FocusPreviousPane,
- MoveFocusLeft,
- MoveFocusLeftOrPreviousTab,
- MoveFocusDown,
- MoveFocusUp,
- MoveFocusRight,
- MoveFocusRightOrNextTab,
- Exit,
- ScrollUp,
- ScrollDown,
- PageScrollUp,
- PageScrollDown,
- ClearScroll,
- CloseFocusedPane,
- ToggleActiveSyncTab,
- ToggleActiveTerminalFullscreen,
- SetSelectable,
- SetInvisibleBorders,
- SetMaxHeight,
- ClosePane,
- ApplyLayout,
- NewTab,
- SwitchTabNext,
- SwitchTabPrev,
- CloseTab,
- GoToTab,
- UpdateTabName,
- TerminalResize,
- ChangeMode,
-}
-
-// FIXME: Just deriving EnumDiscriminants from strum will remove the need for any of this!!!
-impl From<&ScreenInstruction> for ScreenContext {
- fn from(screen_instruction: &ScreenInstruction) -> Self {
- match *screen_instruction {
- ScreenInstruction::PtyBytes(..) => ScreenContext::HandlePtyBytes,
- ScreenInstruction::Render => ScreenContext::Render,
- ScreenInstruction::NewPane(_) => ScreenContext::NewPane,
- ScreenInstruction::HorizontalSplit(_) => ScreenContext::HorizontalSplit,
- ScreenInstruction::VerticalSplit(_) => ScreenContext::VerticalSplit,
- ScreenInstruction::WriteCharacter(_) => ScreenContext::WriteCharacter,
- ScreenInstruction::ResizeLeft => ScreenContext::ResizeLeft,
- ScreenInstruction::ResizeRight => ScreenContext::ResizeRight,
- ScreenInstruction::ResizeDown => ScreenContext::ResizeDown,
- ScreenInstruction::ResizeUp => ScreenContext::ResizeUp,
- ScreenInstruction::SwitchFocus => ScreenContext::SwitchFocus,
- ScreenInstruction::FocusNextPane => ScreenContext::FocusNextPane,
- ScreenInstruction::FocusPreviousPane => ScreenContext::FocusPreviousPane,
- ScreenInstruction::MoveFocusLeft => ScreenContext::MoveFocusLeft,
- ScreenInstruction::MoveFocusLeftOrPreviousTab => {
- ScreenContext::MoveFocusLeftOrPreviousTab
- }
- ScreenInstruction::MoveFocusDown => ScreenContext::MoveFocusDown,
- ScreenInstruction::MoveFocusUp => ScreenContext::MoveFocusUp,
- ScreenInstruction::MoveFocusRight => ScreenContext::MoveFocusRight,
- ScreenInstruction::MoveFocusRightOrNextTab => ScreenContext::MoveFocusRightOrNextTab,
- ScreenInstruction::Exit => ScreenContext::Exit,
- ScreenInstruction::ScrollUp => ScreenContext::ScrollUp,
- ScreenInstruction::ScrollDown => ScreenContext::ScrollDown,
- ScreenInstruction::PageScrollUp => ScreenContext::PageScrollUp,
- ScreenInstruction::PageScrollDown => ScreenContext::PageScrollDown,
- ScreenInstruction::ClearScroll => ScreenContext::ClearScroll,
- ScreenInstruction::CloseFocusedPane => ScreenContext::CloseFocusedPane,
- ScreenInstruction::ToggleActiveTerminalFullscreen => {
- ScreenContext::ToggleActiveTerminalFullscreen
- }
- ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable,
- ScreenInstruction::SetInvisibleBorders(..) => ScreenContext::SetInvisibleBorders,
- ScreenInstruction::SetMaxHeight(..) => ScreenContext::SetMaxHeight,
- ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
- ScreenInstruction::ApplyLayout(..) => ScreenContext::ApplyLayout,
- ScreenInstruction::NewTab(_) => ScreenContext::NewTab,
- ScreenInstruction::SwitchTabNext => ScreenContext::SwitchTabNext,
- ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev,
- ScreenInstruction::CloseTab => ScreenContext::CloseTab,
- ScreenInstruction::GoToTab(_) => ScreenContext::GoToTab,
- ScreenInstruction::UpdateTabName(_) => ScreenContext::UpdateTabName,
- ScreenInstruction::TerminalResize(_) => ScreenContext::TerminalResize,
- ScreenInstruction::ChangeMode(_) => ScreenContext::ChangeMode,
- ScreenInstruction::ToggleActiveSyncTab => ScreenContext::ToggleActiveSyncTab,
- }
- }
-}
-
-/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
-#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
-pub enum PtyContext {
- SpawnTerminal,
- SpawnTerminalVertically,
- SpawnTerminalHorizontally,
- NewTab,
- ClosePane,
- CloseTab,
- Exit,
-}
-
-impl From<&PtyInstruction> for PtyContext {
- fn from(pty_instruction: &PtyInstruction) -> Self {
- match *pty_instruction {
- PtyInstruction::SpawnTerminal(_) => PtyContext::SpawnTerminal,
- PtyInstruction::SpawnTerminalVertically(_) => PtyContext::SpawnTerminalVertically,
- PtyInstruction::SpawnTerminalHorizontally(_) => PtyContext::SpawnTerminalHorizontally,
- PtyInstruction::ClosePane(_) => PtyContext::ClosePane,
- PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
- PtyInstruction::NewTab => PtyContext::NewTab,
- PtyInstruction::Exit => PtyContext::Exit,
- }
- }
-}
-
-// FIXME: This whole pattern *needs* a macro eventually, it's soul-crushing to write
-
-use crate::wasm_vm::PluginInstruction;
-
-/// Stack call representations corresponding to the different types of [`PluginInstruction`]s.
-#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
-pub enum PluginContext {
- Load,
- Update,
- Render,
- Unload,
- Exit,
-}
-
-impl From<&PluginInstruction> for PluginContext {
- fn from(plugin_instruction: &PluginInstruction) -> Self {
- match *plugin_instruction {
- PluginInstruction::Load(..) => PluginContext::Load,
- PluginInstruction::Update(..) => PluginContext::Update,
- PluginInstruction::Render(..) => PluginContext::Render,
- PluginInstruction::Unload(_) => PluginContext::Unload,
- PluginInstruction::Exit => PluginContext::Exit,
- }
- }
-}
-
-/// Stack call representations corresponding to the different types of [`ClientInstruction`]s.
-#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
-pub enum ClientContext {
- Exit,
- Error,
- UnblockInputThread,
- Render,
- ServerError,
-}
-
-impl From<&ClientInstruction> for ClientContext {
- fn from(client_instruction: &ClientInstruction) -> Self {
- match *client_instruction {
- ClientInstruction::Exit => ClientContext::Exit,
- ClientInstruction::Error(_) => ClientContext::Error,
- ClientInstruction::ServerError(_) => ClientContext::ServerError,
- ClientInstruction::Render(_) => ClientContext::Render,
- ClientInstruction::UnblockInputThread => ClientContext::UnblockInputThread,
- }
- }
-}
-
-/// Stack call representations corresponding to the different types of [`ServerInstruction`]s.
-#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
-pub enum ServerContext {
- NewClient,
- Render,
- UnblockInputThread,
- ClientExit,
- Error,
-}
-
-impl From<&ServerInstruction> for ServerContext {
- fn from(server_instruction: &ServerInstruction) -> Self {
- match *server_instruction {
- ServerInstruction::NewClient(..) => ServerContext::NewClient,
- ServerInstruction::Render(_) => ServerContext::Render,
- ServerInstruction::UnblockInputThread => ServerContext::UnblockInputThread,
- ServerInstruction::ClientExit => ServerContext::ClientExit,
- ServerInstruction::Error(_) => ServerContext::Error,
- }
- }
-}
diff --git a/src/common/input/actions.rs b/src/common/input/actions.rs
deleted file mode 100644
index b806aa3f..00000000
--- a/src/common/input/actions.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-//! Definition of the actions that can be bound to keys.
-
-use serde::{Deserialize, Serialize};
-use zellij_tile::data::InputMode;
-
-/// The four directions (left, right, up, down).
-#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
-pub enum Direction {
- Left,
- Right,
- Up,
- Down,
-}
-
-/// Actions that can be bound to keys.
-#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
-pub enum Action {
- /// Quit Zellij.
- Quit,
- /// Write to the terminal.
- Write(Vec<u8>),
- /// Switch to the specified input mode.
- SwitchToMode(InputMode),
- /// Resize focus pane in specified direction.
- Resize(Direction),
- /// Switch focus to next pane in specified direction.
- FocusNextPane,
- FocusPreviousPane,
- /// Move the focus pane in specified direction.
- SwitchFocus,
- MoveFocus(Direction),
- /// Tries to move the focus pane in specified direction.
- /// If there is no pane in the direction, move to previous/next Tab.
- MoveFocusOrTab(Direction),
- /// Scroll up in focus pane.
- ScrollUp,
- /// Scroll down in focus pane.
- ScrollDown,
- /// Scroll up one page in focus pane.
- PageScrollUp,
- /// Scroll down one page in focus pane.
- PageScrollDown,
- /// Toggle between fullscreen focus pane and normal layout.
- ToggleFocusFullscreen,
- /// Toggle between sending text commands to all panes on the current tab and normal mode.
- ToggleActiveSyncTab,
- /// Open a new pane in the specified direction (relative to focus).
- /// If no direction is specified, will try to use the biggest available space.
- NewPane(Option<Direction>),
- /// Close the focus pane.
- CloseFocus,
- /// Create a new tab.
- NewTab,
- /// Do nothing.
- NoOp,
- /// Go to the next tab.
- GoToNextTab,
- /// Go to the previous tab.
- GoToPreviousTab,
- /// Close the current tab.
- CloseTab,
- GoToTab(u32),
- TabNameInput(Vec<u8>),
-}
diff --git a/src/common/input/config.rs b/src/common/input/config.rs
deleted file mode 100644
index 3dffe71c..00000000
--- a/src/common/input/config.rs
+++ /dev/null
@@ -1,218 +0,0 @@
-//! Deserializes configuration options.
-use std::error;
-use std::fmt::{self, Display};
-use std::fs::File;
-use std::io::{self, Read};
-use std::path::{Path, PathBuf};
-
-use super::keybinds::{Keybinds, KeybindsFromYaml};
-use super::options::Options;
-use crate::cli::{CliArgs, ConfigCli};
-use crate::common::setup;
-
-use serde::{Deserialize, Serialize};
-use std::convert::TryFrom;
-
-const DEFAULT_CONFIG_FILE_NAME: &str = "config.yaml";
-
-type ConfigResult = Result<Config, ConfigError>;
-
-/// Intermediate deserialization config struct
-#[derive(Debug, Deserialize)]
-pub struct ConfigFromYaml {
- #[serde(flatten)]
- pub options: Option<Options>,
- pub keybinds: Option<KeybindsFromYaml>,
-}
-
-/// Main configuration.
-#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
-pub struct Config {
- pub keybinds: Keybinds,
- pub options: Options,
-}
-
-#[derive(Debug)]
-pub enum ConfigError {
- // Deserialization error
- Serde(serde_yaml::Error),
- // Io error
- Io(io::Error),
- // Io error with path context
- IoPath(io::Error, PathBuf),
- // Internal Deserialization Error
- FromUtf8(std::string::FromUtf8Error),
-}
-
-impl Default for Config {
- fn default() -> Self {
- let keybinds = Keybinds::default();
- let options = Options::default();
- Config { keybinds, options }
- }
-}
-
-impl TryFrom<&CliArgs> for Config {
- type Error = ConfigError;
-
- fn try_from(opts: &CliArgs) -> ConfigResult {
- if let Some(ref path) = opts.config {
- return Config::new(&path);
- }
-
- if let Some(ConfigCli::Setup(setup)) = opts.option.clone() {
- if setup.clean {
- return Config::from_default_assets();
- }
- }
-
- let config_dir = opts
- .config_dir
- .clone()
- .or_else(setup::find_default_config_dir);
-
- if let Some(ref config) = config_dir {
- let path = config.join(DEFAULT_CONFIG_FILE_NAME);
- if path.exists() {
- Config::new(&path)
- } else {
- Config::from_default_assets()
- }
- } else {
- Config::from_default_assets()
- }
- }
-}
-
-impl Config {
- /// Uses defaults, but lets config override them.
- pub fn from_yaml(yaml_config: &str) -> ConfigResult {
- let config_from_yaml: ConfigFromYaml = serde_yaml::from_str(&yaml_config)?;
- let keybinds = Keybinds::get_default_keybinds_with_config(config_from_yaml.keybinds);
- let options = Options::from_yaml(config_from_yaml.options);
- Ok(Config { keybinds, options })
- }
-
- /// Deserializes from given path.
- pub fn new(path: &Path) -> ConfigResult {
- match File::open(path) {
- Ok(mut file) => {
- let mut yaml_config = String::new();
- file.read_to_string(&mut yaml_config)
- .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?;
- Ok(Config::from_yaml(&yaml_config)?)
- }
- Err(e) => Err(ConfigError::IoPath(e, path.into())),
- }
- }
-
- /// Gets default configuration from assets
- // TODO Deserialize the Configuration from bytes &[u8],
- // once serde-yaml supports zero-copy
- pub fn from_default_assets() -> ConfigResult {
- Self::from_yaml(String::from_utf8(setup::DEFAULT_CONFIG.to_vec())?.as_str())
- }
-}
-
-impl Display for ConfigError {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- match self {
- ConfigError::Io(ref err) => write!(formatter, "IoError: {}", err),
- ConfigError::IoPath(ref err, ref path) => {
- write!(formatter, "IoError: {}, File: {}", err, path.display(),)
- }
- ConfigError::Serde(ref err) => write!(formatter, "Deserialization error: {}", err),
- ConfigError::FromUtf8(ref err) => write!(formatter, "FromUtf8Error: {}", err),
- }
- }
-}
-
-impl std::error::Error for ConfigError {
- fn cause(&self) -> Option<&dyn error::Error> {
- match *self {
- ConfigError::Io(ref err) => Some(err),
- ConfigError::IoPath(ref err, _) => Some(err),
- ConfigError::Serde(ref err) => Some(err),
- ConfigError::FromUtf8(ref err) => Some(err),
- }
- }
-}
-
-impl From<io::Error> for ConfigError {
- fn from(err: io::Error) -> ConfigError {
- ConfigError::Io(err)
- }
-}
-
-impl From<serde_yaml::Error> for ConfigError {
- fn from(err: serde_yaml::Error) -> ConfigError {
- ConfigError::Serde(err)
- }
-}
-
-impl From<std::string::FromUtf8Error> for ConfigError {
- fn from(err: std::string::FromUtf8Error) -> ConfigError {
- ConfigError::FromUtf8(err)
- }
-}
-
-// The unit test location.
-#[cfg(test)]
-mod config_test {
- use std::io::Write;
-
- use tempfile::tempdir;
-
- use super::*;
-
- #[test]
- fn try_from_cli_args_with_config() {
- let arbitrary_config = PathBuf::from("nonexistent.yaml");
- let mut opts = CliArgs::default();
- opts.config = Some(arbitrary_config);
- println!("OPTS= {:?}", opts);
- let result = Config::try_from(&opts);
- assert!(result.is_err());
- }
-
- #[test]
- fn try_from_cli_args_with_option_clean() {
- use crate::common::setup::Setup;
- let mut opts = CliArgs::default();
- opts.option = Some(ConfigCli::Setup(Setup {
- clean: true,
- ..Setup::default()
- }));
- let result = Config::try_from(&opts);
- assert!(result.is_ok());
- }
-
- #[test]
- fn try_from_cli_args_with_config_dir() {
- let mut opts = CliArgs::default();
- let tmp = tempdir().unwrap();
- File::create(tmp.path().join(DEFAULT_CONFIG_FILE_NAME))
- .unwrap()
- .write_all(b"keybinds: invalid\n")
- .unwrap();
- opts.config_dir = Some(tmp.path().to_path_buf());
- let result = Config::try_from(&opts);
- assert!(result.is_err());
- }
-
- #[test]
- fn try_from_cli_args_with_config_dir_without_config() {
- let mut opts = CliArgs::default();
- let tmp = tempdir().unwrap();
- opts.config_dir = Some(tmp.path().to_path_buf());
- let result = Config::try_from(&opts);
- assert_eq!(result.unwrap(), Config::default());
- }
-
- #[test]
- fn try_from_cli_args_default() {
- let opts = CliArgs::default();
- let result = Config::try_from(&opts);
- assert_eq!(result.unwrap(), Config::default());
- }
-}
diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs
deleted file mode 100644
index 4ac2cf83..00000000
--- a/src/common/input/handler.rs
+++ /dev/null
@@ -1,268 +0,0 @@
-//! Main input logic.
-
-use super::actions::Action;
-use super::keybinds::Keybinds;
-use crate::client::ClientInstruction;
-use crate::common::input::config::Config;
-use crate::common::ipc::ClientToServerMsg;
-use crate::common::thread_bus::{SenderWithContext, OPENCALLS};
-use crate::errors::ContextType;
-use crate::os_input_output::ClientOsApi;
-use crate::CommandIsExecuting;
-
-use termion::input::{TermRead, TermReadEventsAndRaw};
-use zellij_tile::data::{InputMode, Key, ModeInfo, Palette, PluginCapabilities};
-
-/// Handles the dispatching of [`Action`]s according to the current
-/// [`InputMode`], and keep tracks of the current [`InputMode`].
-struct InputHandler {
- /// The current input mode
- mode: InputMode,
- os_input: Box<dyn ClientOsApi>,
- config: Config,
- command_is_executing: CommandIsExecuting,
- send_client_instructions: SenderWithContext<ClientInstruction>,
- should_exit: bool,
- pasting: bool,
-}
-
-impl InputHandler {
- /// Returns a new [`InputHandler`] with the attributes specified as arguments.
- fn new(
- os_input: Box<dyn ClientOsApi>,
- command_is_executing: CommandIsExecuting,
- config: Config,
- send_client_instructions: SenderWithContext<ClientInstruction>,
- ) -> Self {
- InputHandler {
- mode: InputMode::Normal,
- os_input,
- config,
- command_is_executing,
- send_client_instructions,
- should_exit: false,
- pasting: false,
- }
- }
-
- /// Main input event loop. Interprets the terminal [`Event`](termion::event::Event)s
- /// as [`Action`]s according to the current [`InputMode`], and dispatches those actions.
- fn handle_input(&mut self) {
- let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
- err_ctx.add_call(ContextType::StdinHandler);
- let alt_left_bracket = vec![27, 91];
- let bracketed_paste_start = vec![27, 91, 50, 48, 48, 126]; // \u{1b}[200~
- let bracketed_paste_end = vec![27, 91, 50, 48, 49, 126]; // \u{1b}[201
- loop {
- if self.should_exit {
- break;
- }
- let stdin_buffer = self.os_input.read_from_stdin();
- for key_result in stdin_buffer.events_and_raw() {
- match key_result {
- Ok((event, raw_bytes)) => match event {
- termion::event::Event::Key(key) => {
- let key = cast_termion_key(key);
- self.handle_key(&key, raw_bytes);
- }
- termion::event::Event::Unsupported(unsupported_key) => {
- // we have to do this because of a bug in termion
- // this should be a key event and not an unsupported event
- if unsupported_key == alt_left_bracket {
- let key = Key::Alt('[');
- self.handle_key(&key, raw_bytes);
- } else if unsupported_key == bracketed_paste_start {
- self.pasting = true;
- } else if unsupported_key == bracketed_paste_end {
- self.pasting = false;
- } else {
- // this is a hack because termion doesn't recognize certain keys
- // in this case we just forward it to the terminal
- self.handle_unknown_key(raw_bytes);
- }
- }
- termion::event::Event::Mouse(_) => {
- // Mouse events aren't implemented yet,
- // use a NoOp untill then.
- }
- },
- Err(err) => panic!("Encountered read error: {:?}", err),
- }
- }
- }
- }
- fn handle_unknown_key(&mut self, raw_bytes: Vec<u8>) {
- if self.mode == InputMode::Normal || self.mode == InputMode::Locked {
- let action = Action::Write(raw_bytes);
- self.dispatch_action(action);
- }
- }
- fn handle_key(&mut self, key: &Key, raw_bytes: Vec<u8>) {
- let keybinds = &self.config.keybinds;
- if self.pasting {
- // we're inside a paste block, if we're in a mode that allows sending text to the
- // terminal, send all text directly without interpreting it
- // otherwise, just discard the input
- if self.mode == InputMode::Normal || self.mode == InputMode::Locked {
- let action = Action::Write(raw_bytes);
- self.dispatch_action(action);
- }
- } else {
- for action in Keybinds::key_to_actions(&key, raw_bytes, &self.mode, keybinds) {
- let should_exit = self.dispatch_action(action);
- if should_exit {
- self.should_exit = true;
- }
- }
- }
- }
-
- /// Dispatches an [`Action`].
- ///
- /// This function's body dictates what each [`Action`] actually does when
- /// dispatched.
- ///
- /// # Return value
- /// Currently, this function returns a boolean that indicates whether
- /// [`Self::handle_input()`] should break after this action is dispatched.
- /// This is a temporary measure that is only necessary due to the way that the
- /// framework works, and shouldn't be necessary anymore once the test framework
- /// is revised. See [issue#183](https://github.com/zellij-org/zellij/issues/183).
- fn dispatch_action(&mut self, action: Action) -> bool {
- let mut should_break = false;
-
- match action {
- Action::Quit => {
- self.exit();
- should_break = true;
- }
- Action::SwitchToMode(mode) => {
- self.mode = mode;
- self.os_input
- .send_to_server(ClientToServerMsg::Action(action));
- }
- Action::CloseFocus
- | Action::NewPane(_)
- | Action::NewTab
- | Action::GoToNextTab
- | Action::GoToPreviousTab
- | Action::CloseTab
- | Action::GoToTab(_)
- | Action::MoveFocusOrTab(_) => {
- self.command_is_executing.blocking_input_thread();
- self.os_input
- .send_to_server(ClientToServerMsg::Action(action));
- self.command_is_executing
- .wait_until_input_thread_is_unblocked();
- }
- _ => self
- .os_input
- .send_to_server(ClientToServerMsg::Action(action)),
- }
-
- should_break
- }
-
- /// Routine to be called when the input handler exits (at the moment this is the
- /// same as quitting Zellij).
- fn exit(&mut self) {
- self.send_client_instructions
- .send(ClientInstruction::Exit)
- .unwrap();
- }
-}
-
-/// Creates a [`Help`] struct indicating the current [`InputMode`] and its keybinds
-/// (as pairs of [`String`]s).
-// TODO this should probably be automatically generated in some way
-pub fn get_mode_info(
- mode: InputMode,
- palette: Palette,
- capabilities: PluginCapabilities,
-) -> ModeInfo {
- let mut keybinds: Vec<(String, String)> = vec![];
- match mode {
- InputMode::Normal | InputMode::Locked => {}
- InputMode::Resize => {
- keybinds.push(("←↓↑→".to_string(), "Resize".to_string()));
- }
- InputMode::Pane => {
- keybinds.push(("←↓↑→".to_string(), "Move focus".to_string()));
- keybinds.push(("p".to_string(), "Next".to_string()));
- keybinds.push(("n".to_string(), "New".to_string()));
- keybinds.push(("d".to_string(), "Down split".to_string()));
- keybinds.push(("r".to_string(), "Right split".to_string()));
- keybinds.push(("x".to_string(), "Close".to_string()));
- keybinds.push(("f".to_string(), "Fullscreen".to_string()));
- }
- InputMode::Tab => {
- keybinds.push(("←↓↑→".to_string(), "Move focus".to_string()));
- keybinds.push(("n".to_string(), "New".to_string()));
- keybinds.push(("x".to_string(), "Close".to_string()));
- keybinds.push(("r".to_string(), "Rename".to_string()));
- keybinds.push(("s".to_string(), "Sync".to_string()));
- }
- InputMode::Scroll => {
- keybinds.push(("↓↑".to_string(), "Scroll".to_string()));
- keybinds.push(("PgUp/PgDn".to_string(), "Scroll Page".to_string()));
- }
- InputMode::RenameTab => {
- keybinds.push(("Enter".to_string(), "when done".to_string()));
- }
- }
- ModeInfo {
- mode,
- keybinds,
- palette,
- capabilities,
- }
-}
-
-/// Entry point to the module. Instantiates an [`InputHandler`] and starts
-/// its [`InputHandler::handle_input()`] loop.
-pub fn input_loop(
- os_input: Box<dyn ClientOsApi>,
- config: Config,
- command_is_executing: CommandIsExecuting,
- send_client_instructions: SenderWithContext<ClientInstruction>,
-) {
- let _handler = InputHandler::new(
- os_input,
- command_is_executing,
- config,
- send_client_instructions,
- )
- .handle_input();
-}
-
-pub fn parse_keys(input_bytes: &[u8]) -> Vec<Key> {
- input_bytes.keys().flatten().map(cast_termion_key).collect()
-}
-
-// FIXME: This is an absolutely cursed function that should be destroyed as soon
-// as an alternative that doesn't touch zellij-tile can be developed...
-fn cast_termion_key(event: termion::event::Key) -> Key {
- match event {
- termion::event::Key::Backspace => Key::Backspace,
- termion::event::Key::Left => Key::Left,
- termion::event::Key::Right => Key::Right,
- termion::event::Key::Up => Key::Up,
- termion::event::Key::Down => Key::Down,
- termion::event::Key::Home => Key::Home,
- termion::event::Key::End => Key::End,
- termion::event::Key::PageUp => Key::PageUp,
- termion::event::Key::PageDown => Key::PageDown,
- termion::event::Key::BackTab => Key::BackTab,
- termion::event::Key::Delete => Key::Delete,
- termion::event::Key::Insert => Key::Insert,
- termion::event::Key::F(n) => Key::F(n),
- termion::event::Key::Char(c) => Key::Char(c),
- termion::event::Key::Alt(c) => Key::Alt(c),
- termion::event::Key::Ctrl(c) => Key::Ctrl(c),
- termion::event::Key::Null => Key::Null,
- termion::event::Key::Esc => Key::Esc,
- _ => {
- unimplemented!("Encountered an unknown key!")
- }
- }
-}
diff --git a/src/common/input/keybinds.rs b/src/common/input/keybinds.rs
deleted file mode 100644
index 8bef47fd..00000000
--- a/src/common/input/keybinds.rs
+++ /dev/null
@@ -1,301 +0,0 @@
-//! Mapping of inputs to sequences of actions.
-use std::collections::HashMap;
-
-use super::actions::Action;
-use super::config;
-
-use serde::{Deserialize, Serialize};
-use strum::IntoEnumIterator;
-use zellij_tile::data::*;
-
-/// Used in the config struct
-#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
-pub struct Keybinds(HashMap<InputMode, ModeKeybinds>);
-#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
-pub struct ModeKeybinds(HashMap<Key, Vec<Action>>);
-
-/// Intermediate struct used for deserialisation
-/// Used in the config file.
-#[derive(Clone, Debug, PartialEq, Deserialize)]
-pub struct KeybindsFromYaml {
- #[serde(flatten)]
- keybinds: HashMap<InputMode, Vec<KeyActionUnbind>>,
- #[serde(default)]
- unbind: Unbind,
-}
-
-/// Intermediate enum used for deserialisation
-#[derive(Clone, Debug, PartialEq, Deserialize)]
-#[serde(untagged)]
-enum KeyActionUnbind {
- KeyAction(KeyActionFromYaml),
- Unbind(UnbindFromYaml),
-}
-
-/// Intermediate struct used for deserialisation
-#[derive(Clone, Debug, PartialEq, Deserialize)]
-struct KeyActionUnbindFromYaml {
- keybinds: Vec<KeyActionFromYaml>,
- unbind: Unbind,
-}
-
-/// Intermediate struct used for deserialisation
-#[derive(Clone, Debug, PartialEq, Deserialize)]
-pub struct KeyActionFromYaml {
- action: Vec<Action>,
- key: Vec<Key>,
-}
-
-/// Intermediate struct used for deserialisation
-#[derive(Clone, Debug, PartialEq, Deserialize)]
-struct UnbindFromYaml {
- unbind: Unbind,
-}
-
-/// List of keys, for which to disable their respective default actions
-/// `All` is a catch all, and will disable the default actions for all keys.
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize)]
-#[serde(untagged)]
-enum Unbind {
- // This is the correct order, don't rearrange!
- // Suspected Bug in the untagged macro.
- // 1. Keys
- Keys(Vec<Key>),
- // 2. All
- All(bool),
-}
-
-impl Default for Keybinds {
- // Use once per codepath
- // TODO investigate why
- fn default() -> Keybinds {
- Self::from_default_assets()
- }
-}
-
-impl Keybinds {
- pub fn new() -> Keybinds {
- Keybinds(HashMap::<InputMode, ModeKeybinds>::new())
- }
-
- fn from_default_assets() -> Keybinds {
- config::Config::from_default_assets()
- .expect("Keybinds from default assets Error!")
- .keybinds
- }
-
- /// Entrypoint from the config module
- pub fn get_default_keybinds_with_config(from_yaml: Option<KeybindsFromYaml>) -> Keybinds {
- let default_keybinds = match from_yaml.clone() {
- Some(keybinds) => match keybinds.unbind {
- Unbind::All(true) => Keybinds::new(),
- Unbind::All(false) | Unbind::Keys(_) => Keybinds::unbind(keybinds),
- },
- None => Keybinds::default(),
- };
-
- if let Some(keybinds) = from_yaml {
- default_keybinds.merge_keybinds(Keybinds::from(keybinds))
- } else {
- default_keybinds
- }
- }
-
- /// Unbinds the default keybindings in relation to their mode
- fn unbind(from_yaml: KeybindsFromYaml) -> Keybinds {
- let mut keybind_config = Self::new();
- let mut unbind_config: HashMap<InputMode, Unbind> = HashMap::new();
- let keybinds_from_yaml = from_yaml.keybinds;
-
- for mode in InputMode::iter() {
- if let Some(keybinds) = keybinds_from_yaml.get(&mode) {
- for keybind in keybinds.iter() {
- match keybind {
- KeyActionUnbind::Unbind(unbind) => {
- unbind_config.insert(mode, unbind.unbind.clone());
- }
- KeyActionUnbind::KeyAction(key_action_from_yaml) => {
- keybind_config
- .0
- .insert(mode, ModeKeybinds::from(key_action_from_yaml.clone()));
- }
- }
- }
- }
- }
-
- let mut default = Self::default().unbind_mode(unbind_config);
-
- // Toplevel Unbinds
- if let Unbind::Keys(_) = from_yaml.unbind {
- let mut unbind_config: HashMap<InputMode, Unbind> = HashMap::new();
- for mode in InputMode::iter() {
- unbind_config.insert(mode, from_yaml.unbind.clone());
- }
- default = default.unbind_mode(unbind_config);
- };
-
- default.merge_keybinds(keybind_config)
- }
-
- /// Unbind [`Key`] bindings respective to their mode
- fn unbind_mode(&self, unbind: HashMap<InputMode, Unbind>) -> Keybinds {
- let mut keybinds = Keybinds::new();
-
- for mode in InputMode::iter() {
- if let Some(unbind) = unbind.get(&mode) {
- match unbind {
- Unbind::All(true) => {}
- Unbind::Keys(keys) => {
- if let Some(defaults) = self.0.get(&mode) {
- keybinds
- .0
- .insert(mode, defaults.clone().unbind_keys(keys.to_vec()));
- }
- }
- Unbind::All(false) => {
- if let Some(defaults) = self.0.get(&mode) {
- keybinds.0.insert(mode, defaults.clone());
- }
- }
- }
- } else if let Some(defaults) = self.0.get(&mode) {
- keybinds.0.insert(mode, defaults.clone());
- }
- }
- keybinds
- }
-
- /// Merges two Keybinds structs into one Keybinds struct
- /// `other` overrides the ModeKeybinds of `self`.
- fn merge_keybinds(&self, other: Keybinds) -> Keybinds {
- let mut keybinds = Keybinds::new();
-
- for mode in InputMode::iter() {
- let mut mode_keybinds = ModeKeybinds::new();
- if let Some(keybind) = self.0.get(&mode) {
- mode_keybinds.0.extend(keybind.0.clone());
- };
- if let Some(keybind) = other.0.get(&mode) {
- mode_keybinds.0.extend(keybind.0.clone());
- }
- if !mode_keybinds.0.is_empty() {
- keybinds.0.insert(mode, mode_keybinds);
- }
- }
- keybinds
- }
-
- /// Converts a [`Key`] terminal event to a sequence of [`Action`]s according to the current
- /// [`InputMode`] and [`Keybinds`].
- pub fn key_to_actions(
- key: &Key,
- input: Vec<u8>,
- mode: &InputMode,
- keybinds: &Keybinds,
- ) -> Vec<Action> {
- let mode_keybind_or_action = |action: Action| {
- keybinds
- .0
- .get(mode)
- .unwrap_or_else(|| unreachable!("Unrecognized mode: {:?}", mode))
- .0
- .get(key)
- .cloned()
- .unwrap_or_else(|| vec![action])
- };
- match *mode {
- InputMode::Normal | InputMode::Locked => mode_keybind_or_action(Action::Write(input)),
- InputMode::RenameTab => mode_keybind_or_action(Action::TabNameInput(input)),
- _ => mode_keybind_or_action(Action::NoOp),
- }
- }
-}
-
-impl ModeKeybinds {
- fn new() -> ModeKeybinds {
- ModeKeybinds(HashMap::<Key, Vec<Action>>::new())
- }
-
- /// Merges `self` with `other`, if keys are the same, `other` overwrites.
- fn merge(self, other: ModeKeybinds) -> ModeKeybinds {
- let mut merged = self;
- merged.0.extend(other.0);
- merged
- }
-
- /// Remove [`Key`]'s from [`ModeKeybinds`]
- fn unbind_keys(self, unbind: Vec<Key>) -> Self {
- let mut keymap = self;
- for key in unbind {
- keymap.0.remove(&key);
- }
- keymap
- }
-}
-
-impl From<KeybindsFromYaml> for Keybinds {
- fn from(keybinds_from_yaml: KeybindsFromYaml) -> Keybinds {
- let mut keybinds = Keybinds::new();
-
- for mode in InputMode::iter() {
- let mut mode_keybinds = ModeKeybinds::new();
- for key_action in keybinds_from_yaml.keybinds.get(&mode).iter() {
- for keybind in key_action.iter() {
- mode_keybinds = mode_keybinds.merge(ModeKeybinds::from(keybind.clone()));
- }
- }
- keybinds.0.insert(mode, mode_keybinds);
- }
- keybinds
- }
-}
-
-/// For each [`Key`] assigned to [`Action`]s,
-/// map the [`Action`]s to the [`Key`]
-impl From<KeyActionFromYaml> for ModeKeybinds {
- fn from(key_action: KeyActionFromYaml) -> ModeKeybinds {
- let actions = key_action.action;
-
- ModeKeybinds(
- key_action
- .key
- .into_iter()
- .map(|k| (k, actions.clone()))
- .collect::<HashMap<Key, Vec<Action>>>(),
- )
- }
-}
-
-impl From<KeyActionUnbind> for ModeKeybinds {
- fn from(key_action_unbind: KeyActionUnbind) -> ModeKeybinds {
- match key_action_unbind {
- KeyActionUnbind::KeyAction(key_action) => ModeKeybinds::from(key_action),
- KeyActionUnbind::Unbind(_) => ModeKeybinds::new(),
- }
- }
-}
-
-impl From<Vec<KeyActionFromYaml>> for ModeKeybinds {
- fn from(key_action_from_yaml: Vec<KeyActionFromYaml>) -> ModeKeybinds {
- let mut mode_keybinds = ModeKeybinds::new();
-
- for keybind in key_action_from_yaml {
- for key in keybind.key {
- mode_keybinds.0.insert(key, keybind.action.clone());
- }
- }
- mode_keybinds
- }
-}
-
-impl Default for Unbind {
- fn default() -> Unbind {
- Unbind::All(false)
- }
-}
-
-// The unit test location.
-#[cfg(test)]
-#[path = "./unit/keybinds_test.rs"]
-mod keybinds_test;
diff --git a/src/common/input/mod.rs b/src/common/input/mod.rs
deleted file mode 100644
index f30ccf23..00000000
--- a/src/common/input/mod.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-//! The way terminal input is handled.
-
-pub mod actions;
-pub mod config;
-pub mod handler;
-pub mod keybinds;
-pub mod options;
diff --git a/src/common/input/options.rs b/src/common/input/options.rs
deleted file mode 100644
index 33625b65..00000000
--- a/src/common/input/options.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-//! Handles cli and configuration options
-use crate::cli::ConfigCli;
-use serde::{Deserialize, Serialize};
-use structopt::StructOpt;
-
-#[derive(Clone, Default, Debug, PartialEq, Deserialize, Serialize, StructOpt)]
-/// Options that can be set either through the config file,
-/// or cli flags
-pub struct Options {
- /// Allow plugins to use a more simplified layout
- /// that is compatible with more fonts
- #[structopt(long)]
- pub simplified_ui: bool,
-}
-
-impl Options {
- pub fn from_yaml(from_yaml: Option<Options>) -> Options {
- if let Some(opts) = from_yaml {
- opts
- } else {
- Options::default()
- }
- }
-
- /// Merges two [`Options`] structs, a `Some` in `other`
- /// will supercede a `Some` in `self`
- // TODO: Maybe a good candidate for a macro?
- pub fn merge(&self, other: Options) -> Options {
- let simplified_ui = if other.simplified_ui {
- true
- } else {
- self.simplified_ui
- };
-
- Options { simplified_ui }
- }
-
- pub fn from_cli(&self, other: Option<ConfigCli>) -> Options {
- if let Some(ConfigCli::Options(options)) = other {
- Options::merge(&self, options)
- } else {
- self.to_owned()
- }
- }
-}
diff --git a/src/common/input/unit/keybinds_test.rs b/src/common/input/unit/keybinds_test.rs
deleted file mode 100644
index 4767fc15..00000000
--- a/src/common/input/unit/keybinds_test.rs
+++ /dev/null
@@ -1,801 +0,0 @@
-use super::super::actions::*;
-use super::super::keybinds::*;
-use zellij_tile::data::Key;
-
-#[test]
-fn merge_keybinds_merges_different_keys() {
- let mut mode_keybinds_self = ModeKeybinds::new();
- mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]);
- let mut mode_keybinds_other = ModeKeybinds::new();
- mode_keybinds_other
- .0
- .insert(Key::Backspace, vec![Action::NoOp]);
-
- let mut mode_keybinds_expected = ModeKeybinds::new();
- mode_keybinds_expected
- .0
- .insert(Key::F(1), vec![Action::NoOp]);
- mode_keybinds_expected
- .0
- .insert(Key::Backspace, vec![Action::NoOp]);
-
- let mode_keybinds_merged = mode_keybinds_self.merge(mode_keybinds_other);
-
- assert_eq!(mode_keybinds_expected, mode_keybinds_merged);
-}
-
-#[test]
-fn merge_mode_keybinds_overwrites_same_keys() {
- let mut mode_keybinds_self = ModeKeybinds::new();
- mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]);
- let mut mode_keybinds_other = ModeKeybinds::new();
- mode_keybinds_other
- .0
- .insert(Key::F(1), vec![Action::GoToTab(1)]);
-
- let mut mode_keybinds_expected = ModeKeybinds::new();
- mode_keybinds_expected
- .0
- .insert(Key::F(1), vec![Action::GoToTab(1)]);
-
- let mode_keybinds_merged = mode_keybinds_self.merge(mode_keybinds_other);
-
- assert_eq!(mode_keybinds_expected, mode_keybinds_merged);
-}
-
-#[test]
-fn merge_keybinds_merges() {
- let mut mode_keybinds_self = ModeKeybinds::new();
- mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]);
- let mut mode_keybinds_other = ModeKeybinds::new();
- mode_keybinds_other
- .0
- .insert(Key::Backspace, vec![Action::NoOp]);
- let mut keybinds_self = Keybinds::new();
- keybinds_self
- .0
- .insert(InputMode::Normal, mode_keybinds_self.clone());
- let mut keybinds_other = Keybinds::new();
- keybinds_other
- .0
- .insert(InputMode::Resize, mode_keybinds_other.clone());
- let mut keybinds_expected = Keybinds::new();
- keybinds_expected
- .0
- .insert(InputMode::Normal, mode_keybinds_self);
- keybinds_expected
- .0
- .insert(InputMode::Resize, mode_keybinds_other);
-
- assert_eq!(
- keybinds_expected,
- keybinds_self.merge_keybinds(keybinds_other)
- )
-}
-
-#[test]
-fn merge_keybinds_overwrites_same_keys() {
- let mut mode_keybinds_self = ModeKeybinds::new();
- mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]);
- mode_keybinds_self.0.insert(Key::F(2), vec![Action::NoOp]);
- mode_keybinds_self.0.insert(Key::F(3), vec![Action::NoOp]);
- let mut mode_keybinds_other = ModeKeybinds::new();
- mode_keybinds_other
- .0
- .insert(Key::F(1), vec![Action::GoToTab(1)]);
- mode_keybinds_other
- .0
- .insert(Key::F(2), vec![Action::GoToTab(2)]);
- mode_keybinds_other
- .0
- .insert(Key::F(3), vec![Action::GoToTab(3)]);
- let mut keybinds_self = Keybinds::new();
- keybinds_self
- .0
- .insert(InputMode::Normal, mode_keybinds_self.clone());
- let mut keybinds_other = Keybinds::new();
- keybinds_other
- .0
- .insert(InputMode::Normal, mode_keybinds_other.clone());
- let mut keybinds_expected = Keybinds::new();
- keybinds_expected
- .0
- .insert(InputMode::Normal, mode_keybinds_other);
-
- assert_eq!(
- keybinds_expected,
- keybinds_self.merge_keybinds(keybinds_other)
- )
-}
-
-#[test]
-fn from_keyaction_from_yaml_to_mode_keybindings() {
- let actions = vec![Action::NoOp, Action::GoToTab(1)];
- let keyaction = KeyActionFromYaml {
- action: actions.clone(),
- key: vec![Key::F(1), Key::Backspace, Key::Char('t')],
- };
-
- let mut expected = ModeKeybinds::new();
- expected.0.insert(Key::F(1), actions.clone());
- expected.0.insert(Key::Backspace, actions.clone());
- expected.0.insert(Key::Char('t'), actions);
-
- assert_eq!(expected, ModeKeybinds::from(keyaction));
-}
-
-#[test]
-fn toplevel_unbind_unbinds_all() {
- let from_yaml = KeybindsFromYaml {
- unbind: Unbind::All(true),
- keybinds: HashMap::new(),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
-
- assert_eq!(keybinds_from_yaml, Keybinds::new());
-}
-
-#[test]
-fn no_unbind_unbinds_none() {
- let from_yaml = KeybindsFromYaml {
- unbind: Unbind::All(false),
- keybinds: HashMap::new(),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
-
- assert_eq!(keybinds_from_yaml, Keybinds::default());
-}
-
-#[test]
-fn last_keybind_is_taken() {
- let actions_1 = vec![Action::NoOp, Action::NewTab];
- let keyaction_1 = KeyActionFromYaml {
- action: actions_1.clone(),
- key: vec![Key::F(1), Key::Backspace, Key::Char('t')],
- };
- let actions_2 = vec![Action::GoToTab(1)];
- let keyaction_2 = KeyActionFromYaml {
- action: actions_2.clone(),
- key: vec![Key::F(1), Key::Backspace, Key::Char('t')],
- };
-
- let mut expected = ModeKeybinds::new();
- expected.0.insert(Key::F(1), actions_2.clone());
- expected.0.insert(Key::Backspace, actions_2.clone());
- expected.0.insert(Key::Char('t'), actions_2);
-
- assert_eq!(expected, ModeKeybinds::from(vec![keyaction_1, keyaction_2]));
-}
-
-#[test]
-fn last_keybind_overwrites() {
- let actions_1 = vec![Action::NoOp, Action::NewTab];
- let keyaction_1 = KeyActionFromYaml {
- action: actions_1.clone(),
- key: vec![Key::F(1), Key::Backspace, Key::Char('t')],
- };
- let actions_2 = vec![Action::GoToTab(1)];
- let keyaction_2 = KeyActionFromYaml {
- action: actions_2.clone(),
- key: vec![Key::F(1), Key::Char('t')],
- };
-
- let mut expected = ModeKeybinds::new();
- expected.0.insert(Key::F(1), actions_2.clone());
- expected.0.insert(Key::Backspace, actions_1.clone());
- expected.0.insert(Key::Char('t'), actions_2);
-
- assert_eq!(expected, ModeKeybinds::from(vec![keyaction_1, keyaction_2]));
-}
-
-#[test]
-fn unbind_single_mode() {
- let unbind = Unbind::All(true);
- let unbind_from_yaml = UnbindFromYaml { unbind };
- let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)];
-
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbinds);
-
- let keybinds_from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::All(false),
- };
-
- let keybinds = Keybinds::unbind(keybinds_from_yaml);
- let result = keybinds.0.get(&InputMode::Normal);
- assert!(result.is_none());
-}
-
-#[test]
-fn unbind_multiple_modes() {
- let unbind = Unbind::All(true);
- let unbind_from_yaml = UnbindFromYaml { unbind };
- let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)];
-
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbinds.clone());
- keys.insert(InputMode::Pane, key_action_unbinds);
-
- let keybinds_from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::All(false),
- };
-
- let keybinds = Keybinds::unbind(keybinds_from_yaml);
- let normal = keybinds.0.get(&InputMode::Normal);
- let pane = keybinds.0.get(&InputMode::Pane);
- assert!(normal.is_none());
- assert!(pane.is_none());
-}
-
-#[test]
-fn unbind_single_keybind_single_mode() {
- let unbind = Unbind::Keys(vec![Key::Alt('n')]);
- let unbind_from_yaml = UnbindFromYaml { unbind };
- let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)];
-
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbinds);
-
- let keybinds_from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::All(false),
- };
-
- let keybinds = Keybinds::unbind(keybinds_from_yaml);
- let mode_keybinds = keybinds.0.get(&InputMode::Normal);
- let result = mode_keybinds
- .expect("Mode shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- assert!(result.is_none());
-}
-
-#[test]
-fn unbind_single_keybind_multiple_modes() {
- let unbind_n = Unbind::Keys(vec![Key::Alt('n')]);
- let unbind_h = Unbind::Keys(vec![Key::Alt('h')]);
- let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n };
- let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h };
- let key_action_unbinds_n = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)];
- let key_action_unbinds_h = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)];
-
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbinds_n);
- keys.insert(InputMode::Pane, key_action_unbinds_h);
-
- let keybinds_from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::All(false),
- };
-
- let keybinds = Keybinds::unbind(keybinds_from_yaml);
- let normal = keybinds.0.get(&InputMode::Normal);
- let pane = keybinds.0.get(&InputMode::Pane);
- let result_normal = normal
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_pane = pane.expect("Mode shouldn't be empty").0.get(&Key::Alt('h'));
- assert!(result_normal.is_none());
- assert!(result_pane.is_none());
-}
-
-#[test]
-fn unbind_multiple_keybinds_single_mode() {
- let unbind = Unbind::Keys(vec![Key::Alt('n'), Key::Ctrl('p')]);
- let unbind_from_yaml = UnbindFromYaml { unbind };
- let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)];
-
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbinds);
-
- let keybinds_from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::All(false),
- };
-
- let keybinds = Keybinds::unbind(keybinds_from_yaml);
- let mode_keybinds = keybinds.0.get(&InputMode::Normal);
- let result_n = mode_keybinds
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_p = mode_keybinds
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Ctrl('p'));
- assert!(result_n.is_none());
- assert!(result_p.is_none());
-}
-
-#[test]
-fn unbind_multiple_keybinds_multiple_modes() {
- let unbind_normal = Unbind::Keys(vec![Key::Alt('n'), Key::Ctrl('p')]);
- let unbind_resize = Unbind::Keys(vec![Key::Char('h'), Key::Ctrl('r')]);
- let unbind_from_yaml_normal = UnbindFromYaml {
- unbind: unbind_normal,
- };
- let unbind_from_yaml_resize = UnbindFromYaml {
- unbind: unbind_resize,
- };
- let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_normal)];
- let key_action_unbinds_resize = vec![KeyActionUnbind::Unbind(unbind_from_yaml_resize)];
-
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbinds_normal);
- keys.insert(InputMode::Resize, key_action_unbinds_resize);
-
- let keybinds_from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::All(false),
- };
-
- let keybinds = Keybinds::unbind(keybinds_from_yaml);
- let mode_keybinds_normal = keybinds.0.get(&InputMode::Normal);
- let mode_keybinds_resize = keybinds.0.get(&InputMode::Resize);
- let result_normal_1 = mode_keybinds_normal
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_normal_2 = mode_keybinds_normal
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Ctrl('p'));
- let result_resize_1 = mode_keybinds_resize
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Char('h'));
- let result_resize_2 = mode_keybinds_resize
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Ctrl('r'));
- assert!(result_normal_1.is_none());
- assert!(result_resize_1.is_none());
- assert!(result_normal_2.is_none());
- assert!(result_resize_2.is_none());
-}
-
-#[test]
-fn unbind_multiple_keybinds_all_modes() {
- let unbind = Unbind::Keys(vec![Key::Alt('n'), Key::Alt('h')]);
- let keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- let keybinds_from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind,
- };
-
- let keybinds = Keybinds::unbind(keybinds_from_yaml);
- let mode_keybinds_normal = keybinds.0.get(&InputMode::Normal);
- let mode_keybinds_resize = keybinds.0.get(&InputMode::Resize);
- let result_normal_1 = mode_keybinds_normal
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_normal_2 = mode_keybinds_normal
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Ctrl('h'));
- let result_resize_1 = mode_keybinds_resize
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Char('n'));
- let result_resize_2 = mode_keybinds_resize
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Ctrl('h'));
- assert!(result_normal_1.is_none());
- assert!(result_resize_1.is_none());
- assert!(result_normal_2.is_none());
- assert!(result_resize_2.is_none());
-}
-
-#[test]
-fn unbind_all_toplevel_single_key_single_mode() {
- let unbind = Unbind::Keys(vec![Key::Alt('h')]);
- let unbind_from_yaml = UnbindFromYaml { unbind };
- let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml)];
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbinds_normal);
- let from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::All(true),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
- assert_eq!(keybinds_from_yaml, Keybinds::new());
-}
-
-#[test]
-fn unbind_all_toplevel_single_key_multiple_modes() {
- let unbind_n = Unbind::Keys(vec![Key::Alt('n')]);
- let unbind_h = Unbind::Keys(vec![Key::Alt('h')]);
- let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n };
- let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h };
- let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)];
- let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)];
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbinds_normal);
- keys.insert(InputMode::Pane, key_action_unbinds_pane);
- let from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::All(true),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
- assert_eq!(keybinds_from_yaml, Keybinds::new());
-}
-
-#[test]
-fn unbind_all_toplevel_multiple_key_multiple_modes() {
- let unbind_n = Unbind::Keys(vec![Key::Alt('n'), Key::Ctrl('p')]);
- let unbind_h = Unbind::Keys(vec![Key::Alt('h'), Key::Ctrl('t')]);
- let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n };
- let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h };
- let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)];
- let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)];
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbinds_normal);
- keys.insert(InputMode::Pane, key_action_unbinds_pane);
- let from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::All(true),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
- assert_eq!(keybinds_from_yaml, Keybinds::new());
-}
-
-#[test]
-fn unbind_all_toplevel_all_key_multiple_modes() {
- let unbind = Unbind::All(true);
- let unbind_from_yaml = UnbindFromYaml { unbind };
- let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml.clone())];
- let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml)];
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbinds_normal);
- keys.insert(InputMode::Pane, key_action_unbinds_pane);
- let from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::All(true),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
- assert_eq!(keybinds_from_yaml, Keybinds::new());
-}
-
-#[test]
-fn unbind_single_keybind_all_modes() {
- let keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- let from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::Keys(vec![Key::Alt('n')]),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
-
- let result_normal = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_pane = keybinds_from_yaml
- .0
- .get(&InputMode::Pane)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_resize = keybinds_from_yaml
- .0
- .get(&InputMode::Resize)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_tab = keybinds_from_yaml
- .0
- .get(&InputMode::Tab)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
-
- assert!(result_normal.is_none());
- assert!(result_pane.is_none());
- assert!(result_resize.is_none());
- assert!(result_tab.is_none());
-}
-
-#[test]
-fn unbind_single_toplevel_single_key_single_mode_identical() {
- let unbind = Unbind::Keys(vec![Key::Alt('n')]);
- let unbind_from_yaml = UnbindFromYaml { unbind };
- let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)];
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbind);
- let from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::Keys(vec![Key::Alt('n')]),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
-
- let result_normal = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_pane = keybinds_from_yaml
- .0
- .get(&InputMode::Pane)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_resize = keybinds_from_yaml
- .0
- .get(&InputMode::Resize)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_tab = keybinds_from_yaml
- .0
- .get(&InputMode::Tab)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
-
- assert!(result_normal.is_none());
- assert!(result_pane.is_none());
- assert!(result_resize.is_none());
- assert!(result_tab.is_none());
-}
-
-#[test]
-fn unbind_single_toplevel_single_key_single_mode_differing() {
- let unbind = Unbind::Keys(vec![Key::Alt('l')]);
- let unbind_from_yaml = UnbindFromYaml { unbind };
- let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)];
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbind);
- let from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::Keys(vec![Key::Alt('n')]),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
-
- let result_normal_n = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_normal_l = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('l'));
- let result_resize_n = keybinds_from_yaml
- .0
- .get(&InputMode::Resize)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_resize_l = keybinds_from_yaml
- .0
- .get(&InputMode::Resize)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('l'));
-
- assert!(result_normal_n.is_none());
- assert!(result_normal_l.is_none());
- assert!(result_resize_n.is_none());
- assert!(result_resize_l.is_some());
-}
-
-#[test]
-fn unbind_single_toplevel_single_key_multiple_modes() {
- let unbind = Unbind::Keys(vec![Key::Alt('l')]);
- let unbind_from_yaml = UnbindFromYaml { unbind };
- let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)];
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbind.clone());
- keys.insert(InputMode::Pane, key_action_unbind);
- let from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::Keys(vec![Key::Alt('n')]),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
-
- let result_normal_n = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_normal_l = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('l'));
- let result_pane_n = keybinds_from_yaml
- .0
- .get(&InputMode::Pane)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_pane_l = keybinds_from_yaml
- .0
- .get(&InputMode::Pane)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('l'));
-
- assert!(result_normal_n.is_none());
- assert!(result_normal_l.is_none());
- assert!(result_pane_n.is_none());
- assert!(result_pane_l.is_none());
-}
-
-#[test]
-fn unbind_single_toplevel_multiple_keys_single_mode() {
- let unbind = Unbind::Keys(vec![
- Key::Alt('l'),
- Key::Alt('h'),
- Key::Alt('j'),
- Key::Alt('k'),
- ]);
- let unbind_from_yaml = UnbindFromYaml { unbind };
- let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)];
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbind.clone());
- keys.insert(InputMode::Pane, key_action_unbind);
- let from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::Keys(vec![Key::Alt('n')]),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
-
- let result_normal_n = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_normal_l = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('l'));
- let result_normal_k = keybinds_from_yaml
- .0
- .get(&InputMode::Pane)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('k'));
- let result_normal_h = keybinds_from_yaml
- .0
- .get(&InputMode::Pane)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('h'));
-
- assert!(result_normal_n.is_none());
- assert!(result_normal_l.is_none());
- assert!(result_normal_h.is_none());
- assert!(result_normal_k.is_none());
-}
-
-#[test]
-fn unbind_single_toplevel_multiple_keys_multiple_modes() {
- let unbind_normal = Unbind::Keys(vec![Key::Alt('l'), Key::Ctrl('p')]);
- let unbind_from_yaml_normal = UnbindFromYaml {
- unbind: unbind_normal,
- };
- let key_action_unbind_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_normal)];
- let unbind = Unbind::Keys(vec![Key::Alt('l'), Key::Alt('k')]);
- let unbind_from_yaml = UnbindFromYaml { unbind };
- let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)];
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbind_normal);
- keys.insert(InputMode::Pane, key_action_unbind);
- let from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::Keys(vec![Key::Alt('n')]),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
-
- let result_normal_n = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_normal_p = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Ctrl('p'));
- let result_normal_l = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('l'));
- let result_pane_p = keybinds_from_yaml
- .0
- .get(&InputMode::Pane)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Ctrl('p'));
- let result_pane_n = keybinds_from_yaml
- .0
- .get(&InputMode::Pane)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('n'));
- let result_pane_l = keybinds_from_yaml
- .0
- .get(&InputMode::Pane)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Alt('l'));
-
- assert!(result_normal_n.is_none());
- assert!(result_normal_l.is_none());
- assert!(result_normal_p.is_none());
- assert!(result_pane_n.is_none());
- assert!(result_pane_p.is_some());
- assert!(result_pane_l.is_none());
-}
-
-#[test]
-fn uppercase_and_lowercase_are_distinct() {
- let key_action_n = KeyActionFromYaml {
- key: vec![Key::Char('n')],
- action: vec![Action::NewTab],
- };
- let key_action_large_n = KeyActionFromYaml {
- key: vec![Key::Char('N')],
- action: vec![Action::NewPane(None)],
- };
-
- let key_action_unbind = vec![
- KeyActionUnbind::KeyAction(key_action_n),
- KeyActionUnbind::KeyAction(key_action_large_n),
- ];
- let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new();
- keys.insert(InputMode::Normal, key_action_unbind);
- let from_yaml = KeybindsFromYaml {
- keybinds: keys,
- unbind: Unbind::All(false),
- };
-
- let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml));
- let result_n = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Char('n'));
- let result_large_n = keybinds_from_yaml
- .0
- .get(&InputMode::Normal)
- .expect("ModeKeybinds shouldn't be empty")
- .0
- .get(&Key::Char('N'));
-
- assert!(result_n.is_some());
- assert!(result_large_n.is_some());
-}
diff --git a/src/common/ipc.rs b/src/common/ipc.rs
deleted file mode 100644
index af6dd859..00000000
--- a/src/common/ipc.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-//! IPC stuff for starting to split things into a client and server model.
-
-use crate::cli::CliArgs;
-use crate::common::{
- errors::{get_current_ctx, ErrorContext},
- input::{actions::Action, options::Options},
-};
-use crate::panes::PositionAndSize;
-use interprocess::local_socket::LocalSocketStream;
-use nix::unistd::dup;
-use serde::{Deserialize, Serialize};
-use std::io::{self, Write};
-use std::marker::PhantomData;
-use std::os::unix::io::{AsRawFd, FromRawFd};
-
-type SessionId = u64;
-
-#[derive(PartialEq, Eq, Serialize, Deserialize, Hash)]
-pub struct Session {
- // Unique ID for this session
- id: SessionId,
- // Identifier for the underlying IPC primitive (socket, pipe)
- conn_name: String,
- // User configured alias for the session
- alias: String,
-}
-
-// How do we want to connect to a session?
-#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
-pub enum ClientType {
- Reader,
- Writer,
-}
-
-// Types of messages sent from the client to the server
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub enum ClientToServerMsg {
- /*// List which sessions are available
- ListSessions,
- // Create a new session
- CreateSession,
- // Attach to a running session
- AttachToSession(SessionId, ClientType),
- // Force detach
- DetachSession(SessionId),
- // Disconnect from the session we're connected to
- DisconnectFromSession,*/
- ClientExit,
- TerminalResize(PositionAndSize),
- NewClient(PositionAndSize, Box<CliArgs>, Box<Options>),
- Action(Action),
-}
-
-// Types of messages sent from the server to the client
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub enum ServerToClientMsg {
- /*// Info about a particular session
- SessionInfo(Session),
- // A list of sessions
- SessionList(HashSet<Session>),*/
- Render(Option<String>),
- UnblockInputThread,
- Exit,
- ServerError(String),
-}
-
-/// Sends messages on a stream socket, along with an [`ErrorContext`].
-pub struct IpcSenderWithContext<T: Serialize> {
- sender: io::BufWriter<LocalSocketStream>,
- _phantom: PhantomData<T>,
-}
-
-impl<T: Serialize> IpcSenderWithContext<T> {
- /// Returns a sender to the given [LocalSocketStream](interprocess::local_socket::LocalSocketStream).
- pub fn new(sender: LocalSocketStream) -> Self {
- Self {
- sender: io::BufWriter::new(sender),
- _phantom: PhantomData,
- }
- }
-
- /// Sends an event, along with the current [`ErrorContext`], on this [`IpcSenderWithContext`]'s socket.
- pub fn send(&mut self, msg: T) {
- let err_ctx = get_current_ctx();
- bincode::serialize_into(&mut self.sender, &(msg, err_ctx)).unwrap();
- self.sender.flush().unwrap();
- }
-
- /// Returns an [`IpcReceiverWithContext`] with the same socket as this sender.
- pub fn get_receiver<F>(&self) -> IpcReceiverWithContext<F>
- where
- F: for<'de> Deserialize<'de> + Serialize,
- {
- let sock_fd = self.sender.get_ref().as_raw_fd();
- let dup_sock = dup(sock_fd).unwrap();
- let socket = unsafe { LocalSocketStream::from_raw_fd(dup_sock) };
- IpcReceiverWithContext::new(socket)
- }
-}
-
-/// Receives messages on a stream socket, along with an [`ErrorContext`].
-pub struct IpcReceiverWithContext<T> {
- receiver: io::BufReader<LocalSocketStream>,
- _phantom: PhantomData<T>,
-}
-
-impl<T> IpcReceiverWithContext<T>
-where
- T: for<'de> Deserialize<'de> + Serialize,
-{
- /// Returns a receiver to the given [LocalSocketStream](interprocess::local_socket::LocalSocketStream).
- pub fn new(receiver: LocalSocketStream) -> Self {
- Self {
- receiver: io::BufReader::new(receiver),
- _phantom: PhantomData,
- }
- }
-
- /// Receives an event, along with the current [`ErrorContext`], on this [`IpcReceiverWithContext`]'s socket.
- pub fn recv(&mut self) -> (T, ErrorContext) {
- bincode::deserialize_from(&mut self.receiver).unwrap()
- }
-
- /// Returns an [`IpcSenderWithContext`] with the same socket as this receiver.
- pub fn get_sender<F: Serialize>(&self) -> IpcSenderWithContext<F> {
- let sock_fd = self.receiver.get_ref().as_raw_fd();
- let dup_sock = dup(sock_fd).unwrap();
- let socket = unsafe { LocalSocketStream::from_raw_fd(dup_sock) };
- IpcSenderWithContext::new(socket)
- }
-}
diff --git a/src/common/mod.rs b/src/common/mod.rs
deleted file mode 100644
index c91e1e05..00000000
--- a/src/common/mod.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-pub mod command_is_executing;
-pub mod errors;
-pub mod input;
-pub mod ipc;
-pub mod os_input_output;
-pub mod pty;
-pub mod screen;
-pub mod setup;
-pub mod thread_bus;
-pub mod utils;
-pub mod wasm_vm;
-
-use crate::panes::PaneId;
-use crate::server::ServerInstruction;
diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs
deleted file mode 100644
index adde6fdf..00000000
--- a/src/common/os_input_output.rs
+++ /dev/null
@@ -1,414 +0,0 @@
-use crate::common::ipc::{
- ClientToServerMsg, IpcReceiverWithContext, IpcSenderWithContext, ServerToClientMsg,
-};
-use crate::errors::ErrorContext;
-use crate::panes::PositionAndSize;
-use crate::utils::shared::default_palette;
-use interprocess::local_socket::LocalSocketStream;
-use nix::fcntl::{fcntl, FcntlArg, OFlag};
-use nix::pty::{forkpty, Winsize};
-use nix::sys::signal::{kill, Signal};
-use nix::sys::termios;
-use nix::sys::wait::waitpid;
-use nix::unistd::{self, ForkResult, Pid};
-use signal_hook::{consts::signal::*, iterator::Signals};
-use std::io::prelude::*;
-use std::os::unix::{fs::PermissionsExt, io::RawFd};
-use std::path::{Path, PathBuf};
-use std::process::{Child, Command};
-use std::sync::{Arc, Mutex};
-use std::{env, fs, io};
-use zellij_tile::data::Palette;
-
-const UNIX_PERMISSIONS: u32 = 0o700;
-
-pub fn set_permissions(path: &Path) -> io::Result<()> {
- let mut permissions = fs::metadata(path)?.permissions();
- permissions.set_mode(UNIX_PERMISSIONS);
- fs::set_permissions(path, permissions)
-}
-
-fn into_raw_mode(pid: RawFd) {
- let mut tio = termios::tcgetattr(pid).expect("could not get terminal attribute");
- termios::cfmakeraw(&mut tio);
- match termios::tcsetattr(pid, termios::SetArg::TCSANOW, &tio) {
- Ok(_) => {}
- Err(e) => panic!("error {:?}", e),
- };
-}
-
-fn unset_raw_mode(pid: RawFd, orig_termios: termios::Termios) {
- match termios::tcsetattr(pid, termios::SetArg::TCSANOW, &orig_termios) {
- Ok(_) => {}
- Err(e) => panic!("error {:?}", e),
- };
-}
-
-pub fn get_terminal_size_using_fd(fd: RawFd) -> PositionAndSize {
- // TODO: do this with the nix ioctl
- use libc::ioctl;
- use libc::TIOCGWINSZ;
-
- let mut winsize = Winsize {
- ws_row: 0,
- ws_col: 0,
- ws_xpixel: 0,
- ws_ypixel: 0,
- };
-
- unsafe { ioctl(fd, TIOCGWINSZ, &mut winsize) };
- PositionAndSize::from(winsize)
-}
-
-pub fn set_terminal_size_using_fd(fd: RawFd, columns: u16, rows: u16) {
- // TODO: do this with the nix ioctl
- use libc::ioctl;
- use libc::TIOCSWINSZ;
-
- let winsize = Winsize {
- ws_col: columns,
- ws_row: rows,
- ws_xpixel: 0,
- ws_ypixel: 0,
- };
- unsafe { ioctl(fd, TIOCSWINSZ, &winsize) };
-}
-
-/// Handle some signals for the child process. This will loop until the child
-/// process exits.
-fn handle_command_exit(mut child: Child) {
- // register the SIGINT signal (TODO handle more signals)
- let mut signals = ::signal_hook::iterator::Signals::new(&[SIGINT]).unwrap();
- 'handle_exit: loop {
- // test whether the child process has exited
- match child.try_wait() {
- Ok(Some(_status)) => {
- // if the child process has exited, break outside of the loop
- // and exit this function
- // TODO: handle errors?
- break 'handle_exit;
- }
- Ok(None) => {
- ::std::thread::sleep(::std::time::Duration::from_millis(100));
- }
- Err(e) => panic!("error attempting to wait: {}", e),
- }
-
- for signal in signals.pending() {
- if let SIGINT = signal {
- child.kill().unwrap();
- child.wait().unwrap();
- break 'handle_exit;
- }
- }
- }
-}
-
-/// Spawns a new terminal from the parent terminal with [`termios`](termios::Termios)
-/// `orig_termios`.
-///
-/// If a `file_to_open` is given, the text editor specified by environment variable `EDITOR`
-/// (or `VISUAL`, if `EDITOR` is not set) will be started in the new terminal, with the given
-/// file open. If no file is given, the shell specified by environment variable `SHELL` will
-/// be started in the new terminal.
-///
-/// # Panics
-///
-/// This function will panic if both the `EDITOR` and `VISUAL` environment variables are not
-/// set.
-// FIXME this should probably be split into different functions, or at least have less levels
-// of indentation in some way
-fn spawn_terminal(file_to_open: Option<PathBuf>, orig_termios: termios::Termios) -> (RawFd, RawFd) {
- let (pid_primary, pid_secondary): (RawFd, RawFd) = {
- match forkpty(None, Some(&orig_termios)) {
- Ok(fork_pty_res) => {
- let pid_primary = fork_pty_res.master;
- let pid_secondary = match fork_pty_res.fork_result {
- ForkResult::Parent { child } => {
- // fcntl(pid_primary, FcntlArg::F_SETFL(OFlag::empty())).expect("could not fcntl");
- fcntl(pid_primary, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))
- .expect("could not fcntl");
- child
- }
- ForkResult::Child => match file_to_open {
- Some(file_to_open) => {
- if env::var("EDITOR").is_err() && env::var("VISUAL").is_err() {
- panic!("Can't edit files if an editor is not defined. To fix: define the EDITOR or VISUAL environment variables with the path to your editor (eg. /usr/bin/vim)");
- }
- let editor =
- env::var("EDITOR").unwrap_or_else(|_| env::var("VISUAL").unwrap());
-
- let child = Command::new(editor)
- .args(&[file_to_open])
- .spawn()
- .expect("failed to spawn");
- handle_command_exit(child);
- ::std::process::exit(0);
- }
- None => {
- let child = Command::new(env::var("SHELL").unwrap())
- .spawn()
- .expect("failed to spawn");
- handle_command_exit(child);
- ::std::process::exit(0);
- }
- },
- };
- (pid_primary, pid_secondary.as_raw())
- }
- Err(e) => {
- panic!("failed to fork {:?}", e);
- }
- }
- };
- (pid_primary, pid_secondary)
-}
-
-#[derive(Clone)]
-pub struct ServerOsInputOutput {
- orig_termios: Arc<Mutex<termios::Termios>>,
- receive_instructions_from_client: Option<Arc<Mutex<IpcReceiverWithContext<ClientToServerMsg>>>>,
- send_instructions_to_client: Arc<Mutex<Option<IpcSenderWithContext<ServerToClientMsg>>>>,
-}
-
-/// The `ServerOsApi` trait represents an abstract interface to the features of an operating system that
-/// Zellij server requires.
-pub trait ServerOsApi: Send + Sync {
- /// Sets the size of the terminal associated to file descriptor `fd`.
- fn set_terminal_size_using_fd(&mut self, fd: RawFd, cols: u16, rows: u16);
- /// Spawn a new terminal, with an optional file to open in a terminal program.
- fn spawn_terminal(&mut self, file_to_open: Option<PathBuf>) -> (RawFd, RawFd);
- /// Read bytes from the standard output of the virtual terminal referred to by `fd`.
- fn read_from_tty_stdout(&mut self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
- /// Write bytes to the standard input of the virtual terminal referred to by `fd`.
- fn write_to_tty_stdin(&mut self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
- /// Wait until all output written to the object referred to by `fd` has been transmitted.
- fn tcdrain(&mut self, fd: RawFd) -> Result<(), nix::Error>;
- /// Terminate the process with process ID `pid`.
- // FIXME `RawFd` is semantically the wrong type here. It should either be a raw libc::pid_t,
- // or a nix::unistd::Pid. See `man kill.3`, nix::sys::signal::kill (both take an argument
- // called `pid` and of type `pid_t`, and not `fd`)
- fn kill(&mut self, pid: RawFd) -> Result<(), nix::Error>;
- /// Returns a [`Box`] pointer to this [`ServerOsApi`] struct.
- fn box_clone(&self) -> Box<dyn ServerOsApi>;
- /// Receives a message on server-side IPC channel
- fn recv_from_client(&self) -> (ClientToServerMsg, ErrorContext);
- /// Sends a message to client
- fn send_to_client(&self, msg: ServerToClientMsg);
- /// Adds a sender to client
- fn add_client_sender(&mut self);
- /// Update the receiver socket for the client
- fn update_receiver(&mut self, stream: LocalSocketStream);
- fn load_palette(&self) -> Palette;
-}
-
-impl ServerOsApi for ServerOsInputOutput {
- fn set_terminal_size_using_fd(&mut self, fd: RawFd, cols: u16, rows: u16) {
- set_terminal_size_using_fd(fd, cols, rows);
- }
- fn spawn_terminal(&mut self, file_to_open: Option<PathBuf>) -> (RawFd, RawFd) {
- let orig_termios = self.orig_termios.lock().unwrap();
- spawn_terminal(file_to_open, orig_termios.clone())
- }
- fn read_from_tty_stdout(&mut self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> {
- unistd::read(fd, buf)
- }
- fn write_to_tty_stdin(&mut self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> {
- unistd::write(fd, buf)
- }
- fn tcdrain(&mut self, fd: RawFd) -> Result<(), nix::Error> {
- termios::tcdrain(fd)
- }
- fn box_clone(&self) -> Box<dyn ServerOsApi> {
- Box::new((*self).clone())
- }
- fn kill(&mut self, pid: RawFd) -> Result<(), nix::Error> {
- // TODO:
- // Ideally, we should be using SIGINT rather than SIGKILL here, but there are cases in which
- // the terminal we're trying to kill hangs on SIGINT and so all the app gets stuck
- // that's why we're sending SIGKILL here
- // A better solution would be to send SIGINT here and not wait for it, and then have
- // a background thread do the waitpid stuff and send SIGKILL if the process is stuck
- kill(Pid::from_raw(pid), Some(Signal::SIGKILL)).unwrap();
- waitpid(Pid::from_raw(pid), None).unwrap();
- Ok(())
- }
- fn recv_from_client(&self) -> (ClientToServerMsg, ErrorContext) {
- self.receive_instructions_from_client
- .as_ref()
- .unwrap()
- .lock()
- .unwrap()
- .recv()
- }
- fn send_to_client(&self, msg: ServerToClientMsg) {
- self.send_instructions_to_client
- .lock()
- .unwrap()
- .as_mut()
- .unwrap()
- .send(msg);
- }
- fn add_client_sender(&mut self) {
- assert!(self.send_instructions_to_client.lock().unwrap().is_none());
- let sender = self
- .receive_instructions_from_client
- .as_ref()
- .unwrap()
- .lock()
- .unwrap()
- .get_sender();
- *self.send_instructions_to_client.lock().unwrap() = Some(sender);
- }
- fn update_receiver(&mut self, stream: LocalSocketStream) {
- self.receive_instructions_from_client =
- Some(Arc::new(Mutex::new(IpcReceiverWithContext::new(stream))));
- }
- fn load_palette(&self) -> Palette {
- default_palette()
- }
-}
-
-impl Clone for Box<dyn ServerOsApi> {
- fn clone(&self) -> Box<dyn ServerOsApi> {
- self.box_clone()
- }
-}
-
-pub fn get_server_os_input() -> ServerOsInputOutput {
- let current_termios = termios::tcgetattr(0).unwrap();
- let orig_termios = Arc::new(Mutex::new(current_termios));
- ServerOsInputOutput {
- orig_termios,
- receive_instructions_from_client: None,
- send_instructions_to_client: Arc::new(Mutex::new(None)),
- }
-}
-
-#[derive(Clone)]
-pub struct ClientOsInputOutput {
- orig_termios: Arc<Mutex<termios::Termios>>,
- send_instructions_to_server: Arc<Mutex<Option<IpcSenderWithContext<ClientToServerMsg>>>>,
- receive_instructions_from_server: Arc<Mutex<Option<IpcReceiverWithContext<ServerToClientMsg>>>>,
-}
-
-/// The `ClientOsApi` trait represents an abstract interface to the features of an operating system that
-/// Zellij client requires.
-pub trait ClientOsApi: Send + Sync {
- /// Returns the size of the terminal associated to file descriptor `fd`.
- fn get_terminal_size_using_fd(&self, fd: RawFd) -> PositionAndSize;
- /// Set the terminal associated to file descriptor `fd` to
- /// [raw mode](https://en.wikipedia.org/wiki/Terminal_mode).
- fn set_raw_mode(&mut self, fd: RawFd);
- /// Set the terminal associated to file descriptor `fd` to
- /// [cooked mode](https://en.wikipedia.org/wiki/Terminal_mode).
- fn unset_raw_mode(&self, fd: RawFd);
- /// Returns the writer that allows writing to standard output.
- fn get_stdout_writer(&self) -> Box<dyn io::Write>;
- /// Returns the raw contents of standard input.
- fn read_from_stdin(&self) -> Vec<u8>;
- /// Returns a [`Box`] pointer to this [`ClientOsApi`] struct.
- fn box_clone(&self) -> Box<dyn ClientOsApi>;
- /// Sends a message to the server.
- fn send_to_server(&self, msg: ClientToServerMsg);
- /// Receives a message on client-side IPC channel
- // This should be called from the client-side router thread only.
- fn recv_from_server(&self) -> (ServerToClientMsg, ErrorContext);
- fn handle_signals(&self, sigwinch_cb: Box<dyn Fn()>, quit_cb: Box<dyn Fn()>);
- /// Establish a connection with the server socket.
- fn connect_to_server(&self, path: &Path);
-}
-
-impl ClientOsApi for ClientOsInputOutput {
- fn get_terminal_size_using_fd(&self, fd: RawFd) -> PositionAndSize {
- get_terminal_size_using_fd(fd)
- }
- fn set_raw_mode(&mut self, fd: RawFd) {
- into_raw_mode(fd);
- }
- fn unset_raw_mode(&self, fd: RawFd) {
- let orig_termios = self.orig_termios.lock().unwrap();
- unset_raw_mode(fd, orig_termios.clone());
- }
- fn box_clone(&self) -> Box<dyn ClientOsApi> {
- Box::new((*self).clone())
- }
- fn read_from_stdin(&self) -> Vec<u8> {
- let stdin = std::io::stdin();
- let mut stdin = stdin.lock();
- let buffer = stdin.fill_buf().unwrap();
- let length = buffer.len();
- let read_bytes = Vec::from(buffer);
- stdin.consume(length);
- read_bytes
- }
- fn get_stdout_writer(&self) -> Box<dyn io::Write> {
- let stdout = ::std::io::stdout();
- Box::new(stdout)
- }
- fn send_to_server(&self, msg: ClientToServerMsg) {
- self.send_instructions_to_server
- .lock()
- .unwrap()
- .as_mut()
- .unwrap()
- .send(msg);
- }
- fn recv_from_server(&self) -> (ServerToClientMsg, ErrorContext) {
- self.receive_instructions_from_server
- .lock()
- .unwrap()
- .as_mut()
- .unwrap()
- .recv()
- }
- fn handle_signals(&self, sigwinch_cb: Box<dyn Fn()>, quit_cb: Box<dyn Fn()>) {
- let mut signals = Signals::new(&[SIGWINCH, SIGTERM, SIGINT, SIGQUIT, SIGHUP]).unwrap();
- for signal in signals.forever() {
- match signal {
- SIGWINCH => {
- sigwinch_cb();
- }
- SIGTERM | SIGINT | SIGQUIT | SIGHUP => {
- quit_cb();
- break;
- }
- _ => unreachable!(),
- }
- }
- }
- fn connect_to_server(&self, path: &Path) {
- let socket;
- loop {
- match LocalSocketStream::connect(path) {
- Ok(sock) => {
- socket = sock;
- break;
- }
- Err(_) => {
- std::thread::sleep(std::time::Duration::from_millis(50));
- }
- }
- }
- let sender = IpcSenderWithContext::new(socket);
- let receiver = sender.get_receiver();
- *self.send_instructions_to_server.lock().unwrap() = Some(sender);
- *self.receive_instructions_from_server.lock().unwrap() = Some(receiver);
- }
-}
-
-impl Clone for Box<dyn ClientOsApi> {
- fn clone(&self) -> Box<dyn ClientOsApi> {
- self.box_clone()
- }
-}
-
-pub fn get_client_os_input() -> ClientOsInputOutput {
- let current_termios = termios::tcgetattr(0).unwrap();
- let orig_termios = Arc::new(Mutex::new(current_termios));
- ClientOsInputOutput {
- orig_termios,
- send_instructions_to_server: Arc::new(Mutex::new(None)),
- receive_instructions_from_server: Arc::new(Mutex::new(None)),
- }
-}
diff --git a/src/common/pty.rs b/src/common/pty.rs
deleted file mode 100644
index 5f5442b5..00000000
--- a/src/common/pty.rs
+++ /dev/null
@@ -1,293 +0,0 @@
-use async_std::stream::*;
-use async_std::task;
-use async_std::task::*;
-use std::collections::HashMap;
-use std::os::unix::io::RawFd;
-use std::path::PathBuf;
-use std::pin::*;
-use std::time::{Duration, Instant};
-
-use crate::client::panes::PaneId;
-use crate::common::errors::{get_current_ctx, ContextType};
-use crate::common::screen::ScreenInstruction;
-use crate::common::thread_bus::{Bus, ThreadSenders};
-use crate::layout::Layout;
-use crate::os_input_output::ServerOsApi;
-use crate::server::ServerInstruction;
-use crate::utils::logging::debug_to_file;
-use crate::wasm_vm::PluginInstruction;
-
-pub struct ReadFromPid {
- pid: RawFd,
- os_input: Box<dyn ServerOsApi>,
-}
-
-impl ReadFromPid {
- pub fn new(pid: &RawFd, os_input: Box<dyn ServerOsApi>) -> ReadFromPid {
- ReadFromPid {
- pid: *pid,
- os_input,
- }
- }
-}
-
-impl Stream for ReadFromPid {
- type Item = Vec<u8>;
- fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
- let mut read_buffer = [0; 65535];
- let pid = self.pid;
- let read_result = &self.os_input.read_from_tty_stdout(pid, &mut read_buffer);
- match read_result {
- Ok(res) => {
- if *res == 0 {
- // indicates end of file
- Poll::Ready(None)
- } else {
- let res = Some(read_buffer[..*res].to_vec());
- Poll::Ready(res)
- }
- }
- Err(e) => {
- match e {
- nix::Error::Sys(errno) => {
- if *errno == nix::errno::Errno::EAGAIN {
- Poll::Ready(Some(vec![])) // TODO: better with timeout waker somehow
- } else {
- Poll::Ready(None)
- }
- }
- _ => Poll::Ready(None),
- }
- }
- }
- }
-}
-
-pub type VteBytes = Vec<u8>;
-
-/// Instructions related to PTYs (pseudoterminals).
-#[derive(Clone, Debug)]
-pub enum PtyInstruction {
- SpawnTerminal(Option<PathBuf>),
- SpawnTerminalVertically(Option<PathBuf>),
- SpawnTerminalHorizontally(Option<PathBuf>),
- NewTab,
- ClosePane(PaneId),
- CloseTab(Vec<PaneId>),
- Exit,
-}
-
-pub struct Pty {
- pub bus: Bus<PtyInstruction>,
- pub id_to_child_pid: HashMap<RawFd, RawFd>,
- debug_to_file: bool,
- task_handles: HashMap<RawFd, JoinHandle<()>>,
-}
-
-pub fn pty_thread_main(mut pty: Pty, maybe_layout: Option<Layout>) {
- loop {
- let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel");
- err_ctx.add_call(ContextType::Pty((&event).into()));
- match event {
- PtyInstruction::SpawnTerminal(file_to_open) => {
- let pid = pty.spawn_terminal(file_to_open);
- pty.bus
- .senders
- .send_to_screen(ScreenInstruction::NewPane(PaneId::Terminal(pid)))
- .unwrap();
- }
- PtyInstruction::SpawnTerminalVertically(file_to_open) => {
- let pid = pty.spawn_terminal(file_to_open);
- pty.bus
- .senders
- .send_to_screen(ScreenInstruction::VerticalSplit(PaneId::Terminal(pid)))
- .unwrap();
- }
- PtyInstruction::SpawnTerminalHorizontally(file_to_open) => {
- let pid = pty.spawn_terminal(file_to_open);
- pty.bus
- .senders
- .send_to_screen(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)))
- .unwrap();
- }
- PtyInstruction::NewTab => {
- if let Some(layout) = maybe_layout.clone() {
- pty.spawn_terminals_for_layout(layout);
- } else {
- let pid = pty.spawn_terminal(None);
- pty.bus
- .senders
- .send_to_screen(ScreenInstruction::NewTab(pid))
- .unwrap();
- }
- }
- PtyInstruction::ClosePane(id) => {
- pty.close_pane(id);
- pty.bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- PtyInstruction::CloseTab(ids) => {
- pty.close_tab(ids);
- pty.bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- PtyInstruction::Exit => break,
- }
- }
-}
-
-fn stream_terminal_bytes(
- pid: RawFd,
- senders: ThreadSenders,
- os_input: Box<dyn ServerOsApi>,
- debug: bool,
-) -> JoinHandle<()> {
- let mut err_ctx = get_current_ctx();
- task::spawn({
- async move {
- err_ctx.add_call(ContextType::AsyncTask);
- let mut terminal_bytes = ReadFromPid::new(&pid, os_input);
-
- let mut last_byte_receive_time: Option<Instant> = None;
- let mut pending_render = false;
- let max_render_pause = Duration::from_millis(30);
-
- while let Some(bytes) = terminal_bytes.next().await {
- let bytes_is_empty = bytes.is_empty();
- if debug {
- for byte in bytes.iter() {
- debug_to_file(*byte, pid).unwrap();
- }
- }
- if !bytes_is_empty {
- let _ = senders.send_to_screen(ScreenInstruction::PtyBytes(pid, bytes));
- // for UX reasons, if we got something on the wire, we only send the render notice if:
- // 1. there aren't any more bytes on the wire afterwards
- // 2. a certain period (currently 30ms) has elapsed since the last render
- // (otherwise if we get a large amount of data, the display would hang
- // until it's done)
- // 3. the stream has ended, and so we render 1 last time
- match last_byte_receive_time.as_mut() {
- Some(receive_time) => {
- if receive_time.elapsed() > max_render_pause {
- pending_render = false;
- let _ = senders.send_to_screen(ScreenInstruction::Render);
- last_byte_receive_time = Some(Instant::now());
- } else {
- pending_render = true;
- }
- }
- None => {
- last_byte_receive_time = Some(Instant::now());
- pending_render = true;
- }
- };
- } else {
- if pending_render {
- pending_render = false;
- let _ = senders.send_to_screen(ScreenInstruction::Render);
- }
- last_byte_receive_time = None;
- task::sleep(::std::time::Duration::from_millis(10)).await;
- }
- }
- senders.send_to_screen(ScreenInstruction::Render).unwrap();
- #[cfg(not(test))]
- // this is a little hacky, and is because the tests end the file as soon as
- // we read everything, rather than hanging until there is new data
- // a better solution would be to fix the test fakes, but this will do for now
- senders
- .send_to_screen(ScreenInstruction::ClosePane(PaneId::Terminal(pid)))
- .unwrap();
- }
- })
-}
-
-impl Pty {
- pub fn new(bus: Bus<PtyInstruction>, debug_to_file: bool) -> Self {
- Pty {
- bus,
- id_to_child_pid: HashMap::new(),
- debug_to_file,
- task_handles: HashMap::new(),
- }
- }
- pub fn spawn_terminal(&mut self, file_to_open: Option<PathBuf>) -> RawFd {
- let (pid_primary, pid_secondary): (RawFd, RawFd) = self
- .bus
- .os_input
- .as_mut()
- .unwrap()
- .spawn_terminal(file_to_open);
- let task_handle = stream_terminal_bytes(
- pid_primary,
- self.bus.senders.clone(),
- self.bus.os_input.as_ref().unwrap().clone(),
- self.debug_to_file,
- );
- self.task_handles.insert(pid_primary, task_handle);
- self.id_to_child_pid.insert(pid_primary, pid_secondary);
- pid_primary
- }
- pub fn spawn_terminals_for_layout(&mut self, layout: Layout) {
- let total_panes = layout.total_terminal_panes();
- let mut new_pane_pids = vec![];
- for _ in 0..total_panes {
- let (pid_primary, pid_secondary): (RawFd, RawFd) =
- self.bus.os_input.as_mut().unwrap().spawn_terminal(None);
- self.id_to_child_pid.insert(pid_primary, pid_secondary);
- new_pane_pids.push(pid_primary);
- }
- self.bus
- .senders
- .send_to_screen(ScreenInstruction::ApplyLayout(
- layout,
- new_pane_pids.clone(),
- ))
- .unwrap();
- for id in new_pane_pids {
- let task_handle = stream_terminal_bytes(
- id,
- self.bus.senders.clone(),
- self.bus.os_input.as_ref().unwrap().clone(),
- self.debug_to_file,
- );
- self.task_handles.insert(id, task_handle);
- }
- }
- pub fn close_pane(&mut self, id: PaneId) {
- match id {
- PaneId::Terminal(id) => {
- let child_pid = self.id_to_child_pid.remove(&id).unwrap();
- let handle = self.task_handles.remove(&id).unwrap();
- self.bus.os_input.as_mut().unwrap().kill(child_pid).unwrap();
- task::block_on(async {
- handle.cancel().await;
- });
- }
- PaneId::Plugin(pid) => drop(
- self.bus
- .senders
- .send_to_plugin(PluginInstruction::Unload(pid)),
- ),
- }
- }
- pub fn close_tab(&mut self, ids: Vec<PaneId>) {
- ids.iter().for_each(|&id| {
- self.close_pane(id);
- });
- }
-}
-
-impl Drop for Pty {
- fn drop(&mut self) {
- let child_ids: Vec<RawFd> = self.id_to_child_pid.keys().copied().collect();
- for id in child_ids {
- self.close_pane(PaneId::Terminal(id));
- }
- }
-}
diff --git a/src/common/screen.rs b/src/common/screen.rs
deleted file mode 100644
index d2030243..00000000
--- a/src/common/screen.rs
+++ /dev/null
@@ -1,598 +0,0 @@
-//! Things related to [`Screen`]s.
-
-use std::collections::BTreeMap;
-use std::os::unix::io::RawFd;
-use std::str;
-
-use crate::common::input::options::Options;
-use crate::common::pty::{PtyInstruction, VteBytes};
-use crate::common::thread_bus::Bus;
-use crate::errors::ContextType;
-use crate::layout::Layout;
-use crate::panes::PaneId;
-use crate::panes::PositionAndSize;
-use crate::server::ServerInstruction;
-use crate::tab::Tab;
-use crate::wasm_vm::PluginInstruction;
-
-use zellij_tile::data::{Event, InputMode, ModeInfo, Palette, PluginCapabilities, TabInfo};
-
-/// Instructions that can be sent to the [`Screen`].
-#[derive(Debug, Clone)]
-pub enum ScreenInstruction {
- PtyBytes(RawFd, VteBytes),
- Render,
- NewPane(PaneId),
- HorizontalSplit(PaneId),
- VerticalSplit(PaneId),
- WriteCharacter(Vec<u8>),
- ResizeLeft,
- ResizeRight,
- ResizeDown,
- ResizeUp,
- SwitchFocus,
- FocusNextPane,
- FocusPreviousPane,
- MoveFocusLeft,
- MoveFocusLeftOrPreviousTab,
- MoveFocusDown,
- MoveFocusUp,
- MoveFocusRight,
- MoveFocusRightOrNextTab,
- Exit,
- ScrollUp,
- ScrollDown,
- PageScrollUp,
- PageScrollDown,
- ClearScroll,
- CloseFocusedPane,
- ToggleActiveTerminalFullscreen,
- SetSelectable(PaneId, bool),
- SetMaxHeight(PaneId, usize),
- SetInvisibleBorders(PaneId, bool),
- ClosePane(PaneId),
- ApplyLayout(Layout, Vec<RawFd>),
- NewTab(RawFd),
- SwitchTabNext,
- SwitchTabPrev,
- ToggleActiveSyncTab,
- CloseTab,
- GoToTab(u32),
- UpdateTabName(Vec<u8>),
- TerminalResize(PositionAndSize),
- ChangeMode(ModeInfo),
-}
-
-/// A [`Screen`] holds multiple [`Tab`]s, each one holding multiple [`panes`](crate::client::panes).
-/// It only directly controls which tab is active, delegating the rest to the individual `Tab`.
-pub struct Screen {
- /// A Bus for sending and receiving messages with the other threads.
- pub bus: Bus<ScreenInstruction>,
- /// An optional maximal amount of panes allowed per [`Tab`] in this [`Screen`] instance.
- max_panes: Option<usize>,
- /// A map between this [`Screen`]'s tabs and their ID/key.
- tabs: BTreeMap<usize, Tab>,
- /// The full size of this [`Screen`].
- full_screen_ws: PositionAndSize,
- /// The index of this [`Screen`]'s active [`Tab`].
- active_tab_index: Option<usize>,
- mode_info: ModeInfo,
- input_mode: InputMode,
- colors: Palette,
-}
-
-impl Screen {
- /// Creates and returns a new [`Screen`].
- pub fn new(
- bus: Bus<ScreenInstruction>,
- full_screen_ws: &PositionAndSize,
- max_panes: Option<usize>,
- mode_info: ModeInfo,
- input_mode: InputMode,
- colors: Palette,
- ) -> Self {
- Screen {
- bus,
- max_panes,
- full_screen_ws: *full_screen_ws,
- active_tab_index: None,
- tabs: BTreeMap::new(),
- mode_info,
- input_mode,
- colors,
- }
- }
-
- /// Creates a new [`Tab`] in this [`Screen`], containing a single
- /// [pane](crate::client::panes) with PTY file descriptor `pane_id`.
- pub fn new_tab(&mut self, pane_id: RawFd) {
- let tab_index = self.get_new_tab_index();
- let position = self.tabs.len();
- let tab = Tab::new(
- tab_index,
- position,
- String::new(),
- &self.full_screen_ws,
- self.bus.os_input.as_ref().unwrap().clone(),
- self.bus.senders.clone(),
- self.max_panes,
- Some(PaneId::Terminal(pane_id)),
- self.mode_info.clone(),
- self.input_mode,
- self.colors,
- );
- self.active_tab_index = Some(tab_index);
- self.tabs.insert(tab_index, tab);
- self.update_tabs();
- self.render();
- }
-
- /// Returns the index where a new [`Tab`] should be created in this [`Screen`].
- /// Currently, this is right after the last currently existing tab, or `0` if
- /// no tabs exist in this screen yet.
- fn get_new_tab_index(&self) -> usize {
- if let Some(index) = self.tabs.keys().last() {
- *index + 1
- } else {
- 0
- }
- }
-
- /// Sets this [`Screen`]'s active [`Tab`] to the next tab.
- pub fn switch_tab_next(&mut self) {
- let active_tab_pos = self.get_active_tab().unwrap().position;
- let new_tab_pos = (active_tab_pos + 1) % self.tabs.len();
-
- for tab in self.tabs.values_mut() {
- if tab.position == new_tab_pos {
- tab.set_force_render();
- self.active_tab_index = Some(tab.index);
- break;
- }
- }
- self.update_tabs();
- self.render();
- }
-
- /// Sets this [`Screen`]'s active [`Tab`] to the previous tab.
- pub fn switch_tab_prev(&mut self) {
- let active_tab_pos = self.get_active_tab().unwrap().position;
- let new_tab_pos = if active_tab_pos == 0 {
- self.tabs.len() - 1
- } else {
- active_tab_pos - 1
- };
- for tab in self.tabs.values_mut() {
- if tab.position == new_tab_pos {
- tab.set_force_render();
- self.active_tab_index = Some(tab.index);
- break;
- }
- }
- self.update_tabs();
- self.render();
- }
-
- pub fn go_to_tab(&mut self, mut tab_index: usize) {
- tab_index -= 1;
- let active_tab_index = self.get_active_tab().unwrap().index;
- if let Some(t) = self.tabs.values_mut().find(|t| t.position == tab_index) {
- if t.index != active_tab_index {
- t.set_force_render();
- self.active_tab_index = Some(t.index);
- self.update_tabs();
- self.render();
- }
- }
- }
-
- /// Closes this [`Screen`]'s active [`Tab`], exiting the application if it happens
- /// to be the last tab.
- pub fn close_tab(&mut self) {
- let active_tab_index = self.active_tab_index.unwrap();
- if self.tabs.len() > 1 {
- self.switch_tab_prev();
- }
- let active_tab = self.tabs.remove(&active_tab_index).unwrap();
- let pane_ids = active_tab.get_pane_ids();
- // below we don't check the result of sending the CloseTab instruction to the pty thread
- // because this might be happening when the app is closing, at which point the pty thread
- // has already closed and this would result in an error
- self.bus
- .senders
- .send_to_pty(PtyInstruction::CloseTab(pane_ids))
- .unwrap();
- if self.tabs.is_empty() {
- self.active_tab_index = None;
- self.bus
- .senders
- .send_to_server(ServerInstruction::Render(None))
- .unwrap();
- } else {
- for t in self.tabs.values_mut() {
- if t.position > active_tab.position {
- t.position -= 1;
- }
- }
- self.update_tabs();
- }
- }
-
- pub fn resize_to_screen(&mut self, new_screen_size: PositionAndSize) {
- self.full_screen_ws = new_screen_size;
- for (_, tab) in self.tabs.iter_mut() {
- tab.resize_whole_tab(new_screen_size);
- }
- let _ = self.get_active_tab_mut().map(|t| t.set_force_render());
- self.render();
- }
-
- /// Renders this [`Screen`], which amounts to rendering its active [`Tab`].
- pub fn render(&mut self) {
- if let Some(active_tab) = self.get_active_tab_mut() {
- if active_tab.get_active_pane().is_some() {
- active_tab.render();
- } else {
- self.close_tab();
- }
- };
- }
-
- /// Returns a mutable reference to this [`Screen`]'s tabs.
- pub fn get_tabs_mut(&mut self) -> &mut BTreeMap<usize, Tab> {
- &mut self.tabs
- }
-
- /// Returns an immutable reference to this [`Screen`]'s active [`Tab`].
- pub fn get_active_tab(&self) -> Option<&Tab> {
- match self.active_tab_index {
- Some(tab) => self.tabs.get(&tab),
- None => None,
- }
- }
-
- /// Returns a mutable reference to this [`Screen`]'s active [`Tab`].
- pub fn get_active_tab_mut(&mut self) -> Option<&mut Tab> {
- match self.active_tab_index {
- Some(tab) => self.get_tabs_mut().get_mut(&tab),
- None => None,
- }
- }
-
- /// Creates a new [`Tab`] in this [`Screen`], applying the specified [`Layout`]
- /// and switching to it.
- pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>) {
- let tab_index = self.get_new_tab_index();
- let position = self.tabs.len();
- let mut tab = Tab::new(
- tab_index,
- position,
- String::new(),
- &self.full_screen_ws,
- self.bus.os_input.as_ref().unwrap().clone(),
- self.bus.senders.clone(),
- self.max_panes,
- None,
- self.mode_info.clone(),
- self.input_mode,
- self.colors,
- );
- tab.apply_layout(layout, new_pids);
- self.active_tab_index = Some(tab_index);
- self.tabs.insert(tab_index, tab);
- self.update_tabs();
- }
-
- pub fn update_tabs(&self) {
- let mut tab_data = vec![];
- let active_tab_index = self.active_tab_index.unwrap();
- for tab in self.tabs.values() {
- tab_data.push(TabInfo {
- position: tab.position,
- name: tab.name.clone(),
- active: active_tab_index == tab.index,
- is_sync_panes_active: tab.is_sync_panes_active(),
- });
- }
- self.bus
- .senders
- .send_to_plugin(PluginInstruction::Update(None, Event::TabUpdate(tab_data)))
- .unwrap();
- }
-
- pub fn update_active_tab_name(&mut self, buf: Vec<u8>) {
- let s = str::from_utf8(&buf).unwrap();
- let active_tab = self.get_active_tab_mut().unwrap();
- match s {
- "\0" => {
- active_tab.name = String::new();
- }
- "\u{007F}" | "\u{0008}" => {
- //delete and backspace keys
- active_tab.name.pop();
- }
- c => {
- active_tab.name.push_str(c);
- }
- }
- self.update_tabs();
- }
- pub fn change_mode(&mut self, mode_info: ModeInfo) {
- self.mode_info = mode_info;
- for tab in self.tabs.values_mut() {
- tab.mode_info = self.mode_info.clone();
- }
- }
-}
-
-// The box is here in order to make the
-// NewClient enum smaller
-#[allow(clippy::boxed_local)]
-pub fn screen_thread_main(
- bus: Bus<ScreenInstruction>,
- max_panes: Option<usize>,
- full_screen_ws: PositionAndSize,
- config_options: Box<Options>,
-) {
- let colors = bus.os_input.as_ref().unwrap().load_palette();
- let capabilities = config_options.simplified_ui;
-
- let mut screen = Screen::new(
- bus,
- &full_screen_ws,
- max_panes,
- ModeInfo {
- palette: colors,
- capabilities: PluginCapabilities {
- arrow_fonts: capabilities,
- },
- ..ModeInfo::default()
- },
- InputMode::Normal,
- colors,
- );
- loop {
- let (event, mut err_ctx) = screen
- .bus
- .recv()
- .expect("failed to receive event on channel");
- err_ctx.add_call(ContextType::Screen((&event).into()));
- match event {
- ScreenInstruction::PtyBytes(pid, vte_bytes) => {
- let active_tab = screen.get_active_tab_mut().unwrap();
- if active_tab.has_terminal_pid(pid) {
- // it's most likely that this event is directed at the active tab
- // look there first
- active_tab.handle_pty_bytes(pid, vte_bytes);
- } else {
- // if this event wasn't directed at the active tab, start looking
- // in other tabs
- let all_tabs = screen.get_tabs_mut();
- for tab in all_tabs.values_mut() {
- if tab.has_terminal_pid(pid) {
- tab.handle_pty_bytes(pid, vte_bytes);
- break;
- }
- }
- }
- }
- ScreenInstruction::Render => {
- screen.render();
- }
- ScreenInstruction::NewPane(pid) => {
- screen.get_active_tab_mut().unwrap().new_pane(pid);
- screen
- .bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- ScreenInstruction::HorizontalSplit(pid) => {
- screen.get_active_tab_mut().unwrap().horizontal_split(pid);
- screen
- .bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- ScreenInstruction::VerticalSplit(pid) => {
- screen.get_active_tab_mut().unwrap().vertical_split(pid);
- screen
- .bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- ScreenInstruction::WriteCharacter(bytes) => {
- let active_tab = screen.get_active_tab_mut().unwrap();
- match active_tab.is_sync_panes_active() {
- true => active_tab.write_to_terminals_on_current_tab(bytes),
- false => active_tab.write_to_active_terminal(bytes),
- }
- }
- ScreenInstruction::ResizeLeft => {
- screen.get_active_tab_mut().unwrap().resize_left();
- }
- ScreenInstruction::ResizeRight => {
- screen.get_active_tab_mut().unwrap().resize_right();
- }
- ScreenInstruction::ResizeDown => {
- screen.get_active_tab_mut().unwrap().resize_down();
- }
- ScreenInstruction::ResizeUp => {
- screen.get_active_tab_mut().unwrap().resize_up();
- }
- ScreenInstruction::SwitchFocus => {
- screen.get_active_tab_mut().unwrap().move_focus();
- }
- ScreenInstruction::FocusNextPane => {
- screen.get_active_tab_mut().unwrap().focus_next_pane();
- }
- ScreenInstruction::FocusPreviousPane => {
- screen.get_active_tab_mut().unwrap().focus_previous_pane();
- }
- ScreenInstruction::MoveFocusLeft => {
- screen.get_active_tab_mut().unwrap().move_focus_left();
- }
- ScreenInstruction::MoveFocusLeftOrPreviousTab => {
- if !screen.get_active_tab_mut().unwrap().move_focus_left() {
- screen.switch_tab_prev();
- }
- screen
- .bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- ScreenInstruction::MoveFocusDown => {
- screen.get_active_tab_mut().unwrap().move_focus_down();
- }
- ScreenInstruction::MoveFocusRight => {
- screen.get_active_tab_mut().unwrap().move_focus_right();
- }
- ScreenInstruction::MoveFocusRightOrNextTab => {
- if !screen.get_active_tab_mut().unwrap().move_focus_right() {
- screen.switch_tab_next();
- }
- screen
- .bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- ScreenInstruction::MoveFocusUp => {
- screen.get_active_tab_mut().unwrap().move_focus_up();
- }
- ScreenInstruction::ScrollUp => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .scroll_active_terminal_up();
- }
- ScreenInstruction::ScrollDown => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .scroll_active_terminal_down();
- }
- ScreenInstruction::PageScrollUp => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .scroll_active_terminal_up_page();
- }
- ScreenInstruction::PageScrollDown => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .scroll_active_terminal_down_page();
- }
- ScreenInstruction::ClearScroll => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .clear_active_terminal_scroll();
- }
- ScreenInstruction::CloseFocusedPane => {
- screen.get_active_tab_mut().unwrap().close_focused_pane();
- screen.render();
- }
- ScreenInstruction::SetSelectable(id, selectable) => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .set_pane_selectable(id, selectable);
- }
- ScreenInstruction::SetMaxHeight(id, max_height) => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .set_pane_max_height(id, max_height);
- }
- ScreenInstruction::SetInvisibleBorders(id, invisible_borders) => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .set_pane_invisible_borders(id, invisible_borders);
- screen.render();
- }
- ScreenInstruction::ClosePane(id) => {
- screen.get_active_tab_mut().unwrap().close_pane(id);
- screen.render();
- }
- ScreenInstruction::ToggleActiveTerminalFullscreen => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .toggle_active_pane_fullscreen();
- }
- ScreenInstruction::NewTab(pane_id) => {
- screen.new_tab(pane_id);
- screen
- .bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- ScreenInstruction::SwitchTabNext => {
- screen.switch_tab_next();
- screen
- .bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- ScreenInstruction::SwitchTabPrev => {
- screen.switch_tab_prev();
- screen
- .bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- ScreenInstruction::CloseTab => {
- screen.close_tab();
- screen
- .bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- ScreenInstruction::ApplyLayout(layout, new_pane_pids) => {
- screen.apply_layout(layout, new_pane_pids);
- screen
- .bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- ScreenInstruction::GoToTab(tab_index) => {
- screen.go_to_tab(tab_index as usize);
- screen
- .bus
- .senders
- .send_to_server(ServerInstruction::UnblockInputThread)
- .unwrap();
- }
- ScreenInstruction::UpdateTabName(c) => {
- screen.update_active_tab_name(c);
- }
- ScreenInstruction::TerminalResize(new_size) => {
- screen.resize_to_screen(new_size);
- }
- ScreenInstruction::ChangeMode(mode_info) => {
- screen.change_mode(mode_info);
- }
- ScreenInstruction::ToggleActiveSyncTab => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .toggle_sync_panes_is_active();
- screen.update_tabs();
- }
- ScreenInstruction::Exit => {
- break;
- }
- }
- }
-}
diff --git a/src/common/setup.rs b/src/common/setup.rs
deleted file mode 100644
index 2e076776..00000000
--- a/src/common/setup.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-use crate::cli::CliArgs;
-use crate::common::utils::consts::{
- FEATURES, SYSTEM_DEFAULT_CONFIG_DIR, SYSTEM_DEFAULT_DATA_DIR_PREFIX, VERSION, ZELLIJ_PROJ_DIR,
-};
-use crate::os_input_output::set_permissions;
-use directories_next::BaseDirs;
-use serde::{Deserialize, Serialize};
-use std::io::Write;
-use std::{fs, path::Path, path::PathBuf};
-use structopt::StructOpt;
-
-const CONFIG_LOCATION: &str = ".config/zellij";
-const CONFIG_NAME: &str = "config.yaml";
-static ARROW_SEPARATOR: &str = "";
-
-#[macro_export]
-macro_rules! asset_map {
- ($($src:literal => $dst:literal),+ $(,)?) => {
- {
- let mut assets = std::collections::HashMap::new();
- $(
- assets.insert($dst, include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $src)).to_vec());
- )+
- assets
- }
- }
-}
-
-pub mod install {
- use super::*;
-
- pub fn populate_data_dir(data_dir: &Path) {
- // First run installation of default plugins & layouts
- let mut assets = asset_map! {
- "assets/layouts/default.yaml" => "layouts/default.yaml",
- "assets/layouts/strider.yaml" => "layouts/strider.yaml",
- };
- assets.extend(asset_map! {
- "assets/plugins/status-bar.wasm" => "plugins/status-bar.wasm",
- "assets/plugins/tab-bar.wasm" => "plugins/tab-bar.wasm",
- "assets/plugins/strider.wasm" => "plugins/strider.wasm",
- });
- assets.insert("VERSION", VERSION.as_bytes().to_vec());
-
- let last_version = fs::read_to_string(data_dir.join("VERSION")).unwrap_or_default();
- let out_of_date = VERSION != last_version;
-
- for (path, bytes) in assets {
- let path = data_dir.join(path);
- let parent_path = path.parent().unwrap();
- fs::create_dir_all(parent_path).unwrap();
- set_permissions(parent_path).unwrap();
- if out_of_date || !path.exists() {
- fs::write(path, bytes).expect("Failed to install default assets!");
- }
- }
- }
-}
-
-#[cfg(not(test))]
-/// Goes through a predefined list and checks for an already
-/// existing config directory, returns the first match
-pub fn find_default_config_dir() -> Option<PathBuf> {
- default_config_dirs()
- .into_iter()
- .filter(|p| p.is_some())
- .find(|p| p.clone().unwrap().exists())
- .flatten()
-}
-
-#[cfg(test)]
-pub fn find_default_config_dir() -> Option<PathBuf> {
- None
-}
-
-/// Order in which config directories are checked
-fn default_config_dirs() -> Vec<Option<PathBuf>> {
- vec![
- home_config_dir(),
- Some(xdg_config_dir()),
- Some(Path::new(SYSTEM_DEFAULT_CONFIG_DIR).to_path_buf()),
- ]
-}
-
-/// Looks for an existing dir, uses that, else returns a
-/// dir matching the config spec.
-pub fn get_default_data_dir() -> PathBuf {
- vec![
- xdg_data_dir(),
- Path::new(SYSTEM_DEFAULT_DATA_DIR_PREFIX).join("share/zellij"),
- ]
- .into_iter()
- .find(|p| p.exists())
- .unwrap_or_else(xdg_data_dir)
-}
-
-pub fn xdg_config_dir() -> PathBuf {
- ZELLIJ_PROJ_DIR.config_dir().to_owned()
-}
-
-pub fn xdg_data_dir() -> PathBuf {
- ZELLIJ_PROJ_DIR.data_dir().to_owned()
-}
-
-pub fn home_config_dir() -> Option<PathBuf> {
- if let Some(user_dirs) = BaseDirs::new() {
- let config_dir = user_dirs.home_dir().join(CONFIG_LOCATION);
- Some(config_dir)
- } else {
- None
- }
-}
-
-pub fn dump_asset(asset: &[u8]) -> std::io::Result<()> {
- std::io::stdout().write_all(&asset)?;
- Ok(())
-}
-
-pub const DEFAULT_CONFIG: &[u8] = include_bytes!(concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/",
- "assets/config/default.yaml"
-));
-
-pub fn dump_default_config() -> std::io::Result<()> {
- dump_asset(DEFAULT_CONFIG)
-}
-
-#[derive(Debug, Default, Clone, StructOpt, Serialize, Deserialize)]
-pub struct Setup {
- /// Dump the default configuration file to stdout
- #[structopt(long)]
- pub dump_config: bool,
- /// Disables loading of configuration file at default location,
- /// loads the defaults that zellij ships with
- #[structopt(long)]
- pub clean: bool,
- /// Checks the configuration of zellij and displays
- /// currently used directories
- #[structopt(long)]
- pub check: bool,
-
- #[structopt(long)]
- pub generate_completion: Option<String>,
-}
-
-impl Setup {
- /// Entrypoint from main
- pub fn from_cli(&self, opts: CliArgs) -> std::io::Result<()> {
- if self.dump_config {
- dump_default_config()?;
- }
-
- if self.check {
- Setup::check_defaults_config(opts)?;
- }
-
- if let Some(shell) = &self.generate_completion {
- Self::generate_completion(shell.into());
- }
-
- Ok(())
- }
-
- pub fn check_defaults_config(opts: CliArgs) -> std::io::Result<()> {
- let data_dir = opts.data_dir.unwrap_or_else(get_default_data_dir);
- let config_dir = opts.config_dir.or_else(find_default_config_dir);
- let plugin_dir = data_dir.join("plugins");
- let layout_dir = data_dir.join("layouts");
- let system_data_dir = PathBuf::from(SYSTEM_DEFAULT_DATA_DIR_PREFIX).join("share/zellij");
- let config_file = opts
- .config
- .or_else(|| config_dir.clone().map(|p| p.join(CONFIG_NAME)));
-
- let mut message = String::new();
-
- message.push_str(&format!("[Version]: {:?}\n", VERSION));
- if let Some(config_dir) = config_dir {
- message.push_str(&format!("[CONFIG DIR]: {:?}\n", config_dir));
- } else {
- message.push_str(&"[CONFIG DIR]: Not Found\n");
- let mut default_config_dirs = default_config_dirs()
- .iter()
- .filter_map(|p| p.clone())
- .collect::<Vec<PathBuf>>();
- default_config_dirs.dedup();
- message.push_str(
- &" On your system zellij looks in the following config directories by default:\n",
- );
- for dir in default_config_dirs {
- message.push_str(&format!(" {:?}\n", dir));
- }
- }
- if let Some(config_file) = config_file {
- use crate::common::input::config::Config;
- message.push_str(&format!("[CONFIG FILE]: {:?}\n", config_file));
- match Config::new(&config_file) {
- Ok(_) => message.push_str(&"[CONFIG FILE]: Well defined.\n"),
- Err(e) => message.push_str(&format!("[CONFIG ERROR]: {}\n", e)),
- }
- } else {
- message.push_str(&"[CONFIG FILE]: Not Found\n");
- message.push_str(&format!(
- " By default zellij looks for a file called [{}] in the configuration directory\n",
- CONFIG_NAME
- ));
- }
- message.push_str(&format!("[DATA DIR]: {:?}\n", data_dir));
- message.push_str(&format!("[PLUGIN DIR]: {:?}\n", plugin_dir));
- message.push_str(&format!("[LAYOUT DIR]: {:?}\n", layout_dir));
- message.push_str(&format!("[SYSTEM DATA DIR]: {:?}\n", system_data_dir));
-
- message.push_str(&format!("[ARROW SEPARATOR]: {}\n", ARROW_SEPARATOR));
- message.push_str(&" Is the [ARROW_SEPARATOR] displayed correctly?\n");
- message.push_str(&" If not you may want to either start zellij with a compatible mode 'zellij options --simple-ui'\n");
- message.push_str(&" Or check the font that is in use:\n https://zellij.dev/documentation/compatibility.html#the-status-bar-fonts-dont-render-correctly\n");
-
- message.push_str(&format!("[FEATURES]: {:?}\n", FEATURES));
- message.push_str(&"[DOCUMENTATION]: zellij.dev/documentation/\n");
-
- std::io::stdout().write_all(message.as_bytes())?;
-
- Ok(())
- }
- fn generate_completion(shell: String) {
- let shell = match shell.as_ref() {
- "bash" => structopt::clap::Shell::Bash,
- "fish" => structopt::clap::Shell::Fish,
- "zsh" => structopt::clap::Shell::Zsh,
- "powerShell" => structopt::clap::Shell::PowerShell,
- "elvish" => structopt::clap::Shell::Elvish,
- other => {
- eprintln!("Unsupported shell: {}", other);
- std::process::exit(1);
- }
- };
- let mut out = std::io::stdout();
- CliArgs::clap().gen_completions_to("zellij", shell, &mut out);
- }
-}
diff --git a/src/common/thread_bus.rs b/src/common/thread_bus.rs
deleted file mode 100644
index 8827d068..00000000
--- a/src/common/thread_bus.rs
+++ /dev/null
@@ -1,142 +0,0 @@
-//! Definitions and helpers for sending and receiving messages between threads.
-
-use async_std::task_local;
-use std::cell::RefCell;
-use std::sync::mpsc;
-
-use crate::common::pty::PtyInstruction;
-use crate::common::ServerInstruction;
-use crate::errors::{get_current_ctx, ErrorContext};
-use crate::os_input_output::ServerOsApi;
-use crate::screen::ScreenInstruction;
-use crate::wasm_vm::PluginInstruction;
-
-/// An [MPSC](mpsc) asynchronous channel with added error context.
-pub type ChannelWithContext<T> = (
- mpsc::Sender<(T, ErrorContext)>,
- mpsc::Receiver<(T, ErrorContext)>,
-);
-/// An [MPSC](mpsc) synchronous channel with added error context.
-pub type SyncChannelWithContext<T> = (
- mpsc::SyncSender<(T, ErrorContext)>,
- mpsc::Receiver<(T, ErrorContext)>,
-);
-
-/// Wrappers around the two standard [MPSC](mpsc) sender types, [`mpsc::Sender`] and [`mpsc::SyncSender`], with an additional [`ErrorContext`].
-#[derive(Clone)]
-pub enum SenderType<T: Clone> {
- /// A wrapper around an [`mpsc::Sender`], adding an [`ErrorContext`].
- Sender(mpsc::Sender<(T, ErrorContext)>),
- /// A wrapper around an [`mpsc::SyncSender`], adding an [`ErrorContext`].
- SyncSender(mpsc::SyncSender<(T, ErrorContext)>),
-}
-
-/// Sends messages on an [MPSC](std::sync::mpsc) channel, along with an [`ErrorContext`],
-/// synchronously or asynchronously depending on the underlying [`SenderType`].
-#[derive(Clone)]
-pub struct SenderWithContext<T: Clone> {
- sender: SenderType<T>,
-}
-
-impl<T: Clone> SenderWithContext<T> {
- pub fn new(sender: SenderType<T>) -> Self {
- Self { sender }
- }
-
- /// Sends an event, along with the current [`ErrorContext`], on this
- /// [`SenderWithContext`]'s channel.
- pub fn send(&self, event: T) -> Result<(), mpsc::SendError<(T, ErrorContext)>> {
- let err_ctx = get_current_ctx();
- match self.sender {
- SenderType::Sender(ref s) => s.send((event, err_ctx)),
- SenderType::SyncSender(ref s) => s.send((event, err_ctx)),
- }
- }
-}
-
-unsafe impl<T: Clone> Send for SenderWithContext<T> {}
-unsafe impl<T: Clone> Sync for SenderWithContext<T> {}
-
-thread_local!(
- /// A key to some thread local storage (TLS) that holds a representation of the thread's call
- /// stack in the form of an [`ErrorContext`].
- pub static OPENCALLS: RefCell<ErrorContext> = RefCell::default()
-);
-
-task_local! {
- /// A key to some task local storage that holds a representation of the task's call
- /// stack in the form of an [`ErrorContext`].
- pub static ASYNCOPENCALLS: RefCell<ErrorContext> = RefCell::default()
-}
-
-/// A container for senders to the different threads in zellij on the server side
-#[derive(Clone)]
-pub struct ThreadSenders {
- pub to_screen: Option<SenderWithContext<ScreenInstruction>>,
- pub to_pty: Option<SenderWithContext<PtyInstruction>>,
- pub to_plugin: Option<SenderWithContext<PluginInstruction>>,
- pub to_server: Option<SenderWithContext<ServerInstruction>>,
-}
-
-impl ThreadSenders {
- pub fn send_to_screen(
- &self,
- instruction: ScreenInstruction,
- ) -> Result<(), mpsc::SendError<(ScreenInstruction, ErrorContext)>> {
- self.to_screen.as_ref().unwrap().send(instruction)
- }
-
- pub fn send_to_pty(
- &self,
- instruction: PtyInstruction,
- ) -> Result<(), mpsc::SendError<(PtyInstruction, ErrorContext)>> {
- self.to_pty.as_ref().unwrap().send(instruction)
- }
-
- pub fn send_to_plugin(
- &self,
- instruction: PluginInstruction,
- ) -> Result<(), mpsc::SendError<(PluginInstruction, ErrorContext)>> {
- self.to_plugin.as_ref().unwrap().send(instruction)
- }
-
- pub fn send_to_server(
- &self,
- instruction: ServerInstruction,
- ) -> Result<(), mpsc::SendError<(ServerInstruction, ErrorContext)>> {
- self.to_server.as_ref().unwrap().send(instruction)
- }
-}
-
-/// A container for a receiver, OS input and the senders to a given thread
-pub struct Bus<T> {
- pub receiver: mpsc::Receiver<(T, ErrorContext)>,
- pub senders: ThreadSenders,
- pub os_input: Option<Box<dyn ServerOsApi>>,
-}
-
-impl<T> Bus<T> {
- pub fn new(
- receiver: mpsc::Receiver<(T, ErrorContext)>,
- to_screen: Option<&SenderWithContext<ScreenInstruction>>,
- to_pty: Option<&SenderWithContext<PtyInstruction>>,
- to_plugin: Option<&SenderWithContext<PluginInstruction>>,
- to_server: Option<&SenderWithContext<ServerInstruction>>,
- os_input: Option<Box<dyn ServerOsApi>>,
- ) -> Self {
- Bus {
- receiver,
- senders: ThreadSenders {
- to_screen: to_screen.cloned(),
- to_pty: to_pty.cloned(),
- to_plugin: to_plugin.cloned(),
- to_server: to_server.cloned(),
- },
- os_input: os_input.clone(),
- }
- }
-
- pub fn recv(&self) -> Result<(T, ErrorContext), mpsc::RecvError> {
- self.receiver.recv()
- }
-}
diff --git a/src/common/utils/consts.rs b/src/common/utils/consts.rs
deleted file mode 100644
index 42843eb8..00000000
--- a/src/common/utils/consts.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-//! Zellij program-wide constants.
-
-use crate::os_input_output::set_permissions;
-use directories_next::ProjectDirs;
-use lazy_static::lazy_static;
-use nix::unistd::Uid;
-use std::path::PathBuf;
-use std::{env, fs};
-
-pub const ZELLIJ_CONFIG_FILE_ENV: &str = "ZELLIJ_CONFIG_FILE";
-pub const ZELLIJ_CONFIG_DIR_ENV: &str = "ZELLIJ_CONFIG_DIR";
-pub const VERSION: &str = env!("CARGO_PKG_VERSION");
-
-pub const SYSTEM_DEFAULT_CONFIG_DIR: &str = "/etc/zellij";
-pub const SYSTEM_DEFAULT_DATA_DIR_PREFIX: &str = system_default_data_dir();
-
-const fn system_default_data_dir() -> &'static str {
- if let Some(data_dir) = std::option_env!("PREFIX") {
- data_dir
- } else {
- &"/usr"
- }
-}
-
-lazy_static! {
- static ref UID: Uid = Uid::current();
- pub static ref SESSION_NAME: String = names::Generator::default().next().unwrap();
- pub static ref ZELLIJ_PROJ_DIR: ProjectDirs =
- ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
- pub static ref ZELLIJ_IPC_PIPE: PathBuf = {
- let mut ipc_dir = env::var("ZELLIJ_SOCKET_DIR").map_or_else(
- |_| {
- ZELLIJ_PROJ_DIR
- .runtime_dir()
- .map_or_else(|| ZELLIJ_TMP_DIR.clone(), |p| p.to_owned())
- },
- PathBuf::from,
- );
- ipc_dir.push(VERSION);
- fs::create_dir_all(&ipc_dir).unwrap();
- set_permissions(&ipc_dir).unwrap();
- ipc_dir.push(&*SESSION_NAME);
- ipc_dir
- };
- pub static ref ZELLIJ_TMP_DIR: PathBuf =
- PathBuf::from("/tmp/zellij-".to_string() + &format!("{}", *UID));
- pub static ref ZELLIJ_TMP_LOG_DIR: PathBuf = ZELLIJ_TMP_DIR.join("zellij-log");
- pub static ref ZELLIJ_TMP_LOG_FILE: PathBuf = ZELLIJ_TMP_LOG_DIR.join("log.txt");
-}
-
-pub const FEATURES: &[&str] = &[
- #[cfg(feature = "disable_automatic_asset_installation")]
- "disable_automatic_asset_installation",
-];
diff --git a/src/common/utils/logging.rs b/src/common/utils/logging.rs
deleted file mode 100644
index 0dd382d6..00000000
--- a/src/common/utils/logging.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-//! Zellij logging utility functions.
-
-use std::{
- fs,
- io::{self, prelude::*},
- os::unix::io::RawFd,
- path::{Path, PathBuf},
-};
-
-use crate::os_input_output::set_permissions;
-use crate::utils::consts::{ZELLIJ_TMP_LOG_DIR, ZELLIJ_TMP_LOG_FILE};
-
-pub fn atomic_create_file(file_name: &Path) -> io::Result<()> {
- let _ = fs::OpenOptions::new()
- .append(true)
- .create(true)
- .open(file_name)?;
- set_permissions(file_name)
-}
-
-pub fn atomic_create_dir(dir_name: &Path) -> io::Result<()> {
- let result = if let Err(e) = fs::create_dir(dir_name) {
- if e.kind() == std::io::ErrorKind::AlreadyExists {
- Ok(())
- } else {
- Err(e)
- }
- } else {
- Ok(())
- };
- if result.is_ok() {
- set_permissions(dir_name)?;
- }
- result
-}
-
-pub fn debug_log_to_file(mut message: String) -> io::Result<()> {
- message.push('\n');
- debug_log_to_file_without_newline(message)
-}
-
-pub fn debug_log_to_file_without_newline(message: String) -> io::Result<()> {
- atomic_create_file(&*ZELLIJ_TMP_LOG_FILE)?;
- let mut file = fs::OpenOptions::new()
- .append(true)
- .open(&*ZELLIJ_TMP_LOG_FILE)?;
- file.write_all(message.as_bytes())
-}
-
-pub fn _debug_log_to_file_pid_3(message: String, pid: RawFd) -> io::Result<()> {
- if pid == 3 {
- debug_log_to_file(message)
- } else {
- Ok(())
- }
-}
-
-pub fn _delete_log_file() -> io::Result<()> {
- if fs::metadata(&*ZELLIJ_TMP_LOG_FILE).is_ok() {
- fs::remove_file(&*ZELLIJ_TMP_LOG_FILE)
- } else {
- Ok(())
- }
-}
-
-pub fn _delete_log_dir() -> io::Result<()> {
- if fs::metadata(&*ZELLIJ_TMP_LOG_DIR).is_ok() {
- fs::remove_dir_all(&*ZELLIJ_TMP_LOG_DIR)
- } else {
- Ok(())
- }
-}
-
-pub fn debug_to_file(message: u8, pid: RawFd) -> io::Result<()> {
- let mut path = PathBuf::new();
- path.push(&*ZELLIJ_TMP_LOG_DIR);
- path.push(format!("zellij-{}.log", pid.to_string()));
-
- let mut file = fs::OpenOptions::new()
- .append(true)
- .create(true)
- .open(&path)?;
- set_permissions(&path)?;
- file.write_all(&[message])
-}
diff --git a/src/common/utils/mod.rs b/src/common/utils/mod.rs
deleted file mode 100644
index 1cb4df27..00000000
--- a/src/common/utils/mod.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-//! Zellij utilities.
-
-pub mod consts;
-pub mod logging;
-pub mod shared;
diff --git a/src/common/utils/shared.rs b/src/common/utils/shared.rs
deleted file mode 100644
index 26a2f90a..00000000
--- a/src/common/utils/shared.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-//! Some general utility functions.
-
-use std::{iter, str::from_utf8};
-
-use strip_ansi_escapes::strip;
-
-use colors_transform::{Color, Rgb};
-use zellij_tile::data::{Palette, PaletteColor, PaletteSource, Theme};
-
-fn ansi_len(s: &str) -> usize {
- from_utf8(&strip(s.as_bytes()).unwrap())
- .unwrap()
- .chars()
- .count()
-}
-
-pub fn adjust_to_size(s: &str, rows: usize, columns: usize) -> String {
- s.lines()
- .map(|l| {
- let actual_len = ansi_len(l);
- if actual_len > columns {
- let mut line = String::from(l);
- line.truncate(columns);
- line
- } else {
- [l, &str::repeat(" ", columns - ansi_len(l))].concat()
- }
- })
- .chain(iter::repeat(str::repeat(" ", columns)))
- .take(rows)
- .collect::<Vec<_>>()
- .join("\n\r")
-}
-
-// Colors
-pub mod colors {
- pub const WHITE: u8 = 255;
- pub const GREEN: u8 = 154;
- pub const GRAY: u8 = 238;
- pub const BRIGHT_GRAY: u8 = 245;
- pub const RED: u8 = 88;
- pub const ORANGE: u8 = 166;
- pub const BLACK: u8 = 16;
-}
-
-pub fn _hex_to_rgb(hex: &str) -> (u8, u8, u8) {
- let rgb = Rgb::from_hex_str(hex)
- .expect("The passed argument must be a valid hex color")
- .as_tuple();
- (rgb.0 as u8, rgb.1 as u8, rgb.2 as u8)
-}
-
-pub fn default_palette() -> Palette {
- Palette {
- source: PaletteSource::Default,
- theme: Theme::Dark,
- fg: PaletteColor::EightBit(colors::BRIGHT_GRAY),
- bg: PaletteColor::EightBit(colors::GRAY),
- black: PaletteColor::EightBit(colors::BLACK),
- red: PaletteColor::EightBit(colors::RED),
- green: PaletteColor::EightBit(colors::GREEN),
- yellow: PaletteColor::EightBit(colors::GRAY),
- blue: PaletteColor::EightBit(colors::GRAY),
- magenta: PaletteColor::EightBit(colors::GRAY),
- cyan: PaletteColor::EightBit(colors::GRAY),
- white: PaletteColor::EightBit(colors::WHITE),
- orange: PaletteColor::EightBit(colors::ORANGE),
- }
-}
-
-// Dark magic
-pub fn _detect_theme(bg: PaletteColor) -> Theme {
- match bg {
- PaletteColor::Rgb((r, g, b)) => {
- // HSP, P stands for perceived brightness
- let hsp: f64 = (0.299 * (r as f64 * r as f64)
- + 0.587 * (g as f64 * g as f64)
- + 0.114 * (b as f64 * b as f64))
- .sqrt();
- match hsp > 127.5 {
- true => Theme::Light,
- false => Theme::Dark,
- }
- }
- _ => Theme::Dark,
- }
-}
-
-// (this was shamelessly copied from alacritty)
-//
-// This returns the current terminal version as a unique number based on the
-// semver version. The different versions are padded to ensure that a higher semver version will
-// always report a higher version number.
-pub fn version_number(mut version: &str) -> usize {
- if let Some(separator) = version.rfind('-') {
- version = &version[..separator];
- }
-
- let mut version_number = 0;
-
- let semver_versions = version.split('.');
- for (i, semver_version) in semver_versions.rev().enumerate() {
- let semver_number = semver_version.parse::<usize>().unwrap_or(0);
- version_number += usize::pow(100, i as u32) * semver_number;
- }
-
- version_number
-}
diff --git a/src/common/wasm_vm.rs b/src/common/wasm_vm.rs
deleted file mode 100644
index 51eb4ed8..00000000
--- a/src/common/wasm_vm.rs
+++ /dev/null
@@ -1,267 +0,0 @@
-use std::collections::{HashMap, HashSet};
-use std::fs;
-use std::path::PathBuf;
-use std::process;
-use std::str::FromStr;
-use std::sync::{mpsc::Sender, Arc, Mutex};
-use std::thread;
-use std::time::{Duration, Instant};
-
-use serde::{de::DeserializeOwned, Serialize};
-use wasmer::{
- imports, ChainableNamedResolver, Function, ImportObject, Instance, Module, Store, Value,
- WasmerEnv,
-};
-use wasmer_wasi::{Pipe, WasiEnv, WasiState};
-use zellij_tile::data::{Event, EventType, PluginIds};
-
-use crate::common::errors::ContextType;
-use crate::common::pty::PtyInstruction;
-use crate::common::screen::ScreenInstruction;
-use crate::common::thread_bus::{Bus, ThreadSenders};
-use crate::common::PaneId;
-
-#[derive(Clone, Debug)]
-pub enum PluginInstruction {
- Load(Sender<u32>, PathBuf),
- Update(Option<u32>, Event), // Focused plugin / broadcast, event data
- Render(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols
- Unload(u32),
- Exit,
-}
-
-#[derive(WasmerEnv, Clone)]
-pub struct PluginEnv {
- pub plugin_id: u32,
- pub senders: ThreadSenders,
- pub wasi_env: WasiEnv,
- pub subscriptions: Arc<Mutex<HashSet<EventType>>>,
-}
-
-// Thread main --------------------------------------------------------------------------------------------------------
-pub fn wasm_thread_main(bus: Bus<PluginInstruction>, store: Store, data_dir: PathBuf) {
- let mut plugin_id = 0;
- let mut plugin_map = HashMap::new();
- loop {
- let (event, mut err_ctx) = bus.recv().expect("failed to receive event on channel");
- err_ctx.add_call(ContextType::Plugin((&event).into()));
- match event {
- PluginInstruction::Load(pid_tx, path) => {
- let plugin_dir = data_dir.join("plugins/");
- let wasm_bytes = fs::read(&path)
- .or_else(|_| fs::read(&path.with_extension("wasm")))
- .or_else(|_| fs::read(&plugin_dir.join(&path).with_extension("wasm")))
- .unwrap_or_else(|_| panic!("cannot find plugin {}", &path.display()));
-
- // FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that
- let module = Module::new(&store, &wasm_bytes).unwrap();
-
- let output = Pipe::new();
- let input = Pipe::new();
- let mut wasi_env = WasiState::new("Zellij")
- .env("CLICOLOR_FORCE", "1")
- .preopen(|p| {
- p.directory(".") // FIXME: Change this to a more meaningful dir
- .alias(".")
- .read(true)
- .write(true)
- .create(true)
- })
- .unwrap()
- .stdin(Box::new(input))
- .stdout(Box::new(output))
- .finalize()
- .unwrap();
-
- let wasi = wasi_env.import_object(&module).unwrap();
-
- let plugin_env = PluginEnv {
- plugin_id,
- senders: bus.senders.clone(),
- wasi_env,
- subscriptions: Arc::new(Mutex::new(HashSet::new())),
- };
-
- let zellij = zellij_exports(&store, &plugin_env);
- let instance = Instance::new(&module, &zellij.chain_back(wasi)).unwrap();
-
- let start = instance.exports.get_function("_start").unwrap();
-
- // This eventually calls the `.load()` method
- start.call(&[]).unwrap();
-
- plugin_map.insert(plugin_id, (instance, plugin_env));
- pid_tx.send(plugin_id).unwrap();
- plugin_id += 1;
- }
- PluginInstruction::Update(pid, event) => {
- for (&i, (instance, plugin_env)) in &plugin_map {
- let subs = plugin_env.subscriptions.lock().unwrap();
- // FIXME: This is very janky... Maybe I should write my own macro for Event -> EventType?
- let event_type = EventType::from_str(&event.to_string()).unwrap();
- if (pid.is_none() || pid == Some(i)) && subs.contains(&event_type) {
- let update = instance.exports.get_function("update").unwrap();
- wasi_write_object(&plugin_env.wasi_env, &event);
- update.call(&[]).unwrap();
- }
- }
- drop(bus.senders.send_to_screen(ScreenInstruction::Render));
- }
- PluginInstruction::Render(buf_tx, pid, rows, cols) => {
- let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
-
- let render = instance.exports.get_function("render").unwrap();
-
- render
- .call(&[Value::I32(rows as i32), Value::I32(cols as i32)])
- .unwrap();
-
- buf_tx.send(wasi_read_string(&plugin_env.wasi_env)).unwrap();
- }
- PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)),
- PluginInstruction::Exit => break,
- }
- }
-}
-
-// Plugin API ---------------------------------------------------------------------------------------------------------
-
-pub fn zellij_exports(store: &Store, plugin_env: &PluginEnv) -> ImportObject {
- macro_rules! zellij_export {
- ($($host_function:ident),+ $(,)?) => {
- imports! {
- "zellij" => {
- $(stringify!($host_function) =>
- Function::new_native_with_env(store, plugin_env.clone(), $host_function),)+
- }
- }
- }
- }
-
- zellij_export! {
- host_subscribe,
- host_unsubscribe,
- host_set_invisible_borders,
- host_set_max_height,
- host_set_selectable,
- host_get_plugin_ids,
- host_open_file,
- host_set_timeout,
- }
-}
-
-fn host_subscribe(plugin_env: &PluginEnv) {
- let mut subscriptions = plugin_env.subscriptions.lock().unwrap();
- let new: HashSet<EventType> = wasi_read_object(&plugin_env.wasi_env);
- subscriptions.extend(new);
-}
-
-fn host_unsubscribe(plugin_env: &PluginEnv) {
- let mut subscriptions = plugin_env.subscriptions.lock().unwrap();
- let old: HashSet<EventType> = wasi_read_object(&plugin_env.wasi_env);
- subscriptions.retain(|k| !old.contains(k));
-}
-
-fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) {
- let selectable = selectable != 0;
- plugin_env
- .senders
- .send_to_screen(ScreenInstruction::SetSelectable(
- PaneId::Plugin(plugin_env.plugin_id),
- selectable,
- ))
- .unwrap()
-}
-
-fn host_set_max_height(plugin_env: &PluginEnv, max_height: i32) {
- let max_height = max_height as usize;
- plugin_env
- .senders
- .send_to_screen(ScreenInstruction::SetMaxHeight(
- PaneId::Plugin(plugin_env.plugin_id),
- max_height,
- ))
- .unwrap()
-}
-
-fn host_set_invisible_borders(plugin_env: &PluginEnv, invisible_borders: i32) {
- let invisible_borders = invisible_borders != 0;
- plugin_env
- .senders
- .send_to_screen(ScreenInstruction::SetInvisibleBorders(
- PaneId::Plugin(plugin_env.plugin_id),
- invisible_borders,
- ))
- .unwrap()
-}
-
-fn host_get_plugin_ids(plugin_env: &PluginEnv) {
- let ids = PluginIds {
- plugin_id: plugin_env.plugin_id,
- zellij_pid: process::id(),
- };
- wasi_write_object(&plugin_env.wasi_env, &ids);
-}
-
-fn host_open_file(plugin_env: &PluginEnv) {
- let path: PathBuf = wasi_read_object(&plugin_env.wasi_env);
- plugin_env
- .senders
- .send_to_pty(PtyInstruction::SpawnTerminal(Some(path)))
- .unwrap();
-}
-
-fn host_set_timeout(plugin_env: &PluginEnv, secs: f64) {
- // There is a fancy, high-performance way to do this with zero additional threads:
- // If the plugin thread keeps a BinaryHeap of timer structs, it can manage multiple and easily `.peek()` at the
- // next time to trigger in O(1) time. Once the wake-up time is known, the `wasm` thread can use `recv_timeout()`
- // to wait for an event with the timeout set to be the time of the next wake up. If events come in in the meantime,
- // they are handled, but if the timeout triggers, we replace the event from `recv()` with an
- // `Update(pid, TimerEvent)` and pop the timer from the Heap (or reschedule it). No additional threads for as many
- // timers as we'd like.
- //
- // But that's a lot of code, and this is a few lines:
- let send_plugin_instructions = plugin_env.senders.to_plugin.clone();
- let update_target = Some(plugin_env.plugin_id);
- thread::spawn(move || {
- let start_time = Instant::now();
- thread::sleep(Duration::from_secs_f64(secs));
- // FIXME: The way that elapsed time is being calculated here is not exact; it doesn't take into account the
- // time it takes an event to actually reach the plugin after it's sent to the `wasm` thread.
- let elapsed_time = Instant::now().duration_since(start_time).as_secs_f64();
-
- send_plugin_instructions
- .unwrap()
- .send(PluginInstruction::Update(
- update_target,
- Event::Timer(elapsed_time),
- ))
- .unwrap();
- });
-}
-
-// Helper Functions ---------------------------------------------------------------------------------------------------
-
-// FIXME: Unwrap city
-pub fn wasi_read_string(wasi_env: &WasiEnv) -> String {
- let mut state = wasi_env.state();
- let wasi_file = state.fs.stdout_mut().unwrap().as_mut().unwrap();
- let mut buf = String::new();
- wasi_file.read_to_string(&mut buf).unwrap();
- buf
-}
-
-pub fn wasi_write_string(wasi_env: &WasiEnv, buf: &str) {
- let mut state = wasi_env.state();
- let wasi_file = state.fs.stdin_mut().unwrap().as_mut().unwrap();
- writeln!(wasi_file, "{}\r", buf).unwrap();
-}
-
-pub fn wasi_write_object(wasi_env: &WasiEnv, object: &impl Serialize) {
- wasi_write_string(wasi_env, &serde_json::to_string(&object).unwrap());
-}
-
-pub fn wasi_read_object<T: DeserializeOwned>(wasi_env: &WasiEnv) -> T {
- let json = wasi_read_string(wasi_env);
- serde_json::from_str(&json).unwrap()
-}
diff --git a/src/main.rs b/src/main.rs
index 49e42ffb..1e1d4764 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,31 +1,22 @@
-mod cli;
-mod client;
-mod common;
-mod server;
#[cfg(test)]
mod tests;
-use client::{boundaries, layout, panes, start_client, tab};
-use common::{
- command_is_executing, errors, os_input_output, pty, screen, setup::Setup, utils, wasm_vm,
-};
-use server::start_server;
+use std::convert::TryFrom;
use structopt::StructOpt;
-
-use crate::cli::CliArgs;
-use crate::command_is_executing::CommandIsExecuting;
-use crate::common::input::config::Config;
-use crate::os_input_output::{get_client_os_input, get_server_os_input};
-use crate::utils::{
+use zellij_client::{os_input_output::get_client_os_input, start_client};
+use zellij_server::{os_input_output::get_server_os_input, start_server};
+use zellij_utils::{
+ cli::{CliArgs, ConfigCli},
consts::{ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR},
+ input::config::Config,
logging::*,
+ setup::Setup,
};
-use std::convert::TryFrom;
pub fn main() {
let opts = CliArgs::from_args();
- if let Some(crate::cli::ConfigCli::Setup(setup)) = opts.option.clone() {
+ if let Some(ConfigCli::Setup(setup)) = opts.option.clone() {
Setup::from_cli(&setup, opts).expect("Failed to print to stdout");
std::process::exit(0);
} else {
@@ -39,10 +30,22 @@ pub fn main() {
atomic_create_dir(&*ZELLIJ_TMP_DIR).unwrap();
atomic_create_dir(&*ZELLIJ_TMP_LOG_DIR).unwrap();
if let Some(path) = opts.server {
- let os_input = get_server_os_input();
+ let os_input = match get_server_os_input() {
+ Ok(server_os_input) => server_os_input,
+ Err(e) => {
+ eprintln!("failed to open terminal:\n{}", e);
+ std::process::exit(1);
+ }
+ };
start_server(Box::new(os_input), path);
} else {
- let os_input = get_client_os_input();
+ let os_input = match get_client_os_input() {
+ Ok(os_input) => os_input,
+ Err(e) => {
+ eprintln!("failed to open terminal:\n{}", e);
+ std::process::exit(1);
+ }
+ };
start_client(Box::new(os_input), opts, config);
}
}
diff --git a/src/server/mod.rs b/src/server/mod.rs
deleted file mode 100644
index 12cc2229..00000000
--- a/src/server/mod.rs
+++ /dev/null
@@ -1,291 +0,0 @@
-pub mod route;
-
-use std::sync::{Arc, RwLock};
-use std::thread;
-use std::{path::PathBuf, sync::mpsc};
-use wasmer::Store;
-use zellij_tile::data::PluginCapabilities;
-
-use crate::cli::CliArgs;
-use crate::common::thread_bus::{Bus, ThreadSenders};
-use crate::common::{
- errors::ContextType,
- input::options::Options,
- ipc::{ClientToServerMsg, ServerToClientMsg},
- os_input_output::ServerOsApi,
- pty::{pty_thread_main, Pty, PtyInstruction},
- screen::{screen_thread_main, ScreenInstruction},
- setup::{get_default_data_dir, install::populate_data_dir},
- thread_bus::{ChannelWithContext, SenderType, SenderWithContext, SyncChannelWithContext},
- wasm_vm::{wasm_thread_main, PluginInstruction},
-};
-use crate::layout::Layout;
-use crate::panes::PositionAndSize;
-use route::route_thread_main;
-
-/// Instructions related to server-side application including the
-/// ones sent by client to server
-#[derive(Debug, Clone)]
-pub enum ServerInstruction {
- NewClient(PositionAndSize, Box<CliArgs>, Box<Options>),
- Render(Option<String>),
- UnblockInputThread,
- ClientExit,
- Error(String),
-}
-
-impl From<ClientToServerMsg> for ServerInstruction {
- fn from(instruction: ClientToServerMsg) -> Self {
- match instruction {
- ClientToServerMsg::ClientExit => ServerInstruction::ClientExit,
- ClientToServerMsg::NewClient(pos, opts, options) => {
- ServerInstruction::NewClient(pos, opts, options)
- }
- _ => unreachable!(),
- }
- }
-}
-
-pub struct SessionMetaData {
- pub senders: ThreadSenders,
- pub capabilities: PluginCapabilities,
- screen_thread: Option<thread::JoinHandle<()>>,
- pty_thread: Option<thread::JoinHandle<()>>,
- wasm_thread: Option<thread::JoinHandle<()>>,
-}
-
-impl Drop for SessionMetaData {
- fn drop(&mut self) {
- let _ = self.senders.send_to_pty(PtyInstruction::Exit);
- let _ = self.senders.send_to_screen(ScreenInstruction::Exit);
- let _ = self.senders.send_to_plugin(PluginInstruction::Exit);
- let _ = self.screen_thread.take().unwrap().join();
- let _ = self.pty_thread.take().unwrap().join();
- let _ = self.wasm_thread.take().unwrap().join();
- }
-}
-
-pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
- #[cfg(not(test))]
- daemonize::Daemonize::new()
- .working_directory(std::env::var("HOME").unwrap())
- .umask(0o077)
- .start()
- .expect("could not daemonize the server process");
-
- std::env::set_var(&"ZELLIJ", "0");
-
- let (to_server, server_receiver): SyncChannelWithContext<ServerInstruction> =
- mpsc::sync_channel(50);
- let to_server = SenderWithContext::new(SenderType::SyncSender(to_server));
- let sessions: Arc<RwLock<Option<SessionMetaData>>> = Arc::new(RwLock::new(None));
-
- #[cfg(not(test))]
- std::panic::set_hook({
- use crate::errors::handle_panic;
- let to_server = to_server.clone();
- Box::new(move |info| {
- handle_panic(info, &to_server);
- })
- });
-
- #[cfg(test)]
- thread::Builder::new()
- .name("server_router".to_string())
- .spawn({
- let sessions = sessions.clone();
- let os_input = os_input.clone();
- let to_server = to_server.clone();
-
- move || route_thread_main(sessions, os_input, to_server)
- })
- .unwrap();
- #[cfg(not(test))]
- let _ = thread::Builder::new()
- .name("server_listener".to_string())
- .spawn({
- use crate::common::os_input_output::set_permissions;
- use interprocess::local_socket::LocalSocketListener;
-
- let os_input = os_input.clone();
- let sessions = sessions.clone();
- let to_server = to_server.clone();
- let socket_path = socket_path.clone();
- move || {
- drop(std::fs::remove_file(&socket_path));
- let listener = LocalSocketListener::bind(&*socket_path).unwrap();
- set_permissions(&socket_path).unwrap();
- for stream in listener.incoming() {
- match stream {
- Ok(stream) => {
- let mut os_input = os_input.clone();
- os_input.update_receiver(stream);
- let sessions = sessions.clone();
- let to_server = to_server.clone();
- thread::Builder::new()
- .name("server_router".to_string())
- .spawn({
- let sessions = sessions.clone();
- let os_input = os_input.clone();
- let to_server = to_server.clone();
-
- move || route_thread_main(sessions, os_input, to_server)
- })
- .unwrap();
- }
- Err(err) => {
- panic!("err {:?}", err);
- }
- }
- }
- }
- });
-
- loop {
- let (instruction, mut err_ctx) = server_receiver.recv().unwrap();
- err_ctx.add_call(ContextType::IPCServer((&instruction).into()));
- match instruction {
- ServerInstruction::NewClient(full_screen_ws, opts, config_options) => {
- let session_data = init_session(
- os_input.clone(),
- opts,
- config_options,
- to_server.clone(),
- full_screen_ws,
- );
- *sessions.write().unwrap() = Some(session_data);
- sessions
- .read()
- .unwrap()
- .as_ref()
- .unwrap()
- .senders
- .send_to_pty(PtyInstruction::NewTab)
- .unwrap();
- }
- ServerInstruction::UnblockInputThread => {
- os_input.send_to_client(ServerToClientMsg::UnblockInputThread);
- }
- ServerInstruction::ClientExit => {
- *sessions.write().unwrap() = None;
- os_input.send_to_client(ServerToClientMsg::Exit);
- break;
- }
- ServerInstruction::Render(output) => {
- os_input.send_to_client(ServerToClientMsg::Render(output))
- }
- ServerInstruction::Error(backtrace) => {
- os_input.send_to_client(ServerToClientMsg::ServerError(backtrace));
- break;
- }
- }
- }
- #[cfg(not(test))]
- drop(std::fs::remove_file(&socket_path));
-}
-
-fn init_session(
- os_input: Box<dyn ServerOsApi>,
- opts: Box<CliArgs>,
- config_options: Box<Options>,
- to_server: SenderWithContext<ServerInstruction>,
- full_screen_ws: PositionAndSize,
-) -> SessionMetaData {
- let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = mpsc::channel();
- let to_screen = SenderWithContext::new(SenderType::Sender(to_screen));
-
- let (to_plugin, plugin_receiver): ChannelWithContext<PluginInstruction> = mpsc::channel();
- let to_plugin = SenderWithContext::new(SenderType::Sender(to_plugin));
- let (to_pty, pty_receiver): ChannelWithContext<PtyInstruction> = mpsc::channel();
- let to_pty = SenderWithContext::new(SenderType::Sender(to_pty));
-
- // Determine and initialize the data directory
- let data_dir = opts.data_dir.unwrap_or_else(get_default_data_dir);
-
- #[cfg(not(disable_automatic_asset_installation))]
- populate_data_dir(&data_dir);
-
- let capabilities = PluginCapabilities {
- arrow_fonts: config_options.simplified_ui,
- };
-
- // Don't use default layouts in tests, but do everywhere else
- #[cfg(not(test))]
- let default_layout = Some(PathBuf::from("default"));
- #[cfg(test)]
- let default_layout = None;
- let layout_path = opts.layout_path;
- let maybe_layout = opts
- .layout
- .as_ref()
- .map(|p| Layout::from_dir(&p, &data_dir))
- .or_else(|| layout_path.map(|p| Layout::new(&p)))
- .or_else(|| default_layout.map(|p| Layout::from_dir(&p, &data_dir)));
-
- let pty_thread = thread::Builder::new()
- .name("pty".to_string())
- .spawn({
- let pty = Pty::new(
- Bus::new(
- pty_receiver,
- Some(&to_screen),
- None,
- Some(&to_plugin),
- Some(&to_server),
- Some(os_input.clone()),
- ),
- opts.debug,
- );
-
- move || pty_thread_main(pty, maybe_layout)
- })
- .unwrap();
-
- let screen_thread = thread::Builder::new()
- .name("screen".to_string())
- .spawn({
- let screen_bus = Bus::new(
- screen_receiver,
- None,
- Some(&to_pty),
- Some(&to_plugin),
- Some(&to_server),
- Some(os_input.clone()),
- );
- let max_panes = opts.max_panes;
-
- move || {
- screen_thread_main(screen_bus, max_panes, full_screen_ws, config_options);
- }
- })
- .unwrap();
-
- let wasm_thread = thread::Builder::new()
- .name("wasm".to_string())
- .spawn({
- let plugin_bus = Bus::new(
- plugin_receiver,
- Some(&to_screen),
- Some(&to_pty),
- None,
- None,
- None,
- );
- let store = Store::default();
-
- move || wasm_thread_main(plugin_bus, store, data_dir)
- })
- .unwrap();
- SessionMetaData {
- senders: ThreadSenders {
- to_screen: Some(to_screen),
- to_pty: Some(to_pty),
- to_plugin: Some(to_plugin),
- to_server: None,
- },
- capabilities,
- screen_thread: Some(screen_thread),
- pty_thread: Some(pty_thread),
- wasm_thread: Some(wasm_thread),
- }
-}
diff --git a/src/server/route.rs b/src/server/route.rs
deleted file mode 100644
index c435a0e6..00000000
--- a/src/server/route.rs
+++ /dev/null
@@ -1,215 +0,0 @@
-use std::sync::{Arc, RwLock};
-
-use zellij_tile::data::Event;
-
-use crate::common::input::actions::{Action, Direction};
-use crate::common::input::handler::get_mode_info;
-use crate::common::ipc::ClientToServerMsg;
-use crate::common::os_input_output::ServerOsApi;
-use crate::common::pty::PtyInstruction;
-use crate::common::screen::ScreenInstruction;
-use crate::common::thread_bus::SenderWithContext;
-use crate::common::wasm_vm::PluginInstruction;
-use crate::server::{ServerInstruction, SessionMetaData};
-
-fn route_action(action: Action, session: &SessionMetaData, os_input: &dyn ServerOsApi) {
- match action {
- Action::Write(val) => {
- session
- .senders
- .send_to_screen(ScreenInstruction::ClearScroll)
- .unwrap();
- session
- .senders
- .send_to_screen(ScreenInstruction::WriteCharacter(val))
- .unwrap();
- }
- Action::SwitchToMode(mode) => {
- let palette = os_input.load_palette();
- session
- .senders
- .send_to_plugin(PluginInstruction::Update(
- None,
- Event::ModeUpdate(get_mode_info(mode, palette, session.capabilities)),
- ))
- .unwrap();
- session
- .senders
- .send_to_screen(ScreenInstruction::ChangeMode(get_mode_info(
- mode,
- palette,
- session.capabilities,
- )))
- .unwrap();
- session
- .senders
- .send_to_screen(ScreenInstruction::Render)
- .unwrap();
- }
- Action::Resize(direction) => {
- let screen_instr = match direction {
- Direction::Left => ScreenInstruction::ResizeLeft,
- Direction::Right => ScreenInstruction::ResizeRight,
- Direction::Up => ScreenInstruction::ResizeUp,
- Direction::Down => ScreenInstruction::ResizeDown,
- };
- session.senders.send_to_screen(screen_instr).unwrap();
- }
- Action::SwitchFocus => {
- session
- .senders
- .send_to_screen(ScreenInstruction::SwitchFocus)
- .unwrap();
- }
- Action::FocusNextPane => {
- session
- .senders
- .send_to_screen(ScreenInstruction::FocusNextPane)
- .unwrap();
- }
- Action::FocusPreviousPane => {
- session
- .senders
- .send_to_screen(ScreenInstruction::FocusPreviousPane)
- .unwrap();
- }
- Action::MoveFocus(direction) => {
- let screen_instr = match direction {
- Direction::Left => ScreenInstruction::MoveFocusLeft,
- Direction::Right => ScreenInstruction::MoveFocusRight,
- Direction::Up => ScreenInstruction::MoveFocusUp,
- Direction::Down => ScreenInstruction::MoveFocusDown,
- };
- session.senders.send_to_screen(screen_instr).unwrap();
- }
- Action::MoveFocusOrTab(direction) => {
- let screen_instr = match direction {
- Direction::Left => ScreenInstruction::MoveFocusLeftOrPreviousTab,
- Direction::Right => ScreenInstruction::MoveFocusRightOrNextTab,
- _ => unreachable!(),
- };
- session.senders.send_to_screen(screen_instr).unwrap();
- }
- Action::ScrollUp => {
- session
- .senders
- .send_to_screen(ScreenInstruction::ScrollUp)
- .unwrap();
- }
- Action::ScrollDown => {
- session
- .senders
- .send_to_screen(ScreenInstruction::ScrollDown)
- .unwrap();
- }
- Action::PageScrollUp => {
- session
- .senders
- .send_to_screen(ScreenInstruction::PageScrollUp)
- .unwrap();
- }
- Action::PageScrollDown => {
- session
- .senders
- .send_to_screen(ScreenInstruction::PageScrollDown)
- .unwrap();
- }
- Action::ToggleFocusFullscreen => {
- session
- .senders
- .send_to_screen(ScreenInstruction::ToggleActiveTerminalFullscreen)
- .unwrap();
- }
- Action::NewPane(direction) => {
- let pty_instr = match direction {
- Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(None),
- Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(None),
- Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(None),
- Some(Direction::Down) => PtyInstruction::SpawnTerminalHorizontally(None),
- // No direction specified - try to put it in the biggest available spot
- None => PtyInstruction::SpawnTerminal(None),
- };
- session.senders.send_to_pty(pty_instr).unwrap();
- }
- Action::CloseFocus => {
- session
- .senders
- .send_to_screen(ScreenInstruction::CloseFocusedPane)
- .unwrap();
- }
- Action::NewTab => {
- session.senders.send_to_pty(PtyInstruction::NewTab).unwrap();
- }
- Action::GoToNextTab => {
- session
- .senders
- .send_to_screen(ScreenInstruction::SwitchTabNext)
- .unwrap();
- }
- Action::GoToPreviousTab => {
- session
- .senders
- .send_to_screen(ScreenInstruction::SwitchTabPrev)
- .unwrap();
- }
- Action::ToggleActiveSyncTab => {
- session
- .senders
- .send_to_screen(ScreenInstruction::ToggleActiveSyncTab)
- .unwrap();
- }
- Action::CloseTab => {
- session
- .senders
- .send_to_screen(ScreenInstruction::CloseTab)
- .unwrap();
- }
- Action::GoToTab(i) => {
- session
- .senders
- .send_to_screen(ScreenInstruction::GoToTab(i))
- .unwrap();
- }
- Action::TabNameInput(c) => {
- session
- .senders
- .send_to_screen(ScreenInstruction::UpdateTabName(c))
- .unwrap();
- }
- Action::NoOp => {}
- Action::Quit => panic!("Received unexpected action"),
- }
-}
-
-pub fn route_thread_main(
- sessions: Arc<RwLock<Option<SessionMetaData>>>,
- mut os_input: Box<dyn ServerOsApi>,
- to_server: SenderWithContext<ServerInstruction>,
-) {
- loop {
- let (instruction, err_ctx) = os_input.recv_from_client();
- err_ctx.update_thread_ctx();
- let rlocked_sessions = sessions.read().unwrap();
- match instruction {
- ClientToServerMsg::ClientExit => {
- to_server.send(instruction.into()).unwrap();
- break;
- }
- ClientToServerMsg::Action(action) => {
- route_action(action, rlocked_sessions.as_ref().unwrap(), &*os_input);
- }
- ClientToServerMsg::TerminalResize(new_size) => {
- rlocked_sessions
- .as_ref()
- .unwrap()
- .senders
- .send_to_screen(ScreenInstruction::TerminalResize(new_size))
- .unwrap();
- }
- ClientToServerMsg::NewClient(..) => {
- os_input.add_client_sender();
- to_server.send(instruction.into()).unwrap();
- }
- }
- }
-}
diff --git a/src/tests/fakes.rs b/src/tests/fakes.rs
index 98ec694a..09cc2ba3 100644
--- a/src/tests/fakes.rs
+++ b/src/tests/fakes.rs
@@ -1,4 +1,5 @@
-use crate::panes::PositionAndSize;
+use crate::tests::possible_tty_inputs::{get_possible_tty_inputs, Bytes};
+use crate::tests::utils::commands::{QUIT, SLEEP};
use interprocess::local_socket::LocalSocketStream;
use std::collections::{HashMap, VecDeque};
use std::io::Write;
@@ -6,15 +7,16 @@ use std::os::unix::io::RawFd;
use std::path::PathBuf;
use std::sync::{mpsc, Arc, Condvar, Mutex};
use std::time::{Duration, Instant};
-
-use crate::common::ipc::{ClientToServerMsg, ServerToClientMsg};
-use crate::common::thread_bus::{ChannelWithContext, SenderType, SenderWithContext};
-use crate::errors::ErrorContext;
-use crate::os_input_output::{ClientOsApi, ServerOsApi};
-use crate::tests::possible_tty_inputs::{get_possible_tty_inputs, Bytes};
-use crate::tests::utils::commands::{QUIT, SLEEP};
-use crate::utils::shared::default_palette;
+use zellij_client::os_input_output::ClientOsApi;
+use zellij_server::os_input_output::ServerOsApi;
use zellij_tile::data::Palette;
+use zellij_utils::{
+ channels::{ChannelWithContext, SenderType, SenderWithContext},
+ errors::ErrorContext,
+ ipc::{ClientToServerMsg, ServerToClientMsg},
+ pane_size::PositionAndSize,
+ shared::default_palette,
+};
const MIN_TIME_BETWEEN_SNAPSHOTS: Duration = Duration::from_millis(150);
@@ -217,6 +219,9 @@ impl ClientOsApi for FakeInputOutput {
}
}
fn connect_to_server(&self, _path: &std::path::Path) {}
+ fn load_palette(&self) -> Palette {
+ default_palette()
+ }
}
impl ServerOsApi for FakeInputOutput {
diff --git a/src/tests/integration/basic.rs b/src/tests/integration/basic.rs
index bb581eb1..640b1c55 100644
--- a/src/tests/integration/basic.rs
+++ b/src/tests/integration/basic.rs
@@ -1,7 +1,6 @@
-use crate::panes::PositionAndSize;
use ::insta::assert_snapshot;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::commands::{
@@ -12,6 +11,7 @@ use crate::tests::utils::commands::{
};
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(fake_win_size.clone())
diff --git a/src/tests/integration/close_pane.rs b/src/tests/integration/close_pane.rs
index cfb912ee..b5ba7f19 100644
--- a/src/tests/integration/close_pane.rs
+++ b/src/tests/integration/close_pane.rs
@@ -1,17 +1,17 @@
-use crate::panes::PositionAndSize;
use ::insta::assert_snapshot;
+use zellij_utils::pane_size::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
-use crate::common::input::config::Config;
use crate::tests::utils::commands::{
CLOSE_PANE_IN_PANE_MODE, ESC, MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT,
RESIZE_DOWN_IN_RESIZE_MODE, RESIZE_LEFT_IN_RESIZE_MODE, RESIZE_MODE, RESIZE_UP_IN_RESIZE_MODE,
SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(fake_win_size.clone())
diff --git a/src/tests/integration/compatibility.rs b/src/tests/integration/compatibility.rs
index 06222a3a..3b28a970 100644
--- a/src/tests/integration/compatibility.rs
+++ b/src/tests/integration/compatibility.rs
@@ -1,15 +1,15 @@
use ::insta::assert_snapshot;
use ::std::collections::HashMap;
-use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::possible_tty_inputs::Bytes;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::utils::commands::QUIT;
+use zellij_utils::input::config::Config;
/*
* These tests are general compatibility tests for non-trivial scenarios running in the terminal.
diff --git a/src/tests/integration/layouts.rs b/src/tests/integration/layouts.rs
index fa46e9f7..e6986924 100644
--- a/src/tests/integration/layouts.rs
+++ b/src/tests/integration/layouts.rs
@@ -1,13 +1,13 @@
use insta::assert_snapshot;
use std::path::PathBuf;
-use crate::common::input::config::Config;
-use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::commands::QUIT;
use crate::tests::utils::get_output_frame_snapshots;
use crate::CliArgs;
+use zellij_utils::input::config::Config;
+use zellij_utils::pane_size::PositionAndSize;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(fake_win_size.clone())
diff --git a/src/tests/integration/move_focus_down.rs b/src/tests/integration/move_focus_down.rs
index b1032bd0..2f18ab62 100644
--- a/src/tests/integration/move_focus_down.rs
+++ b/src/tests/integration/move_focus_down.rs
@@ -1,16 +1,16 @@
use ::insta::assert_snapshot;
-use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_DOWN_IN_PANE_MODE, MOVE_FOCUS_UP_IN_PANE_MODE, PANE_MODE, QUIT,
SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)
diff --git a/src/tests/integration/move_focus_left.rs b/src/tests/integration/move_focus_left.rs
index 4842f342..a9fca49e 100644
--- a/src/tests/integration/move_focus_left.rs
+++ b/src/tests/integration/move_focus_left.rs
@@ -1,17 +1,17 @@
use ::insta::assert_snapshot;
-use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::utils::commands::{
ENTER, MOVE_FOCUS_LEFT_IN_NORMAL_MODE, MOVE_FOCUS_LEFT_IN_PANE_MODE,
MOVE_FOCUS_RIGHT_IN_PANE_MODE, NEW_TAB_IN_TAB_MODE, PANE_MODE, QUIT, SPLIT_DOWN_IN_PANE_MODE,
SPLIT_RIGHT_IN_PANE_MODE, TAB_MODE,
};
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)
diff --git a/src/tests/integration/move_focus_right.rs b/src/tests/integration/move_focus_right.rs
index 6ff6e21a..c85d5b96 100644
--- a/src/tests/integration/move_focus_right.rs
+++ b/src/tests/integration/move_focus_right.rs
@@ -1,17 +1,17 @@
use ::insta::assert_snapshot;
-use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::utils::commands::{
ENTER, MOVE_FOCUS_LEFT_IN_PANE_MODE, MOVE_FOCUS_RIGHT_IN_NORMAL_MODE,
MOVE_FOCUS_RIGHT_IN_PANE_MODE, NEW_TAB_IN_TAB_MODE, PANE_MODE, QUIT, SPLIT_DOWN_IN_PANE_MODE,
SPLIT_RIGHT_IN_PANE_MODE, TAB_MODE,
};
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)
diff --git a/src/tests/integration/move_focus_up.rs b/src/tests/integration/move_focus_up.rs
index 068ded52..d161ff8a 100644
--- a/src/tests/integration/move_focus_up.rs
+++ b/src/tests/integration/move_focus_up.rs
@@ -1,16 +1,16 @@
use ::insta::assert_snapshot;
-use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_DOWN_IN_PANE_MODE, MOVE_FOCUS_UP_IN_PANE_MODE, PANE_MODE, QUIT,
SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)
diff --git a/src/tests/integration/resize_down.rs b/src/tests/integration/resize_down.rs
index cc4b9ec4..4ef45a06 100644
--- a/src/tests/integration/resize_down.rs
+++ b/src/tests/integration/resize_down.rs
@@ -1,17 +1,17 @@
use insta::assert_snapshot;
-use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, RESIZE_DOWN_IN_RESIZE_MODE,
RESIZE_LEFT_IN_RESIZE_MODE, RESIZE_MODE, SLEEP, SPLIT_DOWN_IN_PANE_MODE,
SPLIT_RIGHT_IN_PANE_MODE,
};
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)
diff --git a/src/tests/integration/resize_left.rs b/src/tests/integration/resize_left.rs
index c5ba5427..1b820501 100644
--- a/src/tests/integration/resize_left.rs
+++ b/src/tests/integration/resize_left.rs
@@ -1,16 +1,16 @@
use ::insta::assert_snapshot;
-use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, RESIZE_LEFT_IN_RESIZE_MODE, RESIZE_MODE,
RESIZE_UP_IN_RESIZE_MODE, SLEEP, SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)
diff --git a/src/tests/integration/resize_right.rs b/src/tests/integration/resize_right.rs
index 43409ec1..44601f2c 100644
--- a/src/tests/integration/resize_right.rs
+++ b/src/tests/integration/resize_right.rs
@@ -1,16 +1,16 @@
use ::insta::assert_snapshot;
-use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, RESIZE_MODE, RESIZE_RIGHT_IN_RESIZE_MODE,
RESIZE_UP_IN_RESIZE_MODE, SLEEP, SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)
diff --git a/src/tests/integration/resize_up.rs b/src/tests/integration/resize_up.rs
index 200fcc6e..9aca8d5a 100644
--- a/src/tests/integration/resize_up.rs
+++ b/src/tests/integration/resize_up.rs
@@ -1,16 +1,16 @@
use ::insta::assert_snapshot;
-use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, RESIZE_LEFT_IN_RESIZE_MODE, RESIZE_MODE,
RESIZE_UP_IN_RESIZE_MODE, SLEEP, SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
};
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)
diff --git a/src/tests/integration/tabs.rs b/src/tests/integration/tabs.rs
index 0995d16c..90f82c6d 100644
--- a/src/tests/integration/tabs.rs
+++ b/src/tests/integration/tabs.rs
@@ -2,16 +2,17 @@ use insta::assert_snapshot;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
+use crate::tests::utils::commands::CLOSE_PANE_IN_PANE_MODE;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
-use crate::{panes::PositionAndSize, tests::utils::commands::CLOSE_PANE_IN_PANE_MODE};
-use crate::common::input::config::Config;
use crate::tests::utils::commands::{
CLOSE_TAB_IN_TAB_MODE, NEW_TAB_IN_TAB_MODE, PANE_MODE, QUIT, SPLIT_DOWN_IN_PANE_MODE,
SWITCH_NEXT_TAB_IN_TAB_MODE, SWITCH_PREV_TAB_IN_TAB_MODE, TAB_MODE,
TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE,
};
+use zellij_utils::input::config::Config;
+use zellij_utils::pane_size::PositionAndSize;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)
diff --git a/src/tests/integration/terminal_window_resize.rs b/src/tests/integration/terminal_window_resize.rs
index 2e56300f..2be41c3a 100644
--- a/src/tests/integration/terminal_window_resize.rs
+++ b/src/tests/integration/terminal_window_resize.rs
@@ -1,12 +1,12 @@
-use crate::panes::PositionAndSize;
use ::insta::assert_snapshot;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::commands::QUIT;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(fake_win_size.clone())
diff --git a/src/tests/integration/toggle_fullscreen.rs b/src/tests/integration/toggle_fullscreen.rs
index 00d98170..80257982 100644
--- a/src/tests/integration/toggle_fullscreen.rs
+++ b/src/tests/integration/toggle_fullscreen.rs
@@ -1,16 +1,16 @@
use insta::assert_snapshot;
-use crate::panes::PositionAndSize;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::start;
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
use crate::CliArgs;
+use zellij_utils::pane_size::PositionAndSize;
-use crate::common::input::config::Config;
use crate::tests::utils::commands::{
MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE,
};
+use zellij_utils::input::config::Config;
fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
FakeInputOutput::new(*fake_win_size)
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 7aaad79f..3e548db7 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -4,12 +4,10 @@ pub mod possible_tty_inputs;
pub mod tty_inputs;
pub mod utils;
-use crate::cli::CliArgs;
-use crate::client::start_client;
-use crate::common::input::config::Config;
-use crate::os_input_output::{ClientOsApi, ServerOsApi};
-use crate::server::start_server;
use std::path::PathBuf;
+use zellij_client::{os_input_output::ClientOsApi, start_client};
+use zellij_server::{os_input_output::ServerOsApi, start_server};
+use zellij_utils::{cli::CliArgs, input::config::Config};
pub fn start(
client_os_input: Box<dyn ClientOsApi>,
diff --git a/src/tests/utils.rs b/src/tests/utils.rs
index f01c1ac5..441f9bce 100644
--- a/src/tests/utils.rs
+++ b/src/tests/utils.rs
@@ -1,5 +1,6 @@
-use crate::panes::PositionAndSize;
-use crate::panes::TerminalPane;
+use zellij_server::{panes::TerminalPane, tab::Pane};
+use zellij_tile::data::Palette;
+use zellij_utils::pane_size::PositionAndSize;
pub fn get_output_frame_snapshots(
output_frames: &[Vec<u8>],
@@ -7,7 +8,7 @@ pub fn get_output_frame_snapshots(
) -> Vec<String> {
let mut vte_parser = vte::Parser::new();
let main_pid = 0;
- let mut terminal_output = TerminalPane::new(main_pid, *win_size);
+ let mut terminal_output = TerminalPane::new(main_pid, *win_size, Palette::default());
let mut snapshots = vec![];
for frame in output_frames.iter() {