summaryrefslogtreecommitdiffstats
path: root/zellij-utils/src
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2022-12-24 15:48:04 +0100
committerGitHub <noreply@github.com>2022-12-24 15:48:04 +0100
commit799fa5de8dfd0b826be0f903796bba48a470127e (patch)
tree8c202e116b757162cadd0cc350da256b9741e248 /zellij-utils/src
parent17205793e4cd2da5eb431dacfc87dab574080b5b (diff)
Floating panes in layouts (#2047)
* work * tests passing * tests: floating panes in layouts * panes(plugins): floating plugins working * refactor(tab): layout applier * style(comment): remove outdated * style(fmt): rustfmt
Diffstat (limited to 'zellij-utils/src')
-rw-r--r--zellij-utils/src/input/actions.rs23
-rw-r--r--zellij-utils/src/input/layout.rs98
-rw-r--r--zellij-utils/src/input/unit/layout_test.rs184
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap5
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap5
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap4
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templates.snap4
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templates.snap4
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap4
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap6
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap4
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tabs_and_floating_panes.snap102
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_edit.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_command_propagated_to_its_consumer_edit.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap4
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap4
-rw-r--r--zellij-utils/src/kdl/kdl_layout_parser.rs800
-rw-r--r--zellij-utils/src/kdl/mod.rs2
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options-2.snap3
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap3
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap3
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap3
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options-2.snap3
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_plugins_override_config_plugins.snap3
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap3
-rw-r--r--zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap3
50 files changed, 1177 insertions, 178 deletions
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index e1a45bf5c..7bb13540c 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -1,7 +1,7 @@
//! Definition of the actions that can be bound to keys.
use super::command::RunCommandAction;
-use super::layout::{Layout, PaneLayout};
+use super::layout::{FloatingPanesLayout, Layout, PaneLayout};
use crate::cli::CliAction;
use crate::data::InputMode;
use crate::data::{Direction, Resize};
@@ -162,7 +162,7 @@ pub enum Action {
PaneNameInput(Vec<u8>),
UndoRenamePane,
/// Create a new tab, optionally with a specified tab layout.
- NewTab(Option<PaneLayout>, Option<String>), // the String is the tab name
+ NewTab(Option<PaneLayout>, Vec<FloatingPanesLayout>, Option<String>), // the String is the tab name
/// Do nothing.
NoOp,
/// Go to the next tab.
@@ -368,15 +368,24 @@ impl Action {
if tabs.len() > 1 {
return Err(format!("Tab layout cannot itself have tabs"));
} else if !tabs.is_empty() {
- let (tab_name, layout) = tabs.drain(..).next().unwrap();
+ let (tab_name, layout, floating_panes_layout) =
+ tabs.drain(..).next().unwrap();
let name = tab_name.or(name);
- Ok(vec![Action::NewTab(Some(layout), name)])
+ Ok(vec![Action::NewTab(
+ Some(layout),
+ floating_panes_layout,
+ name,
+ )])
} else {
- let layout = layout.new_tab();
- Ok(vec![Action::NewTab(Some(layout), name)])
+ let (layout, floating_panes_layout) = layout.new_tab();
+ Ok(vec![Action::NewTab(
+ Some(layout),
+ floating_panes_layout,
+ name,
+ )])
}
} else {
- Ok(vec![Action::NewTab(None, name)])
+ Ok(vec![Action::NewTab(None, vec![], name)])
}
},
}
diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs
index 6c4a222cc..eaa2e3950 100644
--- a/zellij-utils/src/input/layout.rs
+++ b/zellij-utils/src/input/layout.rs
@@ -213,9 +213,94 @@ impl fmt::Display for RunPluginLocation {
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
pub struct Layout {
- pub tabs: Vec<(Option<String>, PaneLayout)>,
+ pub tabs: Vec<(Option<String>, PaneLayout, Vec<FloatingPanesLayout>)>,
pub focused_tab_index: Option<usize>,
pub template: Option<PaneLayout>,
+ pub floating_panes_template: Vec<FloatingPanesLayout>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
+pub enum PercentOrFixed {
+ Percent(usize), // 1 to 100
+ Fixed(usize), // An absolute number of columns or rows
+}
+
+impl PercentOrFixed {
+ pub fn to_position(&self, whole: usize) -> usize {
+ match self {
+ PercentOrFixed::Percent(percent) => {
+ (whole as f64 / 100.0 * *percent as f64).ceil() as usize
+ },
+ PercentOrFixed::Fixed(fixed) => {
+ if *fixed > whole {
+ whole
+ } else {
+ *fixed
+ }
+ },
+ }
+ }
+}
+
+impl PercentOrFixed {
+ pub fn is_zero(&self) -> bool {
+ match self {
+ PercentOrFixed::Percent(percent) => *percent == 0,
+ PercentOrFixed::Fixed(fixed) => *fixed == 0,
+ }
+ }
+}
+
+impl FromStr for PercentOrFixed {
+ type Err = Box<dyn std::error::Error>;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ if s.chars().last() == Some('%') {
+ let char_count = s.chars().count();
+ let percent_size = usize::from_str_radix(&s[..char_count.saturating_sub(1)], 10)?;
+ if percent_size <= 100 {
+ Ok(PercentOrFixed::Percent(percent_size))
+ } else {
+ Err("Percent must be between 0 and 100".into())
+ }
+ } else {
+ let fixed_size = usize::from_str_radix(s, 10)?;
+ Ok(PercentOrFixed::Fixed(fixed_size))
+ }
+ }
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
+pub struct FloatingPanesLayout {
+ // TODO: change name to singular
+ pub name: Option<String>,
+ pub height: Option<PercentOrFixed>,
+ pub width: Option<PercentOrFixed>,
+ pub x: Option<PercentOrFixed>,
+ pub y: Option<PercentOrFixed>,
+ pub run: Option<Run>,
+ pub focus: Option<bool>,
+}
+
+impl FloatingPanesLayout {
+ pub fn add_cwd_to_layout(&mut self, cwd: &PathBuf) {
+ match self.run.as_mut() {
+ Some(run) => run.add_cwd(cwd),
+ None => {
+ self.run = Some(Run::Cwd(cwd.clone()));
+ },
+ }
+ }
+}
+
+impl From<&PaneLayout> for FloatingPanesLayout {
+ fn from(pane_layout: &PaneLayout) -> Self {
+ FloatingPanesLayout {
+ name: pane_layout.name.clone(),
+ run: pane_layout.run.clone(),
+ focus: pane_layout.focus,
+ ..Default::default()
+ }
+ }
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
@@ -276,6 +361,8 @@ impl PaneLayout {
Ok(layouts)
}
pub fn extract_run_instructions(&self) -> Vec<Option<Run>> {
+ // the order of these run instructions is significant and needs to be the same
+ // as the order of the "flattened" layout panes received from eg. position_panes_in_space
let mut run_instructions = vec![];
if self.children.is_empty() {
run_instructions.push(self.run.clone());
@@ -452,11 +539,12 @@ impl Layout {
Ok(String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?)
}
- pub fn new_tab(&self) -> PaneLayout {
- match &self.template {
+ pub fn new_tab(&self) -> (PaneLayout, Vec<FloatingPanesLayout>) {
+ let template = match &self.template {
Some(template) => template.clone(),
None => PaneLayout::default(),
- }
+ };
+ (template, self.floating_panes_template.clone())
}
pub fn is_empty(&self) -> bool {
@@ -467,7 +555,7 @@ impl Layout {
!self.tabs.is_empty()
}
- pub fn tabs(&self) -> Vec<(Option<String>, PaneLayout)> {
+ pub fn tabs(&self) -> Vec<(Option<String>, PaneLayout, Vec<FloatingPanesLayout>)> {
// String is the tab name
self.tabs.clone()
}
diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs
index f11c86869..d588fcda6 100644
--- a/zellij-utils/src/input/unit/layout_test.rs
+++ b/zellij-utils/src/input/unit/layout_test.rs
@@ -90,6 +90,175 @@ fn layout_with_nested_panes() {
}
#[test]
+fn layout_with_floating_panes() {
+ let kdl_layout = r#"
+ layout {
+ floating_panes {
+ pane
+ pane {
+ x 10
+ y "10%"
+ width 10
+ height "10%"
+ }
+ pane x=10 y="10%"
+ pane command="htop"
+ }
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
+ let expected_layout = Layout {