aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2022-11-01 09:07:25 +0100
committerGitHub <noreply@github.com>2022-11-01 09:07:25 +0100
commitabc700fc4d10d61c969ad94fa520d7d9336dcf14 (patch)
treea2e6318c4fe3951236cfa758befce8b95393a26e
parentdocs(changelog): status bar fix (diff)
downloadsl0ppij-abc700fc4d10d61c969ad94fa520d7d9336dcf14.tar.gz
sl0ppij-abc700fc4d10d61c969ad94fa520d7d9336dcf14.tar.xz
sl0ppij-abc700fc4d10d61c969ad94fa520d7d9336dcf14.zip
feat(command-panes): allow to start suspended (#1887)
* feat(command-panes): allow panes to start suspended * style(fmt): remove unused code * style(fmt): rustfmt
-rw-r--r--src/main.rs2
-rw-r--r--src/tests/e2e/cases.rs19
-rw-r--r--src/tests/e2e/remote_runner.rs3
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap8
-rw-r--r--zellij-server/src/os_input_output.rs34
-rw-r--r--zellij-server/src/panes/floating_panes/mod.rs3
-rw-r--r--zellij-server/src/panes/grid.rs1
-rw-r--r--zellij-server/src/panes/terminal_character.rs117
-rw-r--r--zellij-server/src/panes/terminal_pane.rs52
-rw-r--r--zellij-server/src/panes/tiled_panes/mod.rs3
-rw-r--r--zellij-server/src/pty.rs179
-rw-r--r--zellij-server/src/screen.rs84
-rw-r--r--zellij-server/src/tab/mod.rs29
-rw-r--r--zellij-server/src/tab/unit/tab_integration_tests.rs16
-rw-r--r--zellij-server/src/tab/unit/tab_tests.rs4
-rw-r--r--zellij-server/src/ui/pane_boundaries_frame.rs33
-rw-r--r--zellij-server/src/unit/screen_tests.rs9
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap2
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap4
-rw-r--r--zellij-utils/src/cli.rs14
-rw-r--r--zellij-utils/src/input/actions.rs3
-rw-r--r--zellij-utils/src/input/command.rs5
-rw-r--r--zellij-utils/src/input/layout.rs11
-rw-r--r--zellij-utils/src/input/unit/layout_test.rs13
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap4
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap4
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap2
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap2
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap4
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap4
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templates.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templates.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap1
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap42
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap3
-rw-r--r--zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap3
-rw-r--r--zellij-utils/src/kdl/kdl_layout_parser.rs28
-rw-r--r--zellij-utils/src/kdl/mod.rs47
52 files changed, 668 insertions, 172 deletions
diff --git a/src/main.rs b/src/main.rs
index 3ed592c5..6203f0f1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -26,6 +26,7 @@ fn main() {
floating,
name,
close_on_exit,
+ start_suspended,
})) = opts.command
{
let command_cli_action = CliAction::NewPane {
@@ -35,6 +36,7 @@ fn main() {
floating,
name,
close_on_exit,
+ start_suspended,
};
commands::send_action_to_session(command_cli_action, opts.session);
std::process::exit(0);
diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs
index 44551d99..359d7416 100644
--- a/src/tests/e2e/cases.rs
+++ b/src/tests/e2e/cases.rs
@@ -1915,6 +1915,25 @@ pub fn send_command_through_the_cli() {
},
})
.add_step(Step {
+ name: "Initial run of suspended command",
+ instruction: |mut remote_terminal: RemoteTerminal| -> bool {
+ let mut step_is_complete = false;
+ if remote_terminal.snapshot_contains("<Ctrl-c>")
+ && remote_terminal.cursor_position_is(0, 0)
+ // cursor does not appear in
+ // suspend_start panes
+ {
+ remote_terminal.send_key(&SPACE); // run script - here we use SPACE
+ // instead of the default ENTER because
+ // sending ENTER over SSH can be a little
+ // problematic (read: I couldn't get it
+ // to pass consistently)
+ step_is_complete = true
+ }
+ step_is_complete
+ },
+ })
+ .add_step(Step {
name: "Wait for command to run",
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs
index dd2c800d..9b007989 100644
--- a/src/tests/e2e/remote_runner.rs
+++ b/src/tests/e2e/remote_runner.rs
@@ -346,7 +346,8 @@ impl RemoteTerminal {
let mut channel = self.channel.lock().unwrap();
channel
.write_all(
- format!("{} run -- \"{}\"\n", ZELLIJ_EXECUTABLE_LOCATION, command).as_bytes(),
+ // note that this is run with the -s flag that suspends the command on startup
+ format!("{} run -s -- \"{}\"\n", ZELLIJ_EXECUTABLE_LOCATION, command).as_bytes(),
)
.unwrap();
channel.flush().unwrap();
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap
index 84d33a03..0603ac26 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap
@@ -1,14 +1,14 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 1968
+assertion_line: 1998
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
┌ Pane #1 ─────────────────────────────────────────────────┐┌ /usr/src/zellij/fixtures/append-echo-script.sh ──────────┐
│$ /usr/src/zellij/x86_64-unknown-linux-musl/release/zellij││foo │
-│ run -- "/usr/src/zellij/fixtures/append-echo-script.sh" ││foo │
-│$ ││█ │
-│ ││ │
+│ run -s -- "/usr/src/zellij/fixtures/append-echo-script.sh││foo │
+│" ││█ │
+│$ ││ │
│ ││ │
│ ││ │
│ ││ │
diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs
index 9a8ce008..02f53dbe 100644
--- a/zellij-server/src/os_input_output.rs
+++ b/zellij-server/src/os_input_output.rs
@@ -280,6 +280,7 @@ fn spawn_terminal(
args,
cwd: None,
hold_on_close: false,
+ hold_on_start: false,
}
},
TerminalAction::RunCommand(command) => command,
@@ -381,6 +382,10 @@ pub trait ServerOsApi: Send + Sync {
quit_cb: Box<dyn Fn(PaneId, Option<i32>, RunCommand) + Send>, // u32 is the exit status
default_editor: Option<PathBuf>,
) -> Result<(u32, RawFd, RawFd), SpawnTerminalError>;
+ // reserves a terminal id without actually opening a terminal
+ fn reserve_terminal_id(&self) -> Result<u32, SpawnTerminalError> {
+ unimplemented!()
+ }
/// Read bytes from the standard output of the virtual terminal referred to by `fd`.
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
/// Creates an `AsyncReader` that can be used to read from `fd` in an async context
@@ -484,6 +489,35 @@ impl ServerOsApi for ServerOsInputOutput {
None => Err(SpawnTerminalError::NoMoreTerminalIds),
}
}
+ fn reserve_terminal_id(&self) -> Result<u32, SpawnTerminalError> {
+ let mut terminal_id = None;
+ {
+ let current_ids: HashSet<u32> = self
+ .terminal_id_to_raw_fd
+ .lock()
+ .unwrap()
+ .keys()
+ .copied()
+ .collect();
+ for i in 0..u32::MAX {
+ let i = i as u32;
+ if !current_ids.contains(&i) {
+ terminal_id = Some(i);
+ break;
+ }
+ }
+ }
+ match terminal_id {
+ Some(terminal_id) => {
+ self.terminal_id_to_raw_fd
+ .lock()
+ .unwrap()
+ .insert(terminal_id, None);
+ Ok(terminal_id)
+ },
+ None => Err(SpawnTerminalError::NoMoreTerminalIds),
+ }
+ }
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> {
unistd::read(fd, buf)
}
diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs
index f686f40b..d17be755 100644
--- a/zellij-server/src/panes/floating_panes/mod.rs
+++ b/zellij-server/src/panes/floating_panes/mod.rs
@@ -156,11 +156,12 @@ impl FloatingPanes {
&mut self,
pane_id: PaneId,
exit_status: Option<i32>,
+ is_first_run: bool,
run_command: RunCommand,
) {
self.panes
.get_mut(&pane_id)
- .map(|p| p.hold(exit_status, run_command));
+ .map(|p| p.hold(exit_status, is_first_run, run_command));
}
pub fn get(&self, pane_id: &PaneId) -> Option<&Box<dyn Pane>> {
self.panes.get(pane_id)
diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs
index 67a882fb..d9d60fef 100644
--- a/zellij-server/src/panes/grid.rs
+++ b/zellij-server/src/panes/grid.rs
@@ -1533,6 +1533,7 @@ impl Grid {
self.sixel_scrolling = false;
self.mouse_mode = MouseMode::NoEncoding;
self.mouse_tracking = MouseTracking::Off;
+ self.cursor_is_hidden = false;
if let Some(images_to_reap) = self.sixel_grid.clear() {
self.sixel_grid.reap_images(images_to_reap);
}
diff --git a/zellij-server/src/panes/terminal_character.rs b/zellij-server/src/panes/terminal_character.rs
index c6fa0adf..68813e7a 100644
--- a/zellij-server/src/panes/terminal_character.rs
+++ b/zellij-server/src/panes/terminal_character.rs
@@ -3,7 +3,12 @@ use std::fmt::{self, Debug, Display, Formatter};
use std::ops::{Index, IndexMut};
use unicode_width::UnicodeWidthChar;
-use zellij_utils::{data::PaletteColor, vte::ParamsIter};
+use unicode_width::UnicodeWidthStr;
+use zellij_utils::input::command::RunCommand;
+use zellij_utils::{
+ data::{PaletteColor, Style},
+ vte::ParamsIter,
+};
use crate::panes::alacritty_functions::parse_sgr_color;
@@ -736,3 +741,113 @@ impl ::std::fmt::Debug for TerminalCharacter {
write!(f, "{}", self.character)
}
}
+
+pub fn render_first_run_banner(
+ columns: usize,
+ rows: usize,
+ style: &Style,
+ run_command: Option<&RunCommand>,
+) -> String {
+ let middle_row = rows / 2;
+ let middle_column = columns / 2;
+ match run_command {
+ Some(run_command) => {
+ let bold_text = RESET_STYLES.bold(Some(AnsiCode::On));
+ let command_color_text = RESET_STYLES
+ .foreground(Some(AnsiCode::from(style.colors.green)))
+ .bold(Some(AnsiCode::On));
+ let waiting_to_run_text = "Waiting to run: ";
+ let command_text = run_command.to_string();
+ let waiting_to_run_text_width = waiting_to_run_text.width() + command_text.width();
+ let column_start_postion = middle_column.saturating_sub(waiting_to_run_text_width / 2);
+ let waiting_to_run_line = format!(
+ "\u{1b}[{};{}H{}{}{}{}{}",
+ middle_row,
+ column_start_postion,
+ bold_text,
+ waiting_to_run_text,
+ command_color_text,
+ command_text,
+ RESET_STYLES
+ );
+
+ let controls_bare_text_first_part = "<";
+ let enter_bare_text = "ENTER";
+ let controls_bare_text_second_part = "> to run, <";
+ let ctrl_c_bare_text = "Ctrl-c";
+ let controls_bare_text_third_part = "> to exit";
+ let controls_color = RESET_STYLES
+ .foreground(Some(AnsiCode::from(style.colors.orange)))
+ .bold(Some(AnsiCode::On));
+ let controls_line_length = controls_bare_text_first_part.len()
+ + enter_bare_text.len()
+ + controls_bare_text_second_part.len()
+ + ctrl_c_bare_text.len()
+ + controls_bare_text_third_part.len();
+ let controls_column_start_position =
+ middle_column.saturating_sub(controls_line_length / 2);
+ let controls_line = format!(
+ "\u{1b}[{};{}H{}<{}{}{}{}> to run, <{}{}{}{}> to exit",
+ middle_row + 2,
+ controls_column_start_position,
+ bold_text,
+ controls_color,
+ enter_bare_text,
+ RESET_STYLES,
+ bold_text,
+ controls_color,
+ ctrl_c_bare_text,
+ RESET_STYLES,
+ bold_text
+ );
+ format!(
+ "\u{1b}[?25l{}{}{}{}",
+ RESET_STYLES, waiting_to_run_line, controls_line, RESET_STYLES
+ )
+ },
+ None => {
+ let bare_text = format!("Waiting to start...");
+ let bare_text_width = bare_text.width();
+ let column_start_postion = middle_column.saturating_sub(bare_text_width / 2);
+ let bold_text = RESET_STYLES.bold(Some(AnsiCode::On));
+ let waiting_to_run_line = format!(
+ "\u{1b}[?25l\u{1b}[{};{}H{}{}{}",
+ middle_row, column_start_postion, bold_text, bare_text, RESET_STYLES
+ );
+
+ let controls_bare_text_first_part = "<";
+ let enter_bare_text = "ENTER";
+ let controls_bare_text_second_part = "> to run, <";
+ let ctrl_c_bare_text = "Ctrl-c";
+ let controls_bare_text_third_part = "> to exit";
+ let controls_color = RESET_STYLES
+ .foreground(Some(AnsiCode::from(style.colors.orange)))
+ .bold(Some(AnsiCode::On));
+ let controls_line_length = controls_bare_text_first_part.len()
+ + enter_bare_text.len()
+ + controls_bare_text_second_part.len()
+ + ctrl_c_bare_text.len()
+ + controls_bare_text_third_part.len();
+ let controls_column_start_position =
+ middle_column.saturating_sub(controls_line_length / 2);
+ let controls_line = format!(
+ "\u{1b}[{};{}H{}<{}{}{}{}> to run, <{}{}{}{}> to exit",
+ middle_row + 2,
+ controls_column_start_position,
+ bold_text,
+ controls_color,
+ enter_bare_text,
+ RESET_STYLES,
+ bold_text,
+ controls_color,
+ ctrl_c_bare_text,
+ RESET_STYLES,
+ bold_text
+ );
+ format!(
+ "\u{1b}[?25l{}{}{}{}",
+ RESET_STYLES, waiting_to_run_line, controls_line, RESET_STYLES
+ )
+ },
+ }
+}
diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs
index 631df0c8..5726ef44 100644
--- a/zellij-server/src/panes/terminal_pane.rs
+++ b/zellij-server/src/panes/terminal_pane.rs
@@ -2,7 +2,7 @@ use crate::output::{CharacterChunk, SixelImageChunk};
use crate::panes::sixel::SixelImageStore;
use crate::panes::{
grid::Grid,
- terminal_character::{TerminalCharacter, EMPTY_TERMINAL_CHARACTER},
+ terminal_character::{render_first_run_banner, TerminalCharacter, EMPTY_TERMINAL_CHARACTER},
};
use crate::panes::{AnsiCode, LinkHandler};
use crate::pty::VteBytes;
@@ -83,6 +83,8 @@ pub enum PaneId {
Plugin(u32), // FIXME: Drop the trait object, make this a wrapper for the struct?
}
+type IsFirstRun = bool;
+
// FIXME: This should hold an os_api handle so that terminal panes can set their own size via FD in
// their `reflow_lines()` method. Drop a Box<dyn ServerOsApi> in here somewhere.
#[allow(clippy::too_many_arguments)]
@@ -104,8 +106,10 @@ pub struct TerminalPane {
borderless: bool,
fake_cursor_locations: HashSet<(usize, usize)>, // (x, y) - these hold a record of previous fake cursors which we need to clear on render
search_term: String,
- is_held: Option<(Option<i32>, RunCommand)>, // a "held" pane means that its command has exited and its waiting for a
- // possible user instruction to be re-run
+ is_held: Option<(Option<i32>, IsFirstRun, RunCommand)>, // a "held" pane means that its command has either exited and the pane is waiting for a
+ // possible user instruction to be re-run, or that the command has not yet been run
+ banner: Option<String>, // a banner to be rendered inside this TerminalPane, used for panes
+ // held on startup and can possibly be used to display some errors
}
impl Pane for TerminalPane {
@@ -170,13 +174,14 @@ impl Pane for TerminalPane {
// needs to be adjusted.
// here we match against those cases - if need be, we adjust the input and if not
// we send back the original input
- if let Some((_exit_status, run_command)) = &self.is_held {
+ if let Some((_exit_status, _is_first_run, run_command)) = &self.is_held {
match input_bytes.as_slice() {
ENTER_CARRIAGE_RETURN | ENTER_NEWLINE | SPACE => {
let run_command = run_command.clone();
self.is_held = None;
self.grid.reset_terminal_state();
self.set_should_render(true);
+ self.remove_banner();
Some(AdjustedInput::ReRunCommandInThisPane(run_command))
},
CTRL_C => Some(AdjustedInput::CloseThisPane),
@@ -395,8 +400,12 @@ impl Pane for TerminalPane {
pane_title,
frame_params,
);
- if let Some((exit_status, _run_command)) = &self.is_held {
- frame.add_exit_status(exit_status.as_ref().copied());
+ if let Some((exit_status, is_first_run, _run_command)) = &self.is_held {
+ if *is_first_run {
+ frame.indicate_first_run();
+ } else {
+ frame.add_exit_status(exit_status.as_ref().copied());
+ }
}
let res = match self.frame.get(&client_id) {
@@ -701,8 +710,11 @@ impl Pane for TerminalPane {
fn is_alternate_mode_active(&self) -> bool {
self.grid.is_alternate_mode_active()
}
- fn hold(&mut self, exit_status: Option<i32>, run_command: RunCommand) {
- self.is_held = Some((exit_status, run_command));
+ fn hold(&mut self, exit_status: Option<i32>, is_first_run: bool, run_command: RunCommand) {
+ self.is_held = Some((exit_status, is_first_run, run_command));
+ if is_first_run {
+ self.render_first_run_banner();
+ }
self.set_should_render(true);
}
}
@@ -752,6 +764,7 @@ impl TerminalPane {
fake_cursor_locations: HashSet::new(),
search_term: String::new(),
is_held: None,
+ banner: None,
}
}
pub fn get_x(&self) -> usize {
@@ -782,6 +795,10 @@ impl TerminalPane {
let rows = self.get_content_rows();
let cols = self.get_content_columns();
self.grid.change_size(rows, cols);
+ if self.banner.is_some() {
+ self.grid.reset_terminal_state();
+ self.render_first_run_banner();
+ }
self.set_should_render(true);
}
pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
@@ -791,6 +808,25 @@ impl TerminalPane {
// (x, y)
self.grid.cursor_coordinates()
}
+ fn render_first_run_banner(&mut self) {
+ let columns = self.get_content_columns();
+ let rows = self.get_content_rows();
+ let banner = match &self.is_held {
+ Some((_exit_status, _is_first_run, run_command)) => {
+ render_first_run_banner(columns, rows, &self.style, Some(run_command))
+ },
+ None => render_first_run_banner(columns, rows, &self.style, None),
+ };
+ self.banner = Some(banner.clone());
+ self.handle_pty_bytes(banner.as_bytes().to_vec());
+ }
+ fn remove_banner(&mut self) {
+ if self.banner.is_some() {
+ self.grid.reset_terminal_state();
+ self.set_should_render(true);
+ self.banner = None;
+ }
+ }
}
#[cfg(test)]
diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs
index 59657c15..7e7b7a1f 100644
--- a/zellij-server/src/panes/tiled_panes/mod.rs
+++ b/zellij-server/src/panes/tiled_panes/mod.rs
@@ -1010,11 +1010,12 @@ impl TiledPanes {
&mut self,
pane_id: PaneId,
exit_status: Option<i32>,
+ is_first_run: bool,
run_command: RunCommand,
) {
self.panes
.get_mut(&pane_id)
- .map(|p| p.hold(exit_status, run_command));
+ .map(|p| p.hold(exit_status, is_first_run, run_command));
}
pub fn panes_to_hide_contains(&self, pane_id: PaneId) -> bool {
self.panes_to_hide.contains(&pane_id)
diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs
index bb5beb5d..0ca0b586 100644
--- a/zellij-server/src/pty.rs
+++ b/zellij-server/src/pty.rs
@@ -108,25 +108,29 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
_ => (false, None, name),
};
match pty.spawn_terminal(terminal_action, client_or_tab_index) {
- Ok(pid) => {
+ Ok((pid, starts_held)) => {
+ let hold_for_command = if starts_held { run_command } else { None };
pty.bus
.senders
.send_to_screen(ScreenInstruction::NewPane(
PaneId::Terminal(pid),
pane_title,
should_float,
+ hold_for_command,
client_or_tab_index,
))
.with_context(err_context)?;
},
Err(SpawnTerminalError::CommandNotFound(pid)) => {
if hold_on_close {
+ let hold_for_command = None; // we do not hold an "error" pane
pty.bus
.senders
.send_to_screen(ScreenInstruction::NewPane(
PaneId::Terminal(pid),
pane_title,
should_float,
+ hold_for_command,
client_or_tab_index,
))
.with_context(err_context)?;
@@ -154,7 +158,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
Some(TerminalAction::OpenFile(temp_file, line_number)),
ClientOrTabIndex::ClientId(client_id),
) {
- Ok(pid) => {
+ Ok((pid, _starts_held)) => {
pty.bus
.senders
.send_to_screen(ScreenInstruction::OpenInPlaceEditor(
@@ -178,23 +182,27 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
_ => (false, None, name),
};
match pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id)) {
- Ok(pid) => {
+ Ok((pid, starts_held)) => {
+ let hold_for_command = if starts_held { run_command } else { None };
pty.bus
.senders
.send_to_screen(ScreenInstruction::VerticalSplit(
PaneId::Terminal(pid),
pane_title,
+ hold_for_command,
client_id,
))
.with_context(err_context)?;
},
Err(SpawnTerminalError::CommandNotFound(pid)) => {
if hold_on_close {
+ let hold_for_command = None; // error panes are never held
pty.bus
.senders
.send_to_screen(ScreenInstruction::VerticalSplit(
PaneId::Terminal(pid),
pane_title,
+ hold_for_command,
client_id,
))
.with_context(err_context)?;
@@ -238,23 +246,27 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
_ => (false, None, name),
};
match pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id)) {
- Ok(pid) => {
+ Ok((pid, starts_held)) => {
+ let hold_for_command = if starts_held { run_command } else { None };
pty.bus
.senders
.send_to_screen(ScreenInstruction::HorizontalSplit(
PaneId::Terminal(pid),
pane_title,
+ hold_for_command,
client_id,
))
.with_context(err_context)?;
},
Err(SpawnTerminalError::CommandNotFound(pid)) => {
if hold_on_close {
+ let hold_for_command = None; // error panes are never held
pty.bus
.senders
.send_to_screen(ScreenInstruction::HorizontalSplit(
PaneId::Terminal(pid),
pane_title,
+ hold_for_command,
client_id,
))
.with_context(err_context)?;
@@ -391,6 +403,7 @@ impl Pty {
command: PathBuf::from(env::var("SHELL").expect("Could not find the SHELL variable")),
cwd, // note: this might also be filled by the calling function, eg. spawn_terminal
hold_on_close: false,
+ hold_on_start: false,
})
}
fn fill_cwd(&self, terminal_action: &mut TerminalAction, client_id: ClientId) {
@@ -416,7 +429,8 @@ impl Pty {
&mut self,
terminal_action: Option<TerminalAction>,
client_or_tab_index: ClientOrTabIndex,
- ) -> Result<u32, SpawnTerminalError> {
+ ) -> Result<(u32, bool), SpawnTerminalError> {
+ // bool is starts_held
// returns the terminal id
let terminal_action = match client_or_tab_index {
ClientOrTabIndex::ClientId(client_id) => {
@@ -429,10 +443,20 @@ impl Pty {
terminal_action.unwrap_or_else(|| self.get_default_terminal(None))
},
};
- let hold_on_close = match &terminal_action {
- TerminalAction::RunCommand(run_command) => run_command.hold_on_close,
- _ => false,
+ let (hold_on_start, hold_on_close) = match &terminal_action {
+ TerminalAction::RunCommand(run_command) => {
+ (run_command.hold_on_start, run_command.hold_on_close)
+ },
+ _ => (false, false),
};
+
+ if hold_on_start {
+ // we don't actually open a terminal in this case, just wait for the user to run it
+ let starts_held = hold_on_start;
+ let terminal_id = self.bus.os_input.as_mut().unwrap().reserve_terminal_id()?;
+ return Ok((terminal_id, starts_held));
+ }
+
let quit_cb = Box::new({
let senders = self.bus.senders.clone();
move |pane_id, exit_status, command| {
@@ -477,7 +501,8 @@ impl Pty {
self.task_handles.insert(terminal_id, terminal_bytes);
self.id_to_child_pid.insert(terminal_id, child_fd);
- Ok(terminal_id)
+ let starts_held = false;
+ Ok((terminal_id, starts_held))
}
pub fn spawn_terminals_for_layout(
&mut self,
@@ -489,10 +514,15 @@ impl Pty {
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>)> =
- vec![]; // (terminal_id,
- // run_command
- // file_descriptor)
+ let mut new_pane_pids: Vec<(
+ u32,
+ bool,
+ Option<RunCommand>,
+ Result<RawFd, SpawnTerminalError>,
+ )> = vec![]; // (terminal_id,
+ // starts_held,
+ // run_command,
+ // file_descriptor)
for run_instruction in extracted_run_instructions {
let quit_cb = Box::new({
let senders = self.bus.senders.clone();
@@ -502,6 +532,7 @@ impl Pty {
});
match run_instruction {
Some(Run::Command(command)) => {
+ let starts_held = command.hold_on_start;
let hold_on_close = command.hold_on_close;
let quit_cb = Box::new({
let senders = self.bus.senders.clone();
@@ -520,34 +551,56 @@ impl Pty {
}
});
let cmd = TerminalAction::RunCommand(command.clone());
- match self
- .bus
- .os_input
- .as_mut()
- .with_context(err_context)?
- .spawn_terminal(cmd, 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,
- Some(command.clone()),
- Ok(pid_primary),
- ));
- },
- Err(SpawnTerminalError::CommandNotFound(terminal_id)) => {
- new_pane_pids.push((
- terminal_id,
- Some(command.clone()),
- Err(SpawnTerminalError::CommandNotFound(terminal_id)),
- ));
- },
- Err(e) => {
- log::error!("Failed to spawn terminal: {}", e);
- },
+ if starts_held {
+ // we don't actually open a terminal in this case, just wait for the user to run it
+ match self.bus.os_input.as_mut().unwrap().reserve_terminal_id() {
+ Ok(terminal_id) => {
+ new_pane_pids.push((
+ terminal_id,
+ starts_held,
+ Some(command.clone()),
+ Ok(terminal_id as i32), // this is not actually correct but gets
+ // stripped later
+ ));
+ },
+ Err(e) => {
+ log::error!("Failed to spawn terminal: {}", e);
+ },
+ }
+ } else {
+ match self
+ .bus
+ .os_input
+ .as_mut()
+ .with_context(err_context)?
+ .spawn_terminal(cmd, 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,
+ starts_held,
+ Some(command.clone()),
+ Ok(pid_primary),
+ ));
+ },
+ Err(SpawnTerminalError::CommandNotFound(terminal_id)) => {
+ let starts_held = false; // we do not hold error panes
+ new_pane_pids.push((
+ terminal_id,
+ starts_held,
+ Some(command.clone()),
+ Err(SpawnTerminalError::CommandNotFound(terminal_id)),
+ ));
+ },
+ Err(e) => {
+ log::error!("Failed to spawn terminal: {}", e);
+ },
+ }
}
},
Some(Run::Cwd(cwd)) => {
+ let starts_held = false; // we do not hold Cwd panes
let shell = self.get_default_terminal(Some(cwd));
match self
.bus
@@ -558,11 +611,12 @@ impl Pty {
{
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)));
+ new_pane_pids.push((terminal_id, starts_held, None, Ok(pid_primary)));
},
Err(SpawnTerminalError::CommandNotFound(terminal_id)) => {
new_pane_pids.push((
terminal_id,
+ starts_held,
None,
Err(SpawnTerminalError::CommandNotFound(terminal_id)),
));
@@ -573,6 +627,7 @@ impl Pty {
}
},
Some(Run::EditFile(path_to_file, line_number)) => {
+ let starts_held = false; // we do not hold edit panes (for now?)
match self
.bus
.os_input
@@ -585,11 +640,13 @@ impl Pty {
) {
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)));
+ new_pane_pids.push((terminal_id, starts_held, None, Ok(pid_primary)));
},
Err(SpawnTerminalError::CommandNotFound(terminal_id)) => {
+ let starts_held = false; // we do not hold error panes
new_pane_pids.push((
terminal_id,
+ starts_held,
None,
Err(SpawnTerminalError::CommandNotFound(terminal_id)),
));
@@ -600,6 +657,7 @@ impl Pty {
}
},
None => {
+ let starts_held = false;
match self
.bus
.os_input
@@ -609,11 +667,12 @@ impl Pty {
{
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)));
+ new_pane_pids.push((terminal_id, starts_held, None, Ok(pid_primary)));
},
Err(SpawnTerminalError::CommandNotFound(terminal_id)) => {
new_pane_pids.push((
terminal_id,
+ starts_held,
None,
Err(SpawnTerminalError::CommandNotFound(terminal_id)),
));
@@ -627,10 +686,17 @@ impl Pty {
Some(Run::Plugin(_)) => {},
}
}
- let new_tab_pane_ids: Vec<u32> = new_pane_pids
+ // Option<RunCommand> should only be Some if the pane starts held
+ let new_tab_pane_ids: Vec<(u32, Option<RunCommand>)> = new_pane_pids
.iter()
- .map(|(terminal_id, _, _)| *terminal_id)
- .collect::<Vec<u32>>();
+ .map(|(terminal_id, starts_held, run_command, _)| {
+ if *starts_held {
+ (*terminal_id, run_command.clone())
+ } else {
+ (*terminal_id, None)
+ }
+ })
+ .collect();
self.bus
.senders
.send_to_screen(ScreenInstruction::NewTab(
@@ -639,7 +705,11 @@ impl Pty {
client_id,
))
.with_context(err_context)?;
- for (terminal_id, run_command, pid_primary) in new_pane_pids {
+ for (terminal_id, starts_held, run_command, pid_primary) in new_pane_pids {
+ if starts_held {
+ // we do not run a command or start listening for bytes on held panes
+ continue;
+ }
match pid_primary {
Ok(pid_primary) => {
let terminal_bytes = task::spawn({
@@ -744,16 +814,21 @@ impl Pty {
let _ = self.task_handles.remove(&id); // if all is well, this shouldn't be here
let _ = self.id_to_child_pid.remove(&id); // if all is wlel, this shouldn't be here
+ let hold_on_close = run_command.hold_on_close;
let quit_cb = Box::new({
let senders = self.bus.senders.clone();
move |pane_id, exit_status, command| {
- // we only re-run held panes, so we'll never close them from Pty
- let _ = senders.send_to_screen(ScreenInstruction::HoldPane(
- pane_id,
- exit_status,
- command,
- None,
- ));
+ if hold_on_close {
+ let _ = senders.send_to_screen(ScreenInstruction::HoldPane(
+ pane_id,
+ exit_status,
+ command,
+ None,
+ ));
+ } else {
+ let _ =
+ senders.send_to_screen(ScreenInstruction::ClosePane(pane_id, None));
+ }
}
});
let (pid_primary, child_fd): (RawFd, RawFd) = self
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index c1fcabc7..42d0c093 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -112,19 +112,27 @@ macro_rules! active_tab_and_connected_client_id {
};
}
+type InitialTitle = String;
+type ShouldFloat = bool;
+type HoldForCommand = Option<RunCommand>;
+
/// Instructions that can be sent to the [`Screen`].
#[derive(Debug, Clone)]
pub enum ScreenInstruction {
PtyBytes(u32, VteBytes),
Render,
- NewPane(PaneId, Option<String>, Option<bool>, ClientOrTabIndex), // String is initial title,
- // bool (if Some) is
- // should_float
+ NewPane(
+ PaneId,
+ Option<InitialTitle>,
+ Option<ShouldFloat>,
+ HoldForCommand,
+ ClientOrTabIndex,
+ ),
OpenInPlaceEditor(PaneId, ClientId),
TogglePaneEmbedOrFloating(ClientId),
ToggleFloatingPanes(ClientId, Option<TerminalAction>),
- HorizontalSplit(PaneId, Option<String>, ClientId), // String is initial title
- VerticalSplit(PaneId, Option<String>, ClientId), // String is initial title
+ HorizontalSplit(PaneId, Option<InitialTitle>, HoldForCommand, ClientId),
+ VerticalSplit(PaneId, Option<InitialTitle>, HoldForCommand, ClientId),
WriteCharacter(Vec<u8>, ClientId),
ResizeLeft(ClientId),
ResizeRight(ClientId),
@@ -167,7 +175,7 @@ pub enum ScreenInstruction {
HoldPane(PaneId, Option<i32>, RunCommand, Option<ClientId>), // Option<i32> is the exit status
UpdatePaneName(Vec<u8>, ClientId),
UndoRenamePane(ClientId),
- NewTab(PaneLayout, Vec<u32>, ClientId),
+ NewTab(PaneLayout, Vec<(u32, HoldForCommand)>, ClientId),
SwitchTabNext(ClientId),
SwitchTabPrev(ClientId),
ToggleActiveSyncTab(ClientId),
@@ -811,7 +819,7 @@ impl Screen {
pub fn new_tab(
&mut self,
layout: PaneLayout,
- new_ids: Vec<u32>,
+ new_ids: Vec<(u32, HoldForCommand)>,
client_id: ClientId,
) -> Result<()> {
let client_id = if self.get_active_tab(client_id).is_some() {
@@ -1227,6 +1235,7 @@ pub(crate) fn screen_thread_main(
pid,
initial_pane_title,
should_float,
+ hold_for_command,
client_or_tab_index,
) => {
match client_or_tab_index {
@@ -1237,10 +1246,27 @@ pub(crate) fn screen_thread_main(
should_float,
Some(client_id)),
?);
+ if let Some(hold_for_command) = hold_for_command {
+ let is_first_run = true;
+ active_tab_and_connected_client_id!(
+ screen,
+ client_id,
+ |tab: &mut Tab, _client_id: ClientId| tab.hold_pane(
+ pid,
+ None,
+ is_first_run,
+ hold_for_command
+ )
+ )
+ }
},
ClientOrTabIndex::TabIndex(tab_index) => {
if let Some(active_tab) = screen.tabs.get_mut(&tab_index) {
active_tab.new_pane(pid, initial_pane_title, should_float, None)?;
+ if let Some(hold_for_command) = hold_for_command {
+ let is_first_run = true;
+ active_tab.hold_pane(pid, None, is_first_run, hold_for_command);
+ }
} else {
log::error!("Tab index not found: {:?}", tab_index);
}
@@ -1275,24 +1301,60 @@ pub(crate) fn screen_thread_main(
screen.render()?;
},
- ScreenInstruction::HorizontalSplit(pid, initial_pane_title, client_id) => {
+ ScreenInstruction::HorizontalSplit(
+ pid,
+ initial_pane_title,
+ hold_for_command,
+ client_id,
+ ) => {
active_tab_and_connected_client_id!(
screen,
client_id,
|tab: &mut Tab, client_id: ClientId| tab.horizontal_split(pid, initial_pane_title, client_id),
?
);
+ if let Some(hold_for_command) = hold_for_command {
+ let is_first_run = true;
+ active_tab_and_connected_client_id!(
+ screen,
+ client_id,
+ |tab: &mut Tab, _client_id: ClientId| tab.hold_pane(
+ pid,
+ None,
+ is_first_run,
+ hold_for_command
+ )
+ );
+ }
screen.unblock_input()?;
screen.update_tabs()?;
screen.render()?;
},
- ScreenInstruction::VerticalSplit(pid, initial_pane_title, client_id) => {
+ ScreenInstruction::VerticalSplit(
+ pid,
+ initial_pane_title,
+ hold_for_command,
+ client_id,
+ ) => {
active_tab_and_connected_client_id!(
screen,
client_id,
|tab: &mut Tab, client_id: ClientId| tab.vertical_split(pid, initial_pane_title, client_id),
?
);
+ if let Some(hold_for_command) = hold_for_command {
+ let is_first_run = true;
+ active_tab_and_connected_client_id!(
+ screen,
+ client_id,
+ |tab: &mut Tab, _client_id: ClientId| tab.hold_pane(
+ pid,
+ None,
+ is_first_run,
+ hold_for_command
+ )
+ );
+ }
screen.unblock_input()?;
screen.update_tabs()?;
screen.render()?;
@@ -1644,18 +1706,20 @@ pub(crate) fn screen_thread_main(
screen.unblock_input()?;
},
ScreenInstruction::HoldPane(id, exit_status, run_command, client_id) => {
+ let is_first_run = false;
match client_id {
Some(client_id) => {
active_tab!(screen, client_id, |tab: &mut Tab| tab.hold_pane(
id,
exit_status,
+ is_first_run,
run_command
));
},
None => {
for tab in screen.tabs.values_mut() {
if tab.get_all_pane_ids().contains(&id) {
- tab.hold_pane(id, exit_status, run_command);
+ tab.hold_pane(id, exit_status, is_first_run, run_command);
break;
}
}
diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs
index 9efcf901..df12b2df 100644
--- a/zellij-server/src/tab/mod.rs
+++ b/zellij-server/src/tab/mod.rs
@@ -61,6 +61,8 @@ macro_rules! resize_pty {
};
}
+type HoldForCommand = Option<RunCommand>;
+
// FIXME: This should be replaced by `RESIZE_PERCENT` at some point
pub const MIN_TERMINAL_HEIGHT: usize = 5;
pub const MIN_TERMINAL_WIDTH: usize = 5;
@@ -354,7 +356,7 @@ pub trait Pane {
// False by default (only terminal-panes support alternate mode)
false
}
- fn hold(&mut self, _exit_status: Option<i32>, _run_command: RunCommand) {
+ fn hold(&mut self, _exit_status: Option<i32>, _is_first_run: bool, _run_command: RunCommand) {
// No-op by default, only terminal panes support holding
}
}
@@ -470,7 +472,7 @@ impl Tab {
pub fn apply_layout(
&mut self,
layout: PaneLayout,
- new_ids: Vec<u32>,
+ new_ids: Vec<(u32, HoldForCommand)>,
tab_index: usize,
client_id: ClientId,
) -> Result<()> {
@@ -532,7 +534,7 @@ impl Tab {
set_focus_pane_id(layout, PaneId::Plugin(pid));
} else {
// there are still panes left to fill, use the pids we received in this method
- if let Some(pid) = new_ids.next() {
+ if let Some((pid, hold_for_command)) = new_ids.next() {
let next_terminal_position = self.get_next_terminal_position();
let initial_title = match &layout.run {
Some(Run::Command(run_command)) => Some(run_command.to_string()),
@@ -552,6 +554,9 @@ impl Tab {
initial_title,
);
new_pane.set_borderless(layout.borderless);
+ if let Some(held_command) = hold_for_command {
+ new_pane.hold(None, true, held_command.clone());
+ }
self.tiled_panes.add_pane_with_existing_geom(
PaneId::Terminal(*pid),
Box::new(new_pane),
@@ -560,7 +565,7 @@ impl Tab {
}
}
}
- for unused_pid in new_ids {
+ for (unused_pid, _) in new_ids {
// this is a bit of a hack and happens because we don't have any central location that
// can query the screen as to how many panes it needs to create a layout
// fixing this will require a bit of an architecture change
@@ -601,7 +606,7 @@ impl Tab {
Ok(())
},
Err(e) => {
- for unused_pid in new_ids {
+ for (unused_pid, _) in new_ids {
self.senders
.send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(unused_pid)))
.with_context(err_context)?;
@@ -1746,11 +1751,19 @@ impl Tab {
closed_pane
}
}
- pub fn hold_pane(&mut self, id: PaneId, exit_status: Option<i32>, run_command: RunCommand) {
+ pub fn hold_pane(
+ &mut self,
+ id: PaneId,
+ exit_status: Option<i32>,
+ is_first_run: bool,
+ run_command: RunCommand,
+ ) {
if self.floating_panes.panes_contain(&id) {
- self.floating_panes.hold_pane(id, exit_status, run_command);
+ self.floating_panes
+ .hold_pane(id, exit_status, is_first_run, run_command);
} else {
- self.tiled_panes.hold_pane(id, exit_status, run_command);
+ self.tiled_panes
+ .hold_pane(id, exit_status, is_first_run, run_command);
}
}
pub fn replace_pane_with_suppressed_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs
index 5944f84d..3f4290fa 100644
--- a/zellij-server/src/tab/unit/tab_integration_tests.rs
+++ b/zellij-server/src/tab/unit/tab_integration_tests.rs
@@ -223,7 +223,7 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab {
terminal_emulator_colors,
terminal_emulator_color_codes,
);
- tab.apply_layout(PaneLayout::default(), vec![1], index, client_id)
+ tab.apply_layout(PaneLayout::default(), vec![(1, None)], index, client_id)
.unwrap();
tab
}
@@ -277,7 +277,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str)
.extract_run_instructions()
.iter()
.enumerate()
- .map(|(i, _)| i as u32)
+ .map(|(i, _)| (i as u32, None))
.collect();
tab.apply_layout(tab_layout, pane_ids, index, client_id)
.unwrap();
@@ -332,14 +332,8 @@ fn create_new_tab_with_mock_pty_writer(
terminal_emulator_colors,
terminal_emulator_color_codes,
);
- tab.apply_layout(
- // LayoutTemplate::default().try_into().unwrap(),
- PaneLayout::default(),
- vec![1],
- index,
- client_id,
- )
- .unwrap();
+ tab.apply_layout(PaneLayout::default(), vec![(1, None)], index, client_id)
+ .unwrap();
tab
}
@@ -393,7 +387,7 @@ fn create_new_tab_with_sixel_support(
terminal_emulator_colors,
terminal_emulator_color_codes,
);
- tab.apply_layout(PaneLayout::default(), vec![1], index, client_id)
+ tab.apply_layout(PaneLayout::default(), vec![(1, None)], index, client_id)
.unwrap();
tab
}
diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs
index 6b100cf6..2539b7a9 100644
--- a/zellij-server/src/tab/unit/tab_tests.rs
+++ b/zellij-server/src/tab/unit/tab_tests.rs
@@ -142,7 +142,7 @@ fn create_new_tab(size: Size) -> Tab {
terminal_emulator_colors,
terminal_emulator_color_codes,
);
- tab.apply_layout(PaneLayout::default(), vec![1], index, client_id)
+ tab.apply_layout(PaneLayout::default(), vec![(1, None)], index, client_id)
.unwrap();
tab
}
@@ -189,7 +189,7 @@ fn create_new_tab_with_cell_size(
terminal_emulator_colors,
terminal_emulator_color_codes,
);
- tab.apply_layout(PaneLayout::default(), vec![1], index, client_id)
+ tab.apply_layout(PaneLayout::default(), vec![(1, None)], index, client_id)
.unwrap();
tab
}
diff --git a/zellij-server/src/ui/pane_boundaries_frame.rs b/zellij-server/src/ui/pane_boundaries_frame.rs
index 61df8470..6209466c 100644
--- a/zellij-server/src/ui/pane_boundaries_frame.rs
+++ b/zellij-server/src/ui/pane_boundaries_frame.rs
@@ -89,6 +89,7 @@ pub struct PaneFrame {
pub other_cursors_exist_in_session: bool,
pub other_focused_clients: Vec<ClientId>,
exit_status: Option<ExitStatus>,
+ is_first_run: bool,
}
impl PaneFrame {
@@ -109,6 +110,7 @@ impl PaneFrame {
other_focused_clients: frame_params.other_focused_clients,
other_cursors_exist_in_session: frame_params.other_cursors_exist_in_session,
exit_status: None,
+ is_first_run: false,
}
}
pub fn add_exit_status(&mut self, exit_status: Option<i32>) {
@@ -117,6 +119,9 @@ impl PaneFrame {
None => Some(ExitStatus::Exited),
};
}
+ pub fn indicate_first_run(&mut self) {
+ self.is_first_run = true;
+ }
fn client_cursor(&self, client_id: ClientId) -> Vec<TerminalCharacter> {
let color = client_id_to_colors(client_id, self.style.colors);
background_color(" ", color.map(|c| c.0))
@@ -611,11 +616,7 @@ impl PaneFrame {
}
fn render_held_undertitle(&self) -> Result<Vec<TerminalCharacter>> {
let max_undertitle_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners
- let exit_status = self
- .exit_status
- .with_context(|| format!("failed to render command pane status '{}'", self.title))?; // unwrap is safe because we only call this if
-
- let (mut first_part, first_part_len) = self.first_held_title_part_full(exit_status);
+ let (mut first_part, first_part_len) = self.first_exited_held_title_part_full();
let mut left_boundary =
foreground_color(self.get_corner(boundary_type::BOTTOM_LEFT), self.color);
let mut right_boundary =
@@ -683,7 +684,7 @@ impl PaneFrame {
character_chunks.push(CharacterChunk::new(title, x, y));
} else if row == self.geom.rows - 1 {
// bottom row
- if self.exit_status.is_some() {
+ if self.exit_status.is_some() || self.is_first_run {
let x = self.geom.x;
let y = self.geom.y + row;
character_chunks.push(CharacterChunk::new(
@@ -727,13 +728,10 @@ impl PaneFrame {
}
Ok((character_chunks, None))
}
- fn first_held_title_part_full(
- &self,
- exit_status: ExitStatus,
- ) -> (Vec<TerminalCharacter>, usize) {
+ fn first_exited_held_title_part_full(&self) -> (Vec<TerminalCharacter>, usize) {
// (title part, length)
- match exit_status {
- ExitStatus::Code(exit_code) => {
+ match self.exit_status {
+ Some(ExitStatus::Code(exit_code)) => {
let mut first_part = vec![];
let left_bracket = " [ ";
let exited_text = "EXIT CODE: ";
@@ -759,7 +757,7 @@ impl PaneFrame {
+ right_bracket.len(),
)
},
- ExitStatus::Exited => {
+ Some(ExitStatus::Exited) => {
let mut first_part = vec![];
let left_bracket = " [ ";
let exited_text = "EXITED";
@@ -775,15 +773,20 @@ impl PaneFrame {
left_bracket.len() + exited_text.len() + right_bracket.len(),
)
},
+ None => (foreground_color(boundary_type::HORIZONTAL, self.color), 1),
}
}
fn second_held_title_part_full(&self) -> (Vec<TerminalCharacter>, usize) {
// (title part, length)
let mut second_part = vec![];
- let left_enter_bracket = "<";
+ let left_enter_bracket = if self.is_first_run { " <" } else { "<" };
let enter_text = "ENTER";
let right_enter_bracket = ">";
- let enter_tip = " to re-run, ";
+ let enter_tip = if self.is_first_run {
+ " to run, "
+ } else {
+ " to re-run, "
+ };
let left_break_bracket = "<";
let break_text = "Ctrl-c";
let right_break_bracket = ">";
diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs
index 299bab6a..8fd1a621 100644
--- a/zellij-server/src/unit/screen_tests.rs
+++ b/zellij-server/src/unit/screen_tests.rs
@@ -284,7 +284,7 @@ impl MockScreen {
let pane_count = pane_layout.extract_run_instructions().len();
let mut pane_ids = vec![];
for i in 0..pane_count {
- pane_ids.push(i as u32);
+ pane_ids.push((i as u32, None));
}
let _ = self.to_screen.send(ScreenInstruction::NewTab(
pane_layout,
@@ -297,7 +297,7 @@ impl MockScreen {
let pane_count = tab_layout.extract_run_instructions().len();
let mut pane_ids = vec![];
for i in 0..pane_count {
- pane_ids.push(i as u32);
+ pane_ids.push((i as u32, None));
}
let _ = self.to_screen.send(ScreenInstruction::NewTab(
tab_layout,
@@ -427,7 +427,7 @@ macro_rules! log_actions_in_thread {
fn new_tab(screen: &mut Screen, pid: u32) {
let client_id = 1;
screen
- .new_tab(PaneLayout::default(), vec![pid], client_id)
+ .new_tab(PaneLayout::default(), vec![(pid, None)], client_id)
.expect("TEST");
}
@@ -1822,6 +1822,7 @@ pub fn send_cli_new_pane_action_with_default_parameters() {
floating: false,
name: None,
close_on_exit: false,
+ start_suspended: false,
};
send_cli_action_to_server(
&session_metadata,
@@ -1861,6 +1862,7 @@ pub fn send_cli_new_pane_action_with_split_direction() {
floating: false,
name: None,
close_on_exit: false,
+ start_suspended: false,
};
send_cli_action_to_server(
&session_metadata,
@@ -1900,6 +1902,7 @@ pub fn send_cli_new_pane_action_with_command_and_cwd() {
floating: false,
name: None,
close_on_exit: false,
+ start_suspended: false,
};
send_cli_action_to_server(
&session_metadata,
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap
index e8f819d8..92e668f4 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap
@@ -1,6 +1,6 @@
---
source: zellij-server/src/./unit/screen_tests.rs
-assertion_line: 1981
+assertion_line: 1989
expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())"
---
[SpawnTerminal(Some(OpenFile("/file/to/edit", Some(100))), Some(false), Some("Editing: /file/to/edit"), ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit]
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap
index 62d76f63..1e88d09c 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap
@@ -1,6 +1,6 @@
---
source: zellij-server/src/./unit/screen_tests.rs
-assertion_line: 1907
+assertion_line: 1915
expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())"
---
-[SpawnTerminalVertically(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true })), None, 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit]
+[SpawnTerminalVertically(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false })), None, 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit]
diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs
index f860f05f..baa53b9a 100644
--- a/zellij-utils/src/cli.rs
+++ b/zellij-utils/src/cli.rs
@@ -141,6 +141,10 @@ pub enum Sessions {
/// Close the pane immediately when its command exits
#[clap(short, long, value_parser, default_value("false"), takes_value(false))]
close_on_exit: bool,
+
+ /// Start the command suspended, only running after you first presses ENTER
+ #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
+ start_suspended: bool,
},
/// Edit file with default $EDITOR / $VISUAL
#[clap(visible_alias = "e")]
@@ -252,6 +256,16 @@ pub enum CliAction {
requires("command")
)]
close_on_exit: bool,
+ /// Start the command suspended, only running it after the you first press ENTER
+ #[clap(
+ short,
+ long,
+ value_parser,
+ default_value("false"),
+ takes_value(false),
+ requires("command")
+ )]
+ start_suspended: bool,
},
/// Open the specified file in a new zellij pane with your default EDITOR
Edit {
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs
index f63390ad..189f3cd9 100644
--- a/zellij-utils/src/input/actions.rs
+++ b/zellij-utils/src/input/actions.rs
@@ -260,11 +260,13 @@ impl Action {
floating,
name,
close_on_exit,
+ start_suspended,
} => {
if !command.is_empty() {
let mut command = command.clone();
let (command, args) = (PathBuf::from(command.remove(0)), command);
let cwd = cwd.or_else(|| std::env::current_dir().ok());
+ let hold_on_start = start_suspended;
let hold_on_close = !close_on_exit;
let run_command_action = RunCommandAction {
command,
@@ -272,6 +274,7 @@ impl Action {
cwd,
direction,
hold_on_close,
+ hold_on_start,
};
if floating {
Ok(vec![Action::NewFloatingPane(
diff --git a/zellij-utils/src/input/command.rs b/zellij-utils/src/input/command.rs
index abb7c1e7..14b87755 100644
--- a/zellij-utils/src/input/command.rs
+++ b/zellij-utils/src/input/command.rs
@@ -19,6 +19,8 @@ pub struct RunCommand {
pub cwd: Option<PathBuf>,
#[serde(default)]
pub hold_on_close: bool,
+ #[serde(default)]
+ pub hold_on_start: bool,
}
impl std::fmt::Display for RunCommand {
@@ -50,6 +52,8 @@ pub struct RunCommandAction {
pub direction: Option<Direction>,
#[serde(default)]
pub hold_on_close: bool,
+ #[serde(default)]
+ pub hold_on_start: bool,
}
impl From<RunCommandAction> for RunCommand {
@@ -59,6 +63,7 @@ impl From<RunCommandAction> for RunCommand {
args: action.args,
cwd: action.cwd,
hold_on_close: action.hold_on_close,
+ hold_on_start: action.hold_on_start,
}
}
}
diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs
index fa50237a..38806c21 100644
--- a/zellij-utils/src/input/layout.rs
+++ b/zellij-utils/src/input/layout.rs
@@ -142,7 +142,7 @@ impl Run {
}
}
pub fn add_close_on_exit(&mut self, close_on_exit: Option<bool>) {
- // overrides the args of a Run::Command if they are Some
+ // overrides the hold_on_close of a Run::Command if it is Some
// and not empty
if let Some(close_on_exit) = close_on_exit {
if let Run::Command(run_command) = self {
@@ -150,6 +150,15 @@ impl Run {
}
}
}
+ pub fn add_start_suspended(&mut self, start_suspended: Option<bool>) {
+ // overrides the hold_on_start of a Run::Command if they are Some
+ // and not empty
+ if let Some(start_suspended) = start_suspended {
+ if let Run::Command(run_command) = self {
+ run_command.hold_on_start = start_suspended;
+ }
+ }
+ }
}
#[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 9614eef4..f11c8686 100644
--- a/zellij-utils/src/input/unit/layout_test.rs
+++ b/zellij-utils/src/input/unit/layout_test.rs
@@ -282,6 +282,19 @@ fn layout_with_command_panes_and_close_on_exit() {
}
#[test]
+fn layout_with_command_panes_and_start_suspended() {
+ let kdl_layout = r#"
+ layout {
+ pane command="htop" {
+ start_suspended true
+ }
+ }
+ "#;
+ let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap();
+ assert_snapshot!(format!("{:#?}", layout));
+}
+
+#[test]
fn layout_with_plugin_panes() {
let kdl_layout = r#"
layout {
diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap
index 6ff62582..8b6d9050 100644
--- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1050
+assertion_line: 1081
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -23,6 +23,7 @@ Layout {
args: [],
cwd: None,
hold_on_close: true,
+ hold_on_start: false,
},
),
),
@@ -45,6 +46,7 @@ Layout {
],
cwd: None,
hold_on_close: true,
+ hold_on_start: false,
},
),
),
diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap
index 4a449d35..4b026d75 100644
--- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1033
+assertion_line: 1046
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -26,6 +26,7 @@ Layout {
],
cwd: None,
hold_on_close: true,
+ hold_on_start: false,
},
),
),
@@ -48,6 +49,7 @@ Layout {
],
cwd: None,
hold_on_close: true,
+ hold_on_start: false,
},
),
),
diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap
index da83cc62..d23bb40c 100644
--- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap
@@ -23,6 +23,7 @@ Layout {
args: [],
cwd: None,
hold_on_close: true,
+ hold_on_start: false,
},
),
),
@@ -42,6 +43,7 @@ Layout {
args: [],
cwd: None,
hold_on_close: false,
+ hold_on_start: false,
},
),
),
diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap
index 3cb80cf7..abce2964 100644
--- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap
@@ -23,6 +23,7 @@ Layout {
args: [],
cwd: None,
hold_on_close: true,
+ hold_on_start: false,
},
),
),
@@ -42,6 +43,7 @@ Layout {
args: [],
cwd: None,
hold_on_close: false,
+ hold_on_start: false,
},
),
),
diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap
index 3c26fbdd..48bb1183 100644
--- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1085
+assertion_line: 1133
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -23,6 +23,7 @@ Layout {
args: [],
cwd: None,
hold_on_close: true,
+ hold_on_start: false,
},
),
),
@@ -44,6 +45,7 @@ Layout {
"/home",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap
index fd1bc15a..7d57e6d0 100644
--- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1068
+assertion_line: 1116
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -25,6 +25,7 @@ Layout {
"/tmp",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
@@ -46,6 +47,7 @@ Layout {
"/",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index a83674d2..58a78678 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1441
+assertion_line: 1516
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -39,6 +39,7 @@ Layout {
"/tmp/./foo/./bar",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 8381dcd1..c6b3ed5f 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1462
+assertion_line: 1537
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -43,6 +43,7 @@ Layout {
"/tmp/./foo/./bar",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 65f28704..cd1a51e1 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1481
+assertion_line: 1556
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -39,6 +39,7 @@ Layout {
"/tmp/./foo/./bar",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 548ae756..ba246e06 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1283
+assertion_line: 1403
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -39,6 +39,7 @@ Layout {
"/tmp",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index d88a8ece..3b546891 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1309
+assertion_line: 1435
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -39,6 +39,7 @@ Layout {
"/tmp",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 0710ae13..50b92892 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1324
+assertion_line: 1455
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -39,6 +39,7 @@ Layout {
"/tmp",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 956df1a5..f5d6b95a 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1295
+assertion_line: 1416
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -39,6 +39,7 @@ Layout {
"/home/foo",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 4bc2f412..09d209c1 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1396
+assertion_line: 1471
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -39,6 +39,7 @@ Layout {
"/tmp/./foo",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap
index 69e3d7f1..32d1341c 100644
--- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap
@@ -23,6 +23,7 @@ Layout {
args: [],
cwd: None,
hold_on_close: false,
+ hold_on_start: false,
},
),
),
diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap
new file mode 100644
index 00000000..136c95fb
--- /dev/null
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap
@@ -0,0 +1,42 @@
+---
+source: zellij-utils/src/input/./unit/layout_test.rs
+assertion_line: 294
+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: "htop",
+ args: [],
+ cwd: None,
+ hold_on_close: true,
+ hold_on_start: 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__layout_with_tab_and_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap
index f32b346d..fbe11152 100644
--- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap
+++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 570
+assertion_line: 583
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -41,6 +41,7 @@ Layout {
args: [],
cwd: None,
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 03adaf85..94a52aac 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1199
+assertion_line: 1287
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -25,6 +25,7 @@ Layout {
"/tmp/bar",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index d01c6516..6f2255cd 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1144
+assertion_line: 1233
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -25,6 +25,7 @@ Layout {
"/tmp/foo",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 596c492b..d659eba3 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1161
+assertion_line: 1250
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -25,6 +25,7 @@ Layout {
"/tmp/bar",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 25bca8db..feb8a88b 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1180
+assertion_line: 1268
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -25,6 +25,7 @@ Layout {
"/tmp/bar",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 05e377b9..6cad38df 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1216
+assertion_line: 1305
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -25,6 +25,7 @@ Layout {
"/tmp/bar",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 8c3863c3..42fb84d2 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1269
+assertion_line: 1357
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -25,6 +25,7 @@ Layout {
"/tmp/bar",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index ba91e574..a09f2ab2 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1251
+assertion_line: 1339
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -25,6 +25,7 @@ Layout {
"/tmp/foo",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 8f05eda1..4508cb07 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1411
+assertion_line: 1486
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -39,6 +39,7 @@ Layout {
"/tmp",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
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
index 32eb17d0..ac1d4240 100644
--- 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
@@ -1,6 +1,6 @@
---
source: zellij-utils/src/input/./unit/layout_test.rs
-assertion_line: 1426
+assertion_line: 1501
expression: "format!(\"{:#?}\", layout)"
---
Layout {
@@ -39,6 +39,7 @@ Layout {
"/tmp/./foo",
),
hold_on_close: true,
+ hold_on_start: false,
},
),
),
diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs
index b92c6a1b..c5a84a7b 100644
--- a/zellij-utils/src/kdl/kdl_layout_parser.rs
+++ b/zellij-utils/src/kdl/kdl_layout_parser.rs
@@ -54,6 +54,7 @@ impl<'a> KdlLayoutParser<'a> {
|| word == "tab"
|| word == "args"
|| word == "close_on_exit"
+ || word == "start_suspended"
|| word == "borderless"
|| word == "focus"
|| word == "name"
@@ -72,6 +73,7 @@ impl<'a> KdlLayoutParser<'a> {
|| property_name == "cwd"
|| property_name == "args"
|| property_name == "close_on_exit"
+ || property_name == "start_suspended"
|| property_name == "split_direction"
|| property_name == "pane"
|| property_name == "children"
@@ -241,15 +243,19 @@ impl<'a> KdlLayoutParser<'a> {
let args = self.parse_args(pane_node)?;
let close_on_exit =
kdl_get_bool_property_or_child_value_with_error!(pane_node, "close_on_exit");
+ let start_suspended =
+ kdl_get_bool_property_or_child_value_with_error!(pane_node, "start_suspended");
if !is_template {
self.assert_no_bare_attributes_in_pane_node(
&command,
&args,
&close_on_exit,
+ &start_suspended,
pane_node,
)?;
}
let hold_on_close = close_on_exit.map(|c| !c).unwrap_or(true);
+ let hold_on_start = start_suspended.map(|c| c).unwrap_or(false);
match (command, edit, cwd) {
(None, None, Some(cwd)) => Ok(Some(Run::Cwd(cwd))),
(Some(command), None, cwd) => Ok(Some(Run::Command(RunCommand {
@@ -257,6 +263,7 @@ impl<'a> KdlLayoutParser<'a> {
args: args.unwrap_or_else(|| vec![]),
cwd,
hold_on_close,
+ hold_on_start,
}))),
(None, Some(edit), Some(cwd)) => Ok(Some(Run::EditFile(cwd.join(edit), None))),
(None, Some(edit), None) => Ok(Some(Run::EditFile(edit, None))),
@@ -380,6 +387,8 @@ impl<'a> KdlLayoutParser<'a> {
let args = self.parse_args(kdl_node)?;
let close_on_exit =
kdl_get_bool_property_or_child_value_with_error!(kdl_node, "close_on_exit");
+ let start_suspended =
+ kdl_get_bool_property_or_child_value_with_error!(kdl_node, "start_suspended");
let split_size = self.parse_split_size(kdl_node)?;
let run = self.parse_command_plugin_or_edit_block_for_template(kdl_node)?;
self.assert_no_bare_attributes_in_pane_node_with_template(
@@ -387,6 +396,7 @@ impl<'a> KdlLayoutParser<'a> {
&pane_template.run,
&args,
&close_on_exit,
+ &start_suspended,
kdl_node,
)?;
self.insert_children_to_pane_template(
@@ -396,10 +406,11 @@ impl<'a> KdlLayoutParser<'a> {
)?;
pane_template.run = Run::merge(&pane_template.run, &run);
if let Some(pane_template_run_command) = pane_template.run.as_mut() {
- // we need to do this because panes consuming a pane_templates
+ // we need to do this because panes consuming a pane_template
// can have bare args without a command
pane_template_run_command.add_args(args);
pane_template_run_command.add_close_on_exit(close_on_exit);
+ pane_template_run_command.add_start_suspended(start_suspended);
};
if let Some(borderless) = borderless {
pane_template.borderless = borderless;
@@ -600,6 +611,7 @@ impl<'a> KdlLayoutParser<'a> {
pane_template_run: &Option<Run>,
args: &Option<Vec<String>>,
close_on_exit: &Option<bool>,
+ start_suspended: &Option<bool>,
pane_node: &KdlNode,
) -> Result<(), ConfigError> {
if let (None, None, true) = (pane_run, pane_template_run, args.is_some()) {
@@ -614,6 +626,12 @@ impl<'a> KdlLayoutParser<'a> {
pane_node
));
}
+ if let (None, None, true) = (pane_run, pane_template_run, start_suspended.is_some()) {
+ return Err(kdl_parsing_error!(
+ format!("start_suspended can only be specified if a command was specified either in the pane_template or in the pane"),
+ pane_node
+ ));
+ }
Ok(())
}
fn assert_no_bare_attributes_in_pane_node(
@@ -621,6 +639,7 @@ impl<'a> KdlLayoutParser<'a> {
command: &Option<PathBuf>,
args: &Option<Vec<String>>,
close_on_exit: &Option<bool>,
+ start_suspended: &Option<bool>,
pane_node: &KdlNode,
) -> Result<(), ConfigError> {
if command.is_none() {
@@ -631,6 +650,13 @@ impl<'a> KdlLayoutParser<'a> {
pane_node.span().len(),
));
}
+ if start_suspended.is_some() {
+ return Err(ConfigError::new_layout_kdl_error(
+ "start_suspended can only be set if a command was specified".into(),
+ pane_node.span().offset(),
+ pane_node.span().len(),
+ ));
+ }
if args.is_some() {
return Err(ConfigError::new_layout_kdl_error(
"args can only be set if a command was specified".into(),
diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs
index 92abcac1..54560bf5 100644
--- a/zellij-utils/src/kdl/mod.rs
+++ b/zellij-utils/src/kdl/mod.rs
@@ -1,7 +1,6 @@
mod kdl_layout_parser;
use crate::data::{InputMode, Key, Palette, PaletteColor};
use crate::envs::EnvironmentVariables;
-use crate::input::command::RunCommand;
use crate::input::config::{Config, ConfigError, KdlError};
use crate::input::keybinds::Keybinds;
use crate::input::layout::{Layout, RunPlugin, RunPluginLocation};
@@ -339,6 +338,16 @@ pub fn kdl_child_string_value_for_entry<'a>(
.and_then(|cwd_value| cwd_value.value().as_string())
}
+pub fn kdl_child_bool_value_for_entry<'a>(
+ command_metadata: &'a KdlDocument,
+ entry_name: &'a str,
+) -> Option<bool> {
+ command_metadata
+ .get(entry_name)
+ .and_then(|cwd| cwd.entries().iter().next())
+ .and_then(|cwd_value| cwd_value.value().as_bool())
+}
+
impl Action {
pub fn new_from_bytes(
action_name: &str,
@@ -741,12 +750,20 @@ impl TryFrom<&KdlNode> for Action {
let direction = command_metadata
.and_then(|c_m| kdl_child_string_value_for_entry(c_m, "direction"))
.and_then(|direction_string| Direction::from_str(direction_string).ok());
+ let hold_on_close = command_metadata
+ .and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "close_on_exit"))
+ .and_then(|close_on_exit| Some(!close_on_exit))
+ .unwrap_or(true);
+ let hold_on_start = command_metadata
+ .and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "start_suspended"))
+ .unwrap_or(false);
let run_command_action = RunCommandAction {
command: PathBuf::from(command),
args,
cwd,
direction,
- hold_on_close: true,
+ hold_on_close,
+ hold_on_start,
};
Ok(Action::Run(run_command_action))
},
@@ -1464,32 +1481,6 @@ impl Keybinds {
}
}
-impl RunCommand {
- pub fn from_kdl(kdl_node: &KdlNode) -> Result<Self, ConfigError> {
- let command = PathBuf::from(kdl_get_child_entry_string_value!(kdl_node, "cmd").ok_or(
- ConfigError::new_kdl_error(
- "Command must have a cmd value".into(),
- kdl_node.span().offset(),
- kdl_node.span().len(),
- ),
- )?);
- let cwd = kdl_get_child_entry_string_value!(kdl_node, "cwd").map(|c| PathBuf::from(c));
- let args = match kdl_get_child!(kdl_node, "args") {
- Some(kdl_args) => kdl_string_arguments!(kdl_args)
- .iter()
- .map(|s| String::from(*s))
- .collect(),
- None => vec![],
- };
- Ok(RunCommand {
- command,
- args,
- cwd,
- hold_on_close: true,
- })
- }
-}
-
impl Config {
pub fn from_kdl(kdl_config: &str, base_config: Option<Config>) -> Result<Config, ConfigError> {
let mut config = base_config.unwrap_or_else(|| Config::default());