mirror of
https://github.com/zellij-org/zellij.git
synced 2024-12-24 01:34:38 +03:00
feat(command-panes): allow to start suspended (#1887)
* feat(command-panes): allow panes to start suspended * style(fmt): remove unused code * style(fmt): rustfmt
This commit is contained in:
parent
6d29c6951e
commit
abc700fc4d
@ -26,6 +26,7 @@ fn main() {
|
|||||||
floating,
|
floating,
|
||||||
name,
|
name,
|
||||||
close_on_exit,
|
close_on_exit,
|
||||||
|
start_suspended,
|
||||||
})) = opts.command
|
})) = opts.command
|
||||||
{
|
{
|
||||||
let command_cli_action = CliAction::NewPane {
|
let command_cli_action = CliAction::NewPane {
|
||||||
@ -35,6 +36,7 @@ fn main() {
|
|||||||
floating,
|
floating,
|
||||||
name,
|
name,
|
||||||
close_on_exit,
|
close_on_exit,
|
||||||
|
start_suspended,
|
||||||
};
|
};
|
||||||
commands::send_action_to_session(command_cli_action, opts.session);
|
commands::send_action_to_session(command_cli_action, opts.session);
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
|
@ -1914,6 +1914,25 @@ pub fn send_command_through_the_cli() {
|
|||||||
step_is_complete
|
step_is_complete
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
.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 {
|
.add_step(Step {
|
||||||
name: "Wait for command to run",
|
name: "Wait for command to run",
|
||||||
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
|
||||||
|
@ -346,7 +346,8 @@ impl RemoteTerminal {
|
|||||||
let mut channel = self.channel.lock().unwrap();
|
let mut channel = self.channel.lock().unwrap();
|
||||||
channel
|
channel
|
||||||
.write_all(
|
.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();
|
.unwrap();
|
||||||
channel.flush().unwrap();
|
channel.flush().unwrap();
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
---
|
---
|
||||||
source: src/tests/e2e/cases.rs
|
source: src/tests/e2e/cases.rs
|
||||||
assertion_line: 1968
|
assertion_line: 1998
|
||||||
expression: last_snapshot
|
expression: last_snapshot
|
||||||
---
|
---
|
||||||
Zellij (e2e-test) Tab #1
|
Zellij (e2e-test) Tab #1
|
||||||
┌ Pane #1 ─────────────────────────────────────────────────┐┌ /usr/src/zellij/fixtures/append-echo-script.sh ──────────┐
|
┌ Pane #1 ─────────────────────────────────────────────────┐┌ /usr/src/zellij/fixtures/append-echo-script.sh ──────────┐
|
||||||
│$ /usr/src/zellij/x86_64-unknown-linux-musl/release/zellij││foo │
|
│$ /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 │
|
||||||
│$ ││█ │
|
│" ││█ │
|
||||||
│ ││ │
|
│$ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
│ ││ │
|
│ ││ │
|
||||||
|
@ -280,6 +280,7 @@ fn spawn_terminal(
|
|||||||
args,
|
args,
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: false,
|
hold_on_close: false,
|
||||||
|
hold_on_start: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TerminalAction::RunCommand(command) => command,
|
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
|
quit_cb: Box<dyn Fn(PaneId, Option<i32>, RunCommand) + Send>, // u32 is the exit status
|
||||||
default_editor: Option<PathBuf>,
|
default_editor: Option<PathBuf>,
|
||||||
) -> Result<(u32, RawFd, RawFd), SpawnTerminalError>;
|
) -> 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`.
|
/// 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>;
|
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
|
/// 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),
|
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> {
|
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> {
|
||||||
unistd::read(fd, buf)
|
unistd::read(fd, buf)
|
||||||
}
|
}
|
||||||
|
@ -156,11 +156,12 @@ impl FloatingPanes {
|
|||||||
&mut self,
|
&mut self,
|
||||||
pane_id: PaneId,
|
pane_id: PaneId,
|
||||||
exit_status: Option<i32>,
|
exit_status: Option<i32>,
|
||||||
|
is_first_run: bool,
|
||||||
run_command: RunCommand,
|
run_command: RunCommand,
|
||||||
) {
|
) {
|
||||||
self.panes
|
self.panes
|
||||||
.get_mut(&pane_id)
|
.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>> {
|
pub fn get(&self, pane_id: &PaneId) -> Option<&Box<dyn Pane>> {
|
||||||
self.panes.get(pane_id)
|
self.panes.get(pane_id)
|
||||||
|
@ -1533,6 +1533,7 @@ impl Grid {
|
|||||||
self.sixel_scrolling = false;
|
self.sixel_scrolling = false;
|
||||||
self.mouse_mode = MouseMode::NoEncoding;
|
self.mouse_mode = MouseMode::NoEncoding;
|
||||||
self.mouse_tracking = MouseTracking::Off;
|
self.mouse_tracking = MouseTracking::Off;
|
||||||
|
self.cursor_is_hidden = false;
|
||||||
if let Some(images_to_reap) = self.sixel_grid.clear() {
|
if let Some(images_to_reap) = self.sixel_grid.clear() {
|
||||||
self.sixel_grid.reap_images(images_to_reap);
|
self.sixel_grid.reap_images(images_to_reap);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,12 @@ use std::fmt::{self, Debug, Display, Formatter};
|
|||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use unicode_width::UnicodeWidthChar;
|
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;
|
use crate::panes::alacritty_functions::parse_sgr_color;
|
||||||
|
|
||||||
@ -736,3 +741,113 @@ impl ::std::fmt::Debug for TerminalCharacter {
|
|||||||
write!(f, "{}", self.character)
|
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
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ use crate::output::{CharacterChunk, SixelImageChunk};
|
|||||||
use crate::panes::sixel::SixelImageStore;
|
use crate::panes::sixel::SixelImageStore;
|
||||||
use crate::panes::{
|
use crate::panes::{
|
||||||
grid::Grid,
|
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::panes::{AnsiCode, LinkHandler};
|
||||||
use crate::pty::VteBytes;
|
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?
|
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
|
// 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.
|
// their `reflow_lines()` method. Drop a Box<dyn ServerOsApi> in here somewhere.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
@ -104,8 +106,10 @@ pub struct TerminalPane {
|
|||||||
borderless: bool,
|
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
|
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,
|
search_term: String,
|
||||||
is_held: Option<(Option<i32>, RunCommand)>, // a "held" pane means that its command has exited and its waiting for a
|
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
|
// 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 {
|
impl Pane for TerminalPane {
|
||||||
@ -170,13 +174,14 @@ impl Pane for TerminalPane {
|
|||||||
// needs to be adjusted.
|
// needs to be adjusted.
|
||||||
// here we match against those cases - if need be, we adjust the input and if not
|
// here we match against those cases - if need be, we adjust the input and if not
|
||||||
// we send back the original input
|
// 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() {
|
match input_bytes.as_slice() {
|
||||||
ENTER_CARRIAGE_RETURN | ENTER_NEWLINE | SPACE => {
|
ENTER_CARRIAGE_RETURN | ENTER_NEWLINE | SPACE => {
|
||||||
let run_command = run_command.clone();
|
let run_command = run_command.clone();
|
||||||
self.is_held = None;
|
self.is_held = None;
|
||||||
self.grid.reset_terminal_state();
|
self.grid.reset_terminal_state();
|
||||||
self.set_should_render(true);
|
self.set_should_render(true);
|
||||||
|
self.remove_banner();
|
||||||
Some(AdjustedInput::ReRunCommandInThisPane(run_command))
|
Some(AdjustedInput::ReRunCommandInThisPane(run_command))
|
||||||
},
|
},
|
||||||
CTRL_C => Some(AdjustedInput::CloseThisPane),
|
CTRL_C => Some(AdjustedInput::CloseThisPane),
|
||||||
@ -395,8 +400,12 @@ impl Pane for TerminalPane {
|
|||||||
pane_title,
|
pane_title,
|
||||||
frame_params,
|
frame_params,
|
||||||
);
|
);
|
||||||
if let Some((exit_status, _run_command)) = &self.is_held {
|
if let Some((exit_status, is_first_run, _run_command)) = &self.is_held {
|
||||||
frame.add_exit_status(exit_status.as_ref().copied());
|
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) {
|
let res = match self.frame.get(&client_id) {
|
||||||
@ -701,8 +710,11 @@ impl Pane for TerminalPane {
|
|||||||
fn is_alternate_mode_active(&self) -> bool {
|
fn is_alternate_mode_active(&self) -> bool {
|
||||||
self.grid.is_alternate_mode_active()
|
self.grid.is_alternate_mode_active()
|
||||||
}
|
}
|
||||||
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) {
|
||||||
self.is_held = Some((exit_status, run_command));
|
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);
|
self.set_should_render(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -752,6 +764,7 @@ impl TerminalPane {
|
|||||||
fake_cursor_locations: HashSet::new(),
|
fake_cursor_locations: HashSet::new(),
|
||||||
search_term: String::new(),
|
search_term: String::new(),
|
||||||
is_held: None,
|
is_held: None,
|
||||||
|
banner: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_x(&self) -> usize {
|
pub fn get_x(&self) -> usize {
|
||||||
@ -782,6 +795,10 @@ impl TerminalPane {
|
|||||||
let rows = self.get_content_rows();
|
let rows = self.get_content_rows();
|
||||||
let cols = self.get_content_columns();
|
let cols = self.get_content_columns();
|
||||||
self.grid.change_size(rows, cols);
|
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);
|
self.set_should_render(true);
|
||||||
}
|
}
|
||||||
pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
|
pub fn read_buffer_as_lines(&self) -> Vec<Vec<TerminalCharacter>> {
|
||||||
@ -791,6 +808,25 @@ impl TerminalPane {
|
|||||||
// (x, y)
|
// (x, y)
|
||||||
self.grid.cursor_coordinates()
|
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)]
|
#[cfg(test)]
|
||||||
|
@ -1010,11 +1010,12 @@ impl TiledPanes {
|
|||||||
&mut self,
|
&mut self,
|
||||||
pane_id: PaneId,
|
pane_id: PaneId,
|
||||||
exit_status: Option<i32>,
|
exit_status: Option<i32>,
|
||||||
|
is_first_run: bool,
|
||||||
run_command: RunCommand,
|
run_command: RunCommand,
|
||||||
) {
|
) {
|
||||||
self.panes
|
self.panes
|
||||||
.get_mut(&pane_id)
|
.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 {
|
pub fn panes_to_hide_contains(&self, pane_id: PaneId) -> bool {
|
||||||
self.panes_to_hide.contains(&pane_id)
|
self.panes_to_hide.contains(&pane_id)
|
||||||
|
@ -108,25 +108,29 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
|
|||||||
_ => (false, None, name),
|
_ => (false, None, name),
|
||||||
};
|
};
|
||||||
match pty.spawn_terminal(terminal_action, client_or_tab_index) {
|
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
|
pty.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::NewPane(
|
.send_to_screen(ScreenInstruction::NewPane(
|
||||||
PaneId::Terminal(pid),
|
PaneId::Terminal(pid),
|
||||||
pane_title,
|
pane_title,
|
||||||
should_float,
|
should_float,
|
||||||
|
hold_for_command,
|
||||||
client_or_tab_index,
|
client_or_tab_index,
|
||||||
))
|
))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
},
|
},
|
||||||
Err(SpawnTerminalError::CommandNotFound(pid)) => {
|
Err(SpawnTerminalError::CommandNotFound(pid)) => {
|
||||||
if hold_on_close {
|
if hold_on_close {
|
||||||
|
let hold_for_command = None; // we do not hold an "error" pane
|
||||||
pty.bus
|
pty.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::NewPane(
|
.send_to_screen(ScreenInstruction::NewPane(
|
||||||
PaneId::Terminal(pid),
|
PaneId::Terminal(pid),
|
||||||
pane_title,
|
pane_title,
|
||||||
should_float,
|
should_float,
|
||||||
|
hold_for_command,
|
||||||
client_or_tab_index,
|
client_or_tab_index,
|
||||||
))
|
))
|
||||||
.with_context(err_context)?;
|
.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)),
|
Some(TerminalAction::OpenFile(temp_file, line_number)),
|
||||||
ClientOrTabIndex::ClientId(client_id),
|
ClientOrTabIndex::ClientId(client_id),
|
||||||
) {
|
) {
|
||||||
Ok(pid) => {
|
Ok((pid, _starts_held)) => {
|
||||||
pty.bus
|
pty.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::OpenInPlaceEditor(
|
.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),
|
_ => (false, None, name),
|
||||||
};
|
};
|
||||||
match pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id)) {
|
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
|
pty.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::VerticalSplit(
|
.send_to_screen(ScreenInstruction::VerticalSplit(
|
||||||
PaneId::Terminal(pid),
|
PaneId::Terminal(pid),
|
||||||
pane_title,
|
pane_title,
|
||||||
|
hold_for_command,
|
||||||
client_id,
|
client_id,
|
||||||
))
|
))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
},
|
},
|
||||||
Err(SpawnTerminalError::CommandNotFound(pid)) => {
|
Err(SpawnTerminalError::CommandNotFound(pid)) => {
|
||||||
if hold_on_close {
|
if hold_on_close {
|
||||||
|
let hold_for_command = None; // error panes are never held
|
||||||
pty.bus
|
pty.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::VerticalSplit(
|
.send_to_screen(ScreenInstruction::VerticalSplit(
|
||||||
PaneId::Terminal(pid),
|
PaneId::Terminal(pid),
|
||||||
pane_title,
|
pane_title,
|
||||||
|
hold_for_command,
|
||||||
client_id,
|
client_id,
|
||||||
))
|
))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
@ -238,23 +246,27 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
|
|||||||
_ => (false, None, name),
|
_ => (false, None, name),
|
||||||
};
|
};
|
||||||
match pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id)) {
|
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
|
pty.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::HorizontalSplit(
|
.send_to_screen(ScreenInstruction::HorizontalSplit(
|
||||||
PaneId::Terminal(pid),
|
PaneId::Terminal(pid),
|
||||||
pane_title,
|
pane_title,
|
||||||
|
hold_for_command,
|
||||||
client_id,
|
client_id,
|
||||||
))
|
))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
},
|
},
|
||||||
Err(SpawnTerminalError::CommandNotFound(pid)) => {
|
Err(SpawnTerminalError::CommandNotFound(pid)) => {
|
||||||
if hold_on_close {
|
if hold_on_close {
|
||||||
|
let hold_for_command = None; // error panes are never held
|
||||||
pty.bus
|
pty.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::HorizontalSplit(
|
.send_to_screen(ScreenInstruction::HorizontalSplit(
|
||||||
PaneId::Terminal(pid),
|
PaneId::Terminal(pid),
|
||||||
pane_title,
|
pane_title,
|
||||||
|
hold_for_command,
|
||||||
client_id,
|
client_id,
|
||||||
))
|
))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
@ -391,6 +403,7 @@ impl Pty {
|
|||||||
command: PathBuf::from(env::var("SHELL").expect("Could not find the SHELL variable")),
|
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
|
cwd, // note: this might also be filled by the calling function, eg. spawn_terminal
|
||||||
hold_on_close: false,
|
hold_on_close: false,
|
||||||
|
hold_on_start: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn fill_cwd(&self, terminal_action: &mut TerminalAction, client_id: ClientId) {
|
fn fill_cwd(&self, terminal_action: &mut TerminalAction, client_id: ClientId) {
|
||||||
@ -416,7 +429,8 @@ impl Pty {
|
|||||||
&mut self,
|
&mut self,
|
||||||
terminal_action: Option<TerminalAction>,
|
terminal_action: Option<TerminalAction>,
|
||||||
client_or_tab_index: ClientOrTabIndex,
|
client_or_tab_index: ClientOrTabIndex,
|
||||||
) -> Result<u32, SpawnTerminalError> {
|
) -> Result<(u32, bool), SpawnTerminalError> {
|
||||||
|
// bool is starts_held
|
||||||
// returns the terminal id
|
// returns the terminal id
|
||||||
let terminal_action = match client_or_tab_index {
|
let terminal_action = match client_or_tab_index {
|
||||||
ClientOrTabIndex::ClientId(client_id) => {
|
ClientOrTabIndex::ClientId(client_id) => {
|
||||||
@ -429,10 +443,20 @@ impl Pty {
|
|||||||
terminal_action.unwrap_or_else(|| self.get_default_terminal(None))
|
terminal_action.unwrap_or_else(|| self.get_default_terminal(None))
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let hold_on_close = match &terminal_action {
|
let (hold_on_start, hold_on_close) = match &terminal_action {
|
||||||
TerminalAction::RunCommand(run_command) => run_command.hold_on_close,
|
TerminalAction::RunCommand(run_command) => {
|
||||||
_ => false,
|
(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 quit_cb = Box::new({
|
||||||
let senders = self.bus.senders.clone();
|
let senders = self.bus.senders.clone();
|
||||||
move |pane_id, exit_status, command| {
|
move |pane_id, exit_status, command| {
|
||||||
@ -477,7 +501,8 @@ impl Pty {
|
|||||||
|
|
||||||
self.task_handles.insert(terminal_id, terminal_bytes);
|
self.task_handles.insert(terminal_id, terminal_bytes);
|
||||||
self.id_to_child_pid.insert(terminal_id, child_fd);
|
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(
|
pub fn spawn_terminals_for_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -489,10 +514,15 @@ impl Pty {
|
|||||||
let mut default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal(None));
|
let mut default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal(None));
|
||||||
self.fill_cwd(&mut default_shell, client_id);
|
self.fill_cwd(&mut default_shell, client_id);
|
||||||
let extracted_run_instructions = layout.extract_run_instructions();
|
let extracted_run_instructions = layout.extract_run_instructions();
|
||||||
let mut new_pane_pids: Vec<(u32, Option<RunCommand>, Result<RawFd, SpawnTerminalError>)> =
|
let mut new_pane_pids: Vec<(
|
||||||
vec![]; // (terminal_id,
|
u32,
|
||||||
// run_command
|
bool,
|
||||||
// file_descriptor)
|
Option<RunCommand>,
|
||||||
|
Result<RawFd, SpawnTerminalError>,
|
||||||
|
)> = vec![]; // (terminal_id,
|
||||||
|
// starts_held,
|
||||||
|
// run_command,
|
||||||
|
// file_descriptor)
|
||||||
for run_instruction in extracted_run_instructions {
|
for run_instruction in extracted_run_instructions {
|
||||||
let quit_cb = Box::new({
|
let quit_cb = Box::new({
|
||||||
let senders = self.bus.senders.clone();
|
let senders = self.bus.senders.clone();
|
||||||
@ -502,6 +532,7 @@ impl Pty {
|
|||||||
});
|
});
|
||||||
match run_instruction {
|
match run_instruction {
|
||||||
Some(Run::Command(command)) => {
|
Some(Run::Command(command)) => {
|
||||||
|
let starts_held = command.hold_on_start;
|
||||||
let hold_on_close = command.hold_on_close;
|
let hold_on_close = command.hold_on_close;
|
||||||
let quit_cb = Box::new({
|
let quit_cb = Box::new({
|
||||||
let senders = self.bus.senders.clone();
|
let senders = self.bus.senders.clone();
|
||||||
@ -520,34 +551,56 @@ impl Pty {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
let cmd = TerminalAction::RunCommand(command.clone());
|
let cmd = TerminalAction::RunCommand(command.clone());
|
||||||
match self
|
if starts_held {
|
||||||
.bus
|
// we don't actually open a terminal in this case, just wait for the user to run it
|
||||||
.os_input
|
match self.bus.os_input.as_mut().unwrap().reserve_terminal_id() {
|
||||||
.as_mut()
|
Ok(terminal_id) => {
|
||||||
.with_context(err_context)?
|
new_pane_pids.push((
|
||||||
.spawn_terminal(cmd, quit_cb, self.default_editor.clone())
|
terminal_id,
|
||||||
{
|
starts_held,
|
||||||
Ok((terminal_id, pid_primary, child_fd)) => {
|
Some(command.clone()),
|
||||||
self.id_to_child_pid.insert(terminal_id, child_fd);
|
Ok(terminal_id as i32), // this is not actually correct but gets
|
||||||
new_pane_pids.push((
|
// stripped later
|
||||||
terminal_id,
|
));
|
||||||
Some(command.clone()),
|
},
|
||||||
Ok(pid_primary),
|
Err(e) => {
|
||||||
));
|
log::error!("Failed to spawn terminal: {}", e);
|
||||||
},
|
},
|
||||||
Err(SpawnTerminalError::CommandNotFound(terminal_id)) => {
|
}
|
||||||
new_pane_pids.push((
|
} else {
|
||||||
terminal_id,
|
match self
|
||||||
Some(command.clone()),
|
.bus
|
||||||
Err(SpawnTerminalError::CommandNotFound(terminal_id)),
|
.os_input
|
||||||
));
|
.as_mut()
|
||||||
},
|
.with_context(err_context)?
|
||||||
Err(e) => {
|
.spawn_terminal(cmd, quit_cb, self.default_editor.clone())
|
||||||
log::error!("Failed to spawn terminal: {}", e);
|
{
|
||||||
},
|
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)) => {
|
Some(Run::Cwd(cwd)) => {
|
||||||
|
let starts_held = false; // we do not hold Cwd panes
|
||||||
let shell = self.get_default_terminal(Some(cwd));
|
let shell = self.get_default_terminal(Some(cwd));
|
||||||
match self
|
match self
|
||||||
.bus
|
.bus
|
||||||
@ -558,11 +611,12 @@ impl Pty {
|
|||||||
{
|
{
|
||||||
Ok((terminal_id, pid_primary, child_fd)) => {
|
Ok((terminal_id, pid_primary, child_fd)) => {
|
||||||
self.id_to_child_pid.insert(terminal_id, 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)) => {
|
Err(SpawnTerminalError::CommandNotFound(terminal_id)) => {
|
||||||
new_pane_pids.push((
|
new_pane_pids.push((
|
||||||
terminal_id,
|
terminal_id,
|
||||||
|
starts_held,
|
||||||
None,
|
None,
|
||||||
Err(SpawnTerminalError::CommandNotFound(terminal_id)),
|
Err(SpawnTerminalError::CommandNotFound(terminal_id)),
|
||||||
));
|
));
|
||||||
@ -573,6 +627,7 @@ impl Pty {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(Run::EditFile(path_to_file, line_number)) => {
|
Some(Run::EditFile(path_to_file, line_number)) => {
|
||||||
|
let starts_held = false; // we do not hold edit panes (for now?)
|
||||||
match self
|
match self
|
||||||
.bus
|
.bus
|
||||||
.os_input
|
.os_input
|
||||||
@ -585,11 +640,13 @@ impl Pty {
|
|||||||
) {
|
) {
|
||||||
Ok((terminal_id, pid_primary, child_fd)) => {
|
Ok((terminal_id, pid_primary, child_fd)) => {
|
||||||
self.id_to_child_pid.insert(terminal_id, 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)) => {
|
Err(SpawnTerminalError::CommandNotFound(terminal_id)) => {
|
||||||
|
let starts_held = false; // we do not hold error panes
|
||||||
new_pane_pids.push((
|
new_pane_pids.push((
|
||||||
terminal_id,
|
terminal_id,
|
||||||
|
starts_held,
|
||||||
None,
|
None,
|
||||||
Err(SpawnTerminalError::CommandNotFound(terminal_id)),
|
Err(SpawnTerminalError::CommandNotFound(terminal_id)),
|
||||||
));
|
));
|
||||||
@ -600,6 +657,7 @@ impl Pty {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
|
let starts_held = false;
|
||||||
match self
|
match self
|
||||||
.bus
|
.bus
|
||||||
.os_input
|
.os_input
|
||||||
@ -609,11 +667,12 @@ impl Pty {
|
|||||||
{
|
{
|
||||||
Ok((terminal_id, pid_primary, child_fd)) => {
|
Ok((terminal_id, pid_primary, child_fd)) => {
|
||||||
self.id_to_child_pid.insert(terminal_id, 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)) => {
|
Err(SpawnTerminalError::CommandNotFound(terminal_id)) => {
|
||||||
new_pane_pids.push((
|
new_pane_pids.push((
|
||||||
terminal_id,
|
terminal_id,
|
||||||
|
starts_held,
|
||||||
None,
|
None,
|
||||||
Err(SpawnTerminalError::CommandNotFound(terminal_id)),
|
Err(SpawnTerminalError::CommandNotFound(terminal_id)),
|
||||||
));
|
));
|
||||||
@ -627,10 +686,17 @@ impl Pty {
|
|||||||
Some(Run::Plugin(_)) => {},
|
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()
|
.iter()
|
||||||
.map(|(terminal_id, _, _)| *terminal_id)
|
.map(|(terminal_id, starts_held, run_command, _)| {
|
||||||
.collect::<Vec<u32>>();
|
if *starts_held {
|
||||||
|
(*terminal_id, run_command.clone())
|
||||||
|
} else {
|
||||||
|
(*terminal_id, None)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
self.bus
|
self.bus
|
||||||
.senders
|
.senders
|
||||||
.send_to_screen(ScreenInstruction::NewTab(
|
.send_to_screen(ScreenInstruction::NewTab(
|
||||||
@ -639,7 +705,11 @@ impl Pty {
|
|||||||
client_id,
|
client_id,
|
||||||
))
|
))
|
||||||
.with_context(err_context)?;
|
.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 {
|
match pid_primary {
|
||||||
Ok(pid_primary) => {
|
Ok(pid_primary) => {
|
||||||
let terminal_bytes = task::spawn({
|
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.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 _ = 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 quit_cb = Box::new({
|
||||||
let senders = self.bus.senders.clone();
|
let senders = self.bus.senders.clone();
|
||||||
move |pane_id, exit_status, command| {
|
move |pane_id, exit_status, command| {
|
||||||
// we only re-run held panes, so we'll never close them from Pty
|
if hold_on_close {
|
||||||
let _ = senders.send_to_screen(ScreenInstruction::HoldPane(
|
let _ = senders.send_to_screen(ScreenInstruction::HoldPane(
|
||||||
pane_id,
|
pane_id,
|
||||||
exit_status,
|
exit_status,
|
||||||
command,
|
command,
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
|
} else {
|
||||||
|
let _ =
|
||||||
|
senders.send_to_screen(ScreenInstruction::ClosePane(pane_id, None));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let (pid_primary, child_fd): (RawFd, RawFd) = self
|
let (pid_primary, child_fd): (RawFd, RawFd) = self
|
||||||
|
@ -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`].
|
/// Instructions that can be sent to the [`Screen`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ScreenInstruction {
|
pub enum ScreenInstruction {
|
||||||
PtyBytes(u32, VteBytes),
|
PtyBytes(u32, VteBytes),
|
||||||
Render,
|
Render,
|
||||||
NewPane(PaneId, Option<String>, Option<bool>, ClientOrTabIndex), // String is initial title,
|
NewPane(
|
||||||
// bool (if Some) is
|
PaneId,
|
||||||
// should_float
|
Option<InitialTitle>,
|
||||||
|
Option<ShouldFloat>,
|
||||||
|
HoldForCommand,
|
||||||
|
ClientOrTabIndex,
|
||||||
|
),
|
||||||
OpenInPlaceEditor(PaneId, ClientId),
|
OpenInPlaceEditor(PaneId, ClientId),
|
||||||
TogglePaneEmbedOrFloating(ClientId),
|
TogglePaneEmbedOrFloating(ClientId),
|
||||||
ToggleFloatingPanes(ClientId, Option<TerminalAction>),
|
ToggleFloatingPanes(ClientId, Option<TerminalAction>),
|
||||||
HorizontalSplit(PaneId, Option<String>, ClientId), // String is initial title
|
HorizontalSplit(PaneId, Option<InitialTitle>, HoldForCommand, ClientId),
|
||||||
VerticalSplit(PaneId, Option<String>, ClientId), // String is initial title
|
VerticalSplit(PaneId, Option<InitialTitle>, HoldForCommand, ClientId),
|
||||||
WriteCharacter(Vec<u8>, ClientId),
|
WriteCharacter(Vec<u8>, ClientId),
|
||||||
ResizeLeft(ClientId),
|
ResizeLeft(ClientId),
|
||||||
ResizeRight(ClientId),
|
ResizeRight(ClientId),
|
||||||
@ -167,7 +175,7 @@ pub enum ScreenInstruction {
|
|||||||
HoldPane(PaneId, Option<i32>, RunCommand, Option<ClientId>), // Option<i32> is the exit status
|
HoldPane(PaneId, Option<i32>, RunCommand, Option<ClientId>), // Option<i32> is the exit status
|
||||||
UpdatePaneName(Vec<u8>, ClientId),
|
UpdatePaneName(Vec<u8>, ClientId),
|
||||||
UndoRenamePane(ClientId),
|
UndoRenamePane(ClientId),
|
||||||
NewTab(PaneLayout, Vec<u32>, ClientId),
|
NewTab(PaneLayout, Vec<(u32, HoldForCommand)>, ClientId),
|
||||||
SwitchTabNext(ClientId),
|
SwitchTabNext(ClientId),
|
||||||
SwitchTabPrev(ClientId),
|
SwitchTabPrev(ClientId),
|
||||||
ToggleActiveSyncTab(ClientId),
|
ToggleActiveSyncTab(ClientId),
|
||||||
@ -811,7 +819,7 @@ impl Screen {
|
|||||||
pub fn new_tab(
|
pub fn new_tab(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: PaneLayout,
|
layout: PaneLayout,
|
||||||
new_ids: Vec<u32>,
|
new_ids: Vec<(u32, HoldForCommand)>,
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let client_id = if self.get_active_tab(client_id).is_some() {
|
let client_id = if self.get_active_tab(client_id).is_some() {
|
||||||
@ -1227,6 +1235,7 @@ pub(crate) fn screen_thread_main(
|
|||||||
pid,
|
pid,
|
||||||
initial_pane_title,
|
initial_pane_title,
|
||||||
should_float,
|
should_float,
|
||||||
|
hold_for_command,
|
||||||
client_or_tab_index,
|
client_or_tab_index,
|
||||||
) => {
|
) => {
|
||||||
match client_or_tab_index {
|
match client_or_tab_index {
|
||||||
@ -1237,10 +1246,27 @@ pub(crate) fn screen_thread_main(
|
|||||||
should_float,
|
should_float,
|
||||||
Some(client_id)),
|
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) => {
|
ClientOrTabIndex::TabIndex(tab_index) => {
|
||||||
if let Some(active_tab) = screen.tabs.get_mut(&tab_index) {
|
if let Some(active_tab) = screen.tabs.get_mut(&tab_index) {
|
||||||
active_tab.new_pane(pid, initial_pane_title, should_float, None)?;
|
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 {
|
} else {
|
||||||
log::error!("Tab index not found: {:?}", tab_index);
|
log::error!("Tab index not found: {:?}", tab_index);
|
||||||
}
|
}
|
||||||
@ -1275,24 +1301,60 @@ pub(crate) fn screen_thread_main(
|
|||||||
|
|
||||||
screen.render()?;
|
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!(
|
active_tab_and_connected_client_id!(
|
||||||
screen,
|
screen,
|
||||||
client_id,
|
client_id,
|
||||||
|tab: &mut Tab, client_id: ClientId| tab.horizontal_split(pid, initial_pane_title, 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.unblock_input()?;
|
||||||
screen.update_tabs()?;
|
screen.update_tabs()?;
|
||||||
screen.render()?;
|
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!(
|
active_tab_and_connected_client_id!(
|
||||||
screen,
|
screen,
|
||||||
client_id,
|
client_id,
|
||||||
|tab: &mut Tab, client_id: ClientId| tab.vertical_split(pid, initial_pane_title, 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.unblock_input()?;
|
||||||
screen.update_tabs()?;
|
screen.update_tabs()?;
|
||||||
screen.render()?;
|
screen.render()?;
|
||||||
@ -1644,18 +1706,20 @@ pub(crate) fn screen_thread_main(
|
|||||||
screen.unblock_input()?;
|
screen.unblock_input()?;
|
||||||
},
|
},
|
||||||
ScreenInstruction::HoldPane(id, exit_status, run_command, client_id) => {
|
ScreenInstruction::HoldPane(id, exit_status, run_command, client_id) => {
|
||||||
|
let is_first_run = false;
|
||||||
match client_id {
|
match client_id {
|
||||||
Some(client_id) => {
|
Some(client_id) => {
|
||||||
active_tab!(screen, client_id, |tab: &mut Tab| tab.hold_pane(
|
active_tab!(screen, client_id, |tab: &mut Tab| tab.hold_pane(
|
||||||
id,
|
id,
|
||||||
exit_status,
|
exit_status,
|
||||||
|
is_first_run,
|
||||||
run_command
|
run_command
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
for tab in screen.tabs.values_mut() {
|
for tab in screen.tabs.values_mut() {
|
||||||
if tab.get_all_pane_ids().contains(&id) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,8 @@ macro_rules! resize_pty {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HoldForCommand = Option<RunCommand>;
|
||||||
|
|
||||||
// FIXME: This should be replaced by `RESIZE_PERCENT` at some point
|
// FIXME: This should be replaced by `RESIZE_PERCENT` at some point
|
||||||
pub const MIN_TERMINAL_HEIGHT: usize = 5;
|
pub const MIN_TERMINAL_HEIGHT: usize = 5;
|
||||||
pub const MIN_TERMINAL_WIDTH: 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 by default (only terminal-panes support alternate mode)
|
||||||
false
|
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
|
// No-op by default, only terminal panes support holding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -470,7 +472,7 @@ impl Tab {
|
|||||||
pub fn apply_layout(
|
pub fn apply_layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: PaneLayout,
|
layout: PaneLayout,
|
||||||
new_ids: Vec<u32>,
|
new_ids: Vec<(u32, HoldForCommand)>,
|
||||||
tab_index: usize,
|
tab_index: usize,
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@ -532,7 +534,7 @@ impl Tab {
|
|||||||
set_focus_pane_id(layout, PaneId::Plugin(pid));
|
set_focus_pane_id(layout, PaneId::Plugin(pid));
|
||||||
} else {
|
} else {
|
||||||
// there are still panes left to fill, use the pids we received in this method
|
// 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 next_terminal_position = self.get_next_terminal_position();
|
||||||
let initial_title = match &layout.run {
|
let initial_title = match &layout.run {
|
||||||
Some(Run::Command(run_command)) => Some(run_command.to_string()),
|
Some(Run::Command(run_command)) => Some(run_command.to_string()),
|
||||||
@ -552,6 +554,9 @@ impl Tab {
|
|||||||
initial_title,
|
initial_title,
|
||||||
);
|
);
|
||||||
new_pane.set_borderless(layout.borderless);
|
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(
|
self.tiled_panes.add_pane_with_existing_geom(
|
||||||
PaneId::Terminal(*pid),
|
PaneId::Terminal(*pid),
|
||||||
Box::new(new_pane),
|
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
|
// 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
|
// 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
|
// fixing this will require a bit of an architecture change
|
||||||
@ -601,7 +606,7 @@ impl Tab {
|
|||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
for unused_pid in new_ids {
|
for (unused_pid, _) in new_ids {
|
||||||
self.senders
|
self.senders
|
||||||
.send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(unused_pid)))
|
.send_to_pty(PtyInstruction::ClosePane(PaneId::Terminal(unused_pid)))
|
||||||
.with_context(err_context)?;
|
.with_context(err_context)?;
|
||||||
@ -1746,11 +1751,19 @@ impl Tab {
|
|||||||
closed_pane
|
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) {
|
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 {
|
} 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>> {
|
pub fn replace_pane_with_suppressed_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
|
||||||
|
@ -223,7 +223,7 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab {
|
|||||||
terminal_emulator_colors,
|
terminal_emulator_colors,
|
||||||
terminal_emulator_color_codes,
|
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();
|
.unwrap();
|
||||||
tab
|
tab
|
||||||
}
|
}
|
||||||
@ -277,7 +277,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str)
|
|||||||
.extract_run_instructions()
|
.extract_run_instructions()
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, _)| i as u32)
|
.map(|(i, _)| (i as u32, None))
|
||||||
.collect();
|
.collect();
|
||||||
tab.apply_layout(tab_layout, pane_ids, index, client_id)
|
tab.apply_layout(tab_layout, pane_ids, index, client_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -332,14 +332,8 @@ fn create_new_tab_with_mock_pty_writer(
|
|||||||
terminal_emulator_colors,
|
terminal_emulator_colors,
|
||||||
terminal_emulator_color_codes,
|
terminal_emulator_color_codes,
|
||||||
);
|
);
|
||||||
tab.apply_layout(
|
tab.apply_layout(PaneLayout::default(), vec![(1, None)], index, client_id)
|
||||||
// LayoutTemplate::default().try_into().unwrap(),
|
.unwrap();
|
||||||
PaneLayout::default(),
|
|
||||||
vec![1],
|
|
||||||
index,
|
|
||||||
client_id,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
tab
|
tab
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,7 +387,7 @@ fn create_new_tab_with_sixel_support(
|
|||||||
terminal_emulator_colors,
|
terminal_emulator_colors,
|
||||||
terminal_emulator_color_codes,
|
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();
|
.unwrap();
|
||||||
tab
|
tab
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ fn create_new_tab(size: Size) -> Tab {
|
|||||||
terminal_emulator_colors,
|
terminal_emulator_colors,
|
||||||
terminal_emulator_color_codes,
|
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();
|
.unwrap();
|
||||||
tab
|
tab
|
||||||
}
|
}
|
||||||
@ -189,7 +189,7 @@ fn create_new_tab_with_cell_size(
|
|||||||
terminal_emulator_colors,
|
terminal_emulator_colors,
|
||||||
terminal_emulator_color_codes,
|
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();
|
.unwrap();
|
||||||
tab
|
tab
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,7 @@ pub struct PaneFrame {
|
|||||||
pub other_cursors_exist_in_session: bool,
|
pub other_cursors_exist_in_session: bool,
|
||||||
pub other_focused_clients: Vec<ClientId>,
|
pub other_focused_clients: Vec<ClientId>,
|
||||||
exit_status: Option<ExitStatus>,
|
exit_status: Option<ExitStatus>,
|
||||||
|
is_first_run: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaneFrame {
|
impl PaneFrame {
|
||||||
@ -109,6 +110,7 @@ impl PaneFrame {
|
|||||||
other_focused_clients: frame_params.other_focused_clients,
|
other_focused_clients: frame_params.other_focused_clients,
|
||||||
other_cursors_exist_in_session: frame_params.other_cursors_exist_in_session,
|
other_cursors_exist_in_session: frame_params.other_cursors_exist_in_session,
|
||||||
exit_status: None,
|
exit_status: None,
|
||||||
|
is_first_run: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn add_exit_status(&mut self, exit_status: Option<i32>) {
|
pub fn add_exit_status(&mut self, exit_status: Option<i32>) {
|
||||||
@ -117,6 +119,9 @@ impl PaneFrame {
|
|||||||
None => Some(ExitStatus::Exited),
|
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> {
|
fn client_cursor(&self, client_id: ClientId) -> Vec<TerminalCharacter> {
|
||||||
let color = client_id_to_colors(client_id, self.style.colors);
|
let color = client_id_to_colors(client_id, self.style.colors);
|
||||||
background_color(" ", color.map(|c| c.0))
|
background_color(" ", color.map(|c| c.0))
|
||||||
@ -611,11 +616,7 @@ impl PaneFrame {
|
|||||||
}
|
}
|
||||||
fn render_held_undertitle(&self) -> Result<Vec<TerminalCharacter>> {
|
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 max_undertitle_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners
|
||||||
let exit_status = self
|
let (mut first_part, first_part_len) = self.first_exited_held_title_part_full();
|
||||||
.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 left_boundary =
|
let mut left_boundary =
|
||||||
foreground_color(self.get_corner(boundary_type::BOTTOM_LEFT), self.color);
|
foreground_color(self.get_corner(boundary_type::BOTTOM_LEFT), self.color);
|
||||||
let mut right_boundary =
|
let mut right_boundary =
|
||||||
@ -683,7 +684,7 @@ impl PaneFrame {
|
|||||||
character_chunks.push(CharacterChunk::new(title, x, y));
|
character_chunks.push(CharacterChunk::new(title, x, y));
|
||||||
} else if row == self.geom.rows - 1 {
|
} else if row == self.geom.rows - 1 {
|
||||||
// bottom row
|
// bottom row
|
||||||
if self.exit_status.is_some() {
|
if self.exit_status.is_some() || self.is_first_run {
|
||||||
let x = self.geom.x;
|
let x = self.geom.x;
|
||||||
let y = self.geom.y + row;
|
let y = self.geom.y + row;
|
||||||
character_chunks.push(CharacterChunk::new(
|
character_chunks.push(CharacterChunk::new(
|
||||||
@ -727,13 +728,10 @@ impl PaneFrame {
|
|||||||
}
|
}
|
||||||
Ok((character_chunks, None))
|
Ok((character_chunks, None))
|
||||||
}
|
}
|
||||||
fn first_held_title_part_full(
|
fn first_exited_held_title_part_full(&self) -> (Vec<TerminalCharacter>, usize) {
|
||||||
&self,
|
|
||||||
exit_status: ExitStatus,
|
|
||||||
) -> (Vec<TerminalCharacter>, usize) {
|
|
||||||
// (title part, length)
|
// (title part, length)
|
||||||
match exit_status {
|
match self.exit_status {
|
||||||
ExitStatus::Code(exit_code) => {
|
Some(ExitStatus::Code(exit_code)) => {
|
||||||
let mut first_part = vec![];
|
let mut first_part = vec![];
|
||||||
let left_bracket = " [ ";
|
let left_bracket = " [ ";
|
||||||
let exited_text = "EXIT CODE: ";
|
let exited_text = "EXIT CODE: ";
|
||||||
@ -759,7 +757,7 @@ impl PaneFrame {
|
|||||||
+ right_bracket.len(),
|
+ right_bracket.len(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
ExitStatus::Exited => {
|
Some(ExitStatus::Exited) => {
|
||||||
let mut first_part = vec![];
|
let mut first_part = vec![];
|
||||||
let left_bracket = " [ ";
|
let left_bracket = " [ ";
|
||||||
let exited_text = "EXITED";
|
let exited_text = "EXITED";
|
||||||
@ -775,15 +773,20 @@ impl PaneFrame {
|
|||||||
left_bracket.len() + exited_text.len() + right_bracket.len(),
|
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) {
|
fn second_held_title_part_full(&self) -> (Vec<TerminalCharacter>, usize) {
|
||||||
// (title part, length)
|
// (title part, length)
|
||||||
let mut second_part = vec![];
|
let mut second_part = vec![];
|
||||||
let left_enter_bracket = "<";
|
let left_enter_bracket = if self.is_first_run { " <" } else { "<" };
|
||||||
let enter_text = "ENTER";
|
let enter_text = "ENTER";
|
||||||
let right_enter_bracket = ">";
|
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 left_break_bracket = "<";
|
||||||
let break_text = "Ctrl-c";
|
let break_text = "Ctrl-c";
|
||||||
let right_break_bracket = ">";
|
let right_break_bracket = ">";
|
||||||
|
@ -284,7 +284,7 @@ impl MockScreen {
|
|||||||
let pane_count = pane_layout.extract_run_instructions().len();
|
let pane_count = pane_layout.extract_run_instructions().len();
|
||||||
let mut pane_ids = vec![];
|
let mut pane_ids = vec![];
|
||||||
for i in 0..pane_count {
|
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(
|
let _ = self.to_screen.send(ScreenInstruction::NewTab(
|
||||||
pane_layout,
|
pane_layout,
|
||||||
@ -297,7 +297,7 @@ impl MockScreen {
|
|||||||
let pane_count = tab_layout.extract_run_instructions().len();
|
let pane_count = tab_layout.extract_run_instructions().len();
|
||||||
let mut pane_ids = vec![];
|
let mut pane_ids = vec![];
|
||||||
for i in 0..pane_count {
|
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(
|
let _ = self.to_screen.send(ScreenInstruction::NewTab(
|
||||||
tab_layout,
|
tab_layout,
|
||||||
@ -427,7 +427,7 @@ macro_rules! log_actions_in_thread {
|
|||||||
fn new_tab(screen: &mut Screen, pid: u32) {
|
fn new_tab(screen: &mut Screen, pid: u32) {
|
||||||
let client_id = 1;
|
let client_id = 1;
|
||||||
screen
|
screen
|
||||||
.new_tab(PaneLayout::default(), vec![pid], client_id)
|
.new_tab(PaneLayout::default(), vec![(pid, None)], client_id)
|
||||||
.expect("TEST");
|
.expect("TEST");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1822,6 +1822,7 @@ pub fn send_cli_new_pane_action_with_default_parameters() {
|
|||||||
floating: false,
|
floating: false,
|
||||||
name: None,
|
name: None,
|
||||||
close_on_exit: false,
|
close_on_exit: false,
|
||||||
|
start_suspended: false,
|
||||||
};
|
};
|
||||||
send_cli_action_to_server(
|
send_cli_action_to_server(
|
||||||
&session_metadata,
|
&session_metadata,
|
||||||
@ -1861,6 +1862,7 @@ pub fn send_cli_new_pane_action_with_split_direction() {
|
|||||||
floating: false,
|
floating: false,
|
||||||
name: None,
|
name: None,
|
||||||
close_on_exit: false,
|
close_on_exit: false,
|
||||||
|
start_suspended: false,
|
||||||
};
|
};
|
||||||
send_cli_action_to_server(
|
send_cli_action_to_server(
|
||||||
&session_metadata,
|
&session_metadata,
|
||||||
@ -1900,6 +1902,7 @@ pub fn send_cli_new_pane_action_with_command_and_cwd() {
|
|||||||
floating: false,
|
floating: false,
|
||||||
name: None,
|
name: None,
|
||||||
close_on_exit: false,
|
close_on_exit: false,
|
||||||
|
start_suspended: false,
|
||||||
};
|
};
|
||||||
send_cli_action_to_server(
|
send_cli_action_to_server(
|
||||||
&session_metadata,
|
&session_metadata,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-server/src/./unit/screen_tests.rs
|
source: zellij-server/src/./unit/screen_tests.rs
|
||||||
assertion_line: 1981
|
assertion_line: 1989
|
||||||
expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())"
|
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]
|
[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]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-server/src/./unit/screen_tests.rs
|
source: zellij-server/src/./unit/screen_tests.rs
|
||||||
assertion_line: 1907
|
assertion_line: 1915
|
||||||
expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())"
|
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]
|
||||||
|
@ -141,6 +141,10 @@ pub enum Sessions {
|
|||||||
/// Close the pane immediately when its command exits
|
/// Close the pane immediately when its command exits
|
||||||
#[clap(short, long, value_parser, default_value("false"), takes_value(false))]
|
#[clap(short, long, value_parser, default_value("false"), takes_value(false))]
|
||||||
close_on_exit: bool,
|
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
|
/// Edit file with default $EDITOR / $VISUAL
|
||||||
#[clap(visible_alias = "e")]
|
#[clap(visible_alias = "e")]
|
||||||
@ -252,6 +256,16 @@ pub enum CliAction {
|
|||||||
requires("command")
|
requires("command")
|
||||||
)]
|
)]
|
||||||
close_on_exit: bool,
|
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
|
/// Open the specified file in a new zellij pane with your default EDITOR
|
||||||
Edit {
|
Edit {
|
||||||
|
@ -260,11 +260,13 @@ impl Action {
|
|||||||
floating,
|
floating,
|
||||||
name,
|
name,
|
||||||
close_on_exit,
|
close_on_exit,
|
||||||
|
start_suspended,
|
||||||
} => {
|
} => {
|
||||||
if !command.is_empty() {
|
if !command.is_empty() {
|
||||||
let mut command = command.clone();
|
let mut command = command.clone();
|
||||||
let (command, args) = (PathBuf::from(command.remove(0)), command);
|
let (command, args) = (PathBuf::from(command.remove(0)), command);
|
||||||
let cwd = cwd.or_else(|| std::env::current_dir().ok());
|
let cwd = cwd.or_else(|| std::env::current_dir().ok());
|
||||||
|
let hold_on_start = start_suspended;
|
||||||
let hold_on_close = !close_on_exit;
|
let hold_on_close = !close_on_exit;
|
||||||
let run_command_action = RunCommandAction {
|
let run_command_action = RunCommandAction {
|
||||||
command,
|
command,
|
||||||
@ -272,6 +274,7 @@ impl Action {
|
|||||||
cwd,
|
cwd,
|
||||||
direction,
|
direction,
|
||||||
hold_on_close,
|
hold_on_close,
|
||||||
|
hold_on_start,
|
||||||
};
|
};
|
||||||
if floating {
|
if floating {
|
||||||
Ok(vec![Action::NewFloatingPane(
|
Ok(vec![Action::NewFloatingPane(
|
||||||
|
@ -19,6 +19,8 @@ pub struct RunCommand {
|
|||||||
pub cwd: Option<PathBuf>,
|
pub cwd: Option<PathBuf>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub hold_on_close: bool,
|
pub hold_on_close: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub hold_on_start: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for RunCommand {
|
impl std::fmt::Display for RunCommand {
|
||||||
@ -50,6 +52,8 @@ pub struct RunCommandAction {
|
|||||||
pub direction: Option<Direction>,
|
pub direction: Option<Direction>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub hold_on_close: bool,
|
pub hold_on_close: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub hold_on_start: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RunCommandAction> for RunCommand {
|
impl From<RunCommandAction> for RunCommand {
|
||||||
@ -59,6 +63,7 @@ impl From<RunCommandAction> for RunCommand {
|
|||||||
args: action.args,
|
args: action.args,
|
||||||
cwd: action.cwd,
|
cwd: action.cwd,
|
||||||
hold_on_close: action.hold_on_close,
|
hold_on_close: action.hold_on_close,
|
||||||
|
hold_on_start: action.hold_on_start,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ impl Run {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn add_close_on_exit(&mut self, close_on_exit: Option<bool>) {
|
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
|
// and not empty
|
||||||
if let Some(close_on_exit) = close_on_exit {
|
if let Some(close_on_exit) = close_on_exit {
|
||||||
if let Run::Command(run_command) = self {
|
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)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
@ -281,6 +281,19 @@ fn layout_with_command_panes_and_close_on_exit() {
|
|||||||
assert_snapshot!(format!("{:#?}", layout));
|
assert_snapshot!(format!("{:#?}", layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[test]
|
||||||
fn layout_with_plugin_panes() {
|
fn layout_with_plugin_panes() {
|
||||||
let kdl_layout = r#"
|
let kdl_layout = r#"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1050
|
assertion_line: 1081
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -23,6 +23,7 @@ Layout {
|
|||||||
args: [],
|
args: [],
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -45,6 +46,7 @@ Layout {
|
|||||||
],
|
],
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1033
|
assertion_line: 1046
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -26,6 +26,7 @@ Layout {
|
|||||||
],
|
],
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -48,6 +49,7 @@ Layout {
|
|||||||
],
|
],
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -23,6 +23,7 @@ Layout {
|
|||||||
args: [],
|
args: [],
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -42,6 +43,7 @@ Layout {
|
|||||||
args: [],
|
args: [],
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: false,
|
hold_on_close: false,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -23,6 +23,7 @@ Layout {
|
|||||||
args: [],
|
args: [],
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -42,6 +43,7 @@ Layout {
|
|||||||
args: [],
|
args: [],
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: false,
|
hold_on_close: false,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1085
|
assertion_line: 1133
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -23,6 +23,7 @@ Layout {
|
|||||||
args: [],
|
args: [],
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -44,6 +45,7 @@ Layout {
|
|||||||
"/home",
|
"/home",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1068
|
assertion_line: 1116
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -25,6 +25,7 @@ Layout {
|
|||||||
"/tmp",
|
"/tmp",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -46,6 +47,7 @@ Layout {
|
|||||||
"/",
|
"/",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1441
|
assertion_line: 1516
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -39,6 +39,7 @@ Layout {
|
|||||||
"/tmp/./foo/./bar",
|
"/tmp/./foo/./bar",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1462
|
assertion_line: 1537
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -43,6 +43,7 @@ Layout {
|
|||||||
"/tmp/./foo/./bar",
|
"/tmp/./foo/./bar",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1481
|
assertion_line: 1556
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -39,6 +39,7 @@ Layout {
|
|||||||
"/tmp/./foo/./bar",
|
"/tmp/./foo/./bar",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1283
|
assertion_line: 1403
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -39,6 +39,7 @@ Layout {
|
|||||||
"/tmp",
|
"/tmp",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1309
|
assertion_line: 1435
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -39,6 +39,7 @@ Layout {
|
|||||||
"/tmp",
|
"/tmp",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1324
|
assertion_line: 1455
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -39,6 +39,7 @@ Layout {
|
|||||||
"/tmp",
|
"/tmp",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1295
|
assertion_line: 1416
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -39,6 +39,7 @@ Layout {
|
|||||||
"/home/foo",
|
"/home/foo",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1396
|
assertion_line: 1471
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -39,6 +39,7 @@ Layout {
|
|||||||
"/tmp/./foo",
|
"/tmp/./foo",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -23,6 +23,7 @@ Layout {
|
|||||||
args: [],
|
args: [],
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: false,
|
hold_on_close: false,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 570
|
assertion_line: 583
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -41,6 +41,7 @@ Layout {
|
|||||||
args: [],
|
args: [],
|
||||||
cwd: None,
|
cwd: None,
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1199
|
assertion_line: 1287
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -25,6 +25,7 @@ Layout {
|
|||||||
"/tmp/bar",
|
"/tmp/bar",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1144
|
assertion_line: 1233
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -25,6 +25,7 @@ Layout {
|
|||||||
"/tmp/foo",
|
"/tmp/foo",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1161
|
assertion_line: 1250
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -25,6 +25,7 @@ Layout {
|
|||||||
"/tmp/bar",
|
"/tmp/bar",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1180
|
assertion_line: 1268
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -25,6 +25,7 @@ Layout {
|
|||||||
"/tmp/bar",
|
"/tmp/bar",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1216
|
assertion_line: 1305
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -25,6 +25,7 @@ Layout {
|
|||||||
"/tmp/bar",
|
"/tmp/bar",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1269
|
assertion_line: 1357
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -25,6 +25,7 @@ Layout {
|
|||||||
"/tmp/bar",
|
"/tmp/bar",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1251
|
assertion_line: 1339
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -25,6 +25,7 @@ Layout {
|
|||||||
"/tmp/foo",
|
"/tmp/foo",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1411
|
assertion_line: 1486
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -39,6 +39,7 @@ Layout {
|
|||||||
"/tmp",
|
"/tmp",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: zellij-utils/src/input/./unit/layout_test.rs
|
source: zellij-utils/src/input/./unit/layout_test.rs
|
||||||
assertion_line: 1426
|
assertion_line: 1501
|
||||||
expression: "format!(\"{:#?}\", layout)"
|
expression: "format!(\"{:#?}\", layout)"
|
||||||
---
|
---
|
||||||
Layout {
|
Layout {
|
||||||
@ -39,6 +39,7 @@ Layout {
|
|||||||
"/tmp/./foo",
|
"/tmp/./foo",
|
||||||
),
|
),
|
||||||
hold_on_close: true,
|
hold_on_close: true,
|
||||||
|
hold_on_start: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -54,6 +54,7 @@ impl<'a> KdlLayoutParser<'a> {
|
|||||||
|| word == "tab"
|
|| word == "tab"
|
||||||
|| word == "args"
|
|| word == "args"
|
||||||
|| word == "close_on_exit"
|
|| word == "close_on_exit"
|
||||||
|
|| word == "start_suspended"
|
||||||
|| word == "borderless"
|
|| word == "borderless"
|
||||||
|| word == "focus"
|
|| word == "focus"
|
||||||
|| word == "name"
|
|| word == "name"
|
||||||
@ -72,6 +73,7 @@ impl<'a> KdlLayoutParser<'a> {
|
|||||||
|| property_name == "cwd"
|
|| property_name == "cwd"
|
||||||
|| property_name == "args"
|
|| property_name == "args"
|
||||||
|| property_name == "close_on_exit"
|
|| property_name == "close_on_exit"
|
||||||
|
|| property_name == "start_suspended"
|
||||||
|| property_name == "split_direction"
|
|| property_name == "split_direction"
|
||||||
|| property_name == "pane"
|
|| property_name == "pane"
|
||||||
|| property_name == "children"
|
|| property_name == "children"
|
||||||
@ -241,15 +243,19 @@ impl<'a> KdlLayoutParser<'a> {
|
|||||||
let args = self.parse_args(pane_node)?;
|
let args = self.parse_args(pane_node)?;
|
||||||
let close_on_exit =
|
let close_on_exit =
|
||||||
kdl_get_bool_property_or_child_value_with_error!(pane_node, "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 {
|
if !is_template {
|
||||||
self.assert_no_bare_attributes_in_pane_node(
|
self.assert_no_bare_attributes_in_pane_node(
|
||||||
&command,
|
&command,
|
||||||
&args,
|
&args,
|
||||||
&close_on_exit,
|
&close_on_exit,
|
||||||
|
&start_suspended,
|
||||||
pane_node,
|
pane_node,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
let hold_on_close = close_on_exit.map(|c| !c).unwrap_or(true);
|
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) {
|
match (command, edit, cwd) {
|
||||||
(None, None, Some(cwd)) => Ok(Some(Run::Cwd(cwd))),
|
(None, None, Some(cwd)) => Ok(Some(Run::Cwd(cwd))),
|
||||||
(Some(command), None, cwd) => Ok(Some(Run::Command(RunCommand {
|
(Some(command), None, cwd) => Ok(Some(Run::Command(RunCommand {
|
||||||
@ -257,6 +263,7 @@ impl<'a> KdlLayoutParser<'a> {
|
|||||||
args: args.unwrap_or_else(|| vec![]),
|
args: args.unwrap_or_else(|| vec![]),
|
||||||
cwd,
|
cwd,
|
||||||
hold_on_close,
|
hold_on_close,
|
||||||
|
hold_on_start,
|
||||||
}))),
|
}))),
|
||||||
(None, Some(edit), Some(cwd)) => Ok(Some(Run::EditFile(cwd.join(edit), None))),
|
(None, Some(edit), Some(cwd)) => Ok(Some(Run::EditFile(cwd.join(edit), None))),
|
||||||
(None, Some(edit), None) => Ok(Some(Run::EditFile(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 args = self.parse_args(kdl_node)?;
|
||||||
let close_on_exit =
|
let close_on_exit =
|
||||||
kdl_get_bool_property_or_child_value_with_error!(kdl_node, "close_on_exit");
|
kdl_get_bool_property_or_child_value_with_error!(kdl_node, "close_on_exit");
|
||||||
|
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 split_size = self.parse_split_size(kdl_node)?;
|
||||||
let run = self.parse_command_plugin_or_edit_block_for_template(kdl_node)?;
|
let run = self.parse_command_plugin_or_edit_block_for_template(kdl_node)?;
|
||||||
self.assert_no_bare_attributes_in_pane_node_with_template(
|
self.assert_no_bare_attributes_in_pane_node_with_template(
|
||||||
@ -387,6 +396,7 @@ impl<'a> KdlLayoutParser<'a> {
|
|||||||
&pane_template.run,
|
&pane_template.run,
|
||||||
&args,
|
&args,
|
||||||
&close_on_exit,
|
&close_on_exit,
|
||||||
|
&start_suspended,
|
||||||
kdl_node,
|
kdl_node,
|
||||||
)?;
|
)?;
|
||||||
self.insert_children_to_pane_template(
|
self.insert_children_to_pane_template(
|
||||||
@ -396,10 +406,11 @@ impl<'a> KdlLayoutParser<'a> {
|
|||||||
)?;
|
)?;
|
||||||
pane_template.run = Run::merge(&pane_template.run, &run);
|
pane_template.run = Run::merge(&pane_template.run, &run);
|
||||||
if let Some(pane_template_run_command) = pane_template.run.as_mut() {
|
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
|
// can have bare args without a command
|
||||||
pane_template_run_command.add_args(args);
|
pane_template_run_command.add_args(args);
|
||||||
pane_template_run_command.add_close_on_exit(close_on_exit);
|
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 {
|
if let Some(borderless) = borderless {
|
||||||
pane_template.borderless = borderless;
|
pane_template.borderless = borderless;
|
||||||
@ -600,6 +611,7 @@ impl<'a> KdlLayoutParser<'a> {
|
|||||||
pane_template_run: &Option<Run>,
|
pane_template_run: &Option<Run>,
|
||||||
args: &Option<Vec<String>>,
|
args: &Option<Vec<String>>,
|
||||||
close_on_exit: &Option<bool>,
|
close_on_exit: &Option<bool>,
|
||||||
|
start_suspended: &Option<bool>,
|
||||||
pane_node: &KdlNode,
|
pane_node: &KdlNode,
|
||||||
) -> Result<(), ConfigError> {
|
) -> Result<(), ConfigError> {
|
||||||
if let (None, None, true) = (pane_run, pane_template_run, args.is_some()) {
|
if let (None, None, true) = (pane_run, pane_template_run, args.is_some()) {
|
||||||
@ -614,6 +626,12 @@ impl<'a> KdlLayoutParser<'a> {
|
|||||||
pane_node
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn assert_no_bare_attributes_in_pane_node(
|
fn assert_no_bare_attributes_in_pane_node(
|
||||||
@ -621,6 +639,7 @@ impl<'a> KdlLayoutParser<'a> {
|
|||||||
command: &Option<PathBuf>,
|
command: &Option<PathBuf>,
|
||||||
args: &Option<Vec<String>>,
|
args: &Option<Vec<String>>,
|
||||||
close_on_exit: &Option<bool>,
|
close_on_exit: &Option<bool>,
|
||||||
|
start_suspended: &Option<bool>,
|
||||||
pane_node: &KdlNode,
|
pane_node: &KdlNode,
|
||||||
) -> Result<(), ConfigError> {
|
) -> Result<(), ConfigError> {
|
||||||
if command.is_none() {
|
if command.is_none() {
|
||||||
@ -631,6 +650,13 @@ impl<'a> KdlLayoutParser<'a> {
|
|||||||
pane_node.span().len(),
|
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() {
|
if args.is_some() {
|
||||||
return Err(ConfigError::new_layout_kdl_error(
|
return Err(ConfigError::new_layout_kdl_error(
|
||||||
"args can only be set if a command was specified".into(),
|
"args can only be set if a command was specified".into(),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
mod kdl_layout_parser;
|
mod kdl_layout_parser;
|
||||||
use crate::data::{InputMode, Key, Palette, PaletteColor};
|
use crate::data::{InputMode, Key, Palette, PaletteColor};
|
||||||
use crate::envs::EnvironmentVariables;
|
use crate::envs::EnvironmentVariables;
|
||||||
use crate::input::command::RunCommand;
|
|
||||||
use crate::input::config::{Config, ConfigError, KdlError};
|
use crate::input::config::{Config, ConfigError, KdlError};
|
||||||
use crate::input::keybinds::Keybinds;
|
use crate::input::keybinds::Keybinds;
|
||||||
use crate::input::layout::{Layout, RunPlugin, RunPluginLocation};
|
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())
|
.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 {
|
impl Action {
|
||||||
pub fn new_from_bytes(
|
pub fn new_from_bytes(
|
||||||
action_name: &str,
|
action_name: &str,
|
||||||
@ -741,12 +750,20 @@ impl TryFrom<&KdlNode> for Action {
|
|||||||
let direction = command_metadata
|
let direction = command_metadata
|
||||||
.and_then(|c_m| kdl_child_string_value_for_entry(c_m, "direction"))
|
.and_then(|c_m| kdl_child_string_value_for_entry(c_m, "direction"))
|
||||||
.and_then(|direction_string| Direction::from_str(direction_string).ok());
|
.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 {
|
let run_command_action = RunCommandAction {
|
||||||
command: PathBuf::from(command),
|
command: PathBuf::from(command),
|
||||||
args,
|
args,
|
||||||
cwd,
|
cwd,
|
||||||
direction,
|
direction,
|
||||||
hold_on_close: true,
|
hold_on_close,
|
||||||
|
hold_on_start,
|
||||||
};
|
};
|
||||||
Ok(Action::Run(run_command_action))
|
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 {
|
impl Config {
|
||||||
pub fn from_kdl(kdl_config: &str, base_config: Option<Config>) -> Result<Config, ConfigError> {
|
pub fn from_kdl(kdl_config: &str, base_config: Option<Config>) -> Result<Config, ConfigError> {
|
||||||
let mut config = base_config.unwrap_or_else(|| Config::default());
|
let mut config = base_config.unwrap_or_else(|| Config::default());
|
||||||
|
Loading…
Reference in New Issue
Block a user