summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2023-10-17 11:55:38 +0200
committerGitHub <noreply@github.com>2023-10-17 11:55:38 +0200
commit69eb904426e64649fc7228b0d6803469911267d7 (patch)
treea4c1448de8301f91856917dfcbe3c0f13d0e25ae
parent8378f146c124e40ceed1f63628b9254bafe19c2f (diff)
feat(panes): Add an option to press <ESC> and drop to shell in command panes (#2872)
* feat(panes): ESC to drop to default shell on command panes * style(fmt): rustfmt
-rw-r--r--src/tests/e2e/cases.rs8
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session_with_viewport_serialization.snap4
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap48
-rw-r--r--zellij-server/src/os_input_output.rs22
-rw-r--r--zellij-server/src/panes/terminal_character.rs32
-rw-r--r--zellij-server/src/panes/terminal_pane.rs8
-rw-r--r--zellij-server/src/pty.rs51
-rw-r--r--zellij-server/src/screen.rs1
-rw-r--r--zellij-server/src/tab/mod.rs15
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__base_floating_layout_is_included_in_swap_layouts.snap4
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__base_layout_is_included_in_swap_layouts.snap7
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__floating_layout_with_plugins_and_commands_swaped_properly.snap4
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__layout_with_plugins_and_commands_swaped_properly.snap7
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_floating_layouts_not_including_command_panes_present_in_existing_layout.snap12
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_not_including_command_panes_present_in_existing_layout.snap7
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__swap_layouts_not_including_plugin_panes_present_in_existing_layout.snap7
-rw-r--r--zellij-server/src/tab/unit/tab_integration_tests.rs6
-rw-r--r--zellij-server/src/tab/unit/tab_tests.rs3
-rw-r--r--zellij-server/src/ui/pane_boundaries_frame.rs25
-rw-r--r--zellij-utils/src/errors.rs1
21 files changed, 194 insertions, 82 deletions
diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs
index c5110b691..be337ea92 100644
--- a/src/tests/e2e/cases.rs
+++ b/src/tests/e2e/cases.rs
@@ -2070,7 +2070,7 @@ pub fn send_command_through_the_cli() {
// so when we press "Enter", it will run again and we'll see two "foo"s one after the other,
// that's how we know the whole flow is working
let fake_win_size = Size {
- cols: 120,
+ cols: 150,
rows: 24,
};
let mut test_attempts = 10;
@@ -2118,7 +2118,7 @@ pub fn send_command_through_the_cli() {
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.snapshot_contains("<Ctrl-c>")
- && remote_terminal.cursor_position_is(61, 3)
+ && remote_terminal.cursor_position_is(76, 3)
{
remote_terminal.send_key(&SPACE); // re-run script - here we use SPACE
// instead of the default ENTER because
@@ -2135,7 +2135,7 @@ pub fn send_command_through_the_cli() {
instruction: |mut remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.snapshot_contains("<Ctrl-c>")
- && remote_terminal.cursor_position_is(61, 4)
+ && remote_terminal.cursor_position_is(76, 4)
{
step_is_complete = true
}
@@ -2149,7 +2149,7 @@ pub fn send_command_through_the_cli() {
instruction: |remote_terminal: RemoteTerminal| -> bool {
let mut step_is_complete = false;
if remote_terminal.snapshot_contains("foo")
- && remote_terminal.cursor_position_is(61, 4)
+ && remote_terminal.cursor_position_is(76, 4)
{
step_is_complete = true
}
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session.snap
index a7c58010e..fd55ac42a 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session.snap
@@ -17,10 +17,10 @@ expression: last_snapshot
│ │ │ │ │────────────────────────────┘
│ │ │ Waiting to run: top │ │────────────────────────────┐
│ │ │ │ │ │
-│ │ │ <ENTER> to run, <Ctrl-c> to exit │ │ │
+│ │ │ <ENTER> run, <ESC> drop to shell, <Ctrl-c> exit │ │ │
│ └─│ │ │ │
│ │ │ │ │
-│ └─ <ENTER> to run, <Ctrl-c> to exit ─────────────────────┘ │ │
+│ └─ <ENTER> run, <ESC> drop to shell, <Ctrl-c> exit ──────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────┘ │
│ ││ │
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session_with_viewport_serialization.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session_with_viewport_serialization.snap
index 510313378..f36773605 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session_with_viewport_serialization.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__quit_and_resurrect_session_with_viewport_serialization.snap
@@ -17,10 +17,10 @@ expression: last_snapshot
│ │ │ │ │────────────────────────────┘
│ │ │ Waiting to run: top │ │────────────────────────────┐
│ │ │ │ │ │
-│ │ │ <ENTER> to run, <Ctrl-c> to exit │ │ │
+│ │ │ <ENTER> run, <ESC> drop to shell, <Ctrl-c> exit │ │ │
│ └─│ │ │ │
│ │ │ │ │
-│ └─ <ENTER> to run, <Ctrl-c> to exit ─────────────────────┘ │ │
+│ └─ <ENTER> run, <ESC> drop to shell, <Ctrl-c> exit ──────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────┘ │
│ ││ │
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap
index 343e574a3..4d2555214 100644
--- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap
@@ -1,29 +1,29 @@
---
source: src/tests/e2e/cases.rs
-assertion_line: 2031
+assertion_line: 2175
expression: last_snapshot
---
Zellij (e2e-test)  Tab #1 
-┌ Pane #1 ─────────────────────────────────────────────────┐┌ /usr/src/zellij/fixtures/append-echo-script.sh ──────────┐
-│$ /usr/src/zellij/x86_64-unknown-linux-musl/release/zellij││foo │
-│ run -s -- "/usr/src/zellij/fixtures/append-echo-script.sh││foo │
-│" ││█ │
-│$ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-│ ││ │
-└──────────────────────────────────────────────────────────┘└ [ EXIT CODE: 0 ] <ENTER> to re-run, <Ctrl-c> to exit ────┘
- Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT 
- Tip: Alt + <n> => new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate. Alt + <+|-> => resize pane.
+┌ Pane #1 ────────────────────────────────────────────────────────────────┐┌ /usr/src/zellij/fixtures/append-echo-script.sh ─────────────────────────┐
+│$ /usr/src/zellij/x86_64-unknown-linux-musl/release/zellij run -s -- "/us││foo │
+│r/src/zellij/fixtures/append-echo-script.sh" ││foo │
+│$ ││█ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└─────────────────────────────────────────────────────────────────────────┘└ [ EXIT CODE: 0 ] <ENTER> re-run, <ESC> drop to shell, <Ctrl-c> exit ────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  Alt + <[]>  VERTICAL 
+ Tip: Alt + <n> => open new pane. Alt + <←↓↑→> or Alt + <hjkl> => navigate between panes. Alt + <+|-> => increase/decrease pane size.
diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs
index 46a0d9974..793e5b665 100644
--- a/zellij-server/src/os_input_output.rs
+++ b/zellij-server/src/os_input_output.rs
@@ -31,7 +31,7 @@ use zellij_utils::{
};
use std::{
- collections::{BTreeMap, HashMap, HashSet},
+ collections::{BTreeMap, BTreeSet, HashMap},
env,
fs::File,
io::Write,
@@ -581,7 +581,7 @@ impl ServerOsApi for ServerOsInputOutput {
.with_context(err_context)?;
let mut terminal_id = None;
{
- let current_ids: HashSet<u32> = self
+ let current_ids: BTreeSet<u32> = self
.terminal_id_to_raw_fd
.lock()
.to_anyhow()
@@ -589,13 +589,7 @@ impl ServerOsApi for ServerOsInputOutput {
.keys()
.copied()
.collect();
- for i in 0..u32::MAX {
- let i = i as u32;
- if !current_ids.contains(&i) {
- terminal_id = Some(i);
- break;
- }
- }
+ terminal_id = current_ids.last().map(|l| l + 1).or(Some(0));
}
match terminal_id {
Some(terminal_id) => {
@@ -628,7 +622,7 @@ impl ServerOsApi for ServerOsInputOutput {
let mut terminal_id = None;
{
- let current_ids: HashSet<u32> = self
+ let current_ids: BTreeSet<u32> = self
.terminal_id_to_raw_fd
.lock()
.to_anyhow()
@@ -636,13 +630,7 @@ impl ServerOsApi for ServerOsInputOutput {
.keys()
.copied()
.collect();
- for i in 0..u32::MAX {
- let i = i as u32;
- if !current_ids.contains(&i) {
- terminal_id = Some(i);
- break;
- }
- }
+ terminal_id = current_ids.last().map(|l| l + 1).or(Some(0));
}
match terminal_id {
Some(terminal_id) => {
diff --git a/zellij-server/src/panes/terminal_character.rs b/zellij-server/src/panes/terminal_character.rs
index 23ea1a524..71aec7a45 100644
--- a/zellij-server/src/panes/terminal_character.rs
+++ b/zellij-server/src/panes/terminal_character.rs
@@ -773,21 +773,25 @@ pub fn render_first_run_banner(
let controls_bare_text_first_part = "<";
let enter_bare_text = "ENTER";
- let controls_bare_text_second_part = "> to run, <";
+ let controls_bare_text_second_part = "> run, <";
+ let esc_bare_text = "ESC";
+ let controls_bare_text_third_part = "> drop to shell, <";
let ctrl_c_bare_text = "Ctrl-c";
- let controls_bare_text_third_part = "> to exit";
+ let controls_bare_text_fourth_part = "> 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()
+ + esc_bare_text.len()
+ + controls_bare_text_third_part.len()
+ ctrl_c_bare_text.len()
- + controls_bare_text_third_part.len();
+ + controls_bare_text_fourth_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",
+ "\u{1b}[{};{}H{}<{}{}{}{}> run, <{}{}{}{}> drop to shell, <{}{}{}{}> exit",
middle_row + 2,
controls_column_start_position,
bold_text,
@@ -796,6 +800,10 @@ pub fn render_first_run_banner(
RESET_STYLES,
bold_text,
controls_color,
+ esc_bare_text,
+ RESET_STYLES,
+ bold_text,
+ controls_color,
ctrl_c_bare_text,
RESET_STYLES,
bold_text
@@ -817,21 +825,25 @@ pub fn render_first_run_banner(
let controls_bare_text_first_part = "<";
let enter_bare_text = "ENTER";
- let controls_bare_text_second_part = "> to run, <";
+ let controls_bare_text_second_part = "> run, <";
+ let esc_bare_text = "ESC";
+ let controls_bare_text_third_part = "> drop to shell, <";
let ctrl_c_bare_text = "Ctrl-c";
- let controls_bare_text_third_part = "> to exit";
+ let controls_bare_text_fourth_part = "> 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()
+ + esc_bare_text.len()
+ + controls_bare_text_third_part.len()
+ ctrl_c_bare_text.len()
- + controls_bare_text_third_part.len();
+ + controls_bare_text_fourth_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",
+ "\u{1b}[{};{}H{}<{}{}{}{}> run, <{}{}{}{}> drop to shell, <{}{}{}{}> exit",
middle_row + 2,
controls_column_start_position,
bold_text,
@@ -840,6 +852,10 @@ pub fn render_first_run_banner(
RESET_STYLES,
bold_text,
controls_color,
+ esc_bare_text,
+ RESET_STYLES,
+ bold_text,
+ controls_color,
ctrl_c_bare_text,
RESET_STYLES,
bold_text
diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs
index 6c405b6e5..ac4c6da63 100644
--- a/zellij-server/src/panes/terminal_pane.rs
+++ b/zellij-server/src/panes/terminal_pane.rs
@@ -40,6 +40,7 @@ const END_KEY: &[u8] = &[27, 91, 70];
const BRACKETED_PASTE_BEGIN: &[u8] = &[27, 91, 50, 48, 48, 126];
const BRACKETED_PASTE_END: &[u8] = &[27, 91, 50, 48, 49, 126];
const ENTER_NEWLINE: &[u8] = &[10];
+const ESC: &[u8] = &[27];
const ENTER_CARRIAGE_RETURN: &[u8] = &[13];
const SPACE: &[u8] = &[32];
const CTRL_C: &[u8] = &[3]; // TODO: check this to be sure it fits all types of CTRL_C (with mac, etc)
@@ -192,6 +193,13 @@ impl Pane for TerminalPane {
self.remove_banner();
Some(AdjustedInput::ReRunCommandInThisPane(run_command))
},
+ ESC => {
+ self.is_held = None;
+ self.grid.reset_terminal_state();
+ self.set_should_render(true);
+ self.remove_banner();
+ Some(AdjustedInput::DropToShellInThisPane)
+ },
CTRL_C => Some(AdjustedInput::CloseThisPane),
_ => None,
}
diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs
index e355e8c55..6dd38e9fe 100644
--- a/zellij-server/src/pty.rs
+++ b/zellij-server/src/pty.rs
@@ -66,6 +66,7 @@ pub enum PtyInstruction {
ClosePane(PaneId),
CloseTab(Vec<PaneId>),
ReRunCommandInPane(PaneId, RunCommand),
+ DropToShellInPane(PaneId, Option<PathBuf>), // Option<PathBuf> - default shell
SpawnInPlaceTerminal(
Option<TerminalAction>,
Option<String>,
@@ -89,6 +90,7 @@ impl From<&PtyInstruction> for PtyContext {
PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
PtyInstruction::NewTab(..) => PtyContext::NewTab,
PtyInstruction::ReRunCommandInPane(..) => PtyContext::ReRunCommandInPane,
+ PtyInstruction::DropToShellInPane(..) => PtyContext::DropToShellInPane,
PtyInstruction::SpawnInPlaceTerminal(..) => PtyContext::SpawnInPlaceTerminal,
PtyInstruction::DumpLayout(..) => PtyContext::DumpLayout,
PtyInstruction::LogLayoutToHd(..) => PtyContext::LogLayoutToHd,
@@ -532,6 +534,55 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
},
}
},
+ PtyInstruction::DropToShellInPane(pane_id, default_shell) => {
+ let err_context = || format!("failed to rerun command in pane {:?}", pane_id);
+
+ // TODO: get configured default_shell from screen/tab as an option and default to
+ // this otherwise (also look for a place that turns get_default_shell into a
+ // RunCommand, we might have done this before)
+ let run_command = RunCommand {
+ command: default_shell.unwrap_or_else(|| get_default_shell()),
+ hold_on_close: false,
+ hold_on_start: false,
+ // TODO: cwd
+ ..Default::default()
+ };
+ match pty
+ .rerun_command_in_pane(pane_id, run_command.clone())
+ .with_context(err_context)
+ {
+ Ok(..) => {},
+ Err(err) => match err.downcast_ref::<ZellijError>() {
+ Some(ZellijError::CommandNotFound { terminal_id, .. }) => {
+ if run_command.hold_on_close {
+ pty.bus
+ .senders
+ .send_to_screen(ScreenInstruction::PtyBytes(
+ *terminal_id,
+ form