aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authordenis <denismaximov98@gmail.com>2021-04-22 11:45:29 +0300
committerdenis <denismaximov98@gmail.com>2021-04-22 11:45:29 +0300
commit8d98ca7da0bc278a832e4e7de23ff63a84deb529 (patch)
treee7fdfaa6c1ae25e7737dbb36bd1dbcb94d62ab47 /src
parentwip: fixing the first arrow (diff)
parentfix(cargo): reduce crate size by 4.8MB using `cargo diet`, to 77kB (diff)
downloadsl0ppij-8d98ca7da0bc278a832e4e7de23ff63a84deb529.tar.gz
sl0ppij-8d98ca7da0bc278a832e4e7de23ff63a84deb529.tar.xz
sl0ppij-8d98ca7da0bc278a832e4e7de23ff63a84deb529.zip
wip: merge main in
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs18
-rw-r--r--src/client/layout.rs11
-rw-r--r--src/client/pane_resizer.rs172
-rw-r--r--src/client/panes/grid.rs498
-rw-r--r--src/client/panes/plugin_pane.rs2
-rw-r--r--src/client/panes/terminal_character.rs4
-rw-r--r--src/client/panes/terminal_pane.rs376
-rw-r--r--src/client/tab.rs171
-rw-r--r--src/common/errors.rs16
-rw-r--r--src/common/input/actions.rs4
-rw-r--r--src/common/input/config.rs26
-rw-r--r--src/common/input/handler.rs14
-rw-r--r--src/common/input/keybinds.rs69
-rw-r--r--src/common/input/unit/keybinds_test.rs31
-rw-r--r--src/common/install.rs31
-rw-r--r--src/common/mod.rs103
-rw-r--r--src/common/os_input_output.rs13
-rw-r--r--src/common/pty_bus.rs11
-rw-r--r--src/common/screen.rs4
-rw-r--r--src/common/utils/logging.rs6
-rw-r--r--src/common/utils/shared.rs4
-rw-r--r--src/main.rs54
-rw-r--r--src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml19
-rw-r--r--src/tests/integration/basic.rs66
-rw-r--r--src/tests/integration/expansion_boundary.rs117
-rw-r--r--src/tests/integration/mod.rs1
-rw-r--r--src/tests/integration/resize_down.rs13
-rw-r--r--src/tests/integration/resize_up.rs2
-rw-r--r--src/tests/integration/snapshots/zellij__tests__integration__basic__scrolling_page_down_inside_a_pane.snap25
-rw-r--r--src/tests/integration/snapshots/zellij__tests__integration__basic__scrolling_page_up_inside_a_pane.snap25
-rw-r--r--src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__new_panes_are_open_inside_expansion_border.snap55
-rw-r--r--src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__resize_pane_inside_expansion_border.snap55
-rw-r--r--src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__toggling_fullcsreen_in_expansion_border_expands_only_until_border.snap55
-rw-r--r--src/tests/integration/snapshots/zellij__tests__integration__resize_down__cannot_resize_down_when_pane_below_is_at_minimum_height.snap2
-rw-r--r--src/tests/integration/snapshots/zellij__tests__integration__resize_down__resize_down_with_panes_above_and_below.snap5
-rw-r--r--src/tests/integration/snapshots/zellij__tests__integration__resize_up__cannot_resize_up_when_pane_above_is_at_minimum_height.snap2
-rw-r--r--src/tests/utils.rs4
37 files changed, 1028 insertions, 1056 deletions
diff --git a/src/cli.rs b/src/cli.rs
index b1fb8f38..008fb28c 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,6 +1,9 @@
use std::path::PathBuf;
use structopt::StructOpt;
+// TODO add to consts.rs
+const ZELLIJ_CONFIG_ENV: &str = "ZELLIJ_CONFIG";
+
#[derive(StructOpt, Default, Debug)]
#[structopt(name = "zellij")]
pub struct CliArgs {
@@ -20,12 +23,20 @@ pub struct CliArgs {
#[structopt(long)]
pub max_panes: Option<usize>,
+ /// Change where zellij looks for layouts and plugins
+ #[structopt(long)]
+ pub data_dir: Option<PathBuf>,
+
/// Path to a layout yaml file
#[structopt(short, long)]
pub layout: Option<PathBuf>,
+ /// Change where zellij looks for the configuration
+ #[structopt(short, long, env=ZELLIJ_CONFIG_ENV)]
+ pub config: Option<PathBuf>,
+
#[structopt(subcommand)]
- pub config: Option<ConfigCli>,
+ pub option: Option<ConfigCli>,
#[structopt(short, long)]
pub debug: bool,
@@ -33,10 +44,9 @@ pub struct CliArgs {
#[derive(Debug, StructOpt)]
pub enum ConfigCli {
- /// Path to the configuration yaml file
- #[structopt(alias = "c")]
+ /// Change the behaviour of zellij
+ #[structopt(name = "option")]
Config {
- path: Option<PathBuf>,
#[structopt(long)]
/// Disables loading of configuration file at default location
clean: bool,
diff --git a/src/client/layout.rs b/src/client/layout.rs
index 2d1daaf4..e82f9045 100644
--- a/src/client/layout.rs
+++ b/src/client/layout.rs
@@ -1,6 +1,5 @@
-use directories_next::ProjectDirs;
use serde::{Deserialize, Serialize};
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use std::{fs::File, io::prelude::*};
use crate::panes::PositionAndSize;
@@ -177,16 +176,12 @@ pub struct Layout {
pub split_size: Option<SplitSize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub plugin: Option<PathBuf>,
- #[serde(default)]
- pub expansion_boundary: bool,
}
impl Layout {
- pub fn new(layout_path: PathBuf) -> Self {
- let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
- let layout_dir = project_dirs.data_dir().join("layouts/");
+ pub fn new(layout_path: &Path, data_dir: &Path) -> Self {
+ let layout_dir = data_dir.join("layouts/");
let mut layout_file = File::open(&layout_path)
- .or_else(|_| File::open(&layout_path.with_extension("yaml")))
.or_else(|_| File::open(&layout_dir.join(&layout_path).with_extension("yaml")))
.unwrap_or_else(|_| panic!("cannot find layout {}", &layout_path.display()));
diff --git a/src/client/pane_resizer.rs b/src/client/pane_resizer.rs
index 007dc33f..f9ec71b8 100644
--- a/src/client/pane_resizer.rs
+++ b/src/client/pane_resizer.rs
@@ -1,7 +1,10 @@
use crate::os_input_output::OsApi;
use crate::panes::{PaneId, PositionAndSize};
use crate::tab::Pane;
-use std::collections::{BTreeMap, HashSet};
+use std::{
+ cmp::Ordering,
+ collections::{BTreeMap, HashSet},
+};
pub struct PaneResizer<'a> {
panes: &'a mut BTreeMap<PaneId, Box<dyn Pane>>,
@@ -9,9 +12,7 @@ pub struct PaneResizer<'a> {
}
// TODO: currently there are some functions here duplicated with Tab
-// the reason for this is that we need to get rid of the expansion_boundary
-// otherwise we'll have a big separation of concerns issue
-// once that is done, all resizing functions should move here
+// all resizing functions should move here
impl<'a> PaneResizer<'a> {
pub fn new(
@@ -29,63 +30,72 @@ impl<'a> PaneResizer<'a> {
let mut successfully_resized = false;
let mut column_difference: isize = 0;
let mut row_difference: isize = 0;
- if new_size.columns < current_size.columns {
- let reduce_by = current_size.columns - new_size.columns;
- find_reducible_vertical_chain(
- &self.panes,
- reduce_by,
- current_size.columns,
- current_size.rows,
- )
- .map(|panes_to_resize| {
- 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;
- });
- } else if new_size.columns > current_size.columns {
- let increase_by = new_size.columns - current_size.columns;
- find_increasable_vertical_chain(
- &self.panes,
- increase_by,
- current_size.columns,
- current_size.rows,
- )
- .map(|panes_to_resize| {
- 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;
- });
+ 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 => (),
}
- if new_size.rows < current_size.rows {
- let reduce_by = current_size.rows - new_size.rows;
- find_reducible_horizontal_chain(
- &self.panes,
- reduce_by,
- current_size.columns,
- current_size.rows,
- )
- .map(|panes_to_resize| {
- 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;
- });
- } else if new_size.rows > current_size.rows {
- let increase_by = new_size.rows - current_size.rows;
- find_increasable_horizontal_chain(
- &self.panes,
- increase_by,
- current_size.columns,
- current_size.rows,
- )
- .map(|panes_to_resize| {
- 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;
- });
+ 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))
@@ -229,13 +239,12 @@ impl<'a> PaneResizer<'a> {
fn find_next_increasable_horizontal_pane(
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- right_of: &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.as_ref())
+ 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 =
@@ -255,11 +264,11 @@ fn find_next_increasable_horizontal_pane(
fn find_next_increasable_vertical_pane(
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- below: &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.as_ref()), // TODO: the name here is wrong, it should be horizontally_overlaps_with
+ |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));
@@ -278,11 +287,11 @@ fn find_next_increasable_vertical_pane(
fn find_next_reducible_vertical_pane(
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- below: &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.as_ref()), // TODO: the name here is wrong, it should be horizontally_overlaps_with
+ |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 {
@@ -300,13 +309,12 @@ fn find_next_reducible_vertical_pane(
fn find_next_reducible_horizontal_pane(
panes: &BTreeMap<PaneId, Box<dyn Pane>>,
- right_of: &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.as_ref())
+ 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));
@@ -351,7 +359,11 @@ fn find_increasable_horizontal_chain(
if current_pane.x() + current_pane.columns() == screen_width {
return Some(panes_to_resize);
}
- match find_next_increasable_horizontal_pane(panes, &current_pane, increase_by) {
+ 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();
}
@@ -397,7 +409,11 @@ fn find_increasable_vertical_chain(
if current_pane.y() + current_pane.rows() == screen_height {
return Some(panes_to_resize);
}
- match find_next_increasable_vertical_pane(panes, &current_pane, increase_by) {
+ 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();
}
@@ -443,7 +459,11 @@ fn find_reducible_horizontal_chain(
if current_pane.x() + current_pane.columns() == screen_width {
return Some(panes_to_resize);
}
- match find_next_reducible_horizontal_pane(panes, &current_pane, reduce_by) {
+ 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();
}
@@ -489,7 +509,11 @@ fn find_reducible_vertical_chain(
if current_pane.y() + current_pane.rows() == screen_height {
return Some(panes_to_resize);
}
- match find_next_reducible_vertical_pane(panes, &current_pane, increase_by) {
+ 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();
}
diff --git a/src/client/panes/grid.rs b/src/client/panes/grid.rs
index 7651bdc0..c6dd201e 100644
--- a/src/client/panes/grid.rs
+++ b/src/client/panes/grid.rs
@@ -1,9 +1,13 @@
use std::{
cmp::Ordering,
+ collections::VecDeque,
fmt::{self, Debug, Formatter},
};
-static TABSTOP_WIDTH: usize = 8; // TODO: is this always right?
+const TABSTOP_WIDTH: usize = 8; // TODO: is this always right?
+const SCROLL_BACK: usize = 10_000;
+
+use crate::utils::logging::debug_log_to_file;
use crate::panes::terminal_character::{
CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
@@ -26,14 +30,12 @@ fn get_top_non_canonical_rows(rows: &mut Vec<Row>) -> Vec<Row> {
}
}
-fn get_bottom_canonical_row_and_wraps(rows: &mut Vec<Row>) -> Vec<Row> {
+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 {
- index_of_last_non_canonical_row = Some(i);
break;
- } else {
- index_of_last_non_canonical_row = Some(i);
}
}
match index_of_last_non_canonical_row {
@@ -45,7 +47,7 @@ fn get_bottom_canonical_row_and_wraps(rows: &mut Vec<Row>) -> Vec<Row> {
}
fn transfer_rows_down(
- source: &mut Vec<Row>,
+ source: &mut VecDeque<Row>,
destination: &mut Vec<Row>,
count: usize,
max_src_width: Option<usize>,
@@ -58,7 +60,7 @@ fn transfer_rows_down(
break;
}
if next_lines.is_empty() {
- match source.pop() {
+ 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;
@@ -84,13 +86,12 @@ fn transfer_rows_down(
if !next_lines.is_empty() {
match max_src_width {
Some(max_row_width) => {
- let mut excess_rows =
- Row::from_rows(next_lines).split_to_rows_of_length(max_row_width);
- source.append(&mut excess_rows);
+ 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);
- source.push(excess_row);
+ bounded_push(source, excess_row);
}
}
}
@@ -98,7 +99,7 @@ fn transfer_rows_down(
fn transfer_rows_up(
source: &mut Vec<Row>,
- destination: &mut Vec<Row>,
+ destination: &mut VecDeque<Row>,
count: usize,
max_src_width: Option<usize>,
max_dst_width: Option<usize>,
@@ -124,7 +125,7 @@ fn transfer_rows_up(
break; // no more rows
}
}
- destination.push(next_lines.remove(0));
+ bounded_push(destination, next_lines.remove(0));
}
if !next_lines.is_empty() {
match max_src_width {
@@ -142,13 +143,25 @@ fn transfer_rows_up(
}
}
+fn bounded_push(vec: &mut VecDeque<Row>, value: Row) {
+ if vec.len() >= SCROLL_BACK {
+ vec.pop_front();
+ }
+ vec.push_back(value)
+}
+
#[derive(Clone)]
pub struct Grid {
- lines_above: Vec<Row>,
+ lines_above: VecDeque<Row>,
viewport: Vec<Row>,
lines_below: Vec<Row>,
+ alternative_lines_above_viewport_and_cursor: Option<(VecDeque<Row>, Vec<Row>, Cursor)>,
cursor: Cursor,
scroll_region: Option<(usize, usize)>,
+ pending_styles: CharacterStyles,
+ 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 clear_viewport_before_rendering: bool,
pub width: usize,
pub height: usize,
}
@@ -169,13 +182,18 @@ impl Debug for Grid {
impl Grid {
pub fn new(rows: usize, columns: usize) -> Self {
Grid {
- lines_above: vec![],
+ lines_above: VecDeque::with_capacity(SCROLL_BACK),
viewport: vec![Row::new().canonical()],
lines_below: vec![],
cursor: Cursor::new(0, 0),
scroll_region: None,
width: columns,
height: rows,
+ pending_styles: CharacterStyles::new(),
+ should_render: true,
+ cursor_key_mode: false,
+ alternative_lines_above_viewport_and_cursor: None,
+ clear_viewport_before_rendering: false,
}
}
pub fn advance_to_next_tabstop(&mut self, styles: CharacterStyles) {
@@ -237,7 +255,7 @@ impl Grid {
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().unwrap();
+ let line_to_insert_at_viewport_top = self.lines_above.pop_back().unwrap();
self.viewport.insert(0, line_to_insert_at_viewport_top);
}
}
@@ -245,11 +263,11 @@ impl Grid {
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 {
- self.lines_above.push(line_to_push_up);
+ bounded_push(&mut self.lines_above, line_to_push_up);
} else {
- let mut last_line_above = self.lines_above.pop().unwrap();
+ let mut last_line_above = self.lines_above.pop_back().unwrap();
last_line_above.append(&mut line_to_push_up.columns);
- self.lines_above.push(last_line_above);
+ 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);
@@ -265,7 +283,7 @@ impl Grid {
&& viewport_canonical_lines.is_empty()
&& !self.lines_above.is_empty()
{
- let mut first_line_above = self.lines_above.pop().unwrap();
+ 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;
@@ -429,13 +447,35 @@ impl Grid {
self.scroll_down_one_line();
}
}
- pub fn rotate_scroll_region_up(&mut self, _count: usize) {
- // TBD
+ 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) {
- // TBD
+ 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 add_canonical_line(&mut self, pad_character: TerminalCharacter) {
+ 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
@@ -444,8 +484,12 @@ impl Grid {
// 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![pad_character; self.width];
+ let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
self.viewport
.insert(scroll_region_bottom, Row::from_columns(columns).canonical());
return;
@@ -483,6 +527,24 @@ impl Grid {
}
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) => row.add_character_at(terminal_character, self.cursor.x),
None => {
// pad lines until cursor if they do not exist
@@ -496,9 +558,7 @@ impl Grid {
}
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 {
- self.insert_character_at_cursor_position(terminal_character);
- } else {
+ if self.cursor.x >= self.width {
// line wrap
self.cursor.x = 0;
if self.cursor.y == self.height - 1 {
@@ -519,8 +579,8 @@ impl Grid {
self.viewport.push(line_wrapped_row);
}
}
- self.insert_character_at_cursor_position(terminal_character);
}
+ 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) {
@@ -546,15 +606,17 @@ impl Grid {
row.replace_beginning_with(line_part);
}
pub fn clear_all_after_cursor(&mut self, replace_with: TerminalCharacter) {
- let cursor_row = self.viewport.get_mut(self.cursor.y).unwrap();
- cursor_row.truncate(self.cursor.x);
- let mut replace_with_columns_in_cursor_row = vec![replace_with; self.width - self.cursor.x];
- cursor_row.append(&mut replace_with_columns_in_cursor_row);
+ if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y).as_mut() {
+ cursor_row.truncate(self.cursor.x);
+ let mut replace_with_columns_in_cursor_row =
+ vec![replace_with; self.width - self.cursor.x];
+ cursor_row.append(&mut replace_with_columns_in_cursor_row);
- 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());
+ 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_cursor_line(&mut self) {
@@ -600,7 +662,9 @@ impl Grid {
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
- self.viewport.remove(scroll_region_bottom);
+ 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
@@ -621,7 +685,7 @@ impl Grid {
self.cursor.y + count
};
for _ in 0..lines_to_add {
- self.add_canonical_line(pad_character);
+ self.add_canonical_line();
}
self.pad_lines_until(self.cursor.y, pad_character);
}
@@ -663,8 +727,12 @@ impl Grid {
for _ in 0..count {
self.viewport.remove(current_line_index);
let columns = vec![pad_character; self.width];
- self.viewport
- .insert(scroll_region_bottom, Row::from_columns(columns).canonical());
+ 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());
+ }
}
}
}
@@ -683,7 +751,9 @@ impl Grid {
// so we add an empty line where the cursor currently is, and delete the last line
// of the scroll region
for _ in 0..count {
- self.viewport.remove(scroll_region_bottom);
+ 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());
@@ -723,6 +793,320 @@ impl Grid {
.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);
+ }
+}
+
+impl vte::Perform for Grid {
+ fn print(&mut self, c: char) {
+ // apparently, building TerminalCharacter like this without a "new" method
+ // is a little faster
+ let terminal_character = TerminalCharacter {
+ character: c,
+ styles: self.pending_styles,
+ };
+ self.add_character(terminal_character);
+ }
+
+ fn execute(&mut self, byte: u8) {
+ match byte {
+ 8 => {
+ // backspace
+ self.move_cursor_backwards(1);
+ }
+ 9 => {
+ // tab
+ self.advance_to_next_tabstop(self.pending_styles);
+ }
+ 10 => {
+ // 0a, newline
+ self.add_newline();
+ }
+ 13 => {
+ // 0d, carriage return
+ self.move_cursor_to_beginning_of_line();
+ }
+ _ => {}
+ }
+ }
+
+ fn hook(&mut self, _params: &[i64], _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: &[i64], _intermediates: &[u8], _ignore: bool, c: char) {
+ if c == 'm' {
+ self.pending_styles.add_style_from_ansi_params(params);
+ } else if c == 'C' {
+ // move cursor forward
+ let move_by = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ self.move_cursor_forward_until_edge(move_by);
+ } else if c == 'K' {
+ // clear line (0 => right, 1 => left, 2 => all)
+ if params[0] == 0 {
+ let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
+ char_to_replace.styles = self.pending_styles;
+ self.replace_characters_in_line_after_cursor(char_to_replace);
+ } else if params[0] == 1 {
+ let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
+ char_to_replace.styles = self.pending_styles;
+ self.replace_characters_in_line_before_cursor(char_to_replace);
+ } else if params[0] == 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.pending_styles;
+ if params[0] == 0 {
+ self.clear_all_after_cursor(char_to_replace);
+ } else if params[0] == 2 {
+ self.clear_all(char_to_replace);
+ }
+ // TODO: implement 1
+ } else if c == 'H' {
+ // goto row/col
+ // we subtract 1 from the row/column because these are 1 indexed
+ // (except when they are 0, in which case they should be 1
+ // don't look at me, I don't make the rules)
+ let (row, col) = if params.len() == 1 {
+ if params[0] == 0 {
+ (0, params[0] as usize)
+ } else {
+ ((params[0] as usize).saturating_sub(1), params[0] as usize)
+ }
+ } else if params[0] == 0 {
+ (0, (params[1] as usize).saturating_sub(1))
+ } else {
+ (
+ (params[0] as usize).saturating_sub(1),
+ (params[1] as usize).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 = if params[0] == 0 { 1 } else { params[0] };
+ self.move_cursor_up(move_up_count as usize);
+ } else if c == 'B' {
+ // move cursor down until edge of screen
+ let move_down_count = if params[0] == 0 { 1 } else { params[0] };
+ 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 = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ 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.get(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;
+ }
+ _ => {}
+ };
+ }
+ } 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.get(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;
+ }
+ _ => {}
+ };
+ }
+ } else if c == 'r' {
+ if params.len() > 1 {
+ // minus 1 because these are 1 indexed
+ let top_line_index = (params[0] as usize).saturating_sub(1);
+ let bottom_line_index = (params[1] as usize).saturating_sub(1);
+ self.set_scroll_region(top_line_index, bottom_line_index);
+ self.show_cursor();
+ } else {
+ self.clear_scroll_region();
+ }
+ } else if c == 'M' {
+ // delete lines if currently inside scroll region
+ let line_count_to_delete = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ 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 = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ let pad_character = EMPTY_TERMINAL_CHARACTER;
+ self.add_empty_lines_in_scroll_region(line_count_to_add, pad_character);
+ } else if c == 'q' {
+ // ignore for now to run on mac
+ } else if c == 'G' {
+ let column = if params[0] == 0 {
+ 0
+ } else {
+ params[0] as usize - 1
+ };
+ self.move_cursor_to_column(column);
+ } else if c == 'd' {
+ // goto line
+ let line = if params[0] == 0 {
+ 1
+ } else {
+ // minus 1 because this is 1 indexed
+ params[0] as usize - 1
+ };
+ let pad_character = EMPTY_TERMINAL_CHARACTER;
+ self.move_cursor_to_line(line, pad_character);
+ } else if c == 'P' {
+ // erase characters
+ let count = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ self.erase_characters(count, self.pending_styles);
+ } else if c == 'X' {
+ // erase characters and replace with empty characters of current style
+ let count = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ self.replace_with_empty_chars(count, self.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: i64 = *params.get(0).expect("A number of lines was expected.");
+
+ if line_count >= 0 {
+ self.rotate_scroll_region_up(line_count as usize);
+ } else {
+ // TODO: can this actually happen?
+ self.rotate_scroll_region_down(line_count.abs() as usize);
+ }
+ } else if c == 'S' {
+ // move scroll up
+ let count = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ self.rotate_scroll_region_down(count);
+ } else if c == '@' {
+ let count = if params[0] == 0 {
+ 1
+ } else {
+ params[0] as usize
+ };
+ for _ in 0..count {
+ // TODO: should this be styled?
+ self.insert_character_at_cursor_position(EMPTY_TERMINAL_CHARACTER);
+ }
+ } else {
+ let _ = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params));
+ }
+ }
+
+ fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) {
+ match (byte, intermediates.get(0)) {
+ (b'M', None) => {
+ self.move_cursor_up_with_scrolling(1);
+ }
+ (b'c', None) => {
+ self.reset_terminal_state();
+ }
+ _ => {}
+ }
+ }
}
#[derive(Clone)]
@@ -786,10 +1170,24 @@ impl Row {
}
}
}
+ 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
- self.columns.push(terminal_character);
- self.columns.swap_remove(x);
+ 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;
@@ -804,7 +1202,11 @@ impl Row {
self.columns.append(to_append);
}
pub fn replace_beginning_with(&mut self, mut line_part: Vec<TerminalCharacter>) {
- drop(self.columns.drain(0..line_part.len()));
+ 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;
}
@@ -812,7 +1214,9 @@ impl Row {
self.columns.len()
}
pub fn delete_character(&mut self, x: usize) {
- self.columns.remove(x);
+ 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![];
diff --git a/src/client/panes/plugin_pane.rs b/src/client/panes/plugin_pane.rs
index 49cf2aee..24daec08 100644
--- a/src/client/panes/plugin_pane.rs
+++ b/src/client/panes/plugin_pane.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::clippy::if_same_then_else)]
-
use crate::{common::SenderWithContext, pty_bus::VteBytes, tab::Pane, wasm_vm::PluginInstruction};
use std::{sync::mpsc::channel, unimplemented};
diff --git a/src/client/panes/terminal_character.rs b/src/client/panes/terminal_character.rs
index ce967ef8..7569e993 100644
--- a/src/client/panes/terminal_character.rs
+++ b/src/client/panes/terminal_character.rs
@@ -48,7 +48,7 @@ pub enum NamedColor {
}
impl NamedColor {
- fn to_foreground_ansi_code(&self) -> String {
+ fn to_foreground_ansi_code(self) -> String {
match self {
NamedColor::Black => format!("{}", 30),
NamedColor::Red => format!("{}", 31),
@@ -68,7 +68,7 @@ impl NamedColor {
NamedColor::BrightWhite => format!("{}", 97),
}
}
- fn to_background_ansi_code(&self) -> String {
+ fn to_background_ansi_code(self) -> String {
match self {
NamedColor::Black => format!("{}", 40),
NamedColor::Red => format!("{}", 41),
diff --git a/src/client/panes/terminal_pane.rs b/src/client/panes/terminal_pane.rs
index d2473ba3..5d63a100 100644
--- a/src/client/panes/terminal_pane.rs
+++ b/src/client/panes/terminal_pane.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::clippy::if_same_then_else)]
-
use crate::tab::Pane;
use ::nix::pty::Winsize;
use ::std::os::unix::io::RawFd;
@@ -10,7 +8,6 @@ use crate::panes::terminal_character::{
CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER,
};
use crate::pty_bus::VteBytes;
-use crate::utils::logging::debug_log_to_file;
#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Debug)]
pub enum PaneId {
@@ -38,19 +35,14 @@ impl From<Winsize> for PositionAndSize {
}
}
-#[derive(Debug)]
pub struct TerminalPane {
pub grid: Grid,
- pub alternative_grid: Option<Grid>, // for 1049h/l instructions which tell us to switch between these two
pub pid: RawFd,
- pub should_render: bool,
pub selectable: bool,
pub position_and_size: PositionAndSize,
pub position_and_size_override: Option<PositionAndSize>,
- pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "")
pub max_height: Option<usize>,
- pending_styles: CharacterStyles,
- clear_viewport_before_rendering: bool,
+ vte_parser: vte::Parser,
}
impl Pane for TerminalPane {
@@ -86,9 +78,8 @@ impl Pane for TerminalPane {
self.reflow_lines();
}
fn handle_pty_bytes(&mut self, bytes: VteBytes) {
- let mut vte_parser = vte::Parser::new();
for byte in bytes.iter() {
- vte_parser.advance(self, *byte);
+ self.vte_parser.advance(&mut self.grid, *byte);
}
}
fn cursor_coordinates(&self) -> Option<(usize, usize)> {
@@ -103,7 +94,7 @@ impl Pane for TerminalPane {
match input_bytes.as_slice() {
[27, 91, 68] => {
// left arrow
- if self.cursor_key_mode {
+ 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();
@@ -111,7 +102,7 @@ impl Pane for TerminalPane {
}
[27, 91, 67] => {
// right arrow
- if self.cursor_key_mode {
+ 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();
@@ -119,7 +110,7 @@ impl Pane for TerminalPane {
}
[27, 91, 65] => {
// up arrow
- if self.cursor_key_mode {
+ 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();
@@ -127,7 +118,7 @@ impl Pane for TerminalPane {
}
[27, 91, 66] => {
// down arrow
- if self.cursor_key_mode {
+ 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();
@@ -142,10 +133,10 @@ impl Pane for TerminalPane {
self.position_and_size_override
}
fn should_render(&self) -> bool {
- self.should_render
+ self.grid.should_render
}
fn set_should_render(&mut self, should_render: bool) {
- self.should_render = should_render;
+ self.grid.should_render = should_render;
}
fn selectable(&self) -> bool {
self.selectable
@@ -175,7 +166,7 @@ impl Pane for TerminalPane {
let buffer_lines = &self.read_buffer_as_lines();
let display_cols = self.get_columns();
let mut character_styles = CharacterStyles::new();
- if self.clear_viewport_before_rendering {
+ 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();
@@ -188,7 +179,7 @@ impl Pane for TerminalPane {
vte_output.push(EMPTY_TERMINAL_CHARACTER.character);
}
}
- self.clear_viewport_before_rendering = false;
+ self.grid.clear_viewport_before_rendering = false;
}
for (row, line) in buffer_lines.iter().enumerate() {
let x = self.get_x();
@@ -212,7 +203,7 @@ impl Pane for TerminalPane {
}
character_styles.clear();
}
- self.should_render = false;
+ self.grid.should_render = false;
Some(vte_output)
} else {
None
@@ -271,40 +262,31 @@ impl Pane for TerminalPane {
}
fn scroll_up(&mut self, count: usize) {
self.grid.move_viewport_up(count);
- self.mark_for_rerender();
+ self.grid.should_render = true;
}
fn scroll_down(&mut self, count: usize) {
self.grid.move_viewport_down(count);
- self.mark_for_rerender();
+ self.grid.should_render = true;
}
fn clear_scroll(&mut self) {
self.grid.reset_viewport();
- self.mark_for_rerender();
+ self.grid.should_render = true;
}
}
impl TerminalPane {
pub fn new(pid: RawFd, position_and_size: PositionAndSize) -> TerminalPane {
let grid = Grid::new(position_and_size.rows, position_and_size.columns);
- let pending_styles = CharacterStyles::new();
-
TerminalPane {
pid,
grid,
- alternative_grid: None,
- should_render: true,
selectable: true,
- pending_styles,
position_and_size,
position_and_size_override: None,
- cursor_key_mode: false,
- clear_viewport_before_rendering: false,
max_height: None,
+ vte_parser: vte::Parser::new(),
}
}
- pub fn mark_for_rerender(&mut self) {
- self.should_render = true;
- }
pub fn get_x(&self) -> usize {
match self.position_and_size_override {
Some(position_and_size_override) => position_and_size_override.x,
@@ -333,11 +315,7 @@ impl TerminalPane {
let rows = self.get_rows();
let columns = self.get_columns();
self.grid.change_size(rows, columns);
- if let Some(alternative_grid) = self.alternative_grid.as_mut() {
- alternative_grid.change_size(rows, columns);
- }
}
-
pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
self.grid.as_character_lines()
}
@@ -346,328 +324,4 @@ impl TerminalPane {
// (x, y)
self.grid.cursor_coordinates()
}
- pub fn rotate_scroll_region_up(&mut self, count: usize) {
- self.grid.rotate_scroll_region_up(count);
- self.mark_for_rerender();
- }
- pub fn rotate_scroll_region_down(&mut self, count: usize) {
- self.grid.rotate_scroll_region_down(count);
- self.mark_for_rerender();
- }
- fn reset_terminal_state(&mut self) {
- let rows = self.get_rows();
- let columns = self.get_columns();
- self.grid = Grid::new(rows, columns);
- self.alternative_grid = None;
- self.cursor_key_mode = false;
- }
- fn add_newline(&mut self) {
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid.add_canonical_line(pad_character);
- self.mark_for_rerender();
- }
- fn move_to_beginning_of_line(&mut self) {
- self.grid.move_cursor_to_beginning_of_line();
- }
- fn move_cursor_backwards(&mut self, count: usize) {
- self.grid.move_cursor_backwards(count);
- }
- fn _reset_all_ansi_codes(&mut self) {
- self.pending_styles.clear();
- }
-}
-
-impl vte::Perform for TerminalPane {
- fn print(&mut self, c: char) {
- // apparently, building TerminalCharacter like this without a "new" method
- // is a little faster
- let terminal_character = TerminalCharacter {
- character: c,
- styles: self.pending_styles,
- };
- self.grid.add_character(terminal_character);
- }
-
- fn execute(&mut self, byte: u8) {
- match byte {
- 8 => {
- // backspace
- self.move_cursor_backwards(1);
- }
- 9 => {
- // tab
- self.grid.advance_to_next_tabstop(self.pending_styles);
- }
- 10 => {
- // 0a, newline
- self.add_newline();
- }
- 13 => {
- // 0d, carriage return
- self.move_to_beginning_of_line();
- }
- _ => {}
- }
- }
-
- fn hook(&mut self, _params: &[i64], _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: &[i64], _intermediates: &[u8], _ignore: bool, c: char) {
- if c == 'm' {
- self.pending_styles.add_style_from_ansi_params(params);
- } else if c == 'C' {
- // move cursor forward
- let move_by = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- self.grid.move_cursor_forward_until_edge(move_by);
- } else if c == 'K' {
- // clear line (0 => right, 1 => left, 2 => all)
- if params[0] == 0 {
- let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
- char_to_replace.styles = self.pending_styles;
- self.grid
- .replace_characters_in_line_after_cursor(char_to_replace);
- } else if params[0] == 1 {
- let mut char_to_replace = EMPTY_TERMINAL_CHARACTER;
- char_to_replace.styles = self.pending_styles;
- self.grid
- .replace_characters_in_line_before_cursor(char_to_replace);
- } else if params[0] == 2 {
- self.grid.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.pending_styles;
- if params[0] == 0 {
- self.grid.clear_all_after_cursor(char_to_replace);
- } else if params[0] == 2 {
- self.grid.clear_all(char_to_replace);
- }
- // TODO: implement 1
- } else if c == 'H' {
- // goto row/col
- // we subtract 1 from the row/column because these are 1 indexed
- // (except when they are 0, in which case they should be 1
- // don't look at me, I don't make the rules)
- let (row, col) = if params.len() == 1 {
- if params[0] == 0 {
- (0, params[0] as usize)
- } else {
- (params[0] as usize - 1, params[0] as usize)
- }
- } else if params[0] == 0 {
- (0, params[1] as usize - 1)
- } else {
- (params[0] as usize - 1, params[1] as usize - 1)
- };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid.move_cursor_to(col, row, pad_character);
- } else if c == 'A' {
- // move cursor up until edge of screen
- let move_up_count = if params[0] == 0 { 1 } else { params[0] };
- self.grid.move_cursor_up(move_up_count as usize);
- } else if c == 'B' {
- // move cursor down until edge of screen
- let move_down_count = if params[0] == 0 { 1 } else { params[0] };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid
- .move_cursor_down(move_down_count as usize, pad_character);
- } else if c == 'D' {
- let move_back_count = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- self.grid.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.get(0) {
- Some(&1049) => {
- if let Some(alternative_grid) = self.alternative_grid.as_mut() {
- std::mem::swap(&mut self.grid, alternative_grid);
- }
- self.alternative_grid = None;
- self.clear_viewport_before_rendering = true;
- self.mark_for_rerender();
- }
- Some(&25) => {
- self.grid.hide_cursor();
- self.mark_for_rerender();
- }
- Some(&1) => {
- self.cursor_key_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.get(0) {
- Some(&25) => {
- self.grid.show_cursor();
- self.mark_for_rerender();
- }
- Some(&1049) => {
- let columns = self
- .position_and_size_override
- .map(|x| x.columns)
- .unwrap_or(self.position_and_size.columns);
- let rows = self
- .position_and_size_override
- .map(|x| x.rows)
- .unwrap_or(self.position_and_size.rows);
- let current_grid =
- std::mem::replace(&mut self.grid, Grid::new(rows, columns));
- self.alternative_grid = Some(current_grid);
- self.clear_viewport_before_rendering = true;
- }
- Some(&1) => {
- self.cursor_key_mode = true;
- }
- _ => {}
- };
- }
- } else if c == 'r' {
- if params.len() > 1 {
- // minus 1 because these are 1 indexed
- let top_line_index = params[0] as usize - 1;
- let bottom_line_index = params[1] as usize - 1;
- self.grid
- .set_scroll_region(top_line_index, bottom_line_index);
- self.grid.show_cursor();
- } else {
- self.grid.clear_scroll_region();
- }
- } else if c == 't' {
- // TBD - title?
- } else if c == 'n' {
- // TBD - device status report
- } else if c == 'c' {
- // TBD - identify terminal
- } else if c == 'M' {
- // delete lines if currently inside scroll region
- let line_count_to_delete = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid
- .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 = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid
- .add_empty_lines_in_scroll_region(line_count_to_add, pad_character);
- } else if c == 'q' {
- // ignore for now to run on mac
- } else if c == 'G' {
- let column = if params[0] == 0 {
- 0
- } else {
- params[0] as usize - 1
- };
- self.grid.move_cursor_to_column(column);
- } else if c == 'd' {
- // goto line
- let line = if params[0] == 0 {
- 1
- } else {
- // minus 1 because this is 1 indexed
- params[0] as usize - 1
- };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid.move_cursor_to_line(line, pad_character);
- } else if c == 'P' {
- // erase characters
- let count = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- self.grid.erase_characters(count, self.pending_styles);
- } else if c == 'X' {
- // erase characters and replace with empty characters of current style
- let count = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- self.grid
- .replace_with_empty_chars(count, self.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: i64 = *params.get(0).expect("A number of lines was expected.");
-
- if line_count >= 0 {
- self.rotate_scroll_region_up(line_count as usize);
- } else {
- self.rotate_scroll_region_down(line_count.abs() as usize);
- }
- } else if c == 'S' {
- // move scroll up
- let count = if params[0] == 0 {
- 1
- } else {
- params[0] as usize
- };
- let pad_character = EMPTY_TERMINAL_CHARACTER;
- self.grid
- .delete_lines_in_scroll_region(count, pad_character);
- // TODO: since delete_lines_in_scroll_region also adds lines, is the below redundant?
- self.grid
- .add_empty_lines_in_scroll_region(count, pad_character);
- } else {
- let _ = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params));
- }
- }
-
- fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) {
- match (byte, intermediates.get(0)) {
- (b'M', None) => {
- self.grid.move_cursor_up_with_scrolling(1);
- }
- (b'c', None) => {
- self.reset_terminal_state();
- }
- _ => {}
- }
- }
}
diff --git a/src/client/tab.rs b/src/client/tab.rs
index 35fc3915..656c6093 100644
--- a/src/client/tab.rs
+++ b/src/client/tab.rs
@@ -20,7 +20,10 @@ use std::{io::Write, sync::mpsc::channel};
use zellij_tile::data::{colors, 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
-const MIN_TERMINAL_HEIGHT: usize = 2;
+
+// 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>);
@@ -67,7 +70,6 @@ pub struct Tab {
pub send_pty_instructions: SenderWithContext<PtyInstruction>,
pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
pub send_app_instructions: SenderWithContext<AppInstruction>,
- expansion_boundary: Option<PositionAndSize>,
should_clear_display_before_rendering: bool,
pub mode_info: ModeInfo,
pub input_mode: InputMode,
@@ -208,6 +210,7 @@ pub trait Pane {
impl Tab {
// FIXME: Too many arguments here! Maybe bundle all of the senders for the whole program in a struct?
+ #[allow(clippy::too_many_arguments)]
pub fn new(
index: usize,
position: usize,
@@ -250,7 +253,6 @@ impl Tab {
send_app_instructions,
send_pty_instructions,
send_plugin_instructions,
- expansion_boundary: None,
should_clear_display_before_rendering: false,
mode_info,
input_mode,
@@ -293,9 +295,6 @@ impl Tab {
let mut new_pids = new_pids.iter();
for (layout, position_and_size) in positions_and_size {
// Just a regular terminal
- if layout.expansion_boundary {
- self.expansion_boundary = Some(*position_and_size);
- }
if let Some(plugin) = &layout.plugin {
let (pid_tx, pid_rx) = channel();
self.send_plugin_instructions
@@ -365,9 +364,7 @@ impl Tab {
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_pos_and_size = terminal_to_check.position_and_size();
let terminal_can_be_split = terminal_to_check.columns() >= MIN_TERMINAL_WIDTH
- && self.pos_and_size_is_within_expansion_boundary(terminal_pos_and_size)
&& 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));
@@ -627,19 +624,17 @@ impl Tab {
let active_terminal = self.panes.get_mut(&active_pane_id).unwrap();
active_terminal.reset_size_and_position_override();
} else {
- let expand_to = self.expansion_boundary.unwrap_or(self.full_screen_ws);
let panes = self.get_panes();
- let pane_ids_to_hide = panes.filter_map(|(&id, pane)| {
- let position_and_size_for_pane = pane.position_and_size();
- if id != active_pane_id
- && self
- .pos_and_size_is_within_expansion_boundary(position_and_size_for_pane)
- {
- Some(id)
- } else {
- None
- }
- });
+ 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
@@ -647,9 +642,9 @@ impl Tab {
} else {
let active_terminal = self.panes.get_mut(&active_pane_id).unwrap();
active_terminal.override_size_and_position(
- expand_to.x,
- expand_to.y,
- &expand_to,
+ self.full_screen_ws.x,
+ self.full_screen_ws.y,
+ &self.full_screen_ws,
);
}
}
@@ -1503,21 +1498,6 @@ impl Tab {
self.increase_pane_width_left(&terminal_id, count);
}
}
- fn pos_and_size_is_within_expansion_boundary(&self, pos_and_size: PositionAndSize) -> bool {
- match self.expansion_boundary {
- Some(expansion_boundary) => {
- pos_and_size.x >= expansion_boundary.x
- && pos_and_size.x < expansion_boundary.x + expansion_boundary.columns
- && pos_and_size.y >= expansion_boundary.y
- && pos_and_size.y < expansion_boundary.y + expansion_boundary.rows
- && pos_and_size.x + pos_and_size.columns
- <= expansion_boundary.x + expansion_boundary.columns
- && pos_and_size.y + pos_and_size.rows
- <= expansion_boundary.y + expansion_boundary.rows
- }
- None => true, // no expansion boundary, no problem
- }
- }
fn can_increase_pane_and_surroundings_right(
&self,
pane_id: &PaneId,
@@ -1533,17 +1513,11 @@ impl Tab {
}
let mut new_pos_and_size_for_pane = pane.position_and_size();
new_pos_and_size_for_pane.columns += increase_by;
- if !self.pos_and_size_is_within_expansion_boundary(new_pos_and_size_for_pane) {
- 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();
- let position_and_size_for_pane = p.position_and_size();
- self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane)
- && p.columns() > increase_by
- && p.columns() - increase_by >= p.min_width()
+ p.columns() > increase_by && p.columns() - increase_by >= p.min_width()
});
} else {
false
@@ -1564,17 +1538,11 @@ impl Tab {
}
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 !self.pos_and_size_is_within_expansion_boundary(new_pos_and_size_for_pane) {
- 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();
- let position_and_size_for_pane = p.position_and_size();
- self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane)
- && p.columns() > increase_by
- && p.columns() - increase_by >= p.min_width()
+ p.columns() > increase_by && p.columns() - increase_by >= p.min_width()
});
} else {
false
@@ -1595,17 +1563,11 @@ impl Tab {
}
let mut new_pos_and_size_for_pane = pane.position_and_size();
new_pos_and_size_for_pane.rows += increase_by;
- if !self.pos_and_size_is_within_expansion_boundary(new_pos_and_size_for_pane) {
- 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();
- let position_and_size_for_pane = p.position_and_size();
- self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane)
- && p.rows() > increase_by
- && p.rows() - increase_by >= p.min_height()
+ p.rows() > increase_by && p.rows() - increase_by >= p.min_height()
});
} else {
false
@@ -1622,17 +1584,11 @@ impl Tab {
}
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 !self.pos_and_size_is_within_expansion_boundary(new_pos_and_size_for_pane) {
- 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();
- let position_and_size_for_pane = p.position_and_size();
- self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane)
- && p.rows() > increase_by
- && p.rows() - increase_by >= p.min_height()
+ p.rows() > increase_by && p.rows() - increase_by >= p.min_height()
});
} else {
false
@@ -1649,11 +1605,9 @@ impl Tab {
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();
- let position_and_size_for_pane = p.position_and_size();
- self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane)
- && p.max_width()
- .map(|max_width| p.columns() + reduce_by <= max_width)
- .unwrap_or(true) // no max width, increase to your heart's content
+ 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
@@ -1670,11 +1624,9 @@ impl Tab {
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();
- let position_and_size_for_pane = p.position_and_size();
- self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane)
- && p.max_width()
- .map(|max_width| p.columns() + reduce_by <= max_width)
- .unwrap_or(true) // no max width, increase to your heart's content
+ 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
@@ -1691,11 +1643,9 @@ impl Tab {
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();
- let position_and_size_for_pane = p.position_and_size();
- self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane)
- && p.max_height()
- .map(|max_height| p.rows() + reduce_by <= max_height)
- .unwrap_or(true) // no max height, increase to your heart's content
+ 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
@@ -1712,11 +1662,9 @@ impl Tab {
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();
- let position_and_size_for_pane = p.position_and_size();
- self.pos_and_size_is_within_expansion_boundary(position_and_size_for_pane)
- && p.max_height()
- .map(|max_height| p.rows() + reduce_by <= max_height)
- .unwrap_or(true) // no max height, increase to your heart's content
+ 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
@@ -1724,27 +1672,18 @@ impl Tab {
}
pub fn resize_whole_tab(&mut self, new_screen_size: PositionAndSize) {
if self.fullscreen_is_active {
- // this is not ideal but until we get rid of expansion_boundary, it's a necessity
+ // this is not ideal, we can improve this
self.toggle_active_pane_fullscreen();
}
- match PaneResizer::new(&mut self.panes, &mut self.os_api)
- .resize(self.full_screen_ws, new_screen_size)
+ if let Some((column_difference, row_difference)) =
+ PaneResizer::new(&mut self.panes, &mut self.os_api)
+ .resize(self.full_screen_ws, new_screen_size)
{
- Some((column_difference, row_difference)) => {
- self.should_clear_display_before_rendering = true;
- self.expansion_boundary.as_mut().map(|expansion_boundary| {
- // TODO: this is not always accurate
- expansion_boundary.columns =
- (expansion_boundary.columns as isize + column_difference) as usize;
- expansion_boundary.rows =
- (expansion_boundary.rows as isize + row_difference) as usize;
- });
- 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;
- }
- None => {}
+ 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) {
@@ -2242,6 +2181,30 @@ impl Tab {
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
diff --git a/src/common/errors.rs b/src/common/errors.rs
index 9801428c..dd675738 100644
--- a/src/common/errors.rs
+++ b/src/common/errors.rs
@@ -1,7 +1,7 @@
//! Error context system based on a thread-local representation of the call stack, itself based on
//! the instructions that are sent between threads.
-use super::{AppInstruction, OPENCALLS};
+use super::{AppInstruction, ASYNCOPENCALLS, OPENCALLS};
use crate::pty_bus::PtyInstruction;
use crate::screen::ScreenInstruction;
@@ -72,6 +72,12 @@ pub fn handle_panic(
}
}
+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)]
pub struct ErrorContext {
@@ -95,7 +101,9 @@ impl ErrorContext {
break;
}
}
- OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self);
+ ASYNCOPENCALLS
+ .try_with(|ctx| *ctx.borrow_mut() = *self)
+ .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self));
}
}
@@ -188,6 +196,8 @@ pub enum ScreenContext {
Quit,
ScrollUp,
ScrollDown,
+ PageScrollUp,
+ PageScrollDown,
ClearScroll,
CloseFocusedPane,
ToggleActiveTerminalFullscreen,
@@ -230,6 +240,8 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::Quit => ScreenContext::Quit,
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 => {
diff --git a/src/common/input/actions.rs b/src/common/input/actions.rs
index 47a5f763..3bcadf15 100644
--- a/src/common/input/actions.rs
+++ b/src/common/input/actions.rs
@@ -33,6 +33,10 @@ pub enum Action {
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,
/// Open a new pane in the specified direction (relative to focus).
diff --git a/src/common/input/config.rs b/src/common/input/config.rs
index 4bb9e10f..542116d9 100644
--- a/src/common/input/config.rs
+++ b/src/common/input/config.rs
@@ -51,7 +51,6 @@ impl Config {
}
/// Deserializes from given path.
- #[allow(unused_must_use)]
pub fn new(path: &Path) -> ConfigResult {
match File::open(path) {
Ok(mut file) => {
@@ -80,20 +79,23 @@ impl Config {
/// Entry point of the configuration
#[cfg(not(test))]
- pub fn from_cli_config(cli_config: Option<ConfigCli>) -> ConfigResult {
+ pub fn from_cli_config(
+ location: Option<PathBuf>,
+ cli_config: Option<ConfigCli>,
+ ) -> ConfigResult {
+ if let Some(path) = location {
+ return Config::new(&path);
+ }
+
match cli_config {
Some(ConfigCli::Config { clean, .. }) if clean => Ok(Config::default()),
- Some(ConfigCli::Config { path, .. }) if path.is_some() => {
- Ok(Config::new(&path.unwrap())?)
- }
Some(_) | None => Ok(Config::from_default_path()?),
}
}
- //#[allow(unused_must_use)]
/// In order not to mess up tests from changing configurations
#[cfg(test)]
- pub fn from_cli_config(_: Option<ConfigCli>) -> ConfigResult {
+ pub fn from_cli_config(_: Option<PathBuf>, _: Option<ConfigCli>) -> ConfigResult {
Ok(Config::default())
}
}
@@ -139,19 +141,15 @@ mod config_test {
#[test]
fn clean_option_equals_default_config() {
- let no_file = PathBuf::from(r"../fixtures/config/config.yamlll");
- let cli_config = ConfigCli::Config {
- path: Some(no_file),
- clean: true,
- };
- let config = Config::from_cli_config(Some(cli_config)).unwrap();
+ let cli_config = ConfigCli::Config { clean: true };
+ let config = Config::from_cli_config(None, Some(cli_config)).unwrap();
let default = Config::default();
assert_eq!(config, default);
}
#[test]
fn no_config_option_file_equals_default_config() {
- let config = Config::from_cli_config(None).unwrap();
+ let config = Config::from_cli_config(None, None).unwrap();
let default = Config::default();
assert_eq!(config, default);
}
diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs
index 2484f08c..bbb594a3 100644
--- a/src/common/input/handler.rs
+++ b/src/common/input/handler.rs
@@ -59,9 +59,6 @@ impl InputHandler {
fn handle_input(&mut self) {
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
err_ctx.add_call(ContextType::StdinHandler);
- self.send_pty_instructions.update(err_ctx);
- self.send_app_instructions.update(err_ctx);
- self.send_screen_instructions.update(err_ctx);
let keybinds = self.config.keybinds.clone();
let alt_left_bracket = vec![27, 91];
loop {
@@ -188,6 +185,16 @@ impl InputHandler {
.send(ScreenInstruction::ScrollDown)
.unwrap();
}
+ Action::PageScrollUp => {
+ self.send_screen_instructions
+ .send(ScreenInstruction::PageScrollUp)
+ .unwrap();
+ }
+ Action::PageScrollDown => {
+ self.send_screen_instructions
+ .send(ScreenInstruction::PageScrollDown)
+ .unwrap();
+ }
Action::ToggleFocusFullscreen => {
self.send_screen_instructions
.send(ScreenInstruction::ToggleActiveTerminalFullscreen)
@@ -298,6 +305,7 @@ pub fn get_mode_info(mode: InputMode) -> ModeInfo {
}
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()));
diff --git a/src/common/input/keybinds.rs b/src/common/input/keybinds.rs
index 8a064970..8e04c44a 100644
--- a/src/common/input/keybinds.rs
+++ b/src/common/input/keybinds.rs
@@ -7,14 +7,30 @@ use serde::Deserialize;
use strum::IntoEnumIterator;
use zellij_tile::data::*;
+/// Used in the config struct
#[derive(Clone, Debug, PartialEq)]
pub struct Keybinds(HashMap<InputMode, ModeKeybinds>);
#[derive(Clone, Debug, Default, PartialEq)]
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(HashMap<InputMode, Vec<KeyActionFromYaml>>);
+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),
+ // TODO: use the enum
+ //Unbind(UnbindFromYaml),
+}
/// Intermediate struct used for deserialisation
#[derive(Clone, Debug, PartialEq, Deserialize)]
@@ -23,6 +39,22 @@ pub struct KeyActionFromYaml {
key: Vec<Key>,
}
+/// Intermediate struct used for deserialisation
+#[derive(Clone, Debug, PartialEq, Eq, Hash, 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 {
+ All(bool),
+ // TODO@a-kenji: use the enum
+ //Keys(Vec<Key>),
+}
+
impl Default for Keybinds {
fn default() -> Keybinds {
let mut defaults = Keybinds::new();
@@ -41,9 +73,16 @@ impl Keybinds {
Keybinds(HashMap::<InputMode, ModeKeybinds>::new())
}
- pub fn get_default_keybinds_with_config(keybinds: Option<KeybindsFromYaml>) -> Keybinds {
- let default_keybinds = Keybinds::default();
- if let Some(keybinds) = keybinds {
+ 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) => Keybinds::default(),
+ },
+ None => Keybinds::default(),
+ };
+
+ if let Some(keybinds) = from_yaml {
default_keybinds.merge_keybinds(Keybinds::from(keybinds))
} else {
default_keybinds
@@ -301,6 +340,11 @@ impl Keybinds {
defaults.insert(Key::Char('j'), vec![Action::ScrollDown]);
defaults.insert(Key::Char('k'), vec![Action::ScrollUp]);
+ defaults.insert(Key::Ctrl('f'), vec![Action::PageScrollDown]);
+ defaults.insert(Key::Ctrl('b'), vec![Action::PageScrollUp]);
+ defaults.insert(Key::PageDown, vec![Action::PageScrollDown]);
+ defaults.insert(Key::PageUp, vec![Action::PageScrollUp]);
+
defaults.insert(Key::Down, vec![Action::ScrollDown]);
defaults.insert(Key::Up, vec![Action::ScrollUp]);
@@ -383,7 +427,7 @@ impl From<KeybindsFromYaml> for Keybinds {
for mode in InputMode::iter() {
let mut mode_keybinds = ModeKeybinds::new();
- for key_action in keybinds_from_yaml.0.get(&mode).iter() {
+ 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()));
}
@@ -409,6 +453,21 @@ impl From<KeyActionFromYaml> for ModeKeybinds {
}
}
+// Currently an enum for future use
+impl From<KeyActionUnbind> for ModeKeybinds {
+ fn from(key_action_unbind: KeyActionUnbind) -> ModeKeybinds {
+ match key_action_unbind {
+ KeyActionUnbind::KeyAction(key_action) => ModeKeybinds::from(key_action),
+ }
+ }
+}
+
+impl Default for Unbind {
+ fn default() -> Unbind {
+ Unbind::All(false)
+ }
+}
+
// The unit test location.
#[cfg(test)]
#[path = "./unit/keybinds_test.rs"]
diff --git a/src/common/input/unit/keybinds_test.rs b/src/common/input/unit/keybinds_test.rs
index 6411d017..e0d78b62 100644
--- a/src/common/input/unit/keybinds_test.rs
+++ b/src/common/input/unit/keybinds_test.rs
@@ -124,12 +124,25 @@ fn from_keyaction_from_yaml_to_mode_keybindings() {
assert_eq!(expected, ModeKeybinds::from(keyaction));
}
-//#[test]
-//fn from_keybinds_from_yaml_to_keybinds(){
-//let mut keybinds_from_yaml = KeybindsFromYaml(HashMap<InputMode, Vec<KeyActionFromYaml>>);
-//let actions = vec![Action::NoOp, Action::GoToTab(1), ];
-//let keyaction = KeyActionFromYaml {
-//action : actions.clone(),
-//key : vec![ Key::F(1), Key::Backspace , Key::Char('t'), ],
-//};
-//}
+#[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());
+}
+
+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::new());
+}
diff --git a/src/common/install.rs b/src/common/install.rs
index 8b070848..86fdd269 100644
--- a/src/common/install.rs
+++ b/src/common/install.rs
@@ -1,12 +1,41 @@
+use std::{fs, path::Path};
+
+const VERSION: &str = env!("CARGO_PKG_VERSION");
+
#[macro_export]
macro_rules! asset_map {
($($src:literal => $dst:literal),+ $(,)?) => {
{
let mut assets = std::collections::HashMap::new();
$(
- assets.insert($dst, include_bytes!(concat!("../", $src)).to_vec());
+ assets.insert($dst, include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $src)).to_vec());
)+
assets
}
}
}
+
+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);
+ fs::create_dir_all(path.parent().unwrap()).unwrap();
+ if out_of_date || !path.exists() {
+ fs::write(path, bytes).expect("Failed to install default assets!");
+ }
+ }
+}
diff --git a/src/common/mod.rs b/src/common/mod.rs
index b3bcec9b..3bb5ca88 100644
--- a/src/common/mod.rs
+++ b/src/common/mod.rs
@@ -16,6 +16,7 @@ use std::thread;
use std::{collections::HashMap, fs};
use std::{
collections::HashSet,
+ env,
io::Write,
str::FromStr,
sync::{Arc, Mutex},
@@ -26,10 +27,15 @@ use crate::common::input::config::Config;
use crate::layout::Layout;
use crate::panes::PaneId;
use colors_transform::{Color, Rgb};
+use async_std::task_local;
use command_is_executing::CommandIsExecuting;
use directories_next::ProjectDirs;
-use errors::{AppContext, ContextType, ErrorContext, PluginContext, PtyContext, ScreenContext};
+use errors::{
+ get_current_ctx, AppContext, ContextType, ErrorContext, PluginContext, PtyContext,
+ ScreenContext,
+};
use input::handler::input_loop;
+use install::populate_data_dir;
use os_input_output::OsApi;
use pty_bus::{PtyBus, PtyInstruction};
use screen::{Screen, ScreenInstruction};
@@ -76,32 +82,23 @@ enum SenderType<T: Clone> {
/// synchronously or asynchronously depending on the underlying [`SenderType`].
#[derive(Clone)]
pub struct SenderWithContext<T: Clone> {
- err_ctx: ErrorContext,
sender: SenderType<T>,
}
impl<T: Clone> SenderWithContext<T> {
- fn new(err_ctx: ErrorContext, sender: SenderType<T>) -> Self {
- Self { err_ctx, sender }
+ 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, self.err_ctx)),
- SenderType::SyncSender(ref s) => s.send((event, self.err_ctx)),
+ SenderType::Sender(ref s) => s.send((event, err_ctx)),
+ SenderType::SyncSender(ref s) => s.send((event, err_ctx)),
}
}
-
- /// Updates this [`SenderWithContext`]'s [`ErrorContext`]. This is the way one adds
- /// a call to the error context.
- ///
- /// Updating [`ErrorContext`]s works in this way so that these contexts are only ever
- /// allocated on the stack (which is thread-specific), and not on the heap.
- pub fn update(&mut self, new_ctx: ErrorContext) {
- self.err_ctx = new_ctx;
- }
}
unsafe impl<T: Clone> Send for SenderWithContext<T> {}
@@ -113,6 +110,12 @@ thread_local!(
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`].
+ static ASYNCOPENCALLS: RefCell<ErrorContext> = RefCell::default()
+}
+
/// Instructions related to the entire application.
#[derive(Clone)]
pub enum AppInstruction {
@@ -130,7 +133,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.write(take_snapshot.as_bytes())
.unwrap();
- let config = Config::from_cli_config(opts.config)
+ env::set_var(&"ZELLIJ", "0");
+
+ let config = Config::from_cli_config(opts.config, opts.option)
.map_err(|e| {
eprintln!("There was an error in the config file:\n{}", e);
std::process::exit(1);
@@ -144,25 +149,23 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let (send_screen_instructions, receive_screen_instructions): ChannelWithContext<
ScreenInstruction,
> = mpsc::channel();
- let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
- let mut send_screen_instructions =
- SenderWithContext::new(err_ctx, SenderType::Sender(send_screen_instructions));
+ let send_screen_instructions =
+ SenderWithContext::new(SenderType::Sender(send_screen_instructions));
let (send_pty_instructions, receive_pty_instructions): ChannelWithContext<PtyInstruction> =
mpsc::channel();
- let mut send_pty_instructions =
- SenderWithContext::new(err_ctx, SenderType::Sender(send_pty_instructions));
+ let send_pty_instructions = SenderWithContext::new(SenderType::Sender(send_pty_instructions));
let (send_plugin_instructions, receive_plugin_instructions): ChannelWithContext<
PluginInstruction,
> = mpsc::channel();
let send_plugin_instructions =
- SenderWithContext::new(err_ctx, SenderType::Sender(send_plugin_instructions));
+ SenderWithContext::new(SenderType::Sender(send_plugin_instructions));
let (send_app_instructions, receive_app_instructions): SyncChannelWithContext<AppInstruction> =
mpsc::sync_channel(0);
let send_app_instructions =
- SenderWithContext::new(err_ctx, SenderType::SyncSender(send_app_instructions));
+ SenderWithContext::new(SenderType::SyncSender(send_app_instructions));
let mut pty_bus = PtyBus::new(
receive_pty_instructions,
@@ -172,12 +175,22 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
opts.debug,
);
+ // Determine and initialize the data directory
+ let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
+ let data_dir = opts
+ .data_dir
+ .unwrap_or_else(|| project_dirs.data_dir().to_path_buf());
+ populate_data_dir(&data_dir);
+
// 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 maybe_layout = opts.layout.or(default_layout).map(Layout::new);
+ let maybe_layout = opts
+ .layout
+ .or(default_layout)
+ .map(|p| Layout::new(&p, &data_dir));
#[cfg(not(test))]
std::panic::set_hook({
@@ -199,7 +212,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.recv()
.expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Pty(PtyContext::from(&event)));
- pty_bus.send_screen_instructions.update(err_ctx);
match event {
PtyInstruction::SpawnTerminal(file_to_open) => {
let pid = pty_bus.spawn_terminal(file_to_open);
@@ -282,8 +294,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.recv()
.expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Screen(ScreenContext::from(&event)));
- screen.send_app_instructions.update(err_ctx);
- screen.send_pty_instructions.update(err_ctx);
match event {
ScreenInstruction::PtyBytes(pid, vte_bytes) => {
let active_tab = screen.get_active_tab_mut().unwrap();
@@ -369,6 +379,18 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.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()
@@ -443,9 +465,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let wasm_thread = thread::Builder::new()
.name("wasm".to_string())
.spawn({
- let mut send_pty_instructions = send_pty_instructions.clone();
- let mut send_screen_instructions = send_screen_instructions.clone();
- let mut send_app_instructions = send_app_instructions.clone();
+ let send_pty_instructions = send_pty_instructions.clone();
+ let send_screen_instructions = send_screen_instructions.clone();
+ let send_app_instructions = send_app_instructions.clone();
let store = Store::default();
let mut plugin_id = 0;
@@ -455,14 +477,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.recv()
.expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
- send_screen_instructions.update(err_ctx);
- send_pty_instructions.update(err_ctx);
- send_app_instructions.update(err_ctx);
match event {
PluginInstruction::Load(pid_tx, path) => {
- let project_dirs =
- ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
- let plugin_dir = project_dirs.data_dir().join("plugins/");
+ 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")))
@@ -566,16 +583,14 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.name("ipc_server".to_string())
.spawn({
use std::io::Read;
- let mut send_pty_instructions = send_pty_instructions.clone();
- let mut send_screen_instructions = send_screen_instructions.clone();
+ let send_pty_instructions = send_pty_instructions.clone();
+ let send_screen_instructions = send_screen_instructions.clone();
move || {
std::fs::remove_file(ZELLIJ_IPC_PIPE).ok();
let listener = std::os::unix::net::UnixListener::bind(ZELLIJ_IPC_PIPE)
.expect("could not listen on ipc socket");
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
err_ctx.add_call(ContextType::IpcServer);
- send_pty_instructions.update(err_ctx);
- send_screen_instructions.update(err_ctx);
for stream in listener.incoming() {
match stream {
@@ -647,8 +662,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.expect("failed to receive app instruction on channel");
err_ctx.add_call(ContextType::App(AppContext::from(&app_instruction)));
- send_screen_instructions.update(err_ctx);
- send_pty_instructions.update(err_ctx);
match app_instruction {
AppInstruction::Exit => {
break;
@@ -662,7 +675,11 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let _ = wasm_thread.join();
os_input.unset_raw_mode(0);
let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
- let error = format!("{}\n{}", goto_start_of_last_line, backtrace);
+ 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())
diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs
index 013b9f12..35d77fdd 100644
--- a/src/common/os_input_output.rs
+++ b/src/common/os_input_output.rs
@@ -84,15 +84,10 @@ fn handle_command_exit(mut child: Child) {
}
for signal in signals.pending() {
- // FIXME: We need to handle more signals here!
- #[allow(clippy::single_match)]
- match signal {
- SIGINT => {
- child.kill().unwrap();
- child.wait().unwrap();
- break 'handle_exit;
- }
- _ => {}
+ if let SIGINT = signal {
+ child.kill().unwrap();
+ child.wait().unwrap();
+ break 'handle_exit;
}
}
}
diff --git a/src/common/pty_bus.rs b/src/common/pty_bus.rs
index 2cab8ae8..2c132228 100644
--- a/src/common/pty_bus.rs
+++ b/src/common/pty_bus.rs
@@ -8,11 +8,11 @@ use ::std::sync::mpsc::Receiver;
use ::std::time::{Duration, Instant};
use std::path::PathBuf;
-use super::{ScreenInstruction, SenderWithContext, OPENCALLS};
+use super::{ScreenInstruction, SenderWithContext};
use crate::os_input_output::OsApi;
use crate::utils::logging::debug_to_file;
use crate::{
- errors::{ContextType, ErrorContext},
+ errors::{get_current_ctx, ContextType, ErrorContext},
panes::PaneId,
};
use crate::{layout::Layout, wasm_vm::PluginInstruction};
@@ -43,7 +43,7 @@ impl Stream for ReadFromPid {
// indicates end of file
Poll::Ready(None)
} else {
- let res = Some(read_buffer[..=*res].to_vec());
+ let res = Some(read_buffer[..*res].to_vec());
Poll::Ready(res)
}
}
@@ -89,15 +89,14 @@ pub struct PtyBus {
fn stream_terminal_bytes(
pid: RawFd,
- mut send_screen_instructions: SenderWithContext<ScreenInstruction>,
+ send_screen_instructions: SenderWithContext<ScreenInstruction>,
os_input: Box<dyn OsApi>,
debug: bool,
) -> JoinHandle<()> {
- let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
+ let mut err_ctx = get_current_ctx();
task::spawn({
async move {
err_ctx.add_call(ContextType::AsyncTask);
- send_screen_instructions.update(err_ctx);
let mut terminal_bytes = ReadFromPid::new(&pid, os_input);
let mut last_byte_receive_time: Option<Instant> = None;
diff --git a/src/common/screen.rs b/src/common/screen.rs
index 563221e9..03cfa0bf 100644
--- a/src/common/screen.rs
+++ b/src/common/screen.rs
@@ -38,6 +38,8 @@ pub enum ScreenInstruction {
Quit,
ScrollUp,
ScrollDown,
+ PageScrollUp,
+ PageScrollDown,
ClearScroll,
CloseFocusedPane,
ToggleActiveTerminalFullscreen,
@@ -83,7 +85,9 @@ pub struct Screen {
}
impl Screen {
+ // FIXME: This lint needs actual fixing! Maybe by bundling the Senders
/// Creates and returns a new [`Screen`].
+ #[allow(clippy::too_many_arguments)]
pub fn new(
receive_screen_instructions: Receiver<(ScreenInstruction, ErrorContext)>,
send_pty_instructions: SenderWithContext<PtyInstruction>,
diff --git a/src/common/utils/logging.rs b/src/common/utils/logging.rs
index d08639aa..06827bfc 100644
--- a/src/common/utils/logging.rs
+++ b/src/common/utils/logging.rs
@@ -47,8 +47,7 @@ pub fn _debug_log_to_file_pid_3(message: String, pid: RawFd) -> io::Result<()> {
}
}
-#[allow(dead_code)]
-pub fn delete_log_file() -> io::Result<()> {
+pub fn _delete_log_file() -> io::Result<()> {
if fs::metadata(ZELLIJ_TMP_LOG_FILE).is_ok() {
fs::remove_file(ZELLIJ_TMP_LOG_FILE)
} else {
@@ -56,8 +55,7 @@ pub fn delete_log_file() -> io::Result<()> {
}
}
-#[allow(dead_code)]
-pub fn delete_log_dir() -> io::Result<()> {
+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 {
diff --git a/src/common/utils/shared.rs b/src/common/utils/shared.rs
index 5d01e78e..de15d784 100644
--- a/src/common/utils/shared.rs
+++ b/src/common/utils/shared.rs
@@ -22,9 +22,9 @@ pub fn adjust_to_size(s: &str, rows: usize, columns: usize) -> String {
if actual_len > columns {
let mut line = String::from(l);
line.truncate(columns);
- return line;
+ line
} else {
- return [l, &str::repeat(" ", columns - ansi_len(l))].concat();
+ [l, &str::repeat(" ", columns - ansi_len(l))].concat()
}
})
.chain(iter::repeat(str::repeat(" ", columns)))
diff --git a/src/main.rs b/src/main.rs
index 7d8ca6d5..ec249e8a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,23 +1,10 @@
-#[cfg(test)]
-mod tests;
-
mod cli;
mod common;
+#[cfg(test)]
+mod tests;
// TODO mod server;
mod client;
-use client::{boundaries, layout, panes, tab};
-use common::{
- command_is_executing, errors, os_input_output, pty_bus, screen, start, utils, wasm_vm,
- ApiCommand,
-};
-use directories_next::ProjectDirs;
-
-use std::os::unix::net::UnixStream;
-use std::{fs, io::Write};
-
-use structopt::StructOpt;
-
use crate::cli::CliArgs;
use crate::command_is_executing::CommandIsExecuting;
use crate::os_input_output::get_os_input;
@@ -25,37 +12,16 @@ use crate::utils::{
consts::{ZELLIJ_IPC_PIPE, ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR},
logging::*,
};
+use client::{boundaries, layout, panes, tab};
+use common::{
+ command_is_executing, errors, os_input_output, pty_bus, screen, start, utils, wasm_vm,
+ ApiCommand,
+};
+use std::io::Write;
+use std::os::unix::net::UnixStream;
+use structopt::StructOpt;
pub fn main() {
- // First run installation of default plugins & layouts
- let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
- let data_dir = project_dirs.data_dir();
- let mut assets = asset_map! {
- "assets/layouts/default.yaml" => "layouts/default.yaml",
- "assets/layouts/strider.yaml" => "layouts/strider.yaml",
- };
- // FIXME: This is a hideous hack and I hate it (a lot)
- #[cfg(not(feature = "publish"))]
- assets.extend(asset_map! {
- "target/status-bar.wasm" => "plugins/status-bar.wasm",
- "target/tab-bar.wasm" => "plugins/tab-bar.wasm",
- "target/strider.wasm" => "plugins/strider.wasm",
- });
- #[cfg(feature = "publish")]
- 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",
- });
-
- for (path, bytes) in assets {
- let path = data_dir.join(path);
- fs::create_dir_all(path.parent().unwrap()).unwrap();
- if !path.exists() {
- fs::write(path, bytes).expect("Failed to install default assets!");
- }
- }
-
let opts = CliArgs::from_args();
if let Some(split_dir) = opts.split {
match split_dir {
diff --git a/src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml b/src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml
deleted file mode 100644
index 9227c080..00000000
--- a/src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml
+++ /dev/null
@@ -1,19 +0,0 @@
----
-direction: Horizontal
-parts:
- - direction: Vertical
- split_size:
- Percent: 40
- - direction: Vertical
- parts:
- - direction: Horizontal
- split_size:
- Percent: 40
- - direction: Horizontal
- expansion_boundary: true
- - direction: Horizontal
- split_size:
- Percent: 40
- - direction: Vertical
- split_size:
- Percent: 40
diff --git a/src/tests/integration/basic.rs b/src/tests/integration/basic.rs
index 377e25b2..e57f8c75 100644
--- a/src/tests/integration/basic.rs
+++ b/src/tests/integration/basic.rs
@@ -3,8 +3,9 @@ use ::insta::assert_snapshot;
use crate::tests::fakes::FakeInputOutput;
use crate::tests::utils::commands::{
- PANE_MODE, QUIT, SCROLL_DOWN_IN_SCROLL_MODE, SCROLL_MODE, SCROLL_UP_IN_SCROLL_MODE,
- SPAWN_TERMINAL_IN_PANE_MODE, SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
+ PANE_MODE, QUIT, SCROLL_DOWN_IN_SCROLL_MODE, SCROLL_MODE, SCROLL_PAGE_DOWN_IN_SCROLL_MODE,
+ SCROLL_PAGE_UP_IN_SCROLL_MODE, SCROLL_UP_IN_SCROLL_MODE, SPAWN_TERMINAL_IN_PANE_MODE,
+ SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE,
};
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
@@ -238,6 +239,67 @@ pub fn scrolling_down_inside_a_pane() {
}
#[test]
+pub fn scrolling_page_up_inside_a_pane() {
+ let fake_win_size = PositionAndSize {
+ columns: 121,
+ rows: 20,
+ x: 0,
+ y: 0,
+ };
+ let mut fake_input_output = get_fake_os_input(&fake_win_size);
+ fake_input_output.add_terminal_input(&[
+ &PANE_MODE,
+ &SPLIT_DOWN_IN_PANE_MODE,
+ &SPLIT_RIGHT_IN_PANE_MODE,
+ &SCROLL_MODE,
+ &SCROLL_PAGE_UP_IN_SCROLL_MODE,
+ &QUIT,
+ ]);
+ start(Box::new(fake_input_output.clone()), CliArgs::default());
+ let output_frames = fake_input_output
+ .stdout_writer
+ .output_frames
+ .lock()
+ .unwrap();
+ let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size);
+ let snapshot_before_quit =
+ get_next_to_last_snapshot(snapshots).expect("could not find snapshot");
+ assert_snapshot!(snapshot_before_quit);
+}
+
+#[test]
+pub fn scrolling_page_down_inside_a_pane() {
+ let fake_win_size = PositionAndSize {
+ columns: 121,
+ rows: 20,
+ x: 0,
+ y: 0,
+ };
+ let mut fake_input_output = get_fake_os_input(&fake_win_size);
+ fake_input_output.add_terminal_input(&[
+ &PANE_MODE,
+ &SPLIT_DOWN_IN_PANE_MODE,
+ &SPLIT_RIGHT_IN_PANE_MODE,
+ &SCROLL_MODE,
+ &SCROLL_PAGE_UP_IN_SCROLL_MODE,
+ &SCROLL_PAGE_UP_IN_SCROLL_MODE,
+ &SCROLL_PAGE_DOWN_IN_SCROLL_MODE,
+ &SCROLL_PAGE_DOWN_IN_SCROLL_MODE,
+ &QUIT,
+ ]);
+ start(Box::new(fake_input_output.clone()), CliArgs::default());
+ let output_frames = fake_input_output
+ .stdout_writer
+ .output_frames
+ .lock()
+ .unwrap();
+ let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size);
+ let snapshot_before_quit =
+ get_next_to_last_snapshot(snapshots).expect("could not find snapshot");
+ assert_snapshot!(snapshot_before_quit);
+}
+
+#[test]
pub fn max_panes() {
// with the --max-panes option, we only allow a certain amount of panes on screen
// simultaneously, new panes beyond this limit will close older panes on screen
diff --git a/src/tests/integration/expansion_boundary.rs b/src/tests/integration/expansion_boundary.rs
deleted file mode 100644
index c68c1d5a..00000000
--- a/src/tests/integration/expansion_boundary.rs
+++ /dev/null
@@ -1,117 +0,0 @@
-use insta::assert_snapshot;
-use std::path::PathBuf;
-
-use crate::panes::PositionAndSize;
-use crate::tests::fakes::FakeInputOutput;
-use crate::tests::utils::commands::{
- PANE_MODE, QUIT, RESIZE_DOWN_IN_RESIZE_MODE, RESIZE_MODE, SLEEP, SPAWN_TERMINAL_IN_PANE_MODE,
- TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE,
-};
-use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
-use crate::{start, CliArgs};
-
-fn get_fake_os_input(fake_win_size: &PositionAndSize) -> FakeInputOutput {
- FakeInputOutput::new(fake_win_size.clone())
-}
-
-#[test]
-pub fn new_panes_are_open_inside_expansion_border() {
- let fake_win_size = PositionAndSize {
- columns: 121,
- rows: 50,
- x: 0,
- y: 0,
- };
- let mut fake_input_output = get_fake_os_input(&fake_win_size);
- fake_input_output.add_terminal_input(&[
- &PANE_MODE,
- &SPAWN_TERMINAL_IN_PANE_MODE,
- &SLEEP,
- &QUIT,
- ]);
- let mut opts = CliArgs::default();
- opts.layout = Some(PathBuf::from(
- "src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml",
- ));
-
- start(Box::new(fake_input_output.clone()), opts);
- let output_frames = fake_input_output
- .stdout_writer
- .output_frames
- .lock()
- .unwrap();
- let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size);
-
- let next_to_last_snapshot = get_next_to_last_snapshot(snapshots).unwrap();
- assert_snapshot!(next_to_last_snapshot);
-}
-
-#[test]
-pub fn resize_pane_inside_expansion_border() {
- let fake_win_size = PositionAndSize {
- columns: 121,
- rows: 50,
- x: 0,
- y: 0,
- };
- let mut fake_input_output = get_fake_os_input(&fake_win_size);
- fake_input_output.add_terminal_input(&[
- &PANE_MODE,
- &SPAWN_TERMINAL_IN_PANE_MODE,
- &RESIZE_MODE,
- &RESIZE_DOWN_IN_RESIZE_MODE,
- &SLEEP,
- &QUIT,
- ]);
- let mut opts = CliArgs::default();
- opts.layout = Some(PathBuf::from(
- "src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml",
- ));
-
- start(Box::new(fake_input_output.clone()), opts);
- let output_frames = fake_input_output
- .stdout_writer
- .output_frames
- .lock()
- .unwrap();
- let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size);
-
- let next_to_last_snapshot = get_next_to_last_snapshot(snapshots).unwrap();
- assert_snapshot!(next_to_last_snapshot);
-}
-
-#[test]
-pub fn toggling_fullcsreen_in_expansion_border_expands_only_until_border() {
- let fake_win_size = PositionAndSize {
- columns: 121,
- rows: 50,
- x: 0,
- y: 0,
- };
- let mut fake_input_output = get_fake_os_input(&fake_win_size);
- fake_input_output.add_terminal_input(&[
- &PANE_MODE,
- &SPAWN_TERMINAL_IN_PANE_MODE,
- &TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE,
- &SLEEP,
- &QUIT,
- ]);
- let mut opts = CliArgs::default();
- opts.layout = Some(PathBuf::from(
- "src/tests/fixtures/layouts/expansion-boundary-in-the-middle.yaml",
- ));
-
- start(Box::new(fake_input_output.clone()), opts);
- let output_frames = fake_input_output
- .stdout_writer
- .output_frames
- .lock()
- .unwrap();
- let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size);
-
- let next_to_last_snapshot = get_next_to_last_snapshot(snapshots).unwrap();
- assert_snapshot!(next_to_last_snapshot);
-}
-
-// TODO:
-// * fullscreen with expansion boundary
diff --git a/src/tests/integration/mod.rs b/src/tests/integration/mod.rs
index 83cb7d22..0511fc9c 100644
--- a/src/tests/integration/mod.rs
+++ b/src/tests/integration/mod.rs
@@ -1,7 +1,6 @@
pub mod basic;
pub mod close_pane;
pub mod compatibility;
-pub mod expansion_boundary;
pub mod layouts;
pub mod move_focus_down;
pub mod move_focus_left;
diff --git a/src/tests/integration/resize_down.rs b/src/tests/integration/resize_down.rs
index 4957b33e..773eaa86 100644
--- a/src/tests/integration/resize_down.rs
+++ b/src/tests/integration/resize_down.rs
@@ -98,17 +98,18 @@ pub fn resize_down_with_panes_above_and_below() {
// ┌───────────┐ ┌───────────┐
// │ │ │ │
// │ │ │ │
- // ├───────────┤ │ │
- // │███████████│ ==resize=down==> ├───────────┤
- // │███████████│ │███████████│
// ├───────────┤ ├───────────┤
- // │ │ │ │
+ // │███████████│ ==resize=down==> │███████████│
+ // │███████████│ │███████████│
+ // │███████████│ │███████████│
+ // ├───────────┤ │███████████│
+ // │ │ ├───────────┤
// │ │ │ │
// └───────────┘ └───────────┘
// █ == focused pane
let fake_win_size = PositionAndSize {
columns: 121,
- rows: 20,
+ rows: 25,
x: 0,
y: 0,
};
@@ -599,7 +600,7 @@ pub fn cannot_resize_down_when_pane_below_is_at_minimum_height() {
// █ == focused pane
let fake_win_size = PositionAndSize {
columns: 121,
- rows: 5,
+ rows: 7,
x: 0,
y: 0,
};
diff --git a/src/tests/integration/resize_up.rs b/src/tests/integration/resize_up.rs
index 9d4dd05a..5f720172 100644
--- a/src/tests/integration/resize_up.rs
+++ b/src/tests/integration/resize_up.rs
@@ -594,7 +594,7 @@ pub fn cannot_resize_up_when_pane_above_is_at_minimum_height() {
// █ == focused pane
let fake_win_size = PositionAndSize {
columns: 121,
- rows: 5,
+ rows: 7,
x: 0,
y: 0,
};
diff --git a/src/tests/integration/snapshots/zellij__tests__integration__basic__scrolling_page_down_inside_a_pane.snap b/src/tests/integration/snapshots/zellij__tests__integration__basic__scrolling_page_down_inside_a_pane.snap
new file mode 100644
index 00000000..8f4ec6e2
--- /dev/null
+++ b/src/tests/integration/snapshots/zellij__tests__integration__basic__scrolling_page_down_inside_a_pane.snap
@@ -0,0 +1,25 @@
+---
+source: src/tests/integration/basic.rs
+expression: snapshot_before_quit
+
+---
+line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+prompt $
+────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+a │line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+a │line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+a │line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+prompt $ │prompt $ █
diff --git a/src/tests/integration/snapshots/zellij__tests__integration__basic__scrolling_page_up_inside_a_pane.snap b/src/tests/integration/snapshots/zellij__tests__integration__basic__scrolling_page_up_inside_a_pane.snap
new file mode 100644
index 00000000..d37580c6
--- /dev/null
+++ b/src/tests/integration/snapshots/zellij__tests__integration__basic__scrolling_page_up_inside_a_pane.snap
@@ -0,0 +1,25 @@
+---
+source: src/tests/integration/basic.rs
+expression: snapshot_before_quit
+
+---
+line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+prompt $
+────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line4-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+a │line5-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line6-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line7-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+a │line8-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line9-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa│line10-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+a │line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+prompt $ │line12-bb█bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
diff --git a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__new_panes_are_open_inside_expansion_border.snap b/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__new_panes_are_open_inside_expansion_border.snap
deleted file mode 100644
index 661eaf96..00000000
--- a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__new_panes_are_open_inside_expansion_border.snap
+++ /dev/null
@@ -1,55 +0,0 @@
----
-source: src/tests/integration/expansion_boundary.rs
-expression: next_to_last_snapshot
-
----
-line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-prompt $
-───────────────────────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────
-line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbb│line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbb│line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbb│line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│prompt $ │line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb├─────────────────────────┤line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-prompt $ │prompt $ █ │prompt $
-───────────────────────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────
-line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-prompt $
diff --git a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__resize_pane_inside_expansion_border.snap b/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__resize_pane_inside_expansion_border.snap
deleted file mode 100644
index a3d009ce..00000000
--- a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__resize_pane_inside_expansion_border.snap
+++ /dev/null
@@ -1,55 +0,0 @@
----
-source: src/tests/integration/expansion_boundary.rs
-expression: next_to_last_snapshot
-
----
-line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-prompt $
-───────────────────────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────
-line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbb│line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line15-bbbbbbbbbbbbbbbbbb│line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbb│line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbb│line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│prompt $ │line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb├─────────────────────────┤line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-prompt $ │prompt $ █ │prompt $
-───────────────────────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────
-line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-prompt $
diff --git a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__toggling_fullcsreen_in_expansion_border_expands_only_until_border.snap b/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__toggling_fullcsreen_in_expansion_border_expands_only_until_border.snap
deleted file mode 100644
index 41a54185..00000000
--- a/src/tests/integration/snapshots/zellij__tests__integration__expansion_boundary__toggling_fullcsreen_in_expansion_border_expands_only_until_border.snap
+++ /dev/null
@@ -1,55 +0,0 @@
----
-source: src/tests/integration/expansion_boundary.rs
-expression: next_to_last_snapshot
-
----
-line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-prompt $
-───────────────────────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────
-line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line11-bbbbbbbbbbbbbbbbbb│line11-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line12-bbbbbbbbbbbbbbbbbb│line12-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line13-bbbbbbbbbbbbbbbbbb│line13-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbb│line14-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line15-bbbbbbbbbbbbbbbbbb│line15-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbb│line16-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbb│line17-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbb│line18-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbb│line19-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-prompt $ │prompt $ █ │prompt $
-───────────────────────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────
-line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-prompt $
diff --git a/src/tests/integration/snapshots/zellij__tests__integration__resize_down__cannot_resize_down_when_pane_below_is_at_minimum_height.snap b/src/tests/integration/snapshots/zellij__tests__integration__resize_down__cannot_resize_down_when_pane_below_is_at_minimum_height.snap
index 523612a9..5eedc89f 100644
--- a/src/tests/integration/snapshots/zellij__tests__integration__resize_down__cannot_resize_down_when_pane_below_is_at_minimum_height.snap
+++ b/src/tests/integration/snapshots/zellij__tests__integration__resize_down__cannot_resize_down_when_pane_below_is_at_minimum_height.snap
@@ -3,8 +3,10 @@ source: src/tests/integration/resize_down.rs
expression: snapshot_before_quit
---
+line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $ █
diff --git a/src/tests/integration/snapshots/zellij__tests__integration__resize_down__resize_down_with_panes_above_and_below.snap b/src/tests/integration/snapshots/zellij__tests__integration__resize_down__resize_down_with_panes_above_and_below.snap
index 0d404ed1..29c9a76a 100644
--- a/src/tests/integration/snapshots/zellij__tests__integration__resize_down__resize_down_with_panes_above_and_below.snap
+++ b/src/tests/integration/snapshots/zellij__tests__integration__resize_down__resize_down_with_panes_above_and_below.snap
@@ -3,6 +3,8 @@ source: src/tests/integration/resize_down.rs
expression: snapshot_before_quit
---
+line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
@@ -14,6 +16,8 @@ line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
@@ -21,5 +25,6 @@ line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $ █
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $
diff --git a/src/tests/integration/snapshots/zellij__tests__integration__resize_up__cannot_resize_up_when_pane_above_is_at_minimum_height.snap b/src/tests/integration/snapshots/zellij__tests__integration__resize_up__cannot_resize_up_when_pane_above_is_at_minimum_height.snap
index e1f6bee0..5b09c7bd 100644
--- a/src/tests/integration/snapshots/zellij__tests__integration__resize_up__cannot_resize_up_when_pane_above_is_at_minimum_height.snap
+++ b/src/tests/integration/snapshots/zellij__tests__integration__resize_up__cannot_resize_up_when_pane_above_is_at_minimum_height.snap
@@ -3,8 +3,10 @@ source: src/tests/integration/resize_up.rs
expression: snapshot_before_quit
---
+line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
+line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
prompt $ █
diff --git a/src/tests/utils.rs b/src/tests/utils.rs
index 915adfb0..aa622fe7 100644
--- a/src/tests/utils.rs
+++ b/src/tests/utils.rs
@@ -12,7 +12,7 @@ pub fn get_output_frame_snapshots(
let mut snapshots = vec![];
for frame in output_frames.iter() {
for byte in frame.iter() {
- vte_parser.advance(&mut terminal_output, *byte);
+ vte_parser.advance(&mut terminal_output.grid, *byte);
}
let output_lines = terminal_output.read_buffer_as_lines();
let cursor_coordinates = terminal_output.cursor_coordinates();
@@ -63,6 +63,8 @@ pub mod commands {
pub const SCROLL_MODE: [u8; 1] = [19]; // ctrl-s
pub const SCROLL_UP_IN_SCROLL_MODE: [u8; 1] = [107]; // k
pub const SCROLL_DOWN_IN_SCROLL_MODE: [u8; 1] = [106]; // j
+ pub const SCROLL_PAGE_UP_IN_SCROLL_MODE: [u8; 1] = [2]; // ctrl-b
+ pub const SCROLL_PAGE_DOWN_IN_SCROLL_MODE: [u8; 1] = [6]; // ctrl-f
pub const RESIZE_MODE: [u8; 1] = [18]; // ctrl-r
pub const RESIZE_DOWN_IN_RESIZE_MODE: [u8; 1] = [106]; // j