diff options
author | har7an <99636919+har7an@users.noreply.github.com> | 2022-12-08 12:50:28 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-08 12:50:28 +0000 |
commit | 62eaea15833b34f40270dc1ce8f6d57639dc4bdd (patch) | |
tree | eb06c1f3075ccb5ed6f536d427e14c4fdbdef4e2 /zellij-utils/src | |
parent | 420c7c319b1c328af9994f758264753c9467aded (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/src')
13 files changed, 859 insertions, 83 deletions
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, + Increase, + Some( + Right, + ), ), ], Char( @@ -218,6 +233,7 @@ Config { ): [ Resize( Increase, + None, ), ], Char( @@ -225,6 +241,7 @@ Config { ): [ Resize( Decrease, + None, ), ], Char( @@ -232,34 +249,87 @@ Config { ): [ Resize( Increase, + None, + ), + ], + Char( + 'H', + ): [ + Resize( + Decrease, + Some( + Left, + ), + ), + ], + Char( + 'J', |