From e59b09a1b6701b529c1f444596206969a4c3e90a Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Thu, 20 Oct 2022 15:23:20 +0200 Subject: [PATCH] feat(layouts): allow defining a tab cwd (#1828) --- zellij-utils/src/input/layout.rs | 30 +++++ zellij-utils/src/input/unit/layout_test.rs | 101 +++++++++++++++ ...epended_to_panes_with_and_without_cwd.snap | 75 +++++++++++ ...ith_and_without_cwd_in_pane_templates.snap | 119 ++++++++++++++++++ ...with_and_without_cwd_in_tab_templates.snap | 104 +++++++++++++++ ...th_tab_cwd_given_to_panes_without_cwd.snap | 75 +++++++++++ ...t__tab_cwd_given_to_panes_without_cwd.snap | 75 +++++++++++ ...__tab_cwd_prepended_to_panes_with_cwd.snap | 75 +++++++++++ zellij-utils/src/kdl/kdl_layout_parser.rs | 111 ++++++++-------- 9 files changed, 709 insertions(+), 56 deletions(-) create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap create mode 100644 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.snap create mode 100644 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.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap create mode 100644 zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 8e317a8e..b041412e 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -111,6 +111,25 @@ impl Run { (None, None) => None, } } + pub fn add_cwd(&mut self, cwd: &PathBuf) { + match self { + Run::Command(run_command) => match run_command.cwd.as_mut() { + Some(run_cwd) => { + *run_cwd = cwd.join(&run_cwd); + }, + None => { + run_command.cwd = Some(cwd.clone()); + }, + }, + Run::EditFile(path_to_file, _line_number) => { + *path_to_file = cwd.join(&path_to_file); + }, + Run::Cwd(path) => { + *path = cwd.join(&path); + }, + _ => {}, // plugins aren't yet supported + } + } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] @@ -233,6 +252,17 @@ impl PaneLayout { default_layout.children = vec![PaneLayout::default()]; default_layout } + 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())); + }, + } + for child in self.children.iter_mut() { + child.add_cwd_to_layout(cwd); + } + } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index 6155024b..1addb938 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -1379,3 +1379,104 @@ fn global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file .unwrap(); assert_snapshot!(format!("{:#?}", layout)); } + +#[test] +fn global_cwd_with_tab_cwd_given_to_panes_without_cwd() { + let kdl_layout = r#" + layout { + cwd "/tmp" + tab cwd="./foo" { + pane + pane command="tail" + } + // both should have the /tmp/foo cwd + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn tab_cwd_given_to_panes_without_cwd() { + let kdl_layout = r#" + layout { + tab cwd="/tmp" { + pane + pane command="tail" + } + // both should have the /tmp cwd + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn tab_cwd_prepended_to_panes_with_cwd() { + let kdl_layout = r#" + layout { + tab cwd="/tmp" { + pane cwd="./foo" + pane command="tail" cwd="./foo" + } + // both should have the /tmp/foo cwd + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd() { + let kdl_layout = r#" + layout { + cwd "/tmp" + tab cwd="./foo" { + pane // should have /tmp/foo + pane command="tail" cwd="./bar" // should have /tmp/foo/bar + } + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templates() { + let kdl_layout = r#" + layout { + cwd "/tmp" + pane_template name="my_pane_template" { + pane // should have /tmp/foo + pane command="tail" cwd="./bar" // should have /tmp/foo/bar + children + } + tab cwd="./foo" { + my_pane_template { + pane // should have /tmp/foo + } + } + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} + +#[test] +fn global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templates() { + let kdl_layout = r#" + layout { + cwd "/tmp" + tab_template name="my_tab_template" { + pane // should have /tmp/foo + pane command="tail" cwd="./bar" // should have /tmp/foo/bar + children + } + my_tab_template cwd="./foo" { + pane // should have /tmp/foo + } + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + assert_snapshot!(format!("{:#?}", layout)); +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap new file mode 100644 index 00000000..a83674d2 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap @@ -0,0 +1,75 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1441 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + tabs: [ + ( + None, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp/./foo/./bar", + ), + hold_on_close: true, + }, + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ], + focused_tab_index: None, + template: Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), +} diff --git a/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.snap b/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.snap new file mode 100644 index 00000000..8381dcd1 --- /dev/null +++ b/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.snap @@ -0,0 +1,119 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1462 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + tabs: [ + ( + None, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp/./foo/./bar", + ), + hold_on_close: true, + }, + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ], + focused_tab_index: None, + template: Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), +} diff --git a/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.snap b/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.snap new file mode 100644 index 00000000..65f28704 --- /dev/null +++ b/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.snap @@ -0,0 +1,104 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1481 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + tabs: [ + ( + None, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp/./foo/./bar", + ), + hold_on_close: true, + }, + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ], + focused_tab_index: None, + template: Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap new file mode 100644 index 00000000..4bc2f412 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap @@ -0,0 +1,75 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1396 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + tabs: [ + ( + None, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp/./foo", + ), + hold_on_close: true, + }, + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ], + focused_tab_index: None, + template: Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap new file mode 100644 index 00000000..8f05eda1 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap @@ -0,0 +1,75 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1411 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + tabs: [ + ( + None, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp", + ), + hold_on_close: true, + }, + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: Some( + Cwd( + "/tmp", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ], + focused_tab_index: None, + template: Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), +} diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap new file mode 100644 index 00000000..32eb17d0 --- /dev/null +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap @@ -0,0 +1,75 @@ +--- +source: zellij-utils/src/input/./unit/layout_test.rs +assertion_line: 1426 +expression: "format!(\"{:#?}\", layout)" +--- +Layout { + tabs: [ + ( + None, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [ + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Cwd( + "/tmp/./foo", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: Some( + Command( + RunCommand { + command: "tail", + args: [], + cwd: Some( + "/tmp/./foo", + ), + hold_on_close: true, + }, + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ], + split_size: None, + run: Some( + Cwd( + "/tmp", + ), + ), + borderless: false, + focus: None, + external_children_index: None, + }, + ), + ], + focused_tab_index: None, + template: Some( + PaneLayout { + children_split_direction: Horizontal, + name: None, + children: [], + split_size: None, + run: None, + borderless: false, + focus: None, + external_children_index: None, + }, + ), +} diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 514a80e4..28d9e2f1 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -75,7 +75,10 @@ impl<'a> KdlLayoutParser<'a> { || property_name == "children" } fn is_a_valid_tab_property(&self, property_name: &str) -> bool { - property_name == "focus" || property_name == "name" || property_name == "split_direction" + property_name == "focus" + || property_name == "name" + || property_name == "split_direction" + || property_name == "cwd" } fn assert_legal_node_name(&self, name: &str, kdl_node: &KdlNode) -> Result<(), ConfigError> { if name.contains(char::is_whitespace) { @@ -177,14 +180,18 @@ impl<'a> KdlLayoutParser<'a> { None => Ok(None), } } + fn cwd_prefix(&self, tab_cwd: Option<&PathBuf>) -> Result, ConfigError> { + Ok(match (&self.global_cwd, tab_cwd) { + (Some(global_cwd), Some(tab_cwd)) => Some(global_cwd.join(tab_cwd)), + (None, Some(tab_cwd)) => Some(tab_cwd.clone()), + (Some(global_cwd), None) => Some(global_cwd.clone()), + (None, None) => None, + }) + } fn parse_cwd(&self, kdl_node: &KdlNode) -> Result, ConfigError> { Ok( kdl_get_string_property_or_child_value_with_error!(kdl_node, "cwd") - .and_then(|c| match &self.global_cwd { - Some(global_cwd) => Some(global_cwd.join(c)), - None => Some(PathBuf::from(c)), - }) - .or_else(|| self.global_cwd.as_ref().map(|g| g.clone())), + .map(|cwd| PathBuf::from(cwd)), ) } fn parse_pane_command( @@ -196,13 +203,7 @@ impl<'a> KdlLayoutParser<'a> { .map(|c| PathBuf::from(c)); let edit = kdl_get_string_property_or_child_value_with_error!(pane_node, "edit") .map(|c| PathBuf::from(c)); - let cwd = if is_template { - // we fill the global_cwd for templates later - kdl_get_string_property_or_child_value_with_error!(pane_node, "cwd") - .map(|c| PathBuf::from(c)) - } else { - self.parse_cwd(pane_node)? - }; + let cwd = self.parse_cwd(pane_node)?; let args = self.parse_args(pane_node)?; match (command, edit, cwd, args, is_template) { (None, None, Some(cwd), _, _) => Ok(Some(Run::Cwd(cwd))), @@ -233,7 +234,13 @@ impl<'a> KdlLayoutParser<'a> { ) -> Result, ConfigError> { let mut run = self.parse_pane_command(kdl_node, false)?; if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") { - if run.is_some() { + let has_non_cwd_run_prop = run + .map(|r| match r { + Run::Cwd(_) => false, + _ => true, + }) + .unwrap_or(false); + if has_non_cwd_run_prop { return Err(ConfigError::new_kdl_error( "Cannot have both a command/edit and a plugin block for a single pane".into(), plugin_block.span().offset(), @@ -250,7 +257,13 @@ impl<'a> KdlLayoutParser<'a> { ) -> Result, ConfigError> { let mut run = self.parse_pane_command(kdl_node, true)?; if let Some(plugin_block) = kdl_get_child!(kdl_node, "plugin") { - if run.is_some() { + let has_non_cwd_run_prop = run + .map(|r| match r { + Run::Cwd(_) => false, + _ => true, + }) + .unwrap_or(false); + if has_non_cwd_run_prop { return Err(ConfigError::new_kdl_error( "Cannot have both a command/edit and a plugin block for a single pane".into(), plugin_block.span().offset(), @@ -339,7 +352,6 @@ impl<'a> KdlLayoutParser<'a> { pane_template_kdl_node, )?; pane_template.run = Run::merge(&pane_template.run, &run); - self.populate_global_cwd_for_pane_run(&mut pane_template.run)?; if let (Some(Run::Command(pane_template_run_command)), Some(args)) = (pane_template.run.as_mut(), args) { @@ -367,31 +379,6 @@ impl<'a> KdlLayoutParser<'a> { pane_template.external_children_index = None; Ok(pane_template) } - fn populate_global_cwd_for_pane_run( - &self, - pane_run: &mut Option, - ) -> Result<(), ConfigError> { - if let Some(global_cwd) = &self.global_cwd { - match pane_run.as_mut() { - Some(Run::Command(run_command)) => match run_command.cwd.as_mut() { - Some(run_command_cwd) => { - *run_command_cwd = global_cwd.join(&run_command_cwd); - }, - None => { - run_command.cwd = Some(global_cwd.clone()); - }, - }, - Some(Run::EditFile(path_to_file, _line_number)) => { - *path_to_file = global_cwd.join(&path_to_file); - }, - Some(Run::Cwd(pane_template_cwd)) => { - *pane_template_cwd = global_cwd.join(&pane_template_cwd); - }, - _ => {}, - } - } - Ok(()) - } fn parse_split_direction(&self, kdl_node: &KdlNode) -> Result { match kdl_get_string_property_or_child_value_with_error!(kdl_node, "split_direction") { Some(direction) => match SplitDirection::from_str(direction) { @@ -453,21 +440,23 @@ impl<'a> KdlLayoutParser<'a> { self.assert_valid_tab_properties(kdl_node)?; let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); + let tab_cwd = + kdl_get_string_property_or_child_value!(kdl_node, "cwd").map(|c| PathBuf::from(c)); let is_focused = kdl_get_bool_property_or_child_value!(kdl_node, "focus").unwrap_or(false); let children_split_direction = self.parse_split_direction(kdl_node)?; let children = match kdl_children_nodes!(kdl_node) { Some(children) => self.parse_child_pane_nodes_for_tab(children)?, None => vec![], }; - Ok(( - is_focused, - tab_name, - PaneLayout { - children_split_direction, - children, - ..Default::default() - }, - )) + let mut pane_layout = PaneLayout { + children_split_direction, + children, + ..Default::default() + }; + if let Some(cwd_prefix) = &self.cwd_prefix(tab_cwd.as_ref())? { + pane_layout.add_cwd_to_layout(&cwd_prefix); + } + Ok((is_focused, tab_name, pane_layout)) } fn parse_child_pane_nodes_for_tab( &self, @@ -700,6 +689,8 @@ impl<'a> KdlLayoutParser<'a> { // (is_focused, Option, PaneLayout) let tab_name = kdl_get_string_property_or_child_value!(kdl_node, "name").map(|s| s.to_string()); + let tab_cwd = + kdl_get_string_property_or_child_value!(kdl_node, "cwd").map(|c| PathBuf::from(c)); let is_focused = kdl_get_bool_property_or_child_value!(kdl_node, "focus").unwrap_or(false); let children_split_direction = self.parse_split_direction(kdl_node)?; match kdl_children_nodes!(kdl_node) { @@ -725,6 +716,9 @@ impl<'a> KdlLayoutParser<'a> { } }, } + if let Some(cwd_prefix) = self.cwd_prefix(tab_cwd.as_ref())? { + tab_layout.add_cwd_to_layout(&cwd_prefix); + } tab_layout.external_children_index = None; Ok((is_focused, tab_name, tab_layout)) } @@ -1017,7 +1011,11 @@ impl<'a> KdlLayoutParser<'a> { child.span().len(), )); } - child_panes.push(self.parse_pane_node(child)?); + let mut pane_node = self.parse_pane_node(child)?; + if let Some(global_cwd) = &self.global_cwd { + pane_node.add_cwd_to_layout(&global_cwd); + } + child_panes.push(pane_node); } else if child_name == "tab" { if !child_panes.is_empty() { return Err(ConfigError::new_kdl_error( @@ -1064,11 +1062,12 @@ impl<'a> KdlLayoutParser<'a> { child.span().len(), )); } - child_panes.push(self.parse_pane_node_with_template( - child, - pane_template, - &pane_template_kdl_node, - )?); + let mut pane_template = + self.parse_pane_node_with_template(child, pane_template, &pane_template_kdl_node)?; + if let Some(cwd_prefix) = &self.cwd_prefix(None)? { + pane_template.add_cwd_to_layout(&cwd_prefix); + } + child_panes.push(pane_template); } else if !self.is_a_reserved_word(child_name) { return Err(ConfigError::new_kdl_error( format!("Unknown layout node: '{}'", child_name),