summaryrefslogtreecommitdiffstats
path: root/zellij-utils
diff options
context:
space:
mode:
authorhar7an <99636919+har7an@users.noreply.github.com>2022-12-08 12:50:28 +0000
committerGitHub <noreply@github.com>2022-12-08 12:50:28 +0000
commit62eaea15833b34f40270dc1ce8f6d57639dc4bdd (patch)
treeeb06c1f3075ccb5ed6f536d427e14c4fdbdef4e2 /zellij-utils
parent420c7c319b1c328af9994f758264753c9467aded (diff)
Reimplement resize code (#1990)
* server/floating_panes: Start removing `unwrap`s * server/panes: Remove more `unwrap`s in floating panes code. * utils/data: Unify `Direction` type which was previously present in multiple locations. Also start working on a new Resize Method (type `ResizeStrategy`), to remove code duplication in the resize code. * server: Implement new resize handling with the `ResizeStrategy` type. Add a new action with the ability to invoke it from the CLI. Take care to maintain backwards-compatibility in terms of configuring the new resize mode. * utils/layout: Add conversion for SplitDirection from `data::Direction`. * utils/data: Add impl for `Direction` * server/panes: Rework tiled pane resizing but it's currently still broken in a few regards and misses ability to perform "regular" increase/decrease. * server/panes/tiled_panes: Add debug assertion to catch if the total area of all panes (in percent) is different from 100.0 at some point. * server/panes/tiled/grid: Fix resize bug caused by the fact that neighboring plugin panes previously weren't filtered from resize operations, even though they cannot be resized at all. * utils/data: Add `invert` for `Resize` * utils/data: Add member to `ResizeStrategy` that controls whether we invert resize behavior when increasing size towards a bounadry. This maintains current behavior. * server/screen: Handle new attribute in `ResizeStrategy` * server/panes/resizer: Return `anyhow::Error` * server/panes/tiled: Implement resize increase/decrease without specifying a direction (towards all possible directions). Currently broken in some cases. * server/pane/tiled/grid: Don't return early to preserve resize debug assertions. * server/pane/tiled/grid: Fix resize bug caused by checking for the wrong alignments in some cases. Also refactor the code for looking up aligned panes. * server/panes/tiled/grid: Cleanup code and remove log statements and unused functions. * server/panes/float/grid: Invert resize if the floating pane is hitting a boundary already. * plugins/status-bar: Add hints for new resize * server: Use new resize method * server: Fix tests with new functions and result types. * apply rustfmt * utils: Apply rustfmt * server/panes/floating: Fix resize increase behavior which would previously, upon hitting a boundary, cause the pane to invert the resize operation, which is wrong. Instead, it now does not resize floating panes on an undirected resize "increase" in directions where it hits boundaries. * server/panes/tiled: Use correct resize increments The values for the resize increments were previously wrong, causing many of the tests to fail. * server/panes/tiled: Fix resize checks to correctly consider fixed-size panes. * utils/assets/config: Update default config with new keybindings for resize mode. * server/panes/tiled: Fix resize check * server/panes/tiled: Use shortener for `Direction` type in `change_pane_size` function. * server/panes/tiled: Restore resize behavior for undirected resizes, to the way it was before this PR. * server/panes/floating: Fix resize increment for undirected resizes * utils/data: Fix doctest * utils: Fix test snapshots for tests working with the default config * changelog: Add PR #1990
Diffstat (limited to 'zellij-utils')
-rw-r--r--zellij-utils/assets/config/default.kdl12
-rw-r--r--zellij-utils/src/cli.rs10
-rw-r--r--zellij-utils/src/data.rs228
-rw-r--r--zellij-utils/src/errors.rs10
-rw-r--r--zellij-utils/src/input/actions.rs31
-rw-r--r--zellij-utils/src/input/command.rs2
-rw-r--r--zellij-utils/src/input/layout.rs10
-rw-r--r--zellij-utils/src/input/unit/keybinds_test.rs2
-rw-r--r--zellij-utils/src/kdl/mod.rs39
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap122
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap122
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap122
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap122
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap122
14 files changed, 867 insertions, 87 deletions
diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl
index d048b8e50..2397f5bae 100644
--- a/zellij-utils/assets/config/default.kdl
+++ b/zellij-utils/assets/config/default.kdl
@@ -9,10 +9,14 @@ keybinds {
}
resize {
bind "Ctrl n" { SwitchToMode "Normal"; }
- bind "h" "Left" { Resize "Left"; }
- bind "j" "Down" { Resize "Down"; }
- bind "k" "Up" { Resize "Up"; }
- bind "l" "Right" { Resize "Right"; }
+ bind "h" "Left" { Resize "Increase Left"; }
+ bind "j" "Down" { Resize "Increase Down"; }
+ bind "k" "Up" { Resize "Increase Up"; }
+ bind "l" "Right" { Resize "Increase Right"; }
+ bind "H" { Resize "Decrease Left"; }
+ bind "J" { Resize "Decrease Down"; }
+ bind "K" { Resize "Decrease Up"; }
+ bind "L" { Resize "Decrease Right"; }
bind "=" "+" { Resize "Increase"; }
bind "-" { Resize "Decrease"; }
}
diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs
index 694032dc1..1d62e7d07 100644
--- a/zellij-utils/src/cli.rs
+++ b/zellij-utils/src/cli.rs
@@ -1,8 +1,7 @@
-use crate::data::InputMode;
+use crate::data::{Direction, InputMode, Resize};
use crate::setup::Setup;
use crate::{
consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV},
- input::actions::{Direction, ResizeDirection},
input::options::CliOptions,
};
use clap::{Parser, Subcommand};
@@ -184,8 +183,11 @@ pub enum CliAction {
Write { bytes: Vec<u8> },
/// Write characters to the terminal.
WriteChars { chars: String },
- /// Resize the focused pane in the specified direction. [right|left|up|down|+|-]
- Resize { resize_direction: ResizeDirection },
+ /// [increase|decrease] the focused panes area at the [left|down|up|right] border.
+ Resize {
+ resize: Resize,
+ direction: Option<Direction>,
+ },
/// Change focus to the next pane
FocusNextPane,
/// Change focus to the previous pane
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs
index 605cf8980..a0f255daa 100644
--- a/zellij-utils/src/data.rs
+++ b/zellij-utils/src/data.rs
@@ -203,6 +203,25 @@ pub enum Direction {
Down,
}
+impl Direction {
+ pub fn invert(&self) -> Direction {
+ match *self {
+ Direction::Left => Direction::Right,
+ Direction::Down => Direction::Up,
+ Direction::Up => Direction::Down,
+ Direction::Right => Direction::Left,
+ }
+ }
+
+ pub fn is_horizontal(&self) -> bool {
+ matches!(self, Direction::Left | Direction::Right)
+ }
+
+ pub fn is_vertical(&self) -> bool {
+ matches!(self, Direction::Down | Direction::Up)
+ }
+}
+
impl fmt::Display for Direction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
@@ -214,6 +233,215 @@ impl fmt::Display for Direction {
}
}
+impl FromStr for Direction {
+ type Err = String;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "Left" | "left" => Ok(Direction::Left),
+ "Right" | "right" => Ok(Direction::Right),
+ "Up" | "up" => Ok(Direction::Up),
+ "Down" | "down" => Ok(Direction::Down),
+ _ => Err(format!(
+ "Failed to parse Direction. Unknown Direction: {}",
+ s
+ )),
+ }
+ }
+}
+
+/// Resize operation to perform.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
+pub enum Resize {
+ Increase,
+ Decrease,
+}
+
+impl Resize {
+ pub fn invert(&self) -> Self {
+ match self {
+ Resize::Increase => Resize::Decrease,
+ Resize::Decrease => Resize::Increase,
+ }
+ }
+}
+
+impl fmt::Display for Resize {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Resize::Increase => write!(f, "+"),
+ Resize::Decrease => write!(f, "-"),
+ }
+ }
+}
+
+impl FromStr for Resize {
+ type Err = String;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "Increase" | "increase" | "+" => Ok(Resize::Increase),
+ "Decrease" | "decrease" | "-" => Ok(Resize::Decrease),
+ _ => Err(format!(
+ "failed to parse resize type. Unknown specifier '{}'",
+ s
+ )),
+ }
+ }
+}
+
+/// Container type that fully describes resize operations.
+///
+/// This is best thought of as follows:
+///
+/// - `resize` commands how the total *area* of the pane will change as part of this resize
+/// operation.
+/// - `direction` has two meanings:
+/// - `None` means to resize all borders equally
+/// - Anything else means to move the named border to achieve the change in area
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
+pub struct ResizeStrategy {
+ /// Whether to increase or resize total area
+ pub resize: Resize,
+ /// With which border, if any, to change area
+ pub direction: Option<Direction>,
+ /// If set to true (default), increasing resizes towards a viewport border will be inverted.
+ /// I.e. a scenario like this ("increase right"):
+ ///
+ /// ```text
+ /// +---+---+
+ /// | | X |->
+ /// +---+---+
+ /// ```
+ ///
+ /// turns into this ("decrease left"):
+ ///
+ /// ```text
+ /// +---+---+
+ /// | |-> |
+ /// +---+---+
+ /// ```
+ pub invert_on_boundaries: bool,
+}
+
+impl From<Direction> for ResizeStrategy {
+ fn from(direction: Direction) -> Self {
+ ResizeStrategy::new(Resize::Increase, Some(direction))
+ }
+}
+
+impl From<Resize> for ResizeStrategy {
+ fn from(resize: Resize) -> Self {
+ ResizeStrategy::new(resize, None)
+ }
+}
+
+impl ResizeStrategy {
+ pub fn new(resize: Resize, direction: Option<Direction>) -> Self {
+ ResizeStrategy {
+ resize,
+ direction,
+ invert_on_boundaries: true,
+ }
+ }
+
+ pub fn invert(&self) -> ResizeStrategy {
+ let resize = match self.resize {
+ Resize::Increase => Resize::Decrease,
+ Resize::Decrease => Resize::Increase,
+ };
+ let direction = match self.direction {
+ Some(direction) => Some(direction.invert()),
+ None => None,
+ };
+
+ ResizeStrategy::new(resize, direction)
+ }
+
+ pub fn resize_type(&self) -> Resize {
+ self.resize
+ }
+
+ pub fn direction(&self) -> Option<Direction> {
+ self.direction
+ }
+
+ pub fn direction_horizontal(&self) -> bool {
+ matches!(
+ self.direction,
+ Some(Direction::Left) | Some(Direction::Right)
+ )
+ }
+
+ pub fn direction_vertical(&self) -> bool {
+ matches!(self.direction, Some(Direction::Up) | Some(Direction::Down))
+ }
+
+ pub fn resize_increase(&self) -> bool {
+ self.resize == Resize::Increase
+ }
+
+ pub fn resize_decrease(&self) -> bool {
+ self.resize == Resize::Decrease
+ }
+
+ pub fn move_left_border_left(&self) -> bool {
+ (self.resize == Resize::Increase) && (self.direction == Some(Direction::Left))
+ }
+
+ pub fn move_left_border_right(&self) -> bool {
+ (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Left))
+ }
+
+ pub fn move_lower_border_down(&self) -> bool {
+ (self.resize == Resize::Increase) && (self.direction == Some(Direction::Down))
+ }
+
+ pub fn move_lower_border_up(&self) -> bool {
+ (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Down))
+ }
+
+ pub fn move_upper_border_up(&self) -> bool {
+ (self.resize == Resize::Increase) && (self.direction == Some(Direction::Up))
+ }
+
+ pub fn move_upper_border_down(&self) -> bool {
+ (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Up))
+ }
+
+ pub fn move_right_border_right(&self) -> bool {
+ (self.resize == Resize::Increase) && (self.direction == Some(Direction::Right))
+ }
+
+ pub fn move_right_border_left(&self) -> bool {
+ (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Right))
+ }
+
+ pub fn move_all_borders_out(&self) -> bool {
+ (self.resize == Resize::Increase) && (self.direction == None)
+ }
+
+ pub fn move_all_borders_in(&self) -> bool {
+ (self.resize == Resize::Decrease) && (self.direction == None)
+ }
+}
+
+impl fmt::Display for ResizeStrategy {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let resize = match self.resize {
+ Resize::Increase => "increase",
+ Resize::Decrease => "decrease",
+ };
+ let border = match self.direction {
+ Some(Direction::Left) => "left",
+ Some(Direction::Down) => "bottom",
+ Some(Direction::Up) => "top",
+ Some(Direction::Right) => "right",
+ None => "every",
+ };
+
+ write!(f, "{} size on {} border", resize, border)
+ }
+}
+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
// FIXME: This should be extended to handle different button clicks (not just
// left click) and the `ScrollUp` and `ScrollDown` events could probably be
diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs
index 70d4b3f6d..cbd0bdf1d 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -256,6 +256,16 @@ pub enum ScreenContext {
HorizontalSplit,
VerticalSplit,
WriteCharacter,
+ ResizeIncreaseAll,
+ ResizeIncreaseLeft,
+ ResizeIncreaseDown,
+ ResizeIncreaseUp,
+ ResizeIncreaseRight,
+ ResizeDecreaseAll,
+ ResizeDecreaseLeft,
+ ResizeDecreaseDown,
+ ResizeDecreaseUp,
+ ResizeDecreaseRight,
ResizeLeft,
ResizeRight,
ResizeDown,
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index e62a6ec62..e1a45bf5c 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -4,6 +4,7 @@ use super::command::RunCommandAction;
use super::layout::{Layout, PaneLayout};
use crate::cli::CliAction;
use crate::data::InputMode;
+use crate::data::{Direction, Resize};
use crate::input::config::{ConfigError, KdlError};
use crate::input::options::OnForceClose;
use miette::{NamedSource, Report};
@@ -14,30 +15,6 @@ use std::str::FromStr;
use crate::position::Position;
-/// The four directions (left, right, up, down).
-#[derive(Eq, Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
-pub enum Direction {
- Left,
- Right,
- Up,
- Down,
-}
-impl FromStr for Direction {
- type Err = String;
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- match s {
- "Left" | "left" => Ok(Direction::Left),
- "Right" | "right" => Ok(Direction::Right),
- "Up" | "up" => Ok(Direction::Up),
- "Down" | "down" => Ok(Direction::Down),
- _ => Err(format!(
- "Failed to parse Direction. Unknown Direction: {}",
- s
- )),
- }
- }
-}
-
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum ResizeDirection {
Left,
@@ -126,8 +103,8 @@ pub enum Action {
SwitchToMode(InputMode),
/// Switch all connected clients to the specified input mode.
SwitchModeForAllClients(InputMode),
- /// Resize focus pane in specified direction.
- Resize(ResizeDirection),
+ /// Shrink/enlarge focused pane at specified border
+ Resize(Resize, Option<Direction>),
/// Switch focus to next pane in specified direction.
FocusNextPane,
FocusPreviousPane,
@@ -235,7 +212,7 @@ impl Action {
match cli_action {
CliAction::Write { bytes } => Ok(vec![Action::Write(bytes)]),
CliAction::WriteChars { chars } => Ok(vec![Action::WriteChars(chars)]),
- CliAction::Resize { resize_direction } => Ok(vec![Action::Resize(resize_direction)]),
+ CliAction::Resize { resize, direction } => Ok(vec![Action::Resize(resize, direction)]),
CliAction::FocusNextPane => Ok(vec![Action::FocusNextPane]),
CliAction::FocusPreviousPane => Ok(vec![Action::FocusPreviousPane]),
CliAction::MoveFocus { direction } => Ok(vec![Action::MoveFocus(direction)]),
diff --git a/zellij-utils/src/input/command.rs b/zellij-utils/src/input/command.rs
index 14b87755f..3b6f25025 100644
--- a/zellij-utils/src/input/command.rs
+++ b/zellij-utils/src/input/command.rs
@@ -1,5 +1,5 @@
//! Trigger a command
-use super::actions::Direction;
+use crate::data::Direction;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs
index 94b4f9322..6c4a222cc 100644
--- a/zellij-utils/src/input/layout.rs
+++ b/zellij-utils/src/input/layout.rs
@@ -9,6 +9,7 @@
// If plugins should be able to depend on the layout system
// then [`zellij-utils`] could be a proper place.
use crate::{
+ data::Direction,
input::{
command::RunCommand,
config::{Config, ConfigError},
@@ -48,6 +49,15 @@ impl Not for SplitDirection {
}
}
+impl From<Direction> for SplitDirection {
+ fn from(direction: Direction) -> Self {
+ match direction {
+ Direction::Left | Direction::Right => SplitDirection::Horizontal,
+ Direction::Down | Direction::Up => SplitDirection::Vertical,
+ }
+ }
+}
+
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum SplitSize {
#[serde(alias = "percent")]
diff --git a/zellij-utils/src/input/unit/keybinds_test.rs b/zellij-utils/src/input/unit/keybinds_test.rs
index 57126d595..36c81cf81 100644
--- a/zellij-utils/src/input/unit/keybinds_test.rs
+++ b/zellij-utils/src/input/unit/keybinds_test.rs
@@ -1,6 +1,6 @@
use super::super::actions::*;
use super::super::keybinds::*;
-use crate::data::{self, CharOrArrow, Key};
+use crate::data::{self, CharOrArrow, Direction, Key};
use crate::input::config::Config;
use insta::assert_snapshot;
use strum::IntoEnumIterator;
diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs
index 54560bf58..b28a8cc3c 100644
--- a/zellij-utils/src/kdl/mod.rs
+++ b/zellij-utils/src/kdl/mod.rs
@@ -1,5 +1,5 @@
mod kdl_layout_parser;
-use crate::data::{InputMode, Key, Palette, PaletteColor};
+use crate::data::{Direction, InputMode, Key, Palette, PaletteColor, Resize};
use crate::envs::EnvironmentVariables;
use crate::input::config::{Config, ConfigError, KdlError};
use crate::input::keybinds::Keybinds;
@@ -21,7 +21,7 @@ use kdl::{KdlDocument, KdlEntry, KdlNode};
use std::path::PathBuf;
use std::str::FromStr;
-use crate::input::actions::{Action, Direction, ResizeDirection, SearchDirection, SearchOption};
+use crate::input::actions::{Action, SearchDirection, SearchOption};
use crate::input::command::RunCommandAction;
#[macro_export]
@@ -394,14 +394,28 @@ impl Action {
},
},
"Resize" => {
- let direction = ResizeDirection::from_str(string.as_str()).map_err(|_| {
- ConfigError::new_kdl_error(
- format!("Invalid direction: '{}'", string),
- action_node.span().offset(),
- action_node.span().len(),
- )
- })?;
- Ok(Action::Resize(direction))
+ let mut resize: Option<Resize> = None;
+ let mut direction: Option<Direction> = None;
+ for word in string.to_ascii_lowercase().split_whitespace() {
+ match Resize::from_str(word) {
+ Ok(value) => resize = Some(value),
+ Err(_) => match Direction::from_str(word) {
+ Ok(value) => direction = Some(value),
+ Err(_) => {
+ return Err(ConfigError::new_kdl_error(
+ format!(
+ "failed to read either of resize type or direction from '{}'",
+ word
+ ),
+ action_node.span().offset(),
+ action_node.span().len(),
+ ))
+ },
+ },
+ }
+ }
+ let resize = resize.unwrap_or(Resize::Increase);
+ Ok(Action::Resize(resize, direction))
},
"MoveFocus" => {
let direction = Direction::from_str(string.as_str()).map_err(|_| {
@@ -691,6 +705,11 @@ impl TryFrom<&KdlNode> for Action {
action_arguments,
kdl_action
),
+ "ResizeNew" => parse_kdl_action_char_or_string_arguments!(
+ action_name,
+ action_arguments,
+ kdl_action
+ ),
"MoveFocus" => parse_kdl_action_char_or_string_arguments!(
action_name,
action_arguments,
diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap
index ff1a3ff97..779145947 100644
--- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap
+++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap
@@ -12,6 +12,7 @@ Config {
): [
Resize(
Increase,
+ None,
),
],
Alt(
@@ -21,6 +22,7 @@ Config {
): [
Resize(
Decrease,
+ None,
),
],
Alt(
@@ -30,6 +32,7 @@ Config {
): [
Resize(
Increase,
+ None,
),
],
Alt(
@@ -188,22 +191,34 @@ Config {
Resize: {
Left: [
Resize(
- Left,
+ Increase,
+ Some(
+ Left,
+ ),
),
],
Down: [
Resize(
- Down,
+ Increase,
+ Some(
+ Down,
+ ),
),
],
Up: [
Resize(
- Up,
+ Increase,
+ Some(
+ Up,
+ ),
),
],
Right: [
Resize(
- Right,