From 5bcc1bb382659da60f29e4fc79336d3f6a727b57 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Sat, 25 Feb 2023 11:16:11 +0100 Subject: [PATCH] fix(layout): various parser and ui fixes (#2191) * fix(layout): error on nodes outside layout node * fix(layout): move stacked property to pane * fix(layout): various stack exceptions * fix(ui): non-flexible stacked pane titles now take up their full length * fix(ui): stack titles with no-pane-frames take up their proper length * style(fmt): rustfmt --- zellij-server/src/panes/terminal_pane.rs | 8 +- ...h_stacked_children_and_no_pane_frames.snap | 8 +- .../src/tab/unit/tab_integration_tests.rs | 58 +++--- zellij-server/src/ui/pane_boundaries_frame.rs | 4 +- zellij-utils/assets/layouts/compact.swap.kdl | 2 +- zellij-utils/assets/layouts/default.swap.kdl | 2 +- zellij-utils/assets/layouts/strider.swap.kdl | 2 +- zellij-utils/src/input/unit/layout_test.rs | 93 ++++++++++ ...define_stacked_children_for_pane_node.snap | 63 +++++++ ...ne_stacked_children_for_pane_template.snap | 75 ++++++++ zellij-utils/src/kdl/kdl_layout_parser.rs | 175 ++++++++++++------ 11 files changed, 391 insertions(+), 99 deletions(-) create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_node.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_template.snap diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index be14b74f..d48df67a 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -358,13 +358,7 @@ impl Pane for TerminalPane { self.pane_name.clone() }; - let mut frame_geom = self.current_geom(); - if !frame_params.should_draw_pane_frames { - // in this case the width of the frame needs not include the pane corners - frame_geom - .cols - .set_inner(frame_geom.cols.as_usize().saturating_sub(1)); - } + let frame_geom = self.current_geom(); let mut frame = PaneFrame::new( frame_geom.into(), self.grid.scrollback_position_and_length(), diff --git a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_stacked_children_and_no_pane_frames.snap b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_stacked_children_and_no_pane_frames.snap index 7174f10a..ee77150c 100644 --- a/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_stacked_children_and_no_pane_frames.snap +++ b/zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_tiled_layout_with_stacked_children_and_no_pane_frames.snap @@ -1,11 +1,11 @@ --- source: zellij-server/src/tab/./unit/tab_integration_tests.rs -assertion_line: 3204 +assertion_line: 3249 expression: snapshot --- -00 (C): ├─ Pane #2 ───────────────────────────────────────────────── -01 (C): ├─ Pane #3 ───────────────────────────────────────────────── -02 (C): ├─ Pane #4 ───────────────────────────────────────────────── +00 (C): ├─ Pane #2 ────────────────────────────────────────────────── +01 (C): ├─ Pane #3 ────────────────────────────────────────────────── +02 (C): ├─ Pane #4 ────────────────────────────────────────────────── 03 (C): │ 04 (C): │ 05 (C): │ diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 89e85644..b7823dc1 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -3163,7 +3163,7 @@ fn swap_tiled_layout_with_stacked_children() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -3213,7 +3213,7 @@ fn swap_tiled_layout_with_stacked_children_and_no_pane_frames() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -3263,7 +3263,7 @@ fn move_focus_up_with_stacked_panes() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -3315,7 +3315,7 @@ fn move_focus_down_with_stacked_panes() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -3371,7 +3371,7 @@ fn move_focus_right_into_stacked_panes() { tab { pane split_direction="vertical" { pane - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -3431,7 +3431,7 @@ fn move_focus_left_into_stacked_panes() { swap_tiled_layout { tab { pane split_direction="vertical" { - pane { children stacked=true; } + pane stacked=true { children; } pane focus=true } } @@ -3494,7 +3494,7 @@ fn move_focus_up_into_stacked_panes() { pane pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -3558,7 +3558,7 @@ fn move_focus_down_into_stacked_panes() { pane pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -3617,7 +3617,7 @@ fn close_main_stacked_pane() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -3668,7 +3668,7 @@ fn close_main_stacked_pane_in_mid_stack() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -3728,7 +3728,7 @@ fn close_one_liner_stacked_pane_below_main_pane() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -3789,7 +3789,7 @@ fn close_one_liner_stacked_pane_above_main_pane() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -3849,7 +3849,7 @@ fn can_increase_size_of_main_pane_in_stack_horizontally() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -3912,7 +3912,7 @@ fn can_increase_size_of_main_pane_in_stack_vertically() { pane pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -3976,7 +3976,7 @@ fn can_increase_size_of_main_pane_in_stack_non_directionally() { pane pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -4044,7 +4044,7 @@ fn increasing_size_of_main_pane_in_stack_horizontally_does_not_break_stack() { pane focus=true pane } - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -4125,7 +4125,7 @@ fn can_increase_size_into_pane_stack_horizontally() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -4187,7 +4187,7 @@ fn can_increase_size_into_pane_stack_vertically() { pane pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -4252,7 +4252,7 @@ fn can_increase_size_into_pane_stack_non_directionally() { pane pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -4320,7 +4320,7 @@ fn increasing_size_into_main_pane_in_stack_horizontally_does_not_break_stack() { pane focus=true pane } - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -4397,7 +4397,7 @@ fn decreasing_size_of_whole_tab_treats_stacked_panes_properly() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -4457,7 +4457,7 @@ fn increasing_size_of_whole_tab_treats_stacked_panes_properly() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -4521,7 +4521,7 @@ fn cannot_decrease_stack_size_beyond_minimum_height() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -4586,7 +4586,7 @@ fn focus_stacked_pane_over_flexible_pane_with_the_mouse() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -4645,7 +4645,7 @@ fn focus_stacked_pane_under_flexible_pane_with_the_mouse() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -4706,7 +4706,7 @@ fn close_stacked_pane_with_previously_focused_other_pane() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -4774,7 +4774,7 @@ fn close_pane_near_stacked_panes() { tab { pane split_direction="vertical" { pane - pane { children stacked=true; } + pane stacked=true { children; } } } } @@ -4837,7 +4837,7 @@ fn focus_next_pane_expands_stacked_panes() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } @@ -4897,7 +4897,7 @@ fn stacked_panes_can_become_fullscreen() { tab { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } pane } diff --git a/zellij-server/src/ui/pane_boundaries_frame.rs b/zellij-server/src/ui/pane_boundaries_frame.rs index 61ec7a4b..7f8d8017 100644 --- a/zellij-server/src/ui/pane_boundaries_frame.rs +++ b/zellij-server/src/ui/pane_boundaries_frame.rs @@ -343,7 +343,9 @@ impl PaneFrame { self.render_my_and_others_focus(max_length) } else if !self.other_focused_clients.is_empty() { self.render_other_focused_users(max_length) - } else if self.pane_is_stacked_under || self.pane_is_stacked_over { + } else if (self.pane_is_stacked_under || self.pane_is_stacked_over) + && self.exit_status.is_some() + { let (first_part, first_part_len) = self.first_exited_held_title_part_full(); if first_part_len <= max_length { Some((first_part, first_part_len)) diff --git a/zellij-utils/assets/layouts/compact.swap.kdl b/zellij-utils/assets/layouts/compact.swap.kdl index e38fa210..da9fd593 100644 --- a/zellij-utils/assets/layouts/compact.swap.kdl +++ b/zellij-utils/assets/layouts/compact.swap.kdl @@ -51,7 +51,7 @@ swap_tiled_layout name="stacked" { ui min_panes=4 { pane split_direction="vertical" { pane - pane { children stacked=true; } + pane stacked=true { children; } } } } diff --git a/zellij-utils/assets/layouts/default.swap.kdl b/zellij-utils/assets/layouts/default.swap.kdl index 51e8afed..014ce43f 100644 --- a/zellij-utils/assets/layouts/default.swap.kdl +++ b/zellij-utils/assets/layouts/default.swap.kdl @@ -54,7 +54,7 @@ swap_tiled_layout name="stacked" { ui min_panes=5 { pane split_direction="vertical" { pane - pane { children stacked=true; } + pane stacked=true { children; } } } } diff --git a/zellij-utils/assets/layouts/strider.swap.kdl b/zellij-utils/assets/layouts/strider.swap.kdl index c6df1575..95d219b2 100644 --- a/zellij-utils/assets/layouts/strider.swap.kdl +++ b/zellij-utils/assets/layouts/strider.swap.kdl @@ -62,7 +62,7 @@ swap_tiled_layout name="stacked" { ui min_panes=6 { pane split_direction="vertical" { pane focus=true - pane { children stacked=true; } + pane stacked=true { children; } } } } diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index f15fd709..670273b0 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -1849,3 +1849,96 @@ fn can_load_swap_layouts_from_a_different_file() { .unwrap(); assert_snapshot!(format!("{:#?}", layout)); } + +#[test] +fn can_define_stacked_children_for_pane_node() { + let kdl_layout = r#" + layout { + pane stacked=true { + pane + pane + } + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn can_define_stacked_children_for_pane_template() { + let kdl_layout = r#" + layout { + pane_template name="stack" stacked=true { + children + } + stack { + pane + pane + } + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn cannot_define_stacked_panes_for_bare_node() { + let kdl_layout = r#" + layout { + pane stacked=true + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); + assert!(layout.is_err(), "error provided for tab name with space"); +} + +#[test] +fn cannot_define_stacked_panes_with_vertical_split_direction() { + let kdl_layout = r#" + layout { + pane stacked=true split_direction="vertical" { + pane + pane + } + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); + assert!(layout.is_err(), "error provided for tab name with space"); +} + +#[test] +fn cannot_define_stacked_panes_with_grandchildren() { + let kdl_layout = r#" + layout { + pane stacked=true { + pane { + pane + pane + } + pane + } + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); + assert!(layout.is_err(), "error provided for tab name with space"); +} + +#[test] +fn cannot_define_stacked_panes_with_grandchildren_in_pane_template() { + let kdl_layout = r#" + layout { + pane_template name="stack" stacked=true { + children + } + stack { + pane + pane { + pane + pane + } + } + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None, None); + assert!(layout.is_err(), "error provided for tab name with space"); +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_node.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_node.snap new file mode 100644 index 00000000..74a872bc --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_node.snap @@ -0,0 +1,63 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1870 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + tabs: [], + focused_tab_index: None, + template: Some( + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: true, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), + ), + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_template.snap new file mode 100644 index 00000000..9653eda5 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_template.snap @@ -0,0 +1,75 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1893 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + tabs: [], + focused_tab_index: None, + template: Some( + ( + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + TiledPaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: true, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: true, + }, + ], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + children_are_stacked: false, + }, + [], + ), + ), + swap_layouts: [], + swap_tiled_layouts: [], + swap_floating_layouts: [], +} diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 7125d1fe..5d89fab1 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -53,6 +53,8 @@ impl<'a> KdlLayoutParser<'a> { } } fn is_a_reserved_word(&self, word: &str) -> bool { + // note that it's important that none of these words happens to also be a config property, + // otherwise they might collide word == "pane" || word == "layout" || word == "pane_template" @@ -90,6 +92,7 @@ impl<'a> KdlLayoutParser<'a> { || property_name == "split_direction" || property_name == "pane" || property_name == "children" + || property_name == "stacked" } fn is_a_valid_floating_pane_property(&self, property_name: &str) -> bool { property_name == "borderless" @@ -166,6 +169,25 @@ impl<'a> KdlLayoutParser<'a> { Ok(()) } } + fn assert_no_grandchildren_in_stack( + &self, + children: &[KdlNode], + is_part_of_stack: bool, + ) -> Result<(), ConfigError> { + if is_part_of_stack { + for child in children { + if kdl_name!(child) == "pane" || self.pane_templates.get(kdl_name!(child)).is_some() + { + return Err(ConfigError::new_layout_kdl_error( + format!("Stacked panes cannot have children"), + child.span().offset(), + child.span().len(), + )); + } + } + } + Ok(()) + } fn parse_split_size(&self, kdl_node: &KdlNode) -> Result, ConfigError> { if let Some(size) = kdl_get_string_property_or_child_value!(kdl_node, "size") { match SplitSize::from_str(size) { @@ -408,8 +430,14 @@ impl<'a> KdlLayoutParser<'a> { } Ok(run) } - fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result { + fn parse_pane_node( + &self, + kdl_node: &KdlNode, + is_part_of_stack: bool, + ) -> Result { self.assert_valid_pane_properties(kdl_node)?; + let children_are_stacked = + kdl_get_bool_property_or_child_value_with_error!(kdl_node, "stacked").unwrap_or(false); let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus"); let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name") @@ -417,11 +445,26 @@ impl<'a> KdlLayoutParser<'a> { let split_size = self.parse_split_size(kdl_node)?; let run = self.parse_command_plugin_or_edit_block(kdl_node)?; let children_split_direction = self.parse_split_direction(kdl_node)?; - let (external_children_index, children_are_stacked, children) = - match kdl_children_nodes!(kdl_node) { - Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, - None => (None, false, vec![]), - }; + let (external_children_index, children) = match kdl_children_nodes!(kdl_node) { + Some(children) => { + self.assert_no_grandchildren_in_stack(&children, is_part_of_stack)?; + self.parse_child_pane_nodes_for_pane(&children, children_are_stacked)? + }, + None => (None, vec![]), + }; + if children_are_stacked && external_children_index.is_none() && children.is_empty() { + return Err(ConfigError::new_layout_kdl_error( + format!("A stacked pane must have children nodes or possibly a \"children\" node if in a swap_layout"), + kdl_node.span().offset(), + kdl_node.span().len(), + )); + } else if children_are_stacked && children_split_direction == SplitDirection::Vertical { + return Err(ConfigError::new_layout_kdl_error( + format!("Stacked panes cannot be vertical"), + kdl_node.span().offset(), + kdl_node.span().len(), + )); + } self.assert_no_mixed_children_and_properties(kdl_node)?; Ok(TiledPaneLayout { borderless: borderless.unwrap_or_default(), @@ -467,12 +510,16 @@ impl<'a> KdlLayoutParser<'a> { pane_template: &mut TiledPaneLayout, pane_template_kdl_node: &KdlNode, ) -> Result<(), ConfigError> { + let children_are_stacked = + kdl_get_bool_property_or_child_value_with_error!(kdl_node, "stacked") + .unwrap_or(pane_template.children_are_stacked); let children_split_direction = self.parse_split_direction(kdl_node)?; - let (external_children_index, children_are_stacked, pane_parts) = - match kdl_children_nodes!(kdl_node) { - Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, - None => (None, false, vec![]), - }; + let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) { + Some(children) => { + self.parse_child_pane_nodes_for_pane(&children, children_are_stacked)? + }, + None => (None, vec![]), + }; if pane_parts.len() > 0 { let child_panes_layout = TiledPaneLayout { children_split_direction, @@ -493,20 +540,14 @@ impl<'a> KdlLayoutParser<'a> { fn populate_external_children_index( &self, kdl_node: &KdlNode, - ) -> Result, ConfigError> { - // Option<(external_children_index, is_stacked)> + ) -> Result, ConfigError> { + // Option if let Some(pane_child_nodes) = kdl_children_nodes!(kdl_node) { for (i, child) in pane_child_nodes.iter().enumerate() { if kdl_name!(child) == "children" { - let stacked = - kdl_get_bool_property_or_child_value_with_error!(kdl_node, "stacked") - .unwrap_or(false); if let Some(grand_children) = kdl_children_nodes!(child) { - let grand_children: Vec<&str> = grand_children - .iter() - .map(|g| kdl_name!(g)) - .filter(|g| g != &"stacked") - .collect(); + let grand_children: Vec<&str> = + grand_children.iter().map(|g| kdl_name!(g)).collect(); if !grand_children.is_empty() { return Err(ConfigError::new_layout_kdl_error( format!( @@ -518,7 +559,7 @@ impl<'a> KdlLayoutParser<'a> { )); } } - return Ok(Some((i, stacked))); + return Ok(Some(i)); } } } @@ -539,6 +580,8 @@ impl<'a> KdlLayoutParser<'a> { let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus"); let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name") .map(|name| name.to_string()); + let children_are_stacked = + kdl_get_bool_property_or_child_value_with_error!(kdl_node, "stacked"); let args = self.parse_args(kdl_node)?; let close_on_exit = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "close_on_exit"); @@ -547,10 +590,7 @@ impl<'a> KdlLayoutParser<'a> { let split_size = self.parse_split_size(kdl_node)?; let run = self.parse_command_plugin_or_edit_block_for_template(kdl_node)?; - // TODO: change should_insert_children to should_keep_pane_external_children_index - // or smth - let external_children_index_and_is_stacked = if should_mark_external_children_index - { + let external_children_index = if should_mark_external_children_index { self.populate_external_children_index(kdl_node)? } else { None @@ -589,15 +629,18 @@ impl<'a> KdlLayoutParser<'a> { pane_template.split_size = Some(split_size); } if let Some(index_of_children) = pane_template.external_children_index { - pane_template - .children - .insert(index_of_children, TiledPaneLayout::default()); + pane_template.children.insert( + index_of_children, + TiledPaneLayout { + children_are_stacked: children_are_stacked.unwrap_or_default(), + ..Default::default() + }, + ); } - pane_template.external_children_index = - external_children_index_and_is_stacked.map(|(index, _is_stacked)| index); - pane_template.children_are_stacked = external_children_index_and_is_stacked - .map(|(_index, is_stacked)| is_stacked) - .unwrap_or(false); + if let Some(children_are_stacked) = children_are_stacked { + pane_template.children_are_stacked = children_are_stacked; + } + pane_template.external_children_index = external_children_index; Ok(pane_template) }, PaneOrFloatingPane::FloatingPane(_) => { @@ -758,6 +801,8 @@ impl<'a> KdlLayoutParser<'a> { ) -> Result { // pane properties let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); + let children_are_stacked = + kdl_get_bool_property_or_child_value_with_error!(kdl_node, "stacked"); let split_size = self.parse_split_size(kdl_node)?; let split_direction = kdl_get_string_property_or_child_value_with_error!(kdl_node, "split_direction"); @@ -772,6 +817,7 @@ impl<'a> KdlLayoutParser<'a> { let has_pane_properties = borderless.is_some() || split_size.is_some() || split_direction.is_some() + || children_are_stacked.is_some() || has_children_nodes; let has_floating_pane_properties = height.is_some() || width.is_some() || x.is_some() || y.is_some(); @@ -789,6 +835,8 @@ impl<'a> KdlLayoutParser<'a> { // pane properties let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); + let children_are_stacked = + kdl_get_bool_property_or_child_value_with_error!(kdl_node, "stacked"); let split_size = self.parse_split_size(kdl_node)?; let split_direction = kdl_get_string_property_or_child_value_with_error!(kdl_node, "split_direction"); @@ -803,6 +851,7 @@ impl<'a> KdlLayoutParser<'a> { let has_pane_properties = borderless.is_some() || split_size.is_some() || split_direction.is_some() + || children_are_stacked.is_some() || has_children_nodes; let has_floating_pane_properties = height.is_some() || width.is_some() || x.is_some() || y.is_some(); @@ -812,6 +861,9 @@ impl<'a> KdlLayoutParser<'a> { if borderless.is_some() { pane_properties.push("borderless"); } + if children_are_stacked.is_some() { + pane_properties.push("stacked"); + } if split_size.is_some() { pane_properties.push("split_size"); } @@ -905,13 +957,18 @@ impl<'a> KdlLayoutParser<'a> { // pane properties let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); + let children_are_stacked = + kdl_get_bool_property_or_child_value_with_error!(kdl_node, "stacked") + .unwrap_or(false); let split_size = self.parse_split_size(kdl_node)?; let children_split_direction = self.parse_split_direction(kdl_node)?; - let (external_children_index, children_are_stacked, pane_parts) = - match kdl_children_nodes!(kdl_node) { - Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, - None => (None, false, vec![]), - }; + let is_part_of_stack = false; + let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) { + Some(children) => { + self.parse_child_pane_nodes_for_pane(&children, children_are_stacked)? + }, + None => (None, vec![]), + }; self.assert_no_mixed_children_and_properties(kdl_node)?; self.pane_templates.insert( template_name, @@ -983,9 +1040,10 @@ impl<'a> KdlLayoutParser<'a> { child_floating_panes: &mut Vec, ) -> Result, ConfigError> { let mut nodes = vec![]; + let is_part_of_stack = false; for child in children { if kdl_name!(child) == "pane" { - nodes.push(self.parse_pane_node(child)?); + nodes.push(self.parse_pane_node(child, is_part_of_stack)?); } else if let Some((pane_template, pane_template_kdl_node)) = self.pane_templates.get(kdl_name!(child)).cloned() { @@ -1019,23 +1077,18 @@ impl<'a> KdlLayoutParser<'a> { fn parse_child_pane_nodes_for_pane( &self, children: &[KdlNode], - ) -> Result<(Option, bool, Vec), ConfigError> { - // usize is external_children_index, bool is "children_are_stacked" + is_part_of_stack: bool, + ) -> Result<(Option, Vec), ConfigError> { + // usize is external_children_index let mut external_children_index = None; - let mut children_are_stacked = false; let mut nodes = vec![]; for (i, child) in children.iter().enumerate() { if kdl_name!(child) == "pane" { - nodes.push(self.parse_pane_node(child)?); + nodes.push(self.parse_pane_node(child, is_part_of_stack)?); } else if kdl_name!(child) == "children" { - let stacked = kdl_get_bool_property_or_child_value_with_error!(child, "stacked") - .unwrap_or(false); if let Some(grand_children) = kdl_children_nodes!(child) { - let grand_children: Vec<&str> = grand_children - .iter() - .map(|g| kdl_name!(g)) - .filter(|g| g != &"stacked") - .collect(); + let grand_children: Vec<&str> = + grand_children.iter().map(|g| kdl_name!(g)).collect(); if !grand_children.is_empty() { return Err(ConfigError::new_layout_kdl_error( format!( @@ -1048,7 +1101,6 @@ impl<'a> KdlLayoutParser<'a> { } } external_children_index = Some(i); - children_are_stacked = stacked; } else if let Some((pane_template, pane_template_kdl_node)) = self.pane_templates.get(kdl_name!(child)).cloned() { @@ -1067,7 +1119,7 @@ impl<'a> KdlLayoutParser<'a> { )); } } - Ok((external_children_index, children_are_stacked, nodes)) + Ok((external_children_index, nodes)) } fn has_child_nodes(&self, kdl_node: &KdlNode) -> bool { if let Some(children) = kdl_children_nodes!(kdl_node) { @@ -1445,10 +1497,11 @@ impl<'a> KdlLayoutParser<'a> { let mut tab_floating_children = vec![]; let mut external_children_index = None; let mut children_index_offset = 0; + let is_part_of_stack = false; if let Some(children) = kdl_children_nodes!(kdl_node) { for (i, child) in children.iter().enumerate() { if kdl_name!(child) == "pane" { - tab_children.push(self.parse_pane_node(child)?); + tab_children.push(self.parse_pane_node(child, is_part_of_stack)?); } else if kdl_name!(child) == "children" { let node_has_child_nodes = child.children().map(|c| !c.is_empty()).unwrap_or(false); @@ -1925,7 +1978,8 @@ impl<'a> KdlLayoutParser<'a> { )); } if child_name == "pane" { - let mut pane_node = self.parse_pane_node(child)?; + let is_part_of_stack = false; + let mut pane_node = self.parse_pane_node(child, is_part_of_stack)?; if let Some(global_cwd) = &self.global_cwd { pane_node.add_cwd_to_layout(&global_cwd); } @@ -2109,6 +2163,17 @@ impl<'a> KdlLayoutParser<'a> { .filter(|n| kdl_name!(n) == "layout") .count() > 1; + let mut non_layout_nodes_in_root = kdl_layout + .nodes() + .iter() + .filter(|n| kdl_name!(n) != "layout" && self.is_a_reserved_word(kdl_name!(n))); + if let Some(first_non_layout_node) = non_layout_nodes_in_root.next() { + return Err(ConfigError::new_layout_kdl_error( + "This node should be inside the main \"layout\" node".into(), + first_non_layout_node.span().offset(), + first_non_layout_node.span().len(), + )); + } if has_multiple_layout_nodes { return Err(ConfigError::new_layout_kdl_error( "Only one layout node per file allowed".into(),