aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2022-10-14 15:08:32 +0200
committerGitHub <noreply@github.com>2022-10-14 15:08:32 +0200
commitd074bb1cda819bd660b76b3be54b4db10d0eeed4 (patch)
treefa85e65655909167cbd998b564e0a91353c3f5a3
parentdocs(changelog): layout fixes (diff)
downloadsl0ppij-d074bb1cda819bd660b76b3be54b4db10d0eeed4.tar.gz
sl0ppij-d074bb1cda819bd660b76b3be54b4db10d0eeed4.tar.xz
sl0ppij-d074bb1cda819bd660b76b3be54b4db10d0eeed4.zip
feat(layouts): global cwd (#1798)
* feat(layouts): allow defining a global cwd * feat(layouts): allow passing global cwd from cli * style(fmt): rustfmt * fix(layouts): error on mixed cwd and pane children
-rw-r--r--zellij-server/src/os_input_output.rs8
-rw-r--r--zellij-server/src/pty.rs33
-rw-r--r--zellij-server/src/tab/unit/tab_integration_tests.rs2
-rw-r--r--zellij-server/src/unit/screen_tests.rs2
-rw-r--r--zellij-utils/src/cli.rs2
-rw-r--r--zellij-utils/src/input/actions.rs4
-rw-r--r--zellij-utils/src/input/layout.rs45
-rw-r--r--zellij-utils/src/input/unit/layout_test.rs322
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap57
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap57
-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.snap57
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap57
-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.snap43
-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.snap43
-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.snap43
-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.snap43
-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.snap43
-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.snap36
-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.snap43
-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.snap43
-rw-r--r--zellij-utils/src/kdl/kdl_layout_parser.rs195
-rw-r--r--zellij-utils/src/kdl/mod.rs8
22 files changed, 1048 insertions, 138 deletions
diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs
index 9d720c71..7d83085f 100644
--- a/zellij-server/src/os_input_output.rs
+++ b/zellij-server/src/os_input_output.rs
@@ -140,8 +140,14 @@ fn handle_openpty(
let cmd = cmd.clone();
let command = &mut Command::new(cmd.command);
if let Some(current_dir) = cmd.cwd {
- if current_dir.exists() {
+ if current_dir.exists() && current_dir.is_dir() {
command.current_dir(current_dir);
+ } else {
+ // TODO: propagate this to the user
+ log::error!(
+ "Failed to set CWD for new pane. {} does not exist or is not a folder",
+ current_dir.display()
+ );
}
}
command
diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs
index 9df221ce..84088b95 100644
--- a/zellij-server/src/pty.rs
+++ b/zellij-server/src/pty.rs
@@ -364,11 +364,11 @@ impl Pty {
default_editor,
}
}
- pub fn get_default_terminal(&self) -> TerminalAction {
+ pub fn get_default_terminal(&self, cwd: Option<PathBuf>) -> TerminalAction {
TerminalAction::RunCommand(RunCommand {
args: vec![],
command: PathBuf::from(env::var("SHELL").expect("Could not find the SHELL variable")),
- cwd: None, // this should be filled by the calling function, eg. spawn_terminal
+ cwd, // note: this might also be filled by the calling function, eg. spawn_terminal
hold_on_close: false,
})
}
@@ -400,12 +400,12 @@ impl Pty {
let terminal_action = match client_or_tab_index {
ClientOrTabIndex::ClientId(client_id) => {
let mut terminal_action =
- terminal_action.unwrap_or_else(|| self.get_default_terminal());
+ terminal_action.unwrap_or_else(|| self.get_default_terminal(None));
self.fill_cwd(&mut terminal_action, client_id);
terminal_action
},
ClientOrTabIndex::TabIndex(_) => {
- terminal_action.unwrap_or_else(|| self.get_default_terminal())
+ terminal_action.unwrap_or_else(|| self.get_default_terminal(None))
},
};
let hold_on_close = match &terminal_action {
@@ -454,7 +454,7 @@ impl Pty {
default_shell: Option<TerminalAction>,
client_id: ClientId,
) {
- let mut default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal());
+ let mut default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal(None));
self.fill_cwd(&mut default_shell, client_id);
let extracted_run_instructions = layout.extract_run_instructions();
let mut new_pane_pids: Vec<(u32, Option<RunCommand>, Result<RawFd, SpawnTerminalError>)> =
@@ -513,6 +513,29 @@ impl Pty {
},
}
},
+ Some(Run::Cwd(cwd)) => {
+ let shell = self.get_default_terminal(Some(cwd));
+ match self.bus.os_input.as_mut().unwrap().spawn_terminal(
+ shell,
+ quit_cb,
+ self.default_editor.clone(),
+ ) {
+ Ok((terminal_id, pid_primary, child_fd)) => {
+ self.id_to_child_pid.insert(terminal_id, child_fd);
+ new_pane_pids.push((terminal_id, None, Ok(pid_primary)));
+ },
+ Err(SpawnTerminalError::CommandNotFound(terminal_id)) => {
+ new_pane_pids.push((
+ terminal_id,
+ None,
+ Err(SpawnTerminalError::CommandNotFound(terminal_id)),
+ ));
+ },
+ Err(e) => {
+ log::error!("Failed to spawn terminal: {}", e);
+ },
+ }
+ },
None => {
match self.bus.os_input.as_mut().unwrap().spawn_terminal(
default_shell.clone(),
diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs
index f1efe339..42bfc366 100644
--- a/zellij-server/src/tab/unit/tab_integration_tests.rs
+++ b/zellij-server/src/tab/unit/tab_integration_tests.rs
@@ -251,7 +251,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str)
let copy_options = CopyOptions::default();
let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new()));
let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default()));
- let layout = Layout::from_str(layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_str(layout, "layout_file_name".into(), None).unwrap();
let tab_layout = layout.new_tab();
let mut tab = Tab::new(
index,
diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs
index 85e7b4a0..0ab13bf9 100644
--- a/zellij-server/src/unit/screen_tests.rs
+++ b/zellij-server/src/unit/screen_tests.rs
@@ -2207,6 +2207,7 @@ pub fn send_cli_new_tab_action_default_params() {
let new_tab_action = CliAction::NewTab {
name: None,
layout: None,
+ cwd: None,
};
send_cli_action_to_server(
&session_metadata,
@@ -2242,6 +2243,7 @@ pub fn send_cli_new_tab_action_with_name_and_layout() {
"{}/src/unit/fixtures/layout-with-three-panes.kdl",
env!("CARGO_MANIFEST_DIR")
))),
+ cwd: None,
};
send_cli_action_to_server(
&session_metadata,
diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs
index bef1afb3..ae0c2141 100644
--- a/zellij-utils/src/cli.rs
+++ b/zellij-utils/src/cli.rs
@@ -253,5 +253,7 @@ pub enum CliAction {
layout: Option<PathBuf>,
#[clap(short, long, value_parser)]
name: Option<String>,
+ #[clap(short, long, value_parser, requires("layout"))]
+ cwd: Option<PathBuf>,
},
}
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index de1350dd..d337a75a 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -344,12 +344,12 @@ impl Action {
Action::TabNameInput(name.as_bytes().to_vec()),
]),
CliAction::UndoRenameTab => Ok(vec![Action::UndoRenameTab]),
- CliAction::NewTab { name, layout } => {
+ CliAction::NewTab { name, layout, cwd } => {
if let Some(layout_path) = layout {
let (path_to_raw_layout, raw_layout) =
Layout::stringified_from_path_or_default(Some(&layout_path), None)
.map_err(|e| format!("Failed to load layout: {}", e))?;
- let layout = Layout::from_str(&raw_layout, path_to_raw_layout).map_err(|e| {
+ let layout = Layout::from_str(&raw_layout, path_to_raw_layout, cwd).map_err(|e| {
let stringified_error = match e {
ConfigError::KdlError(kdl_error) => {
let error = kdl_error.add_src(layout_path.as_path().as_os_str().to_string_lossy().to_string(), String::from(raw_layout));
diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs
index f823ed40..8299c0bc 100644
--- a/zellij-utils/src/input/layout.rs
+++ b/zellij-utils/src/input/layout.rs
@@ -62,6 +62,41 @@ pub enum Run {
Plugin(RunPlugin),
#[serde(rename = "command")]
Command(RunCommand),
+ Cwd(PathBuf),
+}
+
+impl Run {
+ pub fn merge(base: &Option<Run>, other: &Option<Run>) -> Option<Run> {
+ // TODO: handle Plugin variants once there's a need
+ match (base, other) {
+ (Some(Run::Command(base_run_command)), Some(Run::Command(other_run_command))) => {
+ let mut merged = other_run_command.clone();
+ if merged.cwd.is_none() && base_run_command.cwd.is_some() {
+ merged.cwd = base_run_command.cwd.clone();
+ }
+ if merged.args.is_empty() && !base_run_command.args.is_empty() {
+ merged.args = base_run_command.args.clone();
+ }
+ Some(Run::Command(merged))
+ },
+ (Some(Run::Command(base_run_command)), Some(Run::Cwd(other_cwd))) => {
+ let mut merged = base_run_command.clone();
+ merged.cwd = Some(other_cwd.clone());
+ Some(Run::Command(merged))
+ },
+ (Some(Run::Cwd(base_cwd)), Some(Run::Command(other_command))) => {
+ let mut merged = other_command.clone();
+ if merged.cwd.is_none() {
+ merged.cwd = Some(base_cwd.clone());
+ }
+ Some(Run::Command(merged))
+ },
+ (Some(_base), Some(other)) => Some(other.clone()),
+ (Some(base), _) => Some(base.clone()),
+ (None, Some(other)) => Some(other.clone()),
+ (None, None) => None,
+ }
+ }
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
@@ -252,12 +287,16 @@ impl Layout {
) -> Result<(Layout, Config), ConfigError> {
let (path_to_raw_layout, raw_layout) =
Layout::stringified_from_path_or_default(layout_path, layout_dir)?;
- let layout = Layout::from_kdl(&raw_layout, path_to_raw_layout)?;
+ let layout = Layout::from_kdl(&raw_layout, path_to_raw_layout, None)?;
let config = Config::from_kdl(&raw_layout, Some(config))?; // this merges the two config, with
Ok((layout, config))
}
- pub fn from_str(raw: &str, path_to_raw_layout: String) -> Result<Layout, ConfigError> {
- Layout::from_kdl(raw, path_to_raw_layout)
+ pub fn from_str(
+ raw: &str,
+ path_to_raw_layout: String,
+ cwd: Option<PathBuf>,
+ ) -> Result<Layout, ConfigError> {
+ Layout::from_kdl(raw, path_to_raw_layout, cwd)
}
pub fn stringified_from_dir(
layout: &PathBuf,
diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs
index ce077a42..82a221e2 100644
--- a/zellij-utils/src/input/unit/layout_test.rs
+++ b/zellij-utils/src/input/unit/layout_test.rs
@@ -4,7 +4,7 @@ use insta::assert_snapshot;
#[test]
fn empty_layout() {
let kdl_layout = "layout";
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout::default()),
..Default::default()
@@ -19,7 +19,7 @@ fn layout_with_one_pane() {
pane
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout {
children: vec![PaneLayout::default()],
@@ -39,7 +39,7 @@ fn layout_with_multiple_panes() {
pane
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout {
children: vec![
@@ -68,7 +68,7 @@ fn layout_with_nested_panes() {
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout {
children: vec![
@@ -96,7 +96,7 @@ fn layout_with_tabs() {
tab
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
tabs: vec![(None, PaneLayout::default())],
template: Some(PaneLayout::default()),
@@ -120,7 +120,7 @@ fn layout_with_nested_differing_tabs() {
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
tabs: vec![
(
@@ -160,7 +160,7 @@ fn layout_with_panes_in_different_mixed_split_sizes() {
pane size=2;
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout {
children: vec![
@@ -195,7 +195,7 @@ fn layout_with_command_panes() {
pane command="htop"
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout {
children: vec![PaneLayout {
@@ -220,7 +220,7 @@ fn layout_with_command_panes_and_cwd() {
pane command="htop" cwd="/path/to/my/cwd"
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout {
children: vec![PaneLayout {
@@ -248,7 +248,7 @@ fn layout_with_command_panes_and_cwd_and_args() {
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout {
children: vec![PaneLayout {
@@ -280,7 +280,7 @@ fn layout_with_plugin_panes() {
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout {
children: vec![
@@ -313,7 +313,7 @@ fn layout_with_borderless_panes() {
pane borderless=true
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout {
children: vec![PaneLayout {
@@ -334,7 +334,7 @@ fn layout_with_focused_panes() {
pane focus=true
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout {
children: vec![PaneLayout {
@@ -355,7 +355,7 @@ fn layout_with_pane_names() {
pane name="my awesome pane"
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
template: Some(PaneLayout {
children: vec![PaneLayout {
@@ -377,7 +377,7 @@ fn layout_with_tab_names() {
tab name="my cool tab name 2"
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
tabs: vec![
(
@@ -410,7 +410,7 @@ fn layout_with_focused_tab() {
tab
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
tabs: vec![
(None, PaneLayout::default()),
@@ -444,7 +444,7 @@ fn layout_with_tab_templates() {
one-above-one-below
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
let expected_layout = Layout {
tabs: vec![
(
@@ -518,7 +518,7 @@ fn layout_with_default_tab_template() {
tab
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -545,7 +545,7 @@ fn layout_with_pane_templates() {
left-and-right
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -566,7 +566,7 @@ fn layout_with_tab_and_pane_templates() {
left-right-and-htop
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -587,7 +587,7 @@ fn layout_with_nested_pane_templates() {
left-and-right
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -613,7 +613,7 @@ fn layout_with_nested_branched_pane_templates() {
left-and-right
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -637,7 +637,7 @@ fn circular_dependency_pane_templates_error() {
one
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None);
assert!(layout.is_err(), "circular dependency detected");
}
@@ -659,7 +659,7 @@ fn children_not_as_first_child_of_tab_template() {
horizontal-with-vertical-top
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -682,7 +682,7 @@ fn error_on_more_than_one_children_block_in_tab_template() {
horizontal-with-vertical-top
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None);
assert!(
layout.is_err(),
"error provided for more than one children block"
@@ -707,7 +707,7 @@ fn children_not_as_first_child_of_pane_template() {
horizontal-with-vertical-top
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -730,7 +730,7 @@ fn error_on_more_than_one_children_block_in_pane_template() {
horizontal-with-vertical-top
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None);
assert!(
layout.is_err(),
"error provided for more than one children block"
@@ -759,7 +759,7 @@ fn combined_tab_and_pane_template_both_with_children() {
horizontal-with-vertical-top
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -775,7 +775,7 @@ fn cannot_define_tab_template_name_with_space() {
pane
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None);
assert!(layout.is_err(), "error provided for tab name with space");
}
@@ -791,7 +791,7 @@ fn cannot_define_pane_template_name_with_space() {
pane
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None);
assert!(layout.is_err(), "error provided for tab name with space");
}
@@ -805,7 +805,7 @@ fn cannot_define_panes_and_tabs_on_same_level() {
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None);
assert!(
layout.is_err(),
"error provided for tab and pane on the same level"
@@ -839,7 +839,7 @@ fn cannot_define_tab_template_names_as_keywords() {
",
keyword
);
- let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None);
assert!(
layout.is_err(),
"{}",
@@ -877,7 +877,7 @@ fn cannot_define_pane_template_names_as_keywords() {
",
keyword
);
- let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None);
assert!(
layout.is_err(),
"{}",
@@ -897,7 +897,7 @@ fn error_on_multiple_layout_nodes_in_file() {
layout
"
);
- let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err();
+ let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err();
assert_snapshot!(format!("{:?}", layout_error));
}
@@ -912,7 +912,7 @@ fn error_on_unknown_layout_node() {
}}
"
);
- let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err();
+ let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err();
assert_snapshot!(format!("{:?}", layout_error));
}
@@ -925,7 +925,7 @@ fn error_on_unknown_layout_pane_property() {
}}
"
);
- let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err();
+ let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err();
assert_snapshot!(format!("{:?}", layout_error));
}
@@ -938,7 +938,7 @@ fn error_on_unknown_layout_pane_template_property() {
}}
"
);
- let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err();
+ let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err();
assert_snapshot!(format!("{:?}", layout_error));
}
@@ -951,7 +951,7 @@ fn error_on_unknown_layout_tab_property() {
}}
"
);
- let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err();
+ let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err();
assert_snapshot!(format!("{:?}", layout_error));
}
@@ -964,7 +964,7 @@ fn error_on_unknown_layout_tab_template_property() {
}}
"
);
- let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err();
+ let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err();
assert_snapshot!(format!("{:?}", layout_error));
}
@@ -981,7 +981,7 @@ fn error_on_pane_templates_without_a_name() {
}}
"
);
- let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err();
+ let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err();
assert_snapshot!(format!("{:?}", layout_error));
}
@@ -998,7 +998,7 @@ fn error_on_tab_templates_without_a_name() {
}}
"
);
- let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into()).unwrap_err();
+ let layout_error = Layout::from_kdl(&kdl_layout, "layout_file_name".into(), None).unwrap_err();
assert_snapshot!(format!("{:?}", layout_error));
}
@@ -1011,7 +1011,7 @@ fn error_on_more_than_one_focused_tab() {
tab
}
"#;
- let layout_error = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap_err();
+ let layout_error = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap_err();
assert_snapshot!(format!("{:?}", layout_error));
}
@@ -1029,7 +1029,7 @@ fn args_override_args_in_template() {
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -1046,7 +1046,7 @@ fn args_added_to_args_in_template() {
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -1064,7 +1064,7 @@ fn cwd_override_cwd_in_template() {
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -1081,7 +1081,7 @@ fn cwd_added_to_cwd_in_template() {
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into()).unwrap();
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
assert_snapshot!(format!("{:#?}", layout));
}
@@ -1094,60 +1094,256 @@ fn error_on_mixed_command_and_child_panes() {
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None);
assert!(layout.is_err(), "error provided");
}
#[test]
-fn error_on_bare_args_without_command() {
+fn error_on_mixed_cwd_and_child_panes() {
let kdl_layout = r#"
layout {
- pane {
- args "-f"
+ pane cwd="/tmp" {
+ pane
+ pane
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None);
assert!(layout.is_err(), "error provided");
}
#[test]
-fn error_on_bare_cwd_without_command() {
+fn error_on_bare_args_without_command() {
let kdl_layout = r#"
layout {
pane {
- cwd "/"
+ args "-f"
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None);
assert!(layout.is_err(), "error provided");
}
#[test]
-fn error_on_bare_cwd_in_template_without_command() {
+fn error_on_bare_args_in_template_without_command() {
let kdl_layout = r#"
layout {
pane_template name="my_template"
my_template {
- cwd "/"
+ args "--help"
}
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None);
assert!(layout.is_err(), "error provided");
}
#[test]
-fn error_on_bare_args_in_template_without_command() {
+fn pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd() {
let kdl_layout = r#"
layout {
- pane_template name="my_template"
- my_template {
- args "--help"
+ cwd "/tmp"
+ pane_template name="tail" {
+ command "tail"
+ cwd "bar"
+ }
+ tail command="pwd" {
+ cwd "foo"
}
+ // pane should have /tmp/foo and not /tmp/bar as cwd
}
"#;
- let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into());
- assert!(layout.is_err(), "error provided");
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
+
+#[test]
+fn pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd() {
+ let kdl_layout = r#"
+ layout {
+ cwd "/tmp"
+ pane_template name="tail" {
+ command "tail"
+ cwd "bar"
+ }
+ tail command="pwd"
+ // pane should have /tmp/bar as its cwd with the pwd command
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
+
+#[test]
+fn pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd() {
+ let kdl_layout = r#"
+ layout {
+ cwd "/tmp"
+ pane_template name="tail" {
+ command "tail"
+ }
+ tail command="pwd" {
+ cwd "bar"
+ }
+ // pane should have /tmp/bar as its cwd with the pwd command
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
+
+#[test]
+fn pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd() {
+ let kdl_layout = r#"
+ layout {
+ cwd "/tmp"
+ pane_template name="tail" {
+ command "tail"
+ cwd "foo"
+ }
+ tail {
+ cwd "bar"
+ }
+ // pane should have /tmp/bar as its cwd with the tail command
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
+
+#[test]
+fn pane_template_command_without_cwd_receives_its_consumers_bare_cwd() {
+ let kdl_layout = r#"
+ layout {
+ cwd "/tmp"
+ pane_template name="tail" {
+ command "tail"
+ }
+ tail {
+ cwd "bar"
+ }
+ // pane should have /tmp/bar as its cwd with the tail command
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
+
+#[test]
+fn pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd() {
+ let kdl_layout = r#"
+ layout {
+ cwd "/tmp"
+ pane_template name="tail" {
+ cwd "foo"
+ }
+ tail {
+ cwd "bar"
+ }
+ // pane should have /tmp/foo without a command
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
+
+#[test]
+fn pane_template_with_bare_propagated_to_its_consumer_command_without_cwd() {
+ let kdl_layout = r#"
+ layout {
+ cwd "/tmp"
+ pane_template name="tail" {
+ cwd "foo"
+ }
+ tail command="tail"
+ // pane should have /tmp/foo with the tail command
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
+
+#[test]
+fn pane_template_with_bare_propagated_to_its_consumer_command_with_cwd() {
+ let kdl_layout = r#"
+ layout {
+ cwd "/tmp"
+ pane_template name="tail" {
+ cwd "foo"
+ }
+ tail command="tail" {
+ cwd "bar"
+ }
+ // pane should have /tmp/bar with the tail command
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
+
+#[test]
+fn global_cwd_given_to_panes_without_cwd() {
+ let kdl_layout = r#"
+ layout {
+ 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 global_cwd_prepended_to_panes_with_cwd() {
+ let kdl_layout = r#"
+ layout {
+ cwd "/tmp"
+ pane cwd="foo" // should be /tmp/foo
+ pane command="tail" cwd="/home/foo" // should be /home/foo because its an absolute path
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
+
+#[test]
+fn global_cwd_passed_from_layout_constructor() {
+ // this is used by the new-tab cli action with --cwd
+ let kdl_layout = r#"
+ layout {
+ pane
+ pane command="tail"
+ // both should have the /tmp cwd
+ }
+ "#;
+ let layout = Layout::from_kdl(
+ kdl_layout,
+ "layout_file_name".into(),
+ Some(PathBuf::from("/tmp")),
+ )
+ .unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
+
+#[test]
+fn global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file() {
+ // this is used by the new-tab cli action with --cwd
+ let kdl_layout = r#"
+ layout {
+ cwd "/home"
+ pane
+ pane command="tail"
+ // both should have the /tmp cwd
+ }
+ "#;
+ let layout = Layout::from_kdl(
+ kdl_layout,
+ "layout_file_name".into(),
+ Some(PathBuf::from("/tmp")),
+ )
+ .unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
}
diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap
new file mode 100644
index 00000000..548ae756
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap
@@ -0,0 +1,57 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1283
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ 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: 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_passed_from_layout_constructor.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap
new file mode 100644
index 00000000..d88a8ece
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap
@@ -0,0 +1,57 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1309
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ 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: 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_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap
new file mode 100644
index 00000000..0710ae13
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap
@@ -0,0 +1,57 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1324
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ 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: 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_prepended_to_panes_with_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap
new file mode 100644
index 00000000..956df1a5
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap
@@ -0,0 +1,57 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1295
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ 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(
+ "/home/foo",
+ ),
+ hold_on_close: true,
+ },
+ ),
+ ),
+ borderless: false,
+ focus: None,
+ external_children_index: None,
+ },
+ ],
+ 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__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap
new file mode 100644
index 00000000..03adaf85
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap
@@ -0,0 +1,43 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1199
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [],
+ split_size: None,
+ run: Some(
+ Command(
+ RunCommand {
+ command: "tail",
+ args: [],
+ cwd: Some(
+ "/tmp/bar",
+ ),
+ hold_on_close: true,
+ },
+ ),
+ ),
+ borderless: false,
+ focus: None,
+ external_children_index: None,
+ },
+ ],
+ 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__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap
new file mode 100644
index 00000000..d01c6516
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap
@@ -0,0 +1,43 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1144
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [],
+ split_size: None,
+ run: Some(
+ Command(
+ RunCommand {
+ command: "pwd",
+ args: [],
+ cwd: Some(
+ "/tmp/foo",
+ ),
+ hold_on_close: true,
+ },
+ ),
+ ),
+ borderless: false,
+ focus: None,
+ external_children_index: None,
+ },
+ ],
+ 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__pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd.snap b/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.snap
new file mode 100644
index 00000000..596c492b
--- /dev/null
+++ b/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.snap
@@ -0,0 +1,43 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1161
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [],
+ split_size: None,
+ run: Some(
+ Command(
+ RunCommand {
+ command: "pwd",
+ args: [],
+ cwd: Some(
+ "/tmp/bar",
+ ),
+ hold_on_close: true,
+ },
+ ),
+ ),
+ borderless: false,
+ focus: None,
+ external_children_index: None,
+ },
+ ],
+ 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__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap
new file mode 100644
index 00000000..25bca8db
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap
@@ -0,0 +1,43 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1180
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [],
+ split_size: None,
+ run: Some(
+ Command(
+ RunCommand {
+ command: "pwd",
+ args: [],
+ cwd: Some(
+ "/tmp/bar",
+ ),
+ hold_on_close: true,
+ },
+ ),
+ ),
+ borderless: false,
+ focus: None,
+ external_children_index: None,
+ },
+ ],
+ 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__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap
new file mode 100644
index 00000000..05e377b9
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap
@@ -0,0 +1,43 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1216
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [],
+ split_size: None,
+ run: Some(
+ Command(
+ RunCommand {
+ command: "tail",
+ args: [],
+ cwd: Some(
+ "/tmp/bar",
+ ),
+ hold_on_close: true,
+ },
+ ),
+ ),
+ borderless: false,
+ focus: None,
+ external_children_index: None,
+ },
+ ],
+ 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__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap
new file mode 100644
index 00000000..0f488990
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap
@@ -0,0 +1,36 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1235
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [],
+ split_size: None,
+ run: Some(
+ Cwd(
+ "/tmp/bar",
+ ),
+ ),
+ borderless: false,
+ focus: None,
+ external_children_index: None,
+ },
+ ],
+ 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__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap
new file mode 100644
index 00000000..8c3863c3
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap
@@ -0,0 +1,43 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1269
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [],
+ split_size: None,
+ run: Some(
+ Command(
+ RunCommand {
+ command: "tail",
+ args: [],
+ cwd: Some(
+ "/tmp/bar",
+ ),
+ hold_on_close: true,
+ },
+ ),
+ ),
+ borderless: false,
+ focus: None,
+ external_children_index: None,
+ },
+ ],
+ 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__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap
new file mode 100644
index 00000000..ba91e574
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap
@@ -0,0 +1,43 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 1251
+expression: "format!(\"{:#?}\", layout)"
+---
+Layout {
+ tabs: [],
+ focused_tab_index: None,
+ template: Some(
+ PaneLayout {
+ children_split_direction: Horizontal,
+ name: None,
+ children: [
+ 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: 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 6bf2d372..d7df5889 100644
--- a/zellij-utils/src/kdl/kdl_layout_parser.rs
+++ b/zellij-utils/src/kdl/kdl_layout_parser.rs
@@ -24,6 +24,7 @@ use std::vec::Vec;
use url::Url;
pub struct KdlLayoutParser<'a> {
+ global_cwd: Option<PathBuf>,
raw_layout: &'a str,
tab_templates: HashMap<String, (PaneLayout, KdlNode)>,
pane_templates: HashMap<String, (PaneLayout, KdlNode)>,
@@ -31,12 +32,13 @@ pub struct KdlLayoutParser<'a> {
}
impl<'a> KdlLayoutParser<'a> {
- pub fn new(raw_layout: &'a str) -> Self {
+ pub fn new(raw_layout: &'a str, global_cwd: Option<PathBuf>) -> Self {
KdlLayoutParser {
raw_layout,
tab_templates: HashMap::new(),
pane_templates: HashMap::new(),
default_tab_template: None,
+ global_cwd,
}
}
fn is_a_reserved_word(&self, word: &str) -> bool {
@@ -173,6 +175,16 @@ impl<'a> KdlLayoutParser<'a> {
None => Ok(None),
}
}
+ fn parse_cwd(&self, kdl_node: &KdlNode) -> Result<Option<PathBuf>, 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())),
+ )
+ }
fn parse_pane_command(
&self,
pane_node: &KdlNode,
@@ -180,15 +192,16 @@ impl<'a> KdlLayoutParser<'a> {
) -> Result<Option<Run>, ConfigError> {
let command = kdl_get_string_property_or_child_value_with_error!(pane_node, "command")
.map(|c| PathBuf::from(c));
- let cwd = kdl_get_string_property_or_child_value_with_error!(pane_node, "cwd")
- .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 args = self.parse_args(pane_node)?;
match (command, cwd, args, is_template) {
- (None, Some(_cwd), _, false) => Err(ConfigError::new_kdl_error(
- "cwd can only be set if a command was specified".into(),
- pane_node.span().offset(),
- pane_node.span().len(),
- )),
+ (None, Some(cwd), _, _) => Ok(Some(Run::Cwd(cwd))),
(None, _, Some(_args), false) => Err(ConfigError::new_kdl_error(
"args can only be set if a command was specified".into(),
pane_node.span().offset(),
@@ -263,39 +276,13 @@ impl<'a> KdlLayoutParser<'a> {
..Default::default()
})
}
- fn parse_pane_node_with_template(
+ fn insert_children_to_pane_template(
&self,
kdl_node: &KdlNode,
- mut pane_layout: PaneLayout,
- pane_layout_kdl_node: &KdlNode,
- ) -> Result<PaneLayout, ConfigError> {
- 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")
- .map(|name| name.to_string());
- let args = self.parse_args(kdl_node)?;
- let cwd = kdl_get_string_property_or_child_value_with_error!(kdl_node, "cwd")
- .map(|c| PathBuf::from(c));
- let split_size = self.parse_split_size(kdl_node)?;
- let run = self.parse_command_or_plugin_block_for_template(kdl_node)?;
- if let (None, None, true, true) | (None, None, false, true) | (None, None, true, false) =
- (&run, &pane_layout.run, args.is_some(), cwd.is_some())
- {
- let mut offending_nodes = vec![];
- if args.is_some() {
- offending_nodes.push("args");
- }
- if cwd.is_some() {
- offending_nodes.push("cwd");
- }
- return Err(kdl_parsing_error!(
- format!("{} can only be specified if a command was specified either in the pane_template or in the pane", offending_nodes.join(" and ")),
- kdl_node
- ));
- }
-
+ pane_template: &mut PaneLayout,
+ pane_template_kdl_node: &KdlNode,
+ ) -> Result<(), ConfigError> {
let children_split_direction = self.parse_split_direction(kdl_node)?;
-
let (external_children_index, pane_parts) = match kdl_children_nodes!(kdl_node) {
Some(children) => self.parse_child_pane_nodes_for_pane(&children)?,
None => (None, vec![]),
@@ -307,41 +294,89 @@ impl<'a> KdlLayoutParser<'a> {
external_children_index,
..Default::default()
};
- self.assert_one_children_block(&pane_layout, pane_layout_kdl_node)?;
+ self.assert_one_children_block(&pane_template, pane_template_kdl_node)?;
self.insert_layout_children_or_error(
- &mut pane_layout,
+ pane_template,
child_panes_layout,
- pane_layout_kdl_node,
+ pane_template_kdl_node,
)?;
}
+ Ok(())
+ }
+ fn parse_pane_node_with_template(
+ &self,
+ kdl_node: &KdlNode,
+ mut pane_template: PaneLayout,
+ pane_template_kdl_node: &KdlNode,
+ ) -> Result<PaneLayout, ConfigError> {
+ 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")
+ .map(|name| name.to_string());
+ let args = self.parse_args(kdl_node)?;
+ let split_size = self.parse_split_size(kdl_node)?;
+ let run = self.parse_command_or_plugin_block_for_template(kdl_node)?;
+ self.assert_no_bare_args_in_pane_node_with_template(
+ &run,
+ &pane_template.run,
+ &args,
+ kdl_node,
+ )?;
+ self.insert_children_to_pane_template(
+ kdl_node,
+ &mut pane_template,
+ 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)
+ {
+ if !args.is_empty() {
+ pane_template_run_command.args = args.clone();
+ }
+ }
if let Some(borderless) = borderless {
- pane_layout.borderless = borderless;
+ pane_template.borderless = borderless;
}
if let Some(focus) = focus {
- pane_layout.focus = Some(focus);
+ pane_template.focus = Some(focus);
}
if let Some(name) = name {
- pane_layout.name = Some(name);
+ pane_template.name = Some(name);
}
if let Some(split_size) = split_size {
- pane_layout.split_size = Some(split_size);
- }
- if let Some(run) = run {
- pane_layout.run = Some(run);
- }
- if let (Some(args), Some(Run::Command(run_command))) = (args, pane_layout.run.as_mut()) {
- run_command.args = args.clone();
+ pane_template.split_size = Some(split_size);
}
- if let (Some(cwd), Some(Run::Command(run_command))) = (cwd, pane_layout.run.as_mut()) {
- run_command.cwd = Some(cwd.clone());
- }
- if let Some(index_of_children) = pane_layout.external_children_index {
- pane_layout
+ if let Some(index_of_children) = pane_template.external_children_index {
+ pane_template
.children
.insert(index_of_children, PaneLayout::default());
}
- pane_layout.external_children_index = None;
- Ok(pane_layout)
+ pane_template.external_children_index = None;
+ Ok(pane_template)
+ }
+ fn populate_global_cwd_for_pane_run(
+ &self,
+ pane_run: &mut Option<Run>,
+ ) -> 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::Cwd(pane_template_cwd)) => {
+ *pane_template_cwd = global_cwd.join(&pane_template_cwd);
+ },
+ _ => {},
+ }
+ }
+ Ok(())
}
fn parse_split_direction(&self, kdl_node: &KdlNode) -> Result<SplitDirection, ConfigError> {
match kdl_get_string_property_or_child_value_with_error!(kdl_node, "split_direction") {
@@ -513,6 +548,21 @@ impl<'a> KdlLayoutParser<'a> {
}
false
}
+ fn assert_no_bare_args_in_pane_node_with_template(
+ &self,
+ pane_run: &Option<Run>,
+ pane_template_run: &Option<Run>,
+ args: &Option<Vec<String>>,
+ pane_node: &KdlNode,
+ ) -> Result<(), ConfigError> {
+ if let (None, None, true) = (pane_run, pane_template_run, args.is_some()) {
+ return Err(kdl_parsing_error!(
+ format!("args can only be specified if a command was specified either in the pane_template or in the pane"),
+ pane_node
+ ));
+ }
+ Ok(())
+ }
fn assert_one_children_block(
&self,
layout: &PaneLayout,
@@ -572,10 +622,18 @@ impl<'a> KdlLayoutParser<'a> {
kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless").is_some();
let has_focus_prop =
kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus").is_some();
- let has_run_prop = self.parse_command_or_plugin_block(kdl_node)?.is_some();
+ let has_cwd_prop =
+ kdl_get_string_property_or_child_value_with_error!(kdl_node, "cwd").is_some();
+ let has_non_cwd_run_prop = self
+ .parse_command_or_plugin_block(kdl_node)?
+ .map(|r| match r {
+ Run::Cwd(_) => false,
+ _ => true,
+ })
+ .unwrap_or(false);
let has_nested_nodes_or_children_block = self.has_child_panes_tabs_or_templates(kdl_node);
if has_nested_nodes_or_children_block
- && (has_borderless_prop || has_focus_prop || has_run_prop)
+ && (has_borderless_prop || has_focus_prop || has_non_cwd_run_prop || has_cwd_prop)
{
let mut offending_nodes = vec![];
if has_borderless_prop {
@@ -584,9 +642,12 @@ impl<'a> KdlLayoutParser<'a> {
if has_focus_prop {
offending_nodes.push("focus");
}
- if has_run_prop {
+ if has_non_cwd_run_prop {
offending_nodes.push("command/plugin");
}
+ if has_cwd_prop {
+ offending_nodes.push("cwd");
+ }
Err(ConfigError::new_kdl_error(
format!(
"Cannot have both properties ({}) and nested children",
@@ -821,6 +882,17 @@ impl<'a> KdlLayoutParser<'a> {
}
Ok(())
}
+ fn populate_global_cwd(&mut self, layout_node: &KdlNode) -> Result<(), ConfigError> {
+ // we only populate global cwd from the layout file if another wasn't explicitly passed to us
+ if self.global_cwd.is_none() {
+ if let Some(global_cwd) =
+ kdl_get_string_property_or_child_value_with_error!(layout_node, "cwd")
+ {
+ self.global_cwd = Some(PathBuf::from(global_cwd));
+ }
+ }
+ Ok(())
+ }
fn populate_pane_templates(
&mut self,
layout_children: &[KdlNode],
@@ -1019,6 +1091,7 @@ impl<'a> KdlLayoutParser<'a> {
let mut child_tabs = vec![];
let mut child_panes = vec![];
if let Some(children) = kdl_children_nodes!(layout_node) {
+ self.populate_global_cwd(layout_node)?;
self.populate_pane_templates(children, &kdl_layout)?;
self.populate_tab_templates(children)?;
for child in children {
diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs
index b8d76c7b..e7b7fa50 100644
--- a/zellij-utils/src/kdl/mod.rs
+++ b/zellij-utils/src/kdl/mod.rs
@@ -1266,8 +1266,12 @@ impl RunPlugin {
}
}
impl Layout {
- pub fn from_kdl(raw_layout: &str, file_name: String) -> Result<Self, ConfigError> {
- KdlLayoutParser::new(raw_layout).parse().map_err(|e| {
+ pub fn from_kdl(
+ raw_layout: &str,
+ file_name: String,
+ cwd: Option<PathBuf>,
+ ) -> Result<Self, ConfigError> {
+ KdlLayoutParser::new(raw_layout, cwd).parse().map_err(|e| {
match e {
ConfigError::KdlError(kdl_error) => ConfigError::KdlError(kdl_error.add_src(file_name, String::from(raw_layout))),
ConfigError::KdlDeserializationError(kdl_error) => {