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
This commit is contained in:
Aram Drevekenin 2023-02-25 11:16:11 +01:00 committed by GitHub
parent d65e8220b6
commit 5bcc1bb382
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 391 additions and 99 deletions

View file

@ -358,13 +358,7 @@ impl Pane for TerminalPane {
self.pane_name.clone() self.pane_name.clone()
}; };
let mut frame_geom = self.current_geom(); let 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 mut frame = PaneFrame::new( let mut frame = PaneFrame::new(
frame_geom.into(), frame_geom.into(),
self.grid.scrollback_position_and_length(), self.grid.scrollback_position_and_length(),

View file

@ -1,11 +1,11 @@
--- ---
source: zellij-server/src/tab/./unit/tab_integration_tests.rs source: zellij-server/src/tab/./unit/tab_integration_tests.rs
assertion_line: 3204 assertion_line: 3249
expression: snapshot expression: snapshot
--- ---
00 (C): ├─ Pane #2 ───────────────────────────────────────────────── 00 (C): ├─ Pane #2 ─────────────────────────────────────────────────
01 (C): ├─ Pane #3 ───────────────────────────────────────────────── 01 (C): ├─ Pane #3 ─────────────────────────────────────────────────
02 (C): ├─ Pane #4 ───────────────────────────────────────────────── 02 (C): ├─ Pane #4 ─────────────────────────────────────────────────
03 (C): │ 03 (C): │
04 (C): │ 04 (C): │
05 (C): │ 05 (C): │

View file

@ -3163,7 +3163,7 @@ fn swap_tiled_layout_with_stacked_children() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true 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 { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
} }
} }
@ -3263,7 +3263,7 @@ fn move_focus_up_with_stacked_panes() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
} }
} }
@ -3315,7 +3315,7 @@ fn move_focus_down_with_stacked_panes() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
} }
} }
@ -3371,7 +3371,7 @@ fn move_focus_right_into_stacked_panes() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane pane
pane { children stacked=true; } pane stacked=true { children; }
} }
} }
} }
@ -3431,7 +3431,7 @@ fn move_focus_left_into_stacked_panes() {
swap_tiled_layout { swap_tiled_layout {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane { children stacked=true; } pane stacked=true { children; }
pane focus=true pane focus=true
} }
} }
@ -3494,7 +3494,7 @@ fn move_focus_up_into_stacked_panes() {
pane pane
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -3558,7 +3558,7 @@ fn move_focus_down_into_stacked_panes() {
pane pane
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -3617,7 +3617,7 @@ fn close_main_stacked_pane() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
} }
} }
@ -3668,7 +3668,7 @@ fn close_main_stacked_pane_in_mid_stack() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true 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 { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true 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 { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true 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 { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true 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
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -3976,7 +3976,7 @@ fn can_increase_size_of_main_pane_in_stack_non_directionally() {
pane pane
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -4044,7 +4044,7 @@ fn increasing_size_of_main_pane_in_stack_horizontally_does_not_break_stack() {
pane focus=true pane focus=true
pane pane
} }
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -4125,7 +4125,7 @@ fn can_increase_size_into_pane_stack_horizontally() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true 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
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -4252,7 +4252,7 @@ fn can_increase_size_into_pane_stack_non_directionally() {
pane pane
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -4320,7 +4320,7 @@ fn increasing_size_into_main_pane_in_stack_horizontally_does_not_break_stack() {
pane focus=true pane focus=true
pane pane
} }
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -4397,7 +4397,7 @@ fn decreasing_size_of_whole_tab_treats_stacked_panes_properly() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true 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 { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
} }
} }
@ -4521,7 +4521,7 @@ fn cannot_decrease_stack_size_beyond_minimum_height() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -4586,7 +4586,7 @@ fn focus_stacked_pane_over_flexible_pane_with_the_mouse() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -4645,7 +4645,7 @@ fn focus_stacked_pane_under_flexible_pane_with_the_mouse() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -4706,7 +4706,7 @@ fn close_stacked_pane_with_previously_focused_other_pane() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -4774,7 +4774,7 @@ fn close_pane_near_stacked_panes() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane pane
pane { children stacked=true; } pane stacked=true { children; }
} }
} }
} }
@ -4837,7 +4837,7 @@ fn focus_next_pane_expands_stacked_panes() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }
@ -4897,7 +4897,7 @@ fn stacked_panes_can_become_fullscreen() {
tab { tab {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
pane pane
} }

View file

@ -343,7 +343,9 @@ impl PaneFrame {
self.render_my_and_others_focus(max_length) self.render_my_and_others_focus(max_length)
} else if !self.other_focused_clients.is_empty() { } else if !self.other_focused_clients.is_empty() {
self.render_other_focused_users(max_length) 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(); let (first_part, first_part_len) = self.first_exited_held_title_part_full();
if first_part_len <= max_length { if first_part_len <= max_length {
Some((first_part, first_part_len)) Some((first_part, first_part_len))

View file

@ -51,7 +51,7 @@ swap_tiled_layout name="stacked" {
ui min_panes=4 { ui min_panes=4 {
pane split_direction="vertical" { pane split_direction="vertical" {
pane pane
pane { children stacked=true; } pane stacked=true { children; }
} }
} }
} }

View file

@ -54,7 +54,7 @@ swap_tiled_layout name="stacked" {
ui min_panes=5 { ui min_panes=5 {
pane split_direction="vertical" { pane split_direction="vertical" {
pane pane
pane { children stacked=true; } pane stacked=true { children; }
} }
} }
} }

View file

@ -62,7 +62,7 @@ swap_tiled_layout name="stacked" {
ui min_panes=6 { ui min_panes=6 {
pane split_direction="vertical" { pane split_direction="vertical" {
pane focus=true pane focus=true
pane { children stacked=true; } pane stacked=true { children; }
} }
} }
} }

View file

@ -1849,3 +1849,96 @@ fn can_load_swap_layouts_from_a_different_file() {
.unwrap(); .unwrap();
assert_snapshot!(format!("{:#?}", layout)); 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");
}

View file

@ -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: [],
}

View file

@ -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: [],
}

View file

@ -53,6 +53,8 @@ impl<'a> KdlLayoutParser<'a> {
} }
} }
fn is_a_reserved_word(&self, word: &str) -> bool { 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 == "pane"
|| word == "layout" || word == "layout"
|| word == "pane_template" || word == "pane_template"
@ -90,6 +92,7 @@ impl<'a> KdlLayoutParser<'a> {
|| property_name == "split_direction" || property_name == "split_direction"
|| property_name == "pane" || property_name == "pane"
|| property_name == "children" || property_name == "children"
|| property_name == "stacked"
} }
fn is_a_valid_floating_pane_property(&self, property_name: &str) -> bool { fn is_a_valid_floating_pane_property(&self, property_name: &str) -> bool {
property_name == "borderless" property_name == "borderless"
@ -166,6 +169,25 @@ impl<'a> KdlLayoutParser<'a> {
Ok(()) 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<Option<SplitSize>, ConfigError> { fn parse_split_size(&self, kdl_node: &KdlNode) -> Result<Option<SplitSize>, ConfigError> {
if let Some(size) = kdl_get_string_property_or_child_value!(kdl_node, "size") { if let Some(size) = kdl_get_string_property_or_child_value!(kdl_node, "size") {
match SplitSize::from_str(size) { match SplitSize::from_str(size) {
@ -408,8 +430,14 @@ impl<'a> KdlLayoutParser<'a> {
} }
Ok(run) Ok(run)
} }
fn parse_pane_node(&self, kdl_node: &KdlNode) -> Result<TiledPaneLayout, ConfigError> { fn parse_pane_node(
&self,
kdl_node: &KdlNode,
is_part_of_stack: bool,
) -> Result<TiledPaneLayout, ConfigError> {
self.assert_valid_pane_properties(kdl_node)?; 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 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 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") 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 split_size = self.parse_split_size(kdl_node)?;
let run = self.parse_command_plugin_or_edit_block(kdl_node)?; let run = self.parse_command_plugin_or_edit_block(kdl_node)?;
let children_split_direction = self.parse_split_direction(kdl_node)?; let children_split_direction = self.parse_split_direction(kdl_node)?;
let (external_children_index, children_are_stacked, children) = let (external_children_index, children) = match kdl_children_nodes!(kdl_node) {
match kdl_children_nodes!(kdl_node) { Some(children) => {
Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, self.assert_no_grandchildren_in_stack(&children, is_part_of_stack)?;
None => (None, false, vec![]), 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)?; self.assert_no_mixed_children_and_properties(kdl_node)?;
Ok(TiledPaneLayout { Ok(TiledPaneLayout {
borderless: borderless.unwrap_or_default(), borderless: borderless.unwrap_or_default(),
@ -467,12 +510,16 @@ impl<'a> KdlLayoutParser<'a> {
pane_template: &mut TiledPaneLayout, pane_template: &mut TiledPaneLayout,
pane_template_kdl_node: &KdlNode, pane_template_kdl_node: &KdlNode,
) -> Result<(), ConfigError> { ) -> 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 children_split_direction = self.parse_split_direction(kdl_node)?;
let (external_children_index, children_are_stacked, pane_parts) = let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) {
match kdl_children_nodes!(kdl_node) { Some(children) => {
Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, self.parse_child_pane_nodes_for_pane(&children, children_are_stacked)?
None => (None, false, vec![]), },
}; None => (None, vec![]),
};
if pane_parts.len() > 0 { if pane_parts.len() > 0 {
let child_panes_layout = TiledPaneLayout { let child_panes_layout = TiledPaneLayout {
children_split_direction, children_split_direction,
@ -493,20 +540,14 @@ impl<'a> KdlLayoutParser<'a> {
fn populate_external_children_index( fn populate_external_children_index(
&self, &self,
kdl_node: &KdlNode, kdl_node: &KdlNode,
) -> Result<Option<(usize, bool)>, ConfigError> { ) -> Result<Option<usize>, ConfigError> {
// Option<(external_children_index, is_stacked)> // Option<external_children_index>
if let Some(pane_child_nodes) = kdl_children_nodes!(kdl_node) { if let Some(pane_child_nodes) = kdl_children_nodes!(kdl_node) {
for (i, child) in pane_child_nodes.iter().enumerate() { for (i, child) in pane_child_nodes.iter().enumerate() {
if kdl_name!(child) == "children" { 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) { if let Some(grand_children) = kdl_children_nodes!(child) {
let grand_children: Vec<&str> = grand_children let grand_children: Vec<&str> =
.iter() grand_children.iter().map(|g| kdl_name!(g)).collect();
.map(|g| kdl_name!(g))
.filter(|g| g != &"stacked")
.collect();
if !grand_children.is_empty() { if !grand_children.is_empty() {
return Err(ConfigError::new_layout_kdl_error( return Err(ConfigError::new_layout_kdl_error(
format!( 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 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") let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name")
.map(|name| name.to_string()); .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 args = self.parse_args(kdl_node)?;
let close_on_exit = let close_on_exit =
kdl_get_bool_property_or_child_value_with_error!(kdl_node, "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 split_size = self.parse_split_size(kdl_node)?;
let run = self.parse_command_plugin_or_edit_block_for_template(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 let external_children_index = if should_mark_external_children_index {
// or smth
let external_children_index_and_is_stacked = if should_mark_external_children_index
{
self.populate_external_children_index(kdl_node)? self.populate_external_children_index(kdl_node)?
} else { } else {
None None
@ -589,15 +629,18 @@ impl<'a> KdlLayoutParser<'a> {
pane_template.split_size = Some(split_size); pane_template.split_size = Some(split_size);
} }
if let Some(index_of_children) = pane_template.external_children_index { if let Some(index_of_children) = pane_template.external_children_index {
pane_template pane_template.children.insert(
.children index_of_children,
.insert(index_of_children, TiledPaneLayout::default()); TiledPaneLayout {
children_are_stacked: children_are_stacked.unwrap_or_default(),
..Default::default()
},
);
} }
pane_template.external_children_index = if let Some(children_are_stacked) = children_are_stacked {
external_children_index_and_is_stacked.map(|(index, _is_stacked)| index); pane_template.children_are_stacked = children_are_stacked;
pane_template.children_are_stacked = external_children_index_and_is_stacked }
.map(|(_index, is_stacked)| is_stacked) pane_template.external_children_index = external_children_index;
.unwrap_or(false);
Ok(pane_template) Ok(pane_template)
}, },
PaneOrFloatingPane::FloatingPane(_) => { PaneOrFloatingPane::FloatingPane(_) => {
@ -758,6 +801,8 @@ impl<'a> KdlLayoutParser<'a> {
) -> Result<bool, ConfigError> { ) -> Result<bool, ConfigError> {
// pane properties // pane properties
let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); 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_size = self.parse_split_size(kdl_node)?;
let split_direction = let split_direction =
kdl_get_string_property_or_child_value_with_error!(kdl_node, "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() let has_pane_properties = borderless.is_some()
|| split_size.is_some() || split_size.is_some()
|| split_direction.is_some() || split_direction.is_some()
|| children_are_stacked.is_some()
|| has_children_nodes; || has_children_nodes;
let has_floating_pane_properties = let has_floating_pane_properties =
height.is_some() || width.is_some() || x.is_some() || y.is_some(); height.is_some() || width.is_some() || x.is_some() || y.is_some();
@ -789,6 +835,8 @@ impl<'a> KdlLayoutParser<'a> {
// pane properties // pane properties
let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); 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_size = self.parse_split_size(kdl_node)?;
let split_direction = let split_direction =
kdl_get_string_property_or_child_value_with_error!(kdl_node, "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() let has_pane_properties = borderless.is_some()
|| split_size.is_some() || split_size.is_some()
|| split_direction.is_some() || split_direction.is_some()
|| children_are_stacked.is_some()
|| has_children_nodes; || has_children_nodes;
let has_floating_pane_properties = let has_floating_pane_properties =
height.is_some() || width.is_some() || x.is_some() || y.is_some(); height.is_some() || width.is_some() || x.is_some() || y.is_some();
@ -812,6 +861,9 @@ impl<'a> KdlLayoutParser<'a> {
if borderless.is_some() { if borderless.is_some() {
pane_properties.push("borderless"); pane_properties.push("borderless");
} }
if children_are_stacked.is_some() {
pane_properties.push("stacked");
}
if split_size.is_some() { if split_size.is_some() {
pane_properties.push("split_size"); pane_properties.push("split_size");
} }
@ -905,13 +957,18 @@ impl<'a> KdlLayoutParser<'a> {
// pane properties // pane properties
let borderless = let borderless =
kdl_get_bool_property_or_child_value_with_error!(kdl_node, "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 split_size = self.parse_split_size(kdl_node)?;
let children_split_direction = self.parse_split_direction(kdl_node)?; let children_split_direction = self.parse_split_direction(kdl_node)?;
let (external_children_index, children_are_stacked, pane_parts) = let is_part_of_stack = false;
match kdl_children_nodes!(kdl_node) { let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) {
Some(children) => self.parse_child_pane_nodes_for_pane(&children)?, Some(children) => {
None => (None, false, vec![]), self.parse_child_pane_nodes_for_pane(&children, children_are_stacked)?
}; },
None => (None, vec![]),
};
self.assert_no_mixed_children_and_properties(kdl_node)?; self.assert_no_mixed_children_and_properties(kdl_node)?;
self.pane_templates.insert( self.pane_templates.insert(
template_name, template_name,
@ -983,9 +1040,10 @@ impl<'a> KdlLayoutParser<'a> {
child_floating_panes: &mut Vec<FloatingPaneLayout>, child_floating_panes: &mut Vec<FloatingPaneLayout>,
) -> Result<Vec<TiledPaneLayout>, ConfigError> { ) -> Result<Vec<TiledPaneLayout>, ConfigError> {
let mut nodes = vec![]; let mut nodes = vec![];
let is_part_of_stack = false;
for child in children { for child in children {
if kdl_name!(child) == "pane" { 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)) = } else if let Some((pane_template, pane_template_kdl_node)) =
self.pane_templates.get(kdl_name!(child)).cloned() self.pane_templates.get(kdl_name!(child)).cloned()
{ {
@ -1019,23 +1077,18 @@ impl<'a> KdlLayoutParser<'a> {
fn parse_child_pane_nodes_for_pane( fn parse_child_pane_nodes_for_pane(
&self, &self,
children: &[KdlNode], children: &[KdlNode],
) -> Result<(Option<usize>, bool, Vec<TiledPaneLayout>), ConfigError> { is_part_of_stack: bool,
// usize is external_children_index, bool is "children_are_stacked" ) -> Result<(Option<usize>, Vec<TiledPaneLayout>), ConfigError> {
// usize is external_children_index
let mut external_children_index = None; let mut external_children_index = None;
let mut children_are_stacked = false;
let mut nodes = vec![]; let mut nodes = vec![];
for (i, child) in children.iter().enumerate() { for (i, child) in children.iter().enumerate() {
if kdl_name!(child) == "pane" { 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" { } 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) { if let Some(grand_children) = kdl_children_nodes!(child) {
let grand_children: Vec<&str> = grand_children let grand_children: Vec<&str> =
.iter() grand_children.iter().map(|g| kdl_name!(g)).collect();
.map(|g| kdl_name!(g))
.filter(|g| g != &"stacked")
.collect();
if !grand_children.is_empty() { if !grand_children.is_empty() {
return Err(ConfigError::new_layout_kdl_error( return Err(ConfigError::new_layout_kdl_error(
format!( format!(
@ -1048,7 +1101,6 @@ impl<'a> KdlLayoutParser<'a> {
} }
} }
external_children_index = Some(i); external_children_index = Some(i);
children_are_stacked = stacked;
} else if let Some((pane_template, pane_template_kdl_node)) = } else if let Some((pane_template, pane_template_kdl_node)) =
self.pane_templates.get(kdl_name!(child)).cloned() 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 { fn has_child_nodes(&self, kdl_node: &KdlNode) -> bool {
if let Some(children) = kdl_children_nodes!(kdl_node) { 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 tab_floating_children = vec![];
let mut external_children_index = None; let mut external_children_index = None;
let mut children_index_offset = 0; let mut children_index_offset = 0;
let is_part_of_stack = false;
if let Some(children) = kdl_children_nodes!(kdl_node) { if let Some(children) = kdl_children_nodes!(kdl_node) {
for (i, child) in children.iter().enumerate() { for (i, child) in children.iter().enumerate() {
if kdl_name!(child) == "pane" { 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" { } else if kdl_name!(child) == "children" {
let node_has_child_nodes = let node_has_child_nodes =
child.children().map(|c| !c.is_empty()).unwrap_or(false); child.children().map(|c| !c.is_empty()).unwrap_or(false);
@ -1925,7 +1978,8 @@ impl<'a> KdlLayoutParser<'a> {
)); ));
} }
if child_name == "pane" { 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 { if let Some(global_cwd) = &self.global_cwd {
pane_node.add_cwd_to_layout(&global_cwd); pane_node.add_cwd_to_layout(&global_cwd);
} }
@ -2109,6 +2163,17 @@ impl<'a> KdlLayoutParser<'a> {
.filter(|n| kdl_name!(n) == "layout") .filter(|n| kdl_name!(n) == "layout")
.count() .count()
> 1; > 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 { if has_multiple_layout_nodes {
return Err(ConfigError::new_layout_kdl_error( return Err(ConfigError::new_layout_kdl_error(
"Only one layout node per file allowed".into(), "Only one layout node per file allowed".into(),