From 355463383a18fb9e54afd62d718bed4c085a766f Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 29 Jul 2024 11:40:01 +0200 Subject: [PATCH] feat(plugins): allow opening command panes in the background (hidden) (#3530) * start background pane * open command and edit panes in the background * some cleanups * style(fmt): rustfmt * more cleanups --- .../fixture-plugin-for-tests/src/main.rs | 42 +- default-plugins/status-bar/src/one_line_ui.rs | 8 +- default-plugins/status-bar/src/second_line.rs | 12 +- .../status-bar/src/tip/data/quicknav.rs | 2 +- default-plugins/strider/src/state.rs | 3 +- zellij-server/src/os_input_output.rs | 15 +- .../floating_panes/floating_pane_grid.rs | 2 +- zellij-server/src/panes/floating_panes/mod.rs | 2 +- zellij-server/src/panes/mod.rs | 2 +- zellij-server/src/panes/terminal_pane.rs | 9 + zellij-server/src/plugins/mod.rs | 14 + .../src/plugins/unit/plugin_tests.rs | 220 ++ ...sts__hide_pane_with_id_plugin_command.snap | 13 + ...ommand_pane_background_plugin_command.snap | 37 + ..._command_pane_floating_plugin_command.snap | 1 + ...sts__open_command_pane_plugin_command.snap | 1 + ...ts__open_file_floating_plugin_command.snap | 22 +- ...lugin_tests__open_file_plugin_command.snap | 22 +- ...ile_with_line_floating_plugin_command.snap | 26 +- ...s__open_file_with_line_plugin_command.snap | 26 +- ...open_terminal_floating_plugin_command.snap | 1 + ...n_tests__open_terminal_plugin_command.snap | 1 + ...sts__show_pane_with_id_plugin_command.snap | 14 + zellij-server/src/plugins/wasm_bridge.rs | 16 + zellij-server/src/plugins/zellij_exports.rs | 107 +- zellij-server/src/pty.rs | 145 +- zellij-server/src/route.rs | 16 +- zellij-server/src/screen.rs | 15 +- zellij-server/src/tab/mod.rs | 86 +- .../src/tab/unit/tab_integration_tests.rs | 2262 ++++++++++++++--- zellij-server/src/tab/unit/tab_tests.rs | 24 +- zellij-server/src/unit/screen_tests.rs | 6 +- ...i_edit_action_with_default_parameters.snap | 4 +- ...send_cli_edit_action_with_line_number.snap | 4 +- ..._cli_edit_action_with_split_direction.snap | 4 +- ...w_pane_action_with_default_parameters.snap | 4 +- ...on_with_floating_pane_and_coordinates.snap | 4 +- zellij-tile/src/shim.rs | 27 +- zellij-utils/assets/prost/api.event.rs | 77 +- .../assets/prost/api.plugin_command.rs | 9 +- zellij-utils/src/data.rs | 14 +- zellij-utils/src/input/actions.rs | 20 +- zellij-utils/src/input/command.rs | 30 +- zellij-utils/src/kdl/mod.rs | 4 +- zellij-utils/src/plugin_api/action.rs | 22 +- zellij-utils/src/plugin_api/event.proto | 33 + zellij-utils/src/plugin_api/event.rs | 119 +- .../src/plugin_api/plugin_command.proto | 3 + zellij-utils/src/plugin_api/plugin_command.rs | 81 +- ..._default_config_with_no_cli_arguments.snap | 18 + ...out_env_vars_override_config_env_vars.snap | 18 + ..._layout_themes_override_config_themes.snap | 18 + ..._ui_config_overrides_config_ui_config.snap | 18 + 53 files changed, 3082 insertions(+), 621 deletions(-) create mode 100644 zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__hide_pane_with_id_plugin_command.snap create mode 100644 zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_background_plugin_command.snap create mode 100644 zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_pane_with_id_plugin_command.snap diff --git a/default-plugins/fixture-plugin-for-tests/src/main.rs b/default-plugins/fixture-plugin-for-tests/src/main.rs index 89af6573..70bd2966 100644 --- a/default-plugins/fixture-plugin-for-tests/src/main.rs +++ b/default-plugins/fixture-plugin-for-tests/src/main.rs @@ -166,10 +166,13 @@ impl ZellijPlugin for State { start_or_reload_plugin(plugin_url) }, BareKey::Char('g') if key.has_modifiers(&[KeyModifier::Ctrl]) => { - open_file(FileToOpen { - path: std::path::PathBuf::from("/path/to/my/file.rs"), - ..Default::default() - }); + open_file( + FileToOpen { + path: std::path::PathBuf::from("/path/to/my/file.rs"), + ..Default::default() + }, + BTreeMap::new(), + ); }, BareKey::Char('h') if key.has_modifiers(&[KeyModifier::Ctrl]) => { open_file_floating( @@ -178,14 +181,18 @@ impl ZellijPlugin for State { ..Default::default() }, None, + BTreeMap::new(), ); }, BareKey::Char('i') if key.has_modifiers(&[KeyModifier::Ctrl]) => { - open_file(FileToOpen { - path: std::path::PathBuf::from("/path/to/my/file.rs"), - line_number: Some(42), - ..Default::default() - }); + open_file( + FileToOpen { + path: std::path::PathBuf::from("/path/to/my/file.rs"), + line_number: Some(42), + ..Default::default() + }, + BTreeMap::new(), + ); }, BareKey::Char('j') if key.has_modifiers(&[KeyModifier::Ctrl]) => { open_file_floating( @@ -195,6 +202,7 @@ impl ZellijPlugin for State { ..Default::default() }, None, + BTreeMap::new(), ); }, BareKey::Char('k') if key.has_modifiers(&[KeyModifier::Ctrl]) => { @@ -335,6 +343,22 @@ impl ZellijPlugin for State { .to_owned(), ); }, + BareKey::Char('a') if key.has_modifiers(&[KeyModifier::Alt]) => { + hide_pane_with_id(PaneId::Terminal(1)); + }, + BareKey::Char('b') if key.has_modifiers(&[KeyModifier::Alt]) => { + show_pane_with_id(PaneId::Terminal(1), true); + }, + BareKey::Char('c') if key.has_modifiers(&[KeyModifier::Alt]) => { + open_command_pane_background( + CommandToRun { + path: std::path::PathBuf::from("/path/to/my/file.rs"), + args: vec!["arg1".to_owned(), "arg2".to_owned()], + ..Default::default() + }, + BTreeMap::new(), + ); + }, _ => {}, }, Event::CustomMessage(message, payload) => { diff --git a/default-plugins/status-bar/src/one_line_ui.rs b/default-plugins/status-bar/src/one_line_ui.rs index 7e3b8a4d..8d1227af 100644 --- a/default-plugins/status-bar/src/one_line_ui.rs +++ b/default-plugins/status-bar/src/one_line_ui.rs @@ -677,7 +677,7 @@ fn secondary_keybinds(help: &ModeInfo, tab_info: Option<&TabInfo>, max_len: usiz let mut secondary_info = LinePart::default(); let binds = &help.get_mode_keybinds(); // New Pane - let new_pane_action_key = action_key(binds, &[Action::NewPane(None, None)]); + let new_pane_action_key = action_key(binds, &[Action::NewPane(None, None, false)]); let mut new_pane_key_to_display = new_pane_action_key .iter() .find(|k| k.is_key_with_alt_modifier(BareKey::Char('n'))) @@ -1163,7 +1163,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec Vec<(String, String, Vec Vec<(String, String, Vec Vec<(String, String, Vec { fn add_keybinds(help: &ModeInfo) -> Keygroups { let normal_keymap = help.get_mode_keybinds(); - let new_pane_keys = action_key(&normal_keymap, &[Action::NewPane(None, None)]); + let new_pane_keys = action_key(&normal_keymap, &[Action::NewPane(None, None, false)]); let new_pane = if new_pane_keys.is_empty() { vec![Style::new().bold().paint("UNBOUND")] } else { diff --git a/default-plugins/strider/src/state.rs b/default-plugins/strider/src/state.rs index 4b81c7f2..87f70d4b 100644 --- a/default-plugins/strider/src/state.rs +++ b/default-plugins/strider/src/state.rs @@ -152,9 +152,10 @@ impl State { if let Some(parent_folder) = self.file_list_view.path.parent() { open_file( FileToOpen::new(&self.file_list_view.path).with_cwd(parent_folder.into()), + BTreeMap::new(), ); } else { - open_file(FileToOpen::new(&self.file_list_view.path)); + open_file(FileToOpen::new(&self.file_list_view.path), BTreeMap::new()); } } if self.close_on_selection { diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs index 099a3434..e4263857 100644 --- a/zellij-server/src/os_input_output.rs +++ b/zellij-server/src/os_input_output.rs @@ -288,10 +288,10 @@ fn spawn_terminal( // secondary fd let mut failover_cmd_args = None; let cmd = match terminal_action { - TerminalAction::OpenFile(mut file_to_open, line_number, cwd) => { - if file_to_open.is_relative() { - if let Some(cwd) = cwd.as_ref() { - file_to_open = cwd.join(file_to_open); + TerminalAction::OpenFile(mut payload) => { + if payload.path.is_relative() { + if let Some(cwd) = payload.cwd.as_ref() { + payload.path = cwd.join(payload.path); } } let mut command = default_editor.unwrap_or_else(|| { @@ -306,11 +306,12 @@ fn spawn_terminal( if !command.is_dir() { separate_command_arguments(&mut command, &mut args); } - let file_to_open = file_to_open + let file_to_open = payload + .path .into_os_string() .into_string() .expect("Not valid Utf8 Encoding"); - if let Some(line_number) = line_number { + if let Some(line_number) = payload.line_number { if command.ends_with("vim") || command.ends_with("nvim") || command.ends_with("emacs") @@ -334,7 +335,7 @@ fn spawn_terminal( RunCommand { command, args, - cwd, + cwd: payload.cwd, hold_on_close: false, hold_on_start: false, ..Default::default() diff --git a/zellij-server/src/panes/floating_panes/floating_pane_grid.rs b/zellij-server/src/panes/floating_panes/floating_pane_grid.rs index b8472dfc..75da894e 100644 --- a/zellij-server/src/panes/floating_panes/floating_pane_grid.rs +++ b/zellij-server/src/panes/floating_panes/floating_pane_grid.rs @@ -854,7 +854,7 @@ impl<'a> FloatingPaneGrid<'a> { } } -fn half_size_middle_geom(space: &Viewport, offset: usize) -> PaneGeom { +pub fn half_size_middle_geom(space: &Viewport, offset: usize) -> PaneGeom { let mut geom = PaneGeom { x: space.x + (space.cols as f64 / 4.0).round() as usize + offset, y: space.y + (space.rows as f64 / 4.0).round() as usize + offset, diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs index c9166ce1..2aa74d9c 100644 --- a/zellij-server/src/panes/floating_panes/mod.rs +++ b/zellij-server/src/panes/floating_panes/mod.rs @@ -1,4 +1,4 @@ -mod floating_pane_grid; +pub mod floating_pane_grid; use zellij_utils::{ data::{Direction, PaneInfo, ResizeStrategy}, position::Position, diff --git a/zellij-server/src/panes/mod.rs b/zellij-server/src/panes/mod.rs index 5055d0d3..4cd0fb4b 100644 --- a/zellij-server/src/panes/mod.rs +++ b/zellij-server/src/panes/mod.rs @@ -6,7 +6,7 @@ pub mod sixel; pub mod terminal_character; mod active_panes; -mod floating_panes; +pub mod floating_panes; mod plugin_pane; mod search; mod terminal_pane; diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index 4df05521..6232a3bf 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -98,6 +98,15 @@ impl From for PaneId { } } +impl Into for PaneId { + fn into(self) -> ZellijUtilsPaneId { + match self { + PaneId::Terminal(id) => ZellijUtilsPaneId::Terminal(id), + PaneId::Plugin(id) => ZellijUtilsPaneId::Plugin(id), + } + } +} + type IsFirstRun = bool; // FIXME: This should hold an os_api handle so that terminal panes can set their own size via FD in diff --git a/zellij-server/src/plugins/mod.rs b/zellij-server/src/plugins/mod.rs index 223ecb55..7e97f1d7 100644 --- a/zellij-server/src/plugins/mod.rs +++ b/zellij-server/src/plugins/mod.rs @@ -249,6 +249,7 @@ pub(crate) fn plugin_thread_main( run_plugin_or_alias.populate_run_plugin_if_needed(&plugin_aliases); let cwd = run_plugin_or_alias.get_initial_cwd().or(cwd); let run_plugin = run_plugin_or_alias.get_run_plugin(); + let start_suppressed = false; match wasm_bridge.load_plugin( &run_plugin, Some(tab_index), @@ -268,6 +269,7 @@ pub(crate) fn plugin_thread_main( plugin_id, pane_id_to_replace, cwd, + start_suppressed, Some(client_id), ))); }, @@ -307,6 +309,7 @@ pub(crate) fn plugin_thread_main( // we intentionally do not provide the client_id here because it belongs to // the cli who spawned the command and is not an existing client_id let skip_cache = true; // when reloading we always skip cache + let start_suppressed = false; match wasm_bridge.load_plugin( &Some(run_plugin), Some(tab_index), @@ -328,6 +331,7 @@ pub(crate) fn plugin_thread_main( plugin_id, None, None, + start_suppressed, None, ), )); @@ -365,6 +369,16 @@ pub(crate) fn plugin_thread_main( tab_index, client_id, ) => { + // prefer connected clients so as to avoid opening plugins in the background for + // CLI clients unless no-one else is connected + let client_id = if wasm_bridge.client_is_connected(&client_id) { + client_id + } else if let Some(first_client_id) = wasm_bridge.get_first_client_id() { + first_client_id + } else { + client_id + }; + let mut plugin_ids: HashMap> = HashMap::new(); tab_layout = tab_layout.or_else(|| Some(layout.new_tab().0)); tab_layout diff --git a/zellij-server/src/plugins/unit/plugin_tests.rs b/zellij-server/src/plugins/unit/plugin_tests.rs index 5ef220db..031f0100 100644 --- a/zellij-server/src/plugins/unit/plugin_tests.rs +++ b/zellij-server/src/plugins/unit/plugin_tests.rs @@ -6655,3 +6655,223 @@ pub fn run_plugin_in_specific_cwd() { "File written into plugin initial cwd" ); } + +#[test] +#[ignore] +pub fn hide_pane_with_id_plugin_command() { + let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its + // destructor removes the directory + let plugin_host_folder = PathBuf::from(temp_folder.path()); + let cache_path = plugin_host_folder.join("permissions_test.kdl"); + let (plugin_thread_sender, screen_receiver, teardown) = + create_plugin_thread(Some(plugin_host_folder)); + let plugin_should_float = Some(false); + let plugin_title = Some("test_plugin".to_owned()); + let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { + _allow_exec_host_cmd: false, + location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), + configuration: Default::default(), + ..Default::default() + }); + let tab_index = 1; + let client_id = 1; + let size = Size { + cols: 121, + rows: 20, + }; + let received_screen_instructions = Arc::new(Mutex::new(vec![])); + let screen_thread = grant_permissions_and_log_actions_in_thread!( + received_screen_instructions, + ScreenInstruction::SuppressPane, + screen_receiver, + 1, + &PermissionType::ChangeApplicationState, + cache_path, + plugin_thread_sender, + client_id + ); + + let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); + let _ = plugin_thread_sender.send(PluginInstruction::Load( + plugin_should_float, + false, + plugin_title, + run_plugin, + tab_index, + None, + client_id, + size, + None, + false, + )); + std::thread::sleep(std::time::Duration::from_millis(500)); + let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( + None, + Some(client_id), + Event::Key(KeyWithModifier::new(BareKey::Char('a')).with_alt_modifier()), // this triggers the enent in the fixture plugin + )])); + screen_thread.join().unwrap(); // this might take a while if the cache is cold + teardown(); + let suppress_pane_event = received_screen_instructions + .lock() + .unwrap() + .iter() + .find_map(|i| { + if let ScreenInstruction::SuppressPane(..) = i { + Some(i.clone()) + } else { + None + } + }) + .clone(); + assert_snapshot!(format!("{:#?}", suppress_pane_event)); +} + +#[test] +#[ignore] +pub fn show_pane_with_id_plugin_command() { + let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its + // destructor removes the directory + let plugin_host_folder = PathBuf::from(temp_folder.path()); + let cache_path = plugin_host_folder.join("permissions_test.kdl"); + let (plugin_thread_sender, screen_receiver, teardown) = + create_plugin_thread(Some(plugin_host_folder)); + let plugin_should_float = Some(false); + let plugin_title = Some("test_plugin".to_owned()); + let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { + _allow_exec_host_cmd: false, + location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), + configuration: Default::default(), + ..Default::default() + }); + let tab_index = 1; + let client_id = 1; + let size = Size { + cols: 121, + rows: 20, + }; + let received_screen_instructions = Arc::new(Mutex::new(vec![])); + let screen_thread = grant_permissions_and_log_actions_in_thread!( + received_screen_instructions, + ScreenInstruction::FocusPaneWithId, + screen_receiver, + 1, + &PermissionType::ChangeApplicationState, + cache_path, + plugin_thread_sender, + client_id + ); + + let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); + let _ = plugin_thread_sender.send(PluginInstruction::Load( + plugin_should_float, + false, + plugin_title, + run_plugin, + tab_index, + None, + client_id, + size, + None, + false, + )); + std::thread::sleep(std::time::Duration::from_millis(500)); + let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( + None, + Some(client_id), + Event::Key(KeyWithModifier::new(BareKey::Char('b')).with_alt_modifier()), // this triggers the enent in the fixture plugin + )])); + screen_thread.join().unwrap(); // this might take a while if the cache is cold + teardown(); + let focus_pane_event = received_screen_instructions + .lock() + .unwrap() + .iter() + .find_map(|i| { + if let ScreenInstruction::FocusPaneWithId(..) = i { + Some(i.clone()) + } else { + None + } + }) + .clone(); + assert_snapshot!(format!("{:#?}", focus_pane_event)); +} + +#[test] +#[ignore] +pub fn open_command_pane_background_plugin_command() { + let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its + // destructor removes the directory + let plugin_host_folder = PathBuf::from(temp_folder.path()); + let cache_path = plugin_host_folder.join("permissions_test.kdl"); + let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = + create_plugin_thread_with_pty_receiver(Some(plugin_host_folder)); + let plugin_should_float = Some(false); + let plugin_title = Some("test_plugin".to_owned()); + let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { + _allow_exec_host_cmd: false, + location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), + configuration: Default::default(), + ..Default::default() + }); + let tab_index = 1; + let client_id = 1; + let size = Size { + cols: 121, + rows: 20, + }; + let received_pty_instructions = Arc::new(Mutex::new(vec![])); + let pty_thread = log_actions_in_thread!( + received_pty_instructions, + PtyInstruction::SpawnTerminal, + pty_receiver, + 1 + ); + let received_screen_instructions = Arc::new(Mutex::new(vec![])); + let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( + received_screen_instructions, + ScreenInstruction::Exit, + screen_receiver, + 1, + &PermissionType::ChangeApplicationState, + cache_path, + plugin_thread_sender, + client_id + ); + + let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); + let _ = plugin_thread_sender.send(PluginInstruction::Load( + plugin_should_float, + false, + plugin_title, + run_plugin, + tab_index, + None, + client_id, + size, + None, + false, + )); + std::thread::sleep(std::time::Duration::from_millis(500)); + let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( + None, + Some(client_id), + Event::Key(KeyWithModifier::new(BareKey::Char('c')).with_alt_modifier()), // this triggers the enent in the fixture plugin + )])); + pty_thread.join().unwrap(); // this might take a while if the cache is cold + teardown(); + let new_tab_event = received_pty_instructions + .lock() + .unwrap() + .iter() + .find_map(|i| { + if let PtyInstruction::SpawnTerminal(..) = i { + Some(i.clone()) + } else { + None + } + }) + .clone(); + assert_snapshot!(format!("{:#?}", new_tab_event)); +} diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__hide_pane_with_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__hide_pane_with_id_plugin_command.snap new file mode 100644 index 00000000..43cae6bf --- /dev/null +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__hide_pane_with_id_plugin_command.snap @@ -0,0 +1,13 @@ +--- +source: zellij-server/src/plugins/./unit/plugin_tests.rs +assertion_line: 6727 +expression: "format!(\"{:#?}\", suppress_pane_event)" +--- +Some( + SuppressPane( + Terminal( + 1, + ), + 1, + ), +) diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_background_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_background_plugin_command.snap new file mode 100644 index 00000000..8c86f8f3 --- /dev/null +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_background_plugin_command.snap @@ -0,0 +1,37 @@ +--- +source: zellij-server/src/plugins/./unit/plugin_tests.rs +assertion_line: 6876 +expression: "format!(\"{:#?}\", new_tab_event)" +--- +Some( + SpawnTerminal( + Some( + RunCommand( + RunCommand { + command: "/path/to/my/file.rs", + args: [ + "arg1", + "arg2", + ], + cwd: None, + hold_on_close: true, + hold_on_start: false, + originating_plugin: Some( + OriginatingPlugin { + plugin_id: 0, + client_id: 1, + context: {}, + }, + ), + }, + ), + ), + None, + None, + None, + true, + ClientId( + 1, + ), + ), +) diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_floating_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_floating_plugin_command.snap index f8262b52..a89e1b2f 100644 --- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_floating_plugin_command.snap +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_floating_plugin_command.snap @@ -31,6 +31,7 @@ Some( ), None, None, + false, ClientId( 1, ), diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_plugin_command.snap index a1247d5e..048dc905 100644 --- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_plugin_command.snap +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_plugin_command.snap @@ -31,6 +31,7 @@ Some( ), None, None, + false, ClientId( 1, ), diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_floating_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_floating_plugin_command.snap index 9a1ffe58..e4a14a40 100644 --- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_floating_plugin_command.snap +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_floating_plugin_command.snap @@ -1,17 +1,26 @@ --- source: zellij-server/src/plugins/./unit/plugin_tests.rs -assertion_line: 3846 +assertion_line: 3996 expression: "format!(\"{:#?}\",\n new_tab_event).replace(&format!(\"{:?}\", temp_folder.path()),\n \"\\\"CWD\\\"\")" --- Some( SpawnTerminal( Some( OpenFile( - "/path/to/my/file.rs", - None, - Some( - "CWD", - ), + OpenFilePayload { + path: "/path/to/my/file.rs", + line_number: None, + cwd: Some( + "CWD", + ), + originating_plugin: Some( + OriginatingPlugin { + plugin_id: 0, + client_id: 1, + context: {}, + }, + ), + }, ), ), Some( @@ -21,6 +30,7 @@ Some( "Editing: /path/to/my/file.rs", ), None, + false, ClientId( 1, ), diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_plugin_command.snap index b24094af..ab031be5 100644 --- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_plugin_command.snap +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_plugin_command.snap @@ -1,17 +1,26 @@ --- source: zellij-server/src/plugins/./unit/plugin_tests.rs -assertion_line: 3927 +assertion_line: 4078 expression: "format!(\"{:#?}\",\n new_tab_event).replace(&format!(\"{:?}\", temp_folder.path()),\n \"\\\"CWD\\\"\")" --- Some( SpawnTerminal( Some( OpenFile( - "/path/to/my/file.rs", - None, - Some( - "CWD", - ), + OpenFilePayload { + path: "/path/to/my/file.rs", + line_number: None, + cwd: Some( + "CWD", + ), + originating_plugin: Some( + OriginatingPlugin { + plugin_id: 0, + client_id: 1, + context: {}, + }, + ), + }, ), ), Some( @@ -21,6 +30,7 @@ Some( "Editing: /path/to/my/file.rs", ), None, + false, ClientId( 1, ), diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_floating_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_floating_plugin_command.snap index dca771df..9d9747d8 100644 --- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_floating_plugin_command.snap +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_floating_plugin_command.snap @@ -1,19 +1,28 @@ --- source: zellij-server/src/plugins/./unit/plugin_tests.rs -assertion_line: 4090 +assertion_line: 4243 expression: "format!(\"{:#?}\",\n new_tab_event).replace(&format!(\"{:?}\", temp_folder.path()),\n \"\\\"CWD\\\"\")" --- Some( SpawnTerminal( Some( OpenFile( - "/path/to/my/file.rs", - Some( - 42, - ), - Some( - "CWD", - ), + OpenFilePayload { + path: "/path/to/my/file.rs", + line_number: Some( + 42, + ), + cwd: Some( + "CWD", + ), + originating_plugin: Some( + OriginatingPlugin { + plugin_id: 0, + client_id: 1, + context: {}, + }, + ), + }, ), ), Some( @@ -23,6 +32,7 @@ Some( "Editing: /path/to/my/file.rs", ), None, + false, ClientId( 1, ), diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_plugin_command.snap index fd9d8e82..aa3aa515 100644 --- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_plugin_command.snap +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_plugin_command.snap @@ -1,19 +1,28 @@ --- source: zellij-server/src/plugins/./unit/plugin_tests.rs -assertion_line: 4009 +assertion_line: 4161 expression: "format!(\"{:#?}\",\n new_tab_event).replace(&format!(\"{:?}\", temp_folder.path()),\n \"\\\"CWD\\\"\")" --- Some( SpawnTerminal( Some( OpenFile( - "/path/to/my/file.rs", - Some( - 42, - ), - Some( - "CWD", - ), + OpenFilePayload { + path: "/path/to/my/file.rs", + line_number: Some( + 42, + ), + cwd: Some( + "CWD", + ), + originating_plugin: Some( + OriginatingPlugin { + plugin_id: 0, + client_id: 1, + context: {}, + }, + ), + }, ), ), Some( @@ -23,6 +32,7 @@ Some( "Editing: /path/to/my/file.rs", ), None, + false, ClientId( 1, ), diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_floating_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_floating_plugin_command.snap index 0ade1098..e61ebbdb 100644 --- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_floating_plugin_command.snap +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_floating_plugin_command.snap @@ -24,6 +24,7 @@ Some( ), None, None, + false, ClientId( 1, ), diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_plugin_command.snap index 3491f831..1df64a8f 100644 --- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_plugin_command.snap +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_plugin_command.snap @@ -24,6 +24,7 @@ Some( ), None, None, + false, ClientId( 1, ), diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_pane_with_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_pane_with_id_plugin_command.snap new file mode 100644 index 00000000..f13aded1 --- /dev/null +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_pane_with_id_plugin_command.snap @@ -0,0 +1,14 @@ +--- +source: zellij-server/src/plugins/./unit/plugin_tests.rs +assertion_line: 6798 +expression: "format!(\"{:#?}\", focus_pane_event)" +--- +Some( + FocusPaneWithId( + Terminal( + 1, + ), + true, + 1, + ), +) diff --git a/zellij-server/src/plugins/wasm_bridge.rs b/zellij-server/src/plugins/wasm_bridge.rs index ce68c109..76835183 100644 --- a/zellij-server/src/plugins/wasm_bridge.rs +++ b/zellij-server/src/plugins/wasm_bridge.rs @@ -1193,6 +1193,7 @@ impl WasmBridge { cli_client_id, ) { Ok((plugin_id, client_id)) => { + let start_suppressed = false; drop(self.senders.send_to_screen(ScreenInstruction::AddPlugin( Some(should_float), should_be_open_in_place, @@ -1202,6 +1203,7 @@ impl WasmBridge { plugin_id, pane_id_to_replace, cwd, + start_suppressed, Some(client_id), ))); vec![(plugin_id, Some(client_id))] @@ -1268,6 +1270,17 @@ impl WasmBridge { || (message_cid.is_none() && message_pid == Some(*plugin_id)) || (message_cid == Some(*client_id) && message_pid == Some(*plugin_id)) } + pub fn client_is_connected(&self, client_id: &ClientId) -> bool { + self.connected_clients.lock().unwrap().contains(client_id) + } + pub fn get_first_client_id(&self) -> Option { + self.connected_clients + .lock() + .unwrap() + .iter() + .next() + .copied() + } } fn handle_plugin_successful_loading(senders: &ThreadSenders, plugin_id: PluginId) { @@ -1316,6 +1329,9 @@ fn check_event_permission( | Event::SystemClipboardFailure | Event::CommandPaneOpened(..) | Event::CommandPaneExited(..) + | Event::PaneClosed(..) + | Event::EditPaneOpened(..) + | Event::EditPaneExited(..) | Event::InputReceived => PermissionType::ReadApplicationState, _ => return (PermissionStatus::Granted, None), }; diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs index 8722d64a..4f7f04b6 100644 --- a/zellij-server/src/plugins/zellij_exports.rs +++ b/zellij-server/src/plugins/zellij_exports.rs @@ -2,6 +2,7 @@ use super::PluginInstruction; use crate::background_jobs::BackgroundJob; use crate::plugins::plugin_map::PluginEnv; use crate::plugins::wasm_bridge::handle_plugin_crash; +use crate::pty::{ClientTabIndexOrPaneId, PtyInstruction}; use crate::route::route_action; use crate::ServerInstruction; use log::{debug, warn}; @@ -22,6 +23,7 @@ use zellij_utils::data::{ }; use zellij_utils::input::permission::PermissionCache; use zellij_utils::{ + async_std::task, interprocess::local_socket::LocalSocketStream, ipc::{ClientToServerMsg, IpcSenderWithContext}, }; @@ -37,7 +39,7 @@ use zellij_utils::{ errors::prelude::*, input::{ actions::Action, - command::{RunCommand, RunCommandAction, TerminalAction}, + command::{OpenFilePayload, RunCommand, RunCommandAction, TerminalAction}, layout::{Layout, RunPluginOrAlias}, plugins::PluginType, }, @@ -91,10 +93,14 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) { PluginCommand::SetSelectable(selectable) => set_selectable(env, selectable), PluginCommand::GetPluginIds => get_plugin_ids(env), PluginCommand::GetZellijVersion => get_zellij_version(env), - PluginCommand::OpenFile(file_to_open) => open_file(env, file_to_open), - PluginCommand::OpenFileFloating(file_to_open, floating_pane_coordinates) => { - open_file_floating(env, file_to_open, floating_pane_coordinates) + PluginCommand::OpenFile(file_to_open, context) => { + open_file(env, file_to_open, context) }, + PluginCommand::OpenFileFloating( + file_to_open, + floating_pane_coordinates, + context, + ) => open_file_floating(env, file_to_open, floating_pane_coordinates, context), PluginCommand::OpenTerminal(cwd) => open_terminal(env, cwd.path.try_into()?), PluginCommand::OpenTerminalFloating(cwd, floating_pane_coordinates) => { open_terminal_floating(env, cwd.path.try_into()?, floating_pane_coordinates) @@ -221,8 +227,8 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) { delete_dead_session(session_name)? }, PluginCommand::DeleteAllDeadSessions => delete_all_dead_sessions()?, - PluginCommand::OpenFileInPlace(file_to_open) => { - open_file_in_place(env, file_to_open) + PluginCommand::OpenFileInPlace(file_to_open, context) => { + open_file_in_place(env, file_to_open, context) }, PluginCommand::OpenTerminalInPlace(cwd) => { open_terminal_in_place(env, cwd.path.try_into()?) @@ -258,6 +264,9 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) { PluginCommand::ShowPaneWithId(pane_id, should_float_if_hidden) => { show_pane_with_id(env, pane_id.into(), should_float_if_hidden) }, + PluginCommand::OpenCommandPaneBackground(command_to_run, context) => { + open_command_pane_background(env, command_to_run, context) + }, }, (PermissionStatus::Denied, permission) => { log::error!( @@ -355,6 +364,7 @@ fn request_permission(env: &PluginEnv, permissions: Vec) -> Resu if PermissionCache::from_path_or_default(None) .check_permissions(env.plugin.location.to_string(), &permissions) { + log::info!("PermissionRequestResult 1"); return env .senders .send_to_plugin(PluginInstruction::PermissionRequestResult( @@ -416,22 +426,24 @@ fn get_zellij_version(env: &PluginEnv) { .non_fatal(); } -fn open_file(env: &PluginEnv, file_to_open: FileToOpen) { +fn open_file(env: &PluginEnv, file_to_open: FileToOpen, context: BTreeMap) { let error_msg = || format!("failed to open file in plugin {}", env.name()); let floating = false; let in_place = false; + let start_suppressed = false; let path = env.plugin_cwd.join(file_to_open.path); let cwd = file_to_open .cwd .map(|cwd| env.plugin_cwd.join(cwd)) .or_else(|| Some(env.plugin_cwd.clone())); let action = Action::EditFile( - path, - file_to_open.line_number, - cwd, + OpenFilePayload::new(path, file_to_open.line_number, cwd).with_originating_plugin( + OriginatingPlugin::new(env.plugin_id, env.client_id, context), + ), None, floating, in_place, + start_suppressed, None, ); apply_action!(action, error_msg, env); @@ -441,31 +453,39 @@ fn open_file_floating( env: &PluginEnv, file_to_open: FileToOpen, floating_pane_coordinates: Option, + context: BTreeMap, ) { let error_msg = || format!("failed to open file in plugin {}", env.name()); let floating = true; let in_place = false; + let start_suppressed = false; let path = env.plugin_cwd.join(file_to_open.path); let cwd = file_to_open .cwd .map(|cwd| env.plugin_cwd.join(cwd)) .or_else(|| Some(env.plugin_cwd.clone())); let action = Action::EditFile( - path, - file_to_open.line_number, - cwd, + OpenFilePayload::new(path, file_to_open.line_number, cwd).with_originating_plugin( + OriginatingPlugin::new(env.plugin_id, env.client_id, context), + ), None, floating, in_place, + start_suppressed, floating_pane_coordinates, ); apply_action!(action, error_msg, env); } -fn open_file_in_place(env: &PluginEnv, file_to_open: FileToOpen) { +fn open_file_in_place( + env: &PluginEnv, + file_to_open: FileToOpen, + context: BTreeMap, +) { let error_msg = || format!("failed to open file in plugin {}", env.name()); let floating = false; let in_place = true; + let start_suppressed = false; let path = env.plugin_cwd.join(file_to_open.path); let cwd = file_to_open .cwd @@ -473,12 +493,13 @@ fn open_file_in_place(env: &PluginEnv, file_to_open: FileToOpen) { .or_else(|| Some(env.plugin_cwd.clone())); let action = Action::EditFile( - path, - file_to_open.line_number, - cwd, + OpenFilePayload::new(path, file_to_open.line_number, cwd).with_originating_plugin( + OriginatingPlugin::new(env.plugin_id, env.client_id, context), + ), None, floating, in_place, + start_suppressed, None, ); apply_action!(action, error_msg, env); @@ -633,6 +654,43 @@ fn open_command_pane_in_place( apply_action!(action, error_msg, env); } +fn open_command_pane_background( + env: &PluginEnv, + command_to_run: CommandToRun, + context: BTreeMap, +) { + let command = command_to_run.path; + let cwd = command_to_run.cwd.map(|cwd| env.plugin_cwd.join(cwd)); + let args = command_to_run.args; + let direction = None; + let hold_on_close = true; + let hold_on_start = false; + let start_suppressed = true; + let name = None; + let run_command_action = RunCommandAction { + command, + args, + cwd, + direction, + hold_on_close, + hold_on_start, + originating_plugin: Some(OriginatingPlugin::new( + env.plugin_id, + env.client_id, + context, + )), + }; + let run_cmd = TerminalAction::RunCommand(run_command_action.into()); + let _ = env.senders.send_to_pty(PtyInstruction::SpawnTerminal( + Some(run_cmd), + None, + name, + None, + start_suppressed, + ClientTabIndexOrPaneId::ClientId(env.client_id), + )); +} + fn switch_tab_to(env: &PluginEnv, tab_idx: u32) { env.senders .send_to_screen(ScreenInstruction::GoToTab(tab_idx, Some(env.client_id))) @@ -646,23 +704,13 @@ fn switch_tab_to(env: &PluginEnv, tab_idx: u32) { } fn set_timeout(env: &PluginEnv, secs: f64) { - // There is a fancy, high-performance way to do this with zero additional threads: - // If the plugin thread keeps a BinaryHeap of timer structs, it can manage multiple and easily `.peek()` at the - // next time to trigger in O(1) time. Once the wake-up time is known, the `wasm` thread can use `recv_timeout()` - // to wait for an event with the timeout set to be the time of the next wake up. If events come in in the meantime, - // they are handled, but if the timeout triggers, we replace the event from `recv()` with an - // `Update(pid, TimerEvent)` and pop the timer from the Heap (or reschedule it). No additional threads for as many - // timers as we'd like. - // - // But that's a lot of code, and this is a few lines: let send_plugin_instructions = env.senders.to_plugin.clone(); let update_target = Some(env.plugin_id); let client_id = env.client_id; let plugin_name = env.name(); - // TODO: we should really use an async task for this - thread::spawn(move || { + task::spawn(async move { let start_time = Instant::now(); - thread::sleep(Duration::from_secs_f64(secs)); + task::sleep(Duration::from_secs_f64(secs)).await; // FIXME: The way that elapsed time is being calculated here is not exact; it doesn't take into account the // time it takes an event to actually reach the plugin after it's sent to the `wasm` thread. let elapsed_time = Instant::now().duration_since(start_time).as_secs_f64(); @@ -1446,6 +1494,7 @@ fn check_command_permission( PluginCommand::OpenCommandPane(..) | PluginCommand::OpenCommandPaneFloating(..) | PluginCommand::OpenCommandPaneInPlace(..) + | PluginCommand::OpenCommandPaneBackground(..) | PluginCommand::RunCommand(..) | PluginCommand::ExecCmd(..) => PermissionType::RunCommands, PluginCommand::WebRequest(..) => PermissionType::WebAccess, diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index 74d66a57..68ee8e2c 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -18,7 +18,7 @@ use zellij_utils::{ errors::prelude::*, errors::{ContextType, PtyContext}, input::{ - command::{RunCommand, TerminalAction}, + command::{OpenFilePayload, RunCommand, TerminalAction}, layout::{FloatingPaneLayout, Layout, Run, RunPluginOrAlias, TiledPaneLayout}, }, pane_size::Size, @@ -43,6 +43,7 @@ pub enum PtyInstruction { Option, Option, Option, + bool, // start suppressed ClientTabIndexOrPaneId, ), // bool (if Some) is // should_float, String is an optional pane name @@ -50,6 +51,7 @@ pub enum PtyInstruction { SpawnTerminalVertically(Option, Option, ClientId), // String is an // optional pane // name + // bool is start_suppressed SpawnTerminalHorizontally(Option, Option, ClientId), // String is an // optional pane // name @@ -141,29 +143,36 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) -> Result<()> { should_float, name, floating_pane_coordinates, + start_suppressed, client_or_tab_index, ) => { let err_context = || format!("failed to spawn terminal for {:?}", client_or_tab_index); - let (hold_on_close, run_command, pane_title) = match &terminal_action { - Some(TerminalAction::RunCommand(run_command)) => ( - run_command.hold_on_close, - Some(run_command.clone()), - Some(name.unwrap_or_else(|| run_command.to_string())), - ), - _ => (false, None, name), - }; - let invoked_with = + let (hold_on_close, run_command, pane_title, open_file_payload) = match &terminal_action { - Some(TerminalAction::RunCommand(run_command)) => { - Some(Run::Command(run_command.clone())) - }, - Some(TerminalAction::OpenFile(file, line_number, cwd)) => Some( - Run::EditFile(file.clone(), line_number.clone(), cwd.clone()), + Some(TerminalAction::RunCommand(run_command)) => ( + run_command.hold_on_close, + Some(run_command.clone()), + Some(name.unwrap_or_else(|| run_command.to_string())), + None, ), - _ => None, + Some(TerminalAction::OpenFile(open_file_payload)) => { + (false, None, name, Some(open_file_payload.clone())) + }, + _ => (false, None, name, None), }; + let invoked_with = match &terminal_action { + Some(TerminalAction::RunCommand(run_command)) => { + Some(Run::Command(run_command.clone())) + }, + Some(TerminalAction::OpenFile(payload)) => Some(Run::EditFile( + payload.path.clone(), + payload.line_number.clone(), + payload.cwd.clone(), + )), + _ => None, + }; match pty .spawn_terminal(terminal_action, client_or_tab_index) .with_context(err_context) @@ -191,6 +200,20 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) -> Result<()> { )])) .with_context(err_context)?; } + if let Some(originating_plugin) = + open_file_payload.and_then(|o| o.originating_plugin) + { + let update_event = + Event::EditPaneOpened(pid, originating_plugin.context.clone()); + pty.bus + .senders + .send_to_plugin(PluginInstruction::Update(vec![( + Some(originating_plugin.plugin_id), + Some(originating_plugin.client_id), + update_event, + )])) + .with_context(err_context)?; + } pty.bus .senders @@ -201,6 +224,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) -> Result<()> { hold_for_command, invoked_with, floating_pane_coordinates, + start_suppressed, client_or_tab_index, )) .with_context(err_context)?; @@ -218,6 +242,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) -> Result<()> { hold_for_command, invoked_with, floating_pane_coordinates, + start_suppressed, client_or_tab_index, )) .with_context(err_context)?; @@ -259,16 +284,17 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) -> Result<()> { ), _ => (false, None, name), }; - let invoked_with = - match &terminal_action { - Some(TerminalAction::RunCommand(run_command)) => { - Some(Run::Command(run_command.clone())) - }, - Some(TerminalAction::OpenFile(file, line_number, cwd)) => Some( - Run::EditFile(file.clone(), line_number.clone(), cwd.clone()), - ), - _ => None, - }; + let invoked_with = match &terminal_action { + Some(TerminalAction::RunCommand(run_command)) => { + Some(Run::Command(run_command.clone())) + }, + Some(TerminalAction::OpenFile(payload)) => Some(Run::EditFile( + payload.path.clone(), + payload.line_number.clone(), + payload.cwd.clone(), + )), + _ => None, + }; match pty .spawn_terminal(terminal_action, client_id_tab_index_or_pane_id) .with_context(err_context) @@ -324,7 +350,11 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box) -> Result<()> { || format!("failed to open in-place editor for client {}", client_id); match pty.spawn_terminal( - Some(TerminalAction::OpenFile(temp_file, line_number, None)), + Some(TerminalAction::OpenFile(OpenFilePayload::new( + temp_file, + line_number, + None, + ))), ClientTabIndexOrPaneId::ClientId(client_id), ) { Ok((pid, _starts_held)) => { @@ -761,13 +791,13 @@ impl Pty { TerminalAction::RunCommand(ref mut command) => { command.cwd = Some(cwd); }, - TerminalAction::OpenFile(ref _file, _line_number, ref mut edit_cwd) => { - match edit_cwd.as_mut() { + TerminalAction::OpenFile(ref mut payload) => { + match payload.cwd.as_mut() { Some(edit_cwd) => { *edit_cwd = cwd.join(&edit_cwd); }, None => { - let _ = edit_cwd.insert(cwd.clone()); + let _ = payload.cwd.insert(cwd.clone()); }, }; }, @@ -847,14 +877,24 @@ impl Pty { terminal_action }, }; - let (hold_on_start, hold_on_close, originating_plugin) = match &terminal_action { - TerminalAction::RunCommand(run_command) => ( - run_command.hold_on_start, - run_command.hold_on_close, - run_command.originating_plugin.clone(), - ), - _ => (false, false, None), - }; + let (hold_on_start, hold_on_close, originating_command_plugin, originating_edit_plugin) = + match &terminal_action { + TerminalAction::RunCommand(run_command) => ( + run_command.hold_on_start, + run_command.hold_on_close, + run_command.originating_plugin.clone(), + None, + ), + TerminalAction::OpenFile(open_file_payload) => ( + false, + false, + None, + open_file_payload.originating_plugin.clone(), + ), + _ => (false, false, None, None), + }; + // TODO: CONTINUE HERE - get originating_plugin also from TerminalAction::OpenFile and do + // the right thing in the quit_cb below if hold_on_start { // we don't actually open a terminal in this case, just wait for the user to run it @@ -869,22 +909,35 @@ impl Pty { return Ok((terminal_id, starts_held)); } - let originating_plugin = Arc::new(originating_plugin.clone()); + let originating_command_plugin = Arc::new(originating_command_plugin.clone()); + let originating_edit_plugin = Arc::new(originating_edit_plugin.clone()); let quit_cb = Box::new({ let senders = self.bus.senders.clone(); move |pane_id, exit_status, command| { // if this command originated in a plugin, we send the plugin an event letting it // know the command exited and some other useful information if let PaneId::Terminal(pane_id) = pane_id { - if let Some(originating_plugin) = originating_plugin.as_ref() { + if let Some(originating_command_plugin) = originating_command_plugin.as_ref() { let update_event = Event::CommandPaneExited( pane_id, exit_status, - originating_plugin.context.clone(), + originating_command_plugin.context.clone(), ); let _ = senders.send_to_plugin(PluginInstruction::Update(vec![( - Some(originating_plugin.plugin_id), - Some(originating_plugin.client_id), + Some(originating_command_plugin.plugin_id), + Some(originating_command_plugin.client_id), + update_event, + )])); + } + if let Some(originating_edit_plugin) = originating_edit_plugin.as_ref() { + let update_event = Event::EditPaneExited( + pane_id, + exit_status, + originating_edit_plugin.context.clone(), + ); + let _ = senders.send_to_plugin(PluginInstruction::Update(vec![( + Some(originating_edit_plugin.plugin_id), + Some(originating_edit_plugin.client_id), update_event, )])); } @@ -1200,7 +1253,11 @@ impl Pty { .context("no OS I/O interface found") .with_context(err_context)? .spawn_terminal( - TerminalAction::OpenFile(path_to_file, line_number, cwd), + TerminalAction::OpenFile(OpenFilePayload::new( + path_to_file, + line_number, + cwd, + )), quit_cb, self.default_editor.clone(), ) diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index ab5db049..a08ae99e 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -274,7 +274,7 @@ pub(crate) fn route_action( .send_to_screen(ScreenInstruction::TogglePaneFrames) .with_context(err_context)?; }, - Action::NewPane(direction, name) => { + Action::NewPane(direction, name, start_suppressed) => { let shell = default_shell.clone(); let pty_instr = match direction { Some(Direction::Left) => { @@ -295,22 +295,22 @@ pub(crate) fn route_action( None, name, None, + start_suppressed, ClientTabIndexOrPaneId::ClientId(client_id), ), }; senders.send_to_pty(pty_instr).with_context(err_context)?; }, Action::EditFile( - path_to_file, - line_number, - cwd, + open_file_payload, split_direction, should_float, should_open_in_place, + start_suppressed, floating_pane_coordinates, ) => { - let title = format!("Editing: {}", path_to_file.display()); - let open_file = TerminalAction::OpenFile(path_to_file, line_number, cwd); + let title = format!("Editing: {}", open_file_payload.path.display()); + let open_file = TerminalAction::OpenFile(open_file_payload); let pty_instr = match (split_direction, should_float, should_open_in_place) { (Some(Direction::Left), false, false) => { PtyInstruction::SpawnTerminalVertically(Some(open_file), Some(title), client_id) @@ -348,6 +348,7 @@ pub(crate) fn route_action( Some(should_float), Some(title), floating_pane_coordinates, + start_suppressed, ClientTabIndexOrPaneId::ClientId(client_id), ), }; @@ -394,6 +395,7 @@ pub(crate) fn route_action( Some(should_float), name, floating_pane_coordinates, + false, ClientTabIndexOrPaneId::ClientId(client_id), )) .with_context(err_context)?; @@ -447,6 +449,7 @@ pub(crate) fn route_action( Some(should_float), name, None, + false, ClientTabIndexOrPaneId::ClientId(client_id), ), }; @@ -496,6 +499,7 @@ pub(crate) fn route_action( None, None, None, + false, ClientTabIndexOrPaneId::ClientId(client_id), ), }; diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 189c2571..2484b3eb 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -152,6 +152,7 @@ pub enum ScreenInstruction { HoldForCommand, Option, // invoked with Option, + bool, // start suppressed ClientTabIndexOrPaneId, ), OpenInPlaceEditor(PaneId, ClientId), @@ -312,6 +313,7 @@ pub enum ScreenInstruction { u32, // plugin id Option, Option, // cwd + bool, // start suppressed Option, ), UpdatePluginLoadingStage(u32, LoadingIndication), // u32 - plugin_id @@ -1905,7 +1907,7 @@ impl Screen { tab_index_and_plugin_pane_id = Some((*tab_index, plugin_pane_id)); if move_to_focused_tab && focused_tab_index != *tab_index { plugin_pane_to_move_to_active_tab = - tab.extract_pane(plugin_pane_id, Some(client_id)); + tab.extract_pane(plugin_pane_id, false, Some(client_id)); } break; @@ -1992,7 +1994,7 @@ impl Screen { .with_context(err_context)?; let pane_to_break_is_floating = active_tab.are_floating_panes_visible(); let active_pane = active_tab - .close_pane(active_pane_id, false, Some(client_id)) + .extract_pane(active_pane_id, false, Some(client_id)) .with_context(err_context)?; let active_pane_run_instruction = active_pane.invoked_with().clone(); let tab_index = self.get_new_tab_index(); @@ -2053,7 +2055,7 @@ impl Screen { .with_context(err_context)?; let pane_to_break_is_floating = active_tab.are_floating_panes_visible(); let active_pane = active_tab - .close_pane(active_pane_id, false, Some(client_id)) + .extract_pane(active_pane_id, false, Some(client_id)) .with_context(err_context)?; (active_pane_id, active_pane, pane_to_break_is_floating) }; @@ -2456,6 +2458,7 @@ pub(crate) fn screen_thread_main( hold_for_command, invoked_with, floating_pane_coordinates, + start_suppressed, client_or_tab_index, ) => { match client_or_tab_index { @@ -2466,6 +2469,7 @@ pub(crate) fn screen_thread_main( should_float, invoked_with, floating_pane_coordinates, + start_suppressed, Some(client_id) ) }, ?); @@ -2491,6 +2495,7 @@ pub(crate) fn screen_thread_main( should_float, invoked_with, floating_pane_coordinates, + start_suppressed, None, )?; if let Some(hold_for_command) = hold_for_command { @@ -2999,6 +3004,7 @@ pub(crate) fn screen_thread_main( } }, } + screen.unblock_input()?; screen.log_and_report_session_state()?; }, @@ -3632,6 +3638,7 @@ pub(crate) fn screen_thread_main( plugin_id, pane_id_to_replace, cwd, + start_suppressed, client_id, ) => { let pane_title = pane_title.unwrap_or_else(|| { @@ -3676,6 +3683,7 @@ pub(crate) fn screen_thread_main( should_float, Some(run_plugin), None, + start_suppressed, Some(client_id), ) }, ?); @@ -3688,6 +3696,7 @@ pub(crate) fn screen_thread_main( should_float, Some(run_plugin), None, + start_suppressed, None, )?; } else { diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 92e9eeb9..6b7a2192 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -30,6 +30,7 @@ use self::clipboard::ClipboardProvider; use crate::{ os_input_output::ServerOsApi, output::{CharacterChunk, Output, SixelImageChunk}, + panes::floating_panes::floating_pane_grid::half_size_middle_geom, panes::sixel::SixelImageStore, panes::{FloatingPanes, TiledPanes}, panes::{LinkHandler, PaneId, PluginPane, TerminalPane}, @@ -985,7 +986,7 @@ impl Tab { if let Some(focused_floating_pane_id) = self.floating_panes.active_pane_id(client_id) { if self.tiled_panes.has_room_for_new_pane() { let floating_pane_to_embed = self - .close_pane(focused_floating_pane_id, true, Some(client_id)) + .extract_pane(focused_floating_pane_id, true, Some(client_id)) .with_context(|| format!( "failed to find floating pane (ID: {focused_floating_pane_id:?}) to embed for client {client_id}", )) @@ -1004,7 +1005,7 @@ impl Tab { return Ok(()); } if let Some(embedded_pane_to_float) = - self.close_pane(focused_pane_id, true, Some(client_id)) + self.extract_pane(focused_pane_id, true, Some(client_id)) { self.show_floating_panes(); self.add_floating_pane( @@ -1047,11 +1048,13 @@ impl Tab { Some(client_id) => ClientTabIndexOrPaneId::ClientId(client_id), None => ClientTabIndexOrPaneId::TabIndex(self.index), }; + let should_start_suppressed = false; let instruction = PtyInstruction::SpawnTerminal( default_shell, Some(should_float), name, None, + should_start_suppressed, client_id_or_tab_index, ); self.senders @@ -1071,6 +1074,7 @@ impl Tab { should_float: Option, invoked_with: Option, floating_pane_coordinates: Option, + start_suppressed: bool, client_id: Option, ) -> Result<()> { let err_context = || format!("failed to create new pane with id {pid:?}"); @@ -1081,7 +1085,7 @@ impl Tab { }; self.close_down_to_max_terminals() .with_context(err_context)?; - let new_pane = match pid { + let mut new_pane = match pid { PaneId::Terminal(term_pid) => { let next_terminal_position = self.get_next_terminal_position(); Box::new(TerminalPane::new( @@ -1128,7 +1132,32 @@ impl Tab { )) as Box }, }; - if self.floating_panes.panes_are_visible() { + + if start_suppressed { + // this pane needs to start in the background (suppressed), only accessible if a plugin takes it out + // of there in one way or another + // we need to do some bookkeeping for this pane, namely setting its geom and + // content_offset so that things will appear properly in the terminal - we set it to + // the default geom of the first floating pane - this is just in order to give it some + // reasonable size, when it is shown - if needed - it will be given the proper geom as if it were + // resized + let viewport = { self.viewport.borrow().clone() }; + let new_pane_geom = half_size_middle_geom(&viewport, 0); + new_pane.set_active_at(Instant::now()); + new_pane.set_geom(new_pane_geom); + new_pane.set_content_offset(Offset::frame(1)); + resize_pty!( + new_pane, + self.os_api, + self.senders, + self.character_cell_size + ) + .with_context(err_context)?; + let is_scrollback_editor = false; + self.suppressed_panes + .insert(pid, (is_scrollback_editor, new_pane)); + Ok(()) + } else if self.floating_panes.panes_are_visible() { self.add_floating_pane(new_pane, pid, floating_pane_coordinates, client_id) } else { self.add_tiled_pane(new_pane, pid, client_id) @@ -2448,8 +2477,14 @@ impl Tab { self.get_tiled_panes().map(|(&pid, _)| pid).collect() } pub fn get_all_pane_ids(&self) -> Vec { - // this is here just as a naming thing to make things more explicit - self.get_static_and_floating_pane_ids() + let mut static_and_floating_pane_ids = self.get_static_and_floating_pane_ids(); + let mut suppressed_pane_ids = self + .suppressed_panes + .values() + .map(|(_key, pane)| pane.pid()) + .collect(); + static_and_floating_pane_ids.append(&mut suppressed_pane_ids); + static_and_floating_pane_ids } pub fn get_static_and_floating_pane_ids(&self) -> Vec { self.tiled_panes @@ -2494,26 +2529,22 @@ impl Tab { id: PaneId, ignore_suppressed_panes: bool, client_id: Option, - ) -> Option> { + ) { // we need to ignore suppressed panes when we toggle a pane to be floating/embedded(tiled) // this is because in that case, while we do use this logic, we're not actually closing the // pane, we're moving it - // - // TODO: separate the "close_pane" logic and the "move_pane_somewhere_else" logic, they're - // overloaded here and that's not great if !ignore_suppressed_panes && self.suppressed_panes.contains_key(&id) { return match self.replace_pane_with_suppressed_pane(id) { - Ok(pane) => pane, + Ok(_pane) => {}, Err(e) => { Err::<(), _>(e) .with_context(|| format!("failed to close pane {:?}", id)) .non_fatal(); - None }, }; } if self.floating_panes.panes_contain(&id) { - let closed_pane = self.floating_panes.remove_pane(id); + let _closed_pane = self.floating_panes.remove_pane(id); self.floating_panes.move_clients_out_of_pane(id); if !self.floating_panes.has_panes() { self.hide_floating_panes(); @@ -2529,12 +2560,11 @@ impl Tab { // confusing let _ = self.next_swap_layout(client_id, false); } - closed_pane } else { if self.tiled_panes.fullscreen_is_active() { self.tiled_panes.unset_fullscreen(); } - let closed_pane = self.tiled_panes.remove_pane(id); + let _closed_pane = self.tiled_panes.remove_pane(id); self.set_force_render(); self.tiled_panes.set_force_render(); if self.auto_layout && !self.swap_layouts.is_tiled_damaged() { @@ -2543,14 +2573,30 @@ impl Tab { // confusing let _ = self.next_swap_layout(client_id, false); } - closed_pane - } + }; + let _ = self.senders.send_to_plugin(PluginInstruction::Update(vec![( + None, + None, + Event::PaneClosed(id.into()), + )])); } pub fn extract_pane( &mut self, id: PaneId, + ignore_suppressed_panes: bool, client_id: Option, ) -> Option> { + if !ignore_suppressed_panes && self.suppressed_panes.contains_key(&id) { + return match self.replace_pane_with_suppressed_pane(id) { + Ok(pane) => pane, + Err(e) => { + Err::<(), _>(e) + .with_context(|| format!("failed to close pane {:?}", id)) + .non_fatal(); + None + }, + }; + } if self.floating_panes.panes_contain(&id) { let closed_pane = self.floating_panes.remove_pane(id); self.floating_panes.move_clients_out_of_pane(id); @@ -2609,9 +2655,11 @@ impl Tab { if self.floating_panes.panes_contain(&id) { self.floating_panes .hold_pane(id, exit_status, is_first_run, run_command); - } else { + } else if self.tiled_panes.panes_contain(&id) { self.tiled_panes .hold_pane(id, exit_status, is_first_run, run_command); + } else if let Some(pane) = self.suppressed_panes.values_mut().find(|p| p.1.pid() == id) { + pane.1.hold(exit_status, is_first_run, run_command); } } pub fn replace_pane_with_suppressed_pane( @@ -3772,7 +3820,7 @@ impl Tab { // not take it out of there when another pane is closed (eg. like happens with the // scrollback editor), but it has to take itself out on its own (eg. a plugin using the // show_self() method) - if let Some(pane) = self.close_pane(pane_id, true, Some(client_id)) { + if let Some(pane) = self.extract_pane(pane_id, true, Some(client_id)) { let is_scrollback_editor = false; self.suppressed_panes .insert(pane_id, (is_scrollback_editor, pane)); diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index dae50f28..4d94a777 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -778,7 +778,7 @@ fn dump_screen() { ..Default::default() }); let new_pane_id = PaneId::Terminal(2); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.handle_pty_bytes(2, Vec::from("scratch".as_bytes())) .unwrap(); @@ -806,7 +806,7 @@ fn clear_screen() { ..Default::default() }); let new_pane_id = PaneId::Terminal(2); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.handle_pty_bytes(2, Vec::from("scratch".as_bytes())) .unwrap(); @@ -832,7 +832,7 @@ fn new_floating_pane() { let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.handle_pty_bytes( 2, @@ -860,7 +860,7 @@ fn floating_panes_persist_across_toggles() { let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); // here we send bytes to the pane when it's not visible to make sure they're still handled and @@ -892,7 +892,7 @@ fn toggle_floating_panes_off() { let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.handle_pty_bytes( 2, @@ -921,7 +921,7 @@ fn toggle_floating_panes_on() { let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.handle_pty_bytes( 2, @@ -955,16 +955,56 @@ fn five_new_floating_panes() { let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -999,8 +1039,16 @@ fn increase_floating_pane_size() { let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1029,8 +1077,16 @@ fn decrease_floating_pane_size() { let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1059,8 +1115,16 @@ fn resize_floating_pane_left() { let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1092,8 +1156,16 @@ fn resize_floating_pane_right() { let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1125,8 +1197,16 @@ fn resize_floating_pane_up() { let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1158,8 +1238,16 @@ fn resize_floating_pane_down() { let new_pane_id_1 = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1195,16 +1283,56 @@ fn move_floating_pane_focus_left() { let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1250,16 +1378,56 @@ fn move_floating_pane_focus_right() { let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1306,16 +1474,56 @@ fn move_floating_pane_focus_up() { let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1361,16 +1569,56 @@ fn move_floating_pane_focus_down() { let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1417,16 +1665,56 @@ fn move_floating_pane_focus_with_mouse() { let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1475,16 +1763,56 @@ fn move_pane_focus_with_mouse_to_non_floating_pane() { let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1533,16 +1861,56 @@ fn drag_pane_with_mouse() { let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1591,16 +1959,56 @@ fn mark_text_inside_floating_pane() { let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1657,16 +2065,56 @@ fn resize_tab_with_floating_panes() { let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1711,16 +2159,56 @@ fn shrink_whole_tab_with_floating_panes_horizontally_and_vertically() { let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1761,16 +2249,56 @@ fn shrink_whole_tab_with_floating_panes_horizontally_and_vertically_and_expand_b let new_pane_id_5 = PaneId::Terminal(6); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 2, Vec::from("\n\n\n I am scratch terminal".as_bytes()), @@ -1812,7 +2340,7 @@ fn embed_floating_pane() { let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.handle_pty_bytes( 2, @@ -1840,7 +2368,7 @@ fn float_embedded_pane() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.handle_pty_bytes( 2, @@ -1870,7 +2398,7 @@ fn embed_floating_pane_without_pane_frames() { let mut output = Output::default(); tab.set_pane_frames(false); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.handle_pty_bytes( 2, @@ -1899,7 +2427,7 @@ fn float_embedded_pane_without_pane_frames() { let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); tab.set_pane_frames(false); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.handle_pty_bytes( 2, @@ -2002,7 +2530,7 @@ fn rename_floating_pane() { let mut tab = create_new_tab(size, ModeInfo::default()); let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.handle_pty_bytes( 2, @@ -2098,7 +2626,7 @@ fn move_floating_pane_with_sixel_image() { let mut output = Output::new(sixel_image_store.clone(), character_cell_size, true); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); let fixture = read_fixture("sixel-image-500px.six"); tab.handle_pty_bytes(2, fixture).unwrap(); @@ -2136,7 +2664,7 @@ fn floating_pane_above_sixel_image() { let mut output = Output::new(sixel_image_store.clone(), character_cell_size, true); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); let fixture = read_fixture("sixel-image-500px.six"); tab.handle_pty_bytes(1, fixture).unwrap(); @@ -2194,7 +2722,7 @@ fn suppress_floating_pane() { let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.replace_active_pane_with_editor_pane(editor_pane_id, client_id) .unwrap(); @@ -2250,7 +2778,7 @@ fn close_suppressing_floating_pane() { let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.replace_active_pane_with_editor_pane(editor_pane_id, client_id) .unwrap(); @@ -2310,7 +2838,7 @@ fn suppress_floating_pane_embed_it_and_close_it() { let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.replace_active_pane_with_editor_pane(editor_pane_id, client_id) .unwrap(); @@ -2372,7 +2900,7 @@ fn resize_whole_tab_while_floting_pane_is_suppressed() { let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); tab.replace_active_pane_with_editor_pane(editor_pane_id, client_id) .unwrap(); @@ -2474,7 +3002,7 @@ fn enter_search_floating_pane() { let new_pane_id = PaneId::Terminal(2); let mut output = Output::default(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id, None, None, None, None, Some(client_id)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(client_id)) .unwrap(); let pane_content = read_fixture("grid_copy"); @@ -2979,8 +3507,16 @@ fn move_pane_focus_sends_tty_csi_event() { }); let mut tab = create_new_tab_with_os_api(size, ModeInfo::default(), &os_api); let new_pane_id_1 = PaneId::Terminal(2); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 1, // subscribe to focus events @@ -3014,10 +3550,26 @@ fn move_floating_pane_focus_sends_tty_csi_event() { let new_pane_id_2 = PaneId::Terminal(3); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 1, // subscribe to focus events @@ -3057,10 +3609,26 @@ fn toggle_floating_panes_on_sends_tty_csi_event() { let new_pane_id_2 = PaneId::Terminal(3); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.toggle_floating_panes(Some(client_id), None).unwrap(); tab.handle_pty_bytes( 1, @@ -3101,10 +3669,26 @@ fn toggle_floating_panes_off_sends_tty_csi_event() { let new_pane_id_2 = PaneId::Terminal(3); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_pty_bytes( 1, // subscribe to focus events @@ -3163,8 +3747,16 @@ fn can_swap_tiled_layout_at_runtime() { ); let new_pane_id_1 = PaneId::Terminal(2); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.next_swap_layout(Some(client_id), false).unwrap(); tab.render(&mut output).unwrap(); let snapshot = take_snapshot( @@ -3218,10 +3810,26 @@ fn can_swap_floating_layout_at_runtime() { let new_pane_id_2 = PaneId::Terminal(3); tab.toggle_floating_panes(Some(client_id), None).unwrap(); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.next_swap_layout(Some(client_id), false).unwrap(); tab.render(&mut output).unwrap(); let snapshot = take_snapshot( @@ -3271,8 +3879,16 @@ fn swapping_layouts_after_resize_snaps_to_current_layout() { ); let new_pane_id_1 = PaneId::Terminal(2); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.next_swap_layout(Some(client_id), false).unwrap(); tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) .unwrap(); @@ -3321,12 +3937,36 @@ fn swap_tiled_layout_with_stacked_children() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.render(&mut output).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -3368,12 +4008,36 @@ fn swap_tiled_layout_with_only_stacked_children() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.render(&mut output).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -3418,12 +4082,36 @@ fn swap_tiled_layout_with_stacked_children_and_no_pane_frames() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.render(&mut output).unwrap(); let snapshot = take_snapshot( output.serialize().unwrap().get(&client_id).unwrap(), @@ -3468,12 +4156,36 @@ fn move_focus_up_with_stacked_panes() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.move_focus_right(client_id); tab.move_focus_up(client_id); tab.render(&mut output).unwrap(); @@ -3520,12 +4232,36 @@ fn move_focus_down_with_stacked_panes() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.move_focus_right(client_id); tab.move_focus_up(client_id); tab.move_focus_down(client_id); @@ -3580,6 +4316,7 @@ fn move_focus_right_into_stacked_panes() { None, None, None, + false, Some(client_id), ) .unwrap(); @@ -3648,6 +4385,7 @@ fn move_focus_left_into_stacked_panes() { None, None, None, + false, Some(client_id), ) .unwrap(); @@ -3718,6 +4456,7 @@ fn move_focus_up_into_stacked_panes() { None, None, None, + false, Some(client_id), ) .unwrap(); @@ -3789,6 +4528,7 @@ fn move_focus_down_into_stacked_panes() { None, None, None, + false, Some(client_id), ) .unwrap(); @@ -3850,12 +4590,36 @@ fn close_main_stacked_pane() { let new_pane_id_2 = PaneId::Terminal(3); let new_pane_id_3 = PaneId::Terminal(4); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.close_pane(new_pane_id_2, false, None); tab.render(&mut output).unwrap(); let snapshot = take_snapshot( @@ -3903,16 +4667,56 @@ fn close_main_stacked_pane_in_mid_stack() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.move_focus_right(client_id); tab.move_focus_up(client_id); tab.move_focus_up(client_id); @@ -3963,16 +4767,56 @@ fn close_one_liner_stacked_pane_below_main_pane() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.move_focus_left(client_id); tab.move_focus_right(client_id); tab.move_focus_up(client_id); @@ -4024,16 +4868,56 @@ fn close_one_liner_stacked_pane_above_main_pane() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.move_focus_right(client_id); tab.move_focus_up(client_id); tab.move_focus_up(client_id); @@ -4084,16 +4968,56 @@ fn can_increase_size_of_main_pane_in_stack_horizontally() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.move_focus_right(client_id); tab.resize( client_id, @@ -4148,16 +5072,56 @@ fn can_increase_size_of_main_pane_in_stack_vertically() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.move_focus_right(client_id); tab.resize( client_id, @@ -4212,16 +5176,56 @@ fn can_increase_size_of_main_pane_in_stack_non_directionally() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); let _ = tab.move_focus_up(client_id); let _ = tab.move_focus_right(client_id); tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) @@ -4272,16 +5276,56 @@ fn can_increase_size_into_pane_stack_horizontally() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.resize( client_id, ResizeStrategy::new(Resize::Increase, Some(Direction::Right)), @@ -4335,16 +5379,56 @@ fn can_increase_size_into_pane_stack_vertically() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.move_focus_right(client_id); tab.move_focus_down(client_id); tab.resize( @@ -4400,16 +5484,56 @@ fn can_increase_size_into_pane_stack_non_directionally() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); let _ = tab.move_focus_up(client_id); tab.resize(client_id, ResizeStrategy::new(Resize::Increase, None)) .unwrap(); @@ -4459,16 +5583,56 @@ fn decreasing_size_of_whole_tab_treats_stacked_panes_properly() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.resize_whole_tab(Size { cols: 100, rows: 10, @@ -4519,16 +5683,56 @@ fn increasing_size_of_whole_tab_treats_stacked_panes_properly() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.resize_whole_tab(Size { cols: 100, rows: 10, @@ -4584,16 +5788,56 @@ fn cannot_decrease_stack_size_beyond_minimum_height() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.move_focus_down(client_id); for _ in 0..6 { tab.resize( @@ -4649,16 +5893,56 @@ fn focus_stacked_pane_over_flexible_pane_with_the_mouse() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_left_click(&Position::new(1, 71), client_id) .unwrap(); tab.render(&mut output).unwrap(); @@ -4708,16 +5992,56 @@ fn focus_stacked_pane_under_flexible_pane_with_the_mouse() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_left_click(&Position::new(1, 71), client_id) .unwrap(); tab.handle_left_click(&Position::new(9, 71), client_id) @@ -4769,16 +6093,56 @@ fn close_stacked_pane_with_previously_focused_other_pane() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.handle_left_click(&Position::new(2, 71), client_id) .unwrap(); tab.handle_left_click(&Position::new(1, 71), client_id) @@ -4836,16 +6200,56 @@ fn close_pane_near_stacked_panes() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.close_pane(PaneId::Terminal(6), false, None); tab.render(&mut output).unwrap(); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( @@ -4900,16 +6304,56 @@ fn focus_next_pane_expands_stacked_panes() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.move_focus_left(client_id); tab.focus_next_pane(client_id); tab.render(&mut output).unwrap(); @@ -4960,16 +6404,56 @@ fn stacked_panes_can_become_fullscreen() { let new_pane_id_4 = PaneId::Terminal(5); let new_pane_id_5 = PaneId::Terminal(6); - tab.new_pane(new_pane_id_1, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_2, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_3, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_4, None, None, None, None, Some(client_id)) - .unwrap(); - tab.new_pane(new_pane_id_5, None, None, None, None, Some(client_id)) - .unwrap(); + tab.new_pane( + new_pane_id_1, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_2, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_3, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_4, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + tab.new_pane( + new_pane_id_5, + None, + None, + None, + None, + false, + Some(client_id), + ) + .unwrap(); tab.move_focus_up(client_id); tab.toggle_active_pane_fullscreen(client_id); tab.render(&mut output).unwrap(); @@ -5608,6 +7092,7 @@ fn new_pane_in_auto_layout() { None, None, None, + false, Some(client_id), ) .unwrap(); @@ -6534,6 +8019,7 @@ fn new_floating_pane_in_auto_layout() { Some(should_float), None, None, + false, Some(client_id), ) .unwrap(); diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs index 5ad917ea..a0c04088 100644 --- a/zellij-server/src/tab/unit/tab_tests.rs +++ b/zellij-server/src/tab/unit/tab_tests.rs @@ -571,7 +571,7 @@ fn split_largest_pane() { let mut tab = create_new_tab(size); for i in 2..5 { let new_pane_id = PaneId::Terminal(i); - tab.new_pane(new_pane_id, None, None, None, None, Some(1)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(1)) .unwrap(); } assert_eq!(tab.tiled_panes.panes.len(), 4, "The tab has four panes"); @@ -777,7 +777,7 @@ pub fn cannot_split_panes_horizontally_when_active_pane_is_too_small() { pub fn cannot_split_largest_pane_when_there_is_no_room() { let size = Size { cols: 8, rows: 4 }; let mut tab = create_new_tab(size); - tab.new_pane(PaneId::Terminal(2), None, None, None, None, Some(1)) + tab.new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) .unwrap(); assert_eq!( tab.tiled_panes.panes.len(), @@ -821,7 +821,7 @@ pub fn toggle_focused_pane_fullscreen() { let mut tab = create_new_tab(size); for i in 2..5 { let new_pane_id = PaneId::Terminal(i); - tab.new_pane(new_pane_id, None, None, None, None, Some(1)) + tab.new_pane(new_pane_id, None, None, None, None, false, Some(1)) .unwrap(); } tab.toggle_active_pane_fullscreen(1); @@ -896,16 +896,16 @@ fn switch_to_next_pane_fullscreen() { let mut active_tab = create_new_tab(size); active_tab - .new_pane(PaneId::Terminal(1), None, None, None, None, Some(1)) + .new_pane(PaneId::Terminal(1), None, None, None, None, false, Some(1)) .unwrap(); active_tab - .new_pane(PaneId::Terminal(2), None, None, None, None, Some(1)) + .new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) .unwrap(); active_tab - .new_pane(PaneId::Terminal(3), None, None, None, None, Some(1)) + .new_pane(PaneId::Terminal(3), None, None, None, None, false, Some(1)) .unwrap(); active_tab - .new_pane(PaneId::Terminal(4), None, None, None, None, Some(1)) + .new_pane(PaneId::Terminal(4), None, None, None, None, false, Some(1)) .unwrap(); active_tab.toggle_active_pane_fullscreen(1); @@ -936,16 +936,16 @@ fn switch_to_prev_pane_fullscreen() { //testing four consecutive switches in fullscreen mode active_tab - .new_pane(PaneId::Terminal(1), None, None, None, None, Some(1)) + .new_pane(PaneId::Terminal(1), None, None, None, None, false, Some(1)) .unwrap(); active_tab - .new_pane(PaneId::Terminal(2), None, None, None, None, Some(1)) + .new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) .unwrap(); active_tab - .new_pane(PaneId::Terminal(3), None, None, None, None, Some(1)) + .new_pane(PaneId::Terminal(3), None, None, None, None, false, Some(1)) .unwrap(); active_tab - .new_pane(PaneId::Terminal(4), None, None, None, None, Some(1)) + .new_pane(PaneId::Terminal(4), None, None, None, None, false, Some(1)) .unwrap(); active_tab.toggle_active_pane_fullscreen(1); // order is now 1 2 3 4 @@ -14427,7 +14427,7 @@ fn correctly_resize_frameless_panes_on_pane_close() { let content_size = (pane.get_content_columns(), pane.get_content_rows()); assert_eq!(content_size, (cols, rows)); - tab.new_pane(PaneId::Terminal(2), None, None, None, None, Some(1)) + tab.new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) .unwrap(); tab.close_pane(PaneId::Terminal(2), true, None); diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 09d2d794..1d3c1405 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -1166,7 +1166,7 @@ fn switch_to_tab_with_fullscreen() { { let active_tab = screen.get_active_tab_mut(1).unwrap(); active_tab - .new_pane(PaneId::Terminal(2), None, None, None, None, Some(1)) + .new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) .unwrap(); active_tab.toggle_active_pane_fullscreen(1); } @@ -1281,7 +1281,7 @@ fn attach_after_first_tab_closed() { { let active_tab = screen.get_active_tab_mut(1).unwrap(); active_tab - .new_pane(PaneId::Terminal(2), None, None, None, None, Some(1)) + .new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) .unwrap(); active_tab.toggle_active_pane_fullscreen(1); } @@ -1315,6 +1315,7 @@ fn open_new_floating_pane_with_custom_coordinates() { width: Some(SplitSize::Percent(1)), height: Some(SplitSize::Fixed(2)), }), + false, Some(1), ) .unwrap(); @@ -1348,6 +1349,7 @@ fn open_new_floating_pane_with_custom_coordinates_exceeding_viewport() { width: Some(SplitSize::Fixed(10)), height: Some(SplitSize::Fixed(10)), }), + false, Some(1), ) .unwrap(); diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap index f4e2645a..6ed151b4 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2031 +assertion_line: 2389 expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" --- -[SpawnTerminal(Some(OpenFile("/file/to/edit", None, Some("."))), Some(false), Some("Editing: /file/to/edit"), None, ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] +[SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some(false), Some("Editing: /file/to/edit"), None, false, ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap index e5b22c9f..a6ad8ea0 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2065 +assertion_line: 2427 expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" --- -[SpawnTerminal(Some(OpenFile("/file/to/edit", Some(100), Some("."))), Some(false), Some("Editing: /file/to/edit"), None, ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] +[SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: Some(100), cwd: Some("."), originating_plugin: None })), Some(false), Some("Editing: /file/to/edit"), None, false, ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap index 7b3459d5..fa4e0e3d 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2178 +assertion_line: 2465 expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" --- -[SpawnTerminalHorizontally(Some(OpenFile("/file/to/edit", None, Some("."))), Some("Editing: /file/to/edit"), 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] +[SpawnTerminalHorizontally(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), 10), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap index 39b5ea9d..bf271501 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 1911 +assertion_line: 2222 expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" --- -[SpawnTerminal(None, Some(false), None, None, ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] +[SpawnTerminal(None, Some(false), None, None, false, ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap index a1fd2b91..be12965c 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2349 +assertion_line: 2351 expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" --- -[SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None })), Some(true), None, Some(FloatingPaneCoordinates { x: Some(Fixed(10)), y: None, width: Some(Percent(20)), height: None }), ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] +[SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None })), Some(true), None, Some(FloatingPaneCoordinates { x: Some(Fixed(10)), y: None, width: Some(Percent(20)), height: None }), false, ClientId(10)), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit] diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index 61e193c0..1f30d570 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -76,24 +76,28 @@ pub fn get_zellij_version() -> String { // Host Functions /// Open a file in the user's default `$EDITOR` in a new pane -pub fn open_file(file_to_open: FileToOpen) { - let plugin_command = PluginCommand::OpenFile(file_to_open); +pub fn open_file(file_to_open: FileToOpen, context: BTreeMap) { + let plugin_command = PluginCommand::OpenFile(file_to_open, context); let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); object_to_stdout(&protobuf_plugin_command.encode_to_vec()); unsafe { host_run_plugin_command() }; } /// Open a file in the user's default `$EDITOR` in a new floating pane -pub fn open_file_floating(file_to_open: FileToOpen, coordinates: Option) { - let plugin_command = PluginCommand::OpenFileFloating(file_to_open, coordinates); +pub fn open_file_floating( + file_to_open: FileToOpen, + coordinates: Option, + context: BTreeMap, +) { + let plugin_command = PluginCommand::OpenFileFloating(file_to_open, coordinates, context); let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); object_to_stdout(&protobuf_plugin_command.encode_to_vec()); unsafe { host_run_plugin_command() }; } /// Open a file in the user's default `$EDITOR`, replacing the focused pane -pub fn open_file_in_place(file_to_open: FileToOpen) { - let plugin_command = PluginCommand::OpenFileInPlace(file_to_open); +pub fn open_file_in_place(file_to_open: FileToOpen, context: BTreeMap) { + let plugin_command = PluginCommand::OpenFileInPlace(file_to_open, context); let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); object_to_stdout(&protobuf_plugin_command.encode_to_vec()); unsafe { host_run_plugin_command() }; @@ -159,6 +163,17 @@ pub fn open_command_pane_in_place(command_to_run: CommandToRun, context: BTreeMa unsafe { host_run_plugin_command() }; } +/// Open a new hidden (background) command pane with the specified command and args (this sort of pane allows the user to control the command, re-run it and see its exit status through the Zellij UI). +pub fn open_command_pane_background( + command_to_run: CommandToRun, + context: BTreeMap, +) { + let plugin_command = PluginCommand::OpenCommandPaneBackground(command_to_run, context); + let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); + object_to_stdout(&protobuf_plugin_command.encode_to_vec()); + unsafe { host_run_plugin_command() }; +} + /// Change the focused tab to the specified index (corresponding with the default tab names, to starting at `1`, `0` will be considered as `1`). pub fn switch_tab_to(tab_idx: u32) { let plugin_command = PluginCommand::SwitchTabTo(tab_idx); diff --git a/zellij-utils/assets/prost/api.event.rs b/zellij-utils/assets/prost/api.event.rs index 10de020d..988339bf 100644 --- a/zellij-utils/assets/prost/api.event.rs +++ b/zellij-utils/assets/prost/api.event.rs @@ -11,7 +11,7 @@ pub struct Event { pub name: i32, #[prost( oneof = "event::Payload", - tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17" + tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20" )] pub payload: ::core::option::Option, } @@ -52,10 +52,31 @@ pub mod event { CommandPaneOpenedPayload(super::CommandPaneOpenedPayload), #[prost(message, tag = "17")] CommandPaneExitedPayload(super::CommandPaneExitedPayload), + #[prost(message, tag = "18")] + PaneClosedPayload(super::PaneClosedPayload), + #[prost(message, tag = "19")] + EditPaneOpenedPayload(super::EditPaneOpenedPayload), + #[prost(message, tag = "20")] + EditPaneExitedPayload(super::EditPaneExitedPayload), } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct PaneClosedPayload { + #[prost(message, optional, tag = "1")] + pub pane_id: ::core::option::Option, +} +/// duplicate of plugin_command.PaneId because protobuffs don't like recursive imports +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PaneId { + #[prost(enumeration = "PaneType", tag = "1")] + pub pane_type: i32, + #[prost(uint32, tag = "2")] + pub id: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct CommandPaneOpenedPayload { #[prost(uint32, tag = "1")] pub terminal_pane_id: u32, @@ -64,6 +85,14 @@ pub struct CommandPaneOpenedPayload { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct EditPaneOpenedPayload { + #[prost(uint32, tag = "1")] + pub terminal_pane_id: u32, + #[prost(message, repeated, tag = "2")] + pub context: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct CommandPaneExitedPayload { #[prost(uint32, tag = "1")] pub terminal_pane_id: u32, @@ -74,6 +103,16 @@ pub struct CommandPaneExitedPayload { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct EditPaneExitedPayload { + #[prost(uint32, tag = "1")] + pub terminal_pane_id: u32, + #[prost(int32, optional, tag = "2")] + pub exit_code: ::core::option::Option, + #[prost(message, repeated, tag = "3")] + pub context: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct SessionUpdatePayload { #[prost(message, repeated, tag = "1")] pub session_manifests: ::prost::alloc::vec::Vec, @@ -373,6 +412,9 @@ pub enum EventType { WebRequestResult = 18, CommandPaneOpened = 19, CommandPaneExited = 20, + PaneClosed = 21, + EditPaneOpened = 22, + EditPaneExited = 23, } impl EventType { /// String value of the enum field names used in the ProtoBuf definition. @@ -402,6 +444,9 @@ impl EventType { EventType::WebRequestResult => "WebRequestResult", EventType::CommandPaneOpened => "CommandPaneOpened", EventType::CommandPaneExited => "CommandPaneExited", + EventType::PaneClosed => "PaneClosed", + EventType::EditPaneOpened => "EditPaneOpened", + EventType::EditPaneExited => "EditPaneExited", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -428,6 +473,36 @@ impl EventType { "WebRequestResult" => Some(Self::WebRequestResult), "CommandPaneOpened" => Some(Self::CommandPaneOpened), "CommandPaneExited" => Some(Self::CommandPaneExited), + "PaneClosed" => Some(Self::PaneClosed), + "EditPaneOpened" => Some(Self::EditPaneOpened), + "EditPaneExited" => Some(Self::EditPaneExited), + _ => None, + } + } +} +/// duplicate of plugin_command.PaneType because protobuffs don't like recursive imports +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum PaneType { + Terminal = 0, + Plugin = 1, +} +impl PaneType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + PaneType::Terminal => "Terminal", + PaneType::Plugin => "Plugin", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Terminal" => Some(Self::Terminal), + "Plugin" => Some(Self::Plugin), _ => None, } } diff --git a/zellij-utils/assets/prost/api.plugin_command.rs b/zellij-utils/assets/prost/api.plugin_command.rs index 9ac2d7bf..fa2a9920 100644 --- a/zellij-utils/assets/prost/api.plugin_command.rs +++ b/zellij-utils/assets/prost/api.plugin_command.rs @@ -5,7 +5,7 @@ pub struct PluginCommand { pub name: i32, #[prost( oneof = "plugin_command::Payload", - tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65" + tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66" )] pub payload: ::core::option::Option, } @@ -124,6 +124,8 @@ pub mod plugin_command { HidePaneWithIdPayload(super::HidePaneWithIdPayload), #[prost(message, tag = "65")] ShowPaneWithIdPayload(super::ShowPaneWithIdPayload), + #[prost(message, tag = "66")] + OpenCommandPaneBackgroundPayload(super::OpenCommandPanePayload), } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -245,6 +247,8 @@ pub struct OpenFilePayload { pub file_to_open: ::core::option::Option, #[prost(message, optional, tag = "2")] pub floating_pane_coordinates: ::core::option::Option, + #[prost(message, repeated, tag = "3")] + pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -456,6 +460,7 @@ pub enum CommandName { Reconfigure = 87, HidePaneWithId = 88, ShowPaneWithId = 89, + OpenCommandPaneBackground = 90, } impl CommandName { /// String value of the enum field names used in the ProtoBuf definition. @@ -554,6 +559,7 @@ impl CommandName { CommandName::Reconfigure => "Reconfigure", CommandName::HidePaneWithId => "HidePaneWithId", CommandName::ShowPaneWithId => "ShowPaneWithId", + CommandName::OpenCommandPaneBackground => "OpenCommandPaneBackground", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -649,6 +655,7 @@ impl CommandName { "Reconfigure" => Some(Self::Reconfigure), "HidePaneWithId" => Some(Self::HidePaneWithId), "ShowPaneWithId" => Some(Self::ShowPaneWithId), + "OpenCommandPaneBackground" => Some(Self::OpenCommandPaneBackground), _ => None, } } diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index cb27a706..f968631f 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -915,7 +915,10 @@ pub enum Event { // context CommandPaneOpened(u32, Context), // u32 - terminal_pane_id CommandPaneExited(u32, Option, Context), // u32 - terminal_pane_id, Option - - // exit_code + // exit_code + PaneClosed(PaneId), + EditPaneOpened(u32, Context), // u32 - terminal_pane_id + EditPaneExited(u32, Option, Context), // u32 - terminal_pane_id, Option - exit code } #[derive( @@ -1446,7 +1449,7 @@ pub struct NewPluginArgs { pub skip_cache: bool, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum PaneId { Terminal(u32), Plugin(u32), @@ -1702,8 +1705,8 @@ pub enum PluginCommand { SetSelectable(bool), GetPluginIds, GetZellijVersion, - OpenFile(FileToOpen), - OpenFileFloating(FileToOpen, Option), + OpenFile(FileToOpen, Context), + OpenFileFloating(FileToOpen, Option, Context), OpenTerminal(FileToOpen), // only used for the path as cwd OpenTerminalFloating(FileToOpen, Option), // only used for the path as cwd OpenCommandPane(CommandToRun, Context), @@ -1768,7 +1771,7 @@ pub enum PluginCommand { DeleteDeadSession(String), // String -> session name DeleteAllDeadSessions, // String -> session name OpenTerminalInPlace(FileToOpen), // only used for the path as cwd - OpenFileInPlace(FileToOpen), + OpenFileInPlace(FileToOpen, Context), OpenCommandPaneInPlace(CommandToRun, Context), RunCommand( Vec, // command @@ -1798,4 +1801,5 @@ pub enum PluginCommand { Reconfigure(String), // String -> stringified configuration HidePaneWithId(PaneId), ShowPaneWithId(PaneId, bool), // bool -> should_float_if_hidden + OpenCommandPaneBackground(CommandToRun, Context), } diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 50bcf2af..81572995 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -1,6 +1,6 @@ //! Definition of the actions that can be bound to keys. -use super::command::RunCommandAction; +use super::command::{OpenFilePayload, RunCommandAction}; use super::layout::{ FloatingPaneLayout, Layout, PluginAlias, RunPlugin, RunPluginLocation, RunPluginOrAlias, SwapFloatingLayout, SwapTiledLayout, TiledPaneLayout, @@ -157,17 +157,17 @@ pub enum Action { ToggleActiveSyncTab, /// Open a new pane in the specified direction (relative to focus). /// If no direction is specified, will try to use the biggest available space. - NewPane(Option, Option), // String is an optional pane name - /// Open the file in a new pane using the default editor + NewPane(Option, Option, bool), // String is an optional pane name + /// Open the file in a new pane using the default editor, bool -> start suppressed EditFile( - PathBuf, - Option, - Option, + OpenFilePayload, Option, bool, bool, + bool, Option, - ), // usize is an optional line number, Option is an optional cwd, bool is floating true/false, second bool is in_place + ), // bool is floating true/false, second bool is in_place + // third bool is start_suppressed /// Open a new floating pane NewFloatingPane( Option, @@ -485,13 +485,13 @@ impl Action { file = cwd.join(file); } } + let start_suppressed = false; Ok(vec![Action::EditFile( - file, - line_number, - cwd, + OpenFilePayload::new(file, line_number, cwd), direction, floating, in_place, + start_suppressed, FloatingPaneCoordinates::new(x, y, width, height), )]) }, diff --git a/zellij-utils/src/input/command.rs b/zellij-utils/src/input/command.rs index 2b860f66..d1ec27c2 100644 --- a/zellij-utils/src/input/command.rs +++ b/zellij-utils/src/input/command.rs @@ -5,16 +5,15 @@ use std::path::PathBuf; #[derive(Debug, Clone)] pub enum TerminalAction { - OpenFile(PathBuf, Option, Option), // path to file (should be absolute), optional line_number and an - // optional cwd + OpenFile(OpenFilePayload), RunCommand(RunCommand), } impl TerminalAction { pub fn change_cwd(&mut self, new_cwd: PathBuf) { match self { - TerminalAction::OpenFile(_, _, cwd) => { - *cwd = Some(new_cwd); + TerminalAction::OpenFile(open_file_payload) => { + open_file_payload.cwd = Some(new_cwd); }, TerminalAction::RunCommand(run_command) => { run_command.cwd = Some(new_cwd); @@ -23,6 +22,29 @@ impl TerminalAction { } } +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct OpenFilePayload { + pub path: PathBuf, + pub line_number: Option, + pub cwd: Option, + pub originating_plugin: Option, +} + +impl OpenFilePayload { + pub fn new(path: PathBuf, line_number: Option, cwd: Option) -> Self { + OpenFilePayload { + path, + line_number, + cwd, + originating_plugin: None, + } + } + pub fn with_originating_plugin(mut self, originating_plugin: OriginatingPlugin) -> Self { + self.originating_plugin = Some(originating_plugin); + self + } +} + #[derive(Clone, Debug, Deserialize, Default, Serialize, PartialEq, Eq)] pub struct RunCommand { #[serde(alias = "cmd")] diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index b22c8112..37d02081 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -507,7 +507,7 @@ impl Action { "DumpLayout" => Ok(Action::DumpLayout), "NewPane" => { if string.is_empty() { - return Ok(Action::NewPane(None, None)); + return Ok(Action::NewPane(None, None, false)); } else { let direction = Direction::from_str(string.as_str()).map_err(|_| { ConfigError::new_kdl_error( @@ -516,7 +516,7 @@ impl Action { action_node.span().len(), ) })?; - Ok(Action::NewPane(Some(direction), None)) + Ok(Action::NewPane(Some(direction), None, false)) } }, "SearchToggleOption" => { diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs index b206dba0..a2f5bd76 100644 --- a/zellij-utils/src/plugin_api/action.rs +++ b/zellij-utils/src/plugin_api/action.rs @@ -17,7 +17,7 @@ use crate::data::{Direction, InputMode, ResizeStrategy}; use crate::errors::prelude::*; use crate::input::actions::Action; use crate::input::actions::{SearchDirection, SearchOption}; -use crate::input::command::RunCommandAction; +use crate::input::command::{OpenFilePayload, RunCommandAction}; use crate::input::layout::{ PluginUserConfiguration, RunPlugin, RunPluginLocation, RunPluginOrAlias, }; @@ -220,7 +220,7 @@ impl TryFrom for Action { .and_then(|d| ProtobufResizeDirection::from_i32(d)) .and_then(|d| d.try_into().ok()); let pane_name = payload.pane_name; - Ok(Action::NewPane(direction, pane_name)) + Ok(Action::NewPane(direction, pane_name, false)) }, _ => Err("Wrong payload for Action::NewPane"), }, @@ -236,12 +236,11 @@ impl TryFrom for Action { let should_float = payload.should_float; let should_be_in_place = false; Ok(Action::EditFile( - file_to_edit, - line_number, - cwd, + OpenFilePayload::new(file_to_edit, line_number, cwd), direction, should_float, should_be_in_place, + false, None, )) }, @@ -885,7 +884,7 @@ impl TryFrom for ProtobufAction { name: ProtobufActionName::ToggleActiveSyncTab as i32, optional_payload: None, }), - Action::NewPane(direction, new_pane_name) => { + Action::NewPane(direction, new_pane_name, _start_suppressed) => { let direction = direction.and_then(|direction| { let protobuf_direction: ProtobufResizeDirection = direction.try_into().ok()?; Some(protobuf_direction as i32) @@ -899,20 +898,19 @@ impl TryFrom for ProtobufAction { }) }, Action::EditFile( - path_to_file, - line_number, - cwd, + open_file_payload, direction, should_float, _should_be_in_place, _floating_pane_coordinates, + _start_suppressed, ) => { - let file_to_edit = path_to_file.display().to_string(); - let cwd = cwd.map(|cwd| cwd.display().to_string()); + let file_to_edit = open_file_payload.path.display().to_string(); + let cwd = open_file_payload.cwd.map(|cwd| cwd.display().to_string()); let direction: Option = direction .and_then(|d| ProtobufResizeDirection::try_from(d).ok()) .map(|d| d as i32); - let line_number = line_number.map(|l| l as u32); + let line_number = open_file_payload.line_number.map(|l| l as u32); Ok(ProtobufAction { name: ProtobufActionName::EditFile as i32, optional_payload: Some(OptionalPayload::EditFilePayload(EditFilePayload { diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto index ac1bdd29..1be5ced6 100644 --- a/zellij-utils/src/plugin_api/event.proto +++ b/zellij-utils/src/plugin_api/event.proto @@ -44,6 +44,9 @@ enum EventType { WebRequestResult = 18; CommandPaneOpened = 19; CommandPaneExited = 20; + PaneClosed = 21; + EditPaneOpened = 22; + EditPaneExited = 23; } message EventNameList { @@ -69,20 +72,50 @@ message Event { WebRequestResultPayload web_request_result_payload = 15; CommandPaneOpenedPayload command_pane_opened_payload = 16; CommandPaneExitedPayload command_pane_exited_payload = 17; + PaneClosedPayload pane_closed_payload = 18; + EditPaneOpenedPayload edit_pane_opened_payload = 19; + EditPaneExitedPayload edit_pane_exited_payload = 20; } } +message PaneClosedPayload { + PaneId pane_id = 1; +} + +// duplicate of plugin_command.PaneId because protobuffs don't like recursive imports +message PaneId { + PaneType pane_type = 1; + uint32 id = 2; +} + +// duplicate of plugin_command.PaneType because protobuffs don't like recursive imports +enum PaneType { + Terminal = 0; + Plugin = 1; +} + message CommandPaneOpenedPayload { uint32 terminal_pane_id = 1; repeated ContextItem context = 2; } +message EditPaneOpenedPayload { + uint32 terminal_pane_id = 1; + repeated ContextItem context = 2; +} + message CommandPaneExitedPayload { uint32 terminal_pane_id = 1; optional int32 exit_code = 2; repeated ContextItem context = 3; } +message EditPaneExitedPayload { + uint32 terminal_pane_id = 1; + optional int32 exit_code = 2; + repeated ContextItem context = 3; +} + message SessionUpdatePayload { repeated SessionManifest session_manifests = 1; repeated ResurrectableSession resurrectable_sessions = 2; diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs index c10c6ec0..9dad19e9 100644 --- a/zellij-utils/src/plugin_api/event.rs +++ b/zellij-utils/src/plugin_api/event.rs @@ -6,7 +6,8 @@ pub use super::generated_api::api::{ EventType as ProtobufEventType, FileMetadata as ProtobufFileMetadata, InputModeKeybinds as ProtobufInputModeKeybinds, KeyBind as ProtobufKeyBind, LayoutInfo as ProtobufLayoutInfo, ModeUpdatePayload as ProtobufModeUpdatePayload, - PaneInfo as ProtobufPaneInfo, PaneManifest as ProtobufPaneManifest, + PaneId as ProtobufPaneId, PaneInfo as ProtobufPaneInfo, + PaneManifest as ProtobufPaneManifest, PaneType as ProtobufPaneType, ResurrectableSession as ProtobufResurrectableSession, SessionManifest as ProtobufSessionManifest, TabInfo as ProtobufTabInfo, *, }, @@ -17,8 +18,8 @@ pub use super::generated_api::api::{ #[allow(hidden_glob_reexports)] use crate::data::{ CopyDestination, Event, EventType, FileMetadata, InputMode, KeyWithModifier, LayoutInfo, - ModeInfo, Mouse, PaneInfo, PaneManifest, PermissionStatus, PluginCapabilities, SessionInfo, - Style, TabInfo, + ModeInfo, Mouse, PaneId, PaneInfo, PaneManifest, PermissionStatus, PluginCapabilities, + SessionInfo, Style, TabInfo, }; use crate::errors::prelude::*; @@ -262,6 +263,42 @@ impl TryFrom for Event { )), _ => Err("Malformed payload for the CommandPaneExited Event"), }, + Some(ProtobufEventType::PaneClosed) => match protobuf_event.payload { + Some(ProtobufEventPayload::PaneClosedPayload(pane_closed_payload)) => { + let pane_id = pane_closed_payload + .pane_id + .ok_or("Malformed payload for the PaneClosed Event")?; + Ok(Event::PaneClosed(PaneId::try_from(pane_id)?)) + }, + _ => Err("Malformed payload for the PaneClosed Event"), + }, + Some(ProtobufEventType::EditPaneOpened) => match protobuf_event.payload { + Some(ProtobufEventPayload::EditPaneOpenedPayload(command_pane_opened_payload)) => { + Ok(Event::EditPaneOpened( + command_pane_opened_payload.terminal_pane_id, + command_pane_opened_payload + .context + .into_iter() + .map(|c_i| (c_i.name, c_i.value)) + .collect(), + )) + }, + _ => Err("Malformed payload for the EditPaneOpened Event"), + }, + Some(ProtobufEventType::EditPaneExited) => match protobuf_event.payload { + Some(ProtobufEventPayload::EditPaneExitedPayload(command_pane_exited_payload)) => { + Ok(Event::EditPaneExited( + command_pane_exited_payload.terminal_pane_id, + command_pane_exited_payload.exit_code, + command_pane_exited_payload + .context + .into_iter() + .map(|c_i| (c_i.name, c_i.value)) + .collect(), + )) + }, + _ => Err("Malformed payload for the EditPaneExited Event"), + }, None => Err("Unknown Protobuf Event"), } } @@ -518,6 +555,43 @@ impl TryFrom for ProtobufEvent { )), }) }, + Event::PaneClosed(pane_id) => Ok(ProtobufEvent { + name: ProtobufEventType::PaneClosed as i32, + payload: Some(event::Payload::PaneClosedPayload(PaneClosedPayload { + pane_id: Some(pane_id.try_into()?), + })), + }), + Event::EditPaneOpened(terminal_pane_id, context) => { + let command_pane_opened_payload = EditPaneOpenedPayload { + terminal_pane_id, + context: context + .into_iter() + .map(|(name, value)| ContextItem { name, value }) + .collect(), + }; + Ok(ProtobufEvent { + name: ProtobufEventType::EditPaneOpened as i32, + payload: Some(event::Payload::EditPaneOpenedPayload( + command_pane_opened_payload, + )), + }) + }, + Event::EditPaneExited(terminal_pane_id, exit_code, context) => { + let command_pane_exited_payload = EditPaneExitedPayload { + terminal_pane_id, + exit_code, + context: context + .into_iter() + .map(|(name, value)| ContextItem { name, value }) + .collect(), + }; + Ok(ProtobufEvent { + name: ProtobufEventType::EditPaneExited as i32, + payload: Some(event::Payload::EditPaneExitedPayload( + command_pane_exited_payload, + )), + }) + }, } } } @@ -1023,6 +1097,9 @@ impl TryFrom for EventType { ProtobufEventType::WebRequestResult => EventType::WebRequestResult, ProtobufEventType::CommandPaneOpened => EventType::CommandPaneOpened, ProtobufEventType::CommandPaneExited => EventType::CommandPaneExited, + ProtobufEventType::PaneClosed => EventType::PaneClosed, + ProtobufEventType::EditPaneOpened => EventType::EditPaneOpened, + ProtobufEventType::EditPaneExited => EventType::EditPaneExited, }) } } @@ -1052,6 +1129,9 @@ impl TryFrom for ProtobufEventType { EventType::WebRequestResult => ProtobufEventType::WebRequestResult, EventType::CommandPaneOpened => ProtobufEventType::CommandPaneOpened, EventType::CommandPaneExited => ProtobufEventType::CommandPaneExited, + EventType::PaneClosed => ProtobufEventType::PaneClosed, + EventType::EditPaneOpened => ProtobufEventType::EditPaneOpened, + EventType::EditPaneExited => ProtobufEventType::EditPaneExited, }) } } @@ -1616,3 +1696,36 @@ fn serialize_session_update_event_with_non_default_values() { "Event properly serialized/deserialized without change" ); } + +// note: ProtobufPaneId and ProtobufPaneType are not the same as the ones defined in plugin_command.rs +// this is a duplicate type - we are forced to do this because protobuffs do not support recursive +// imports +impl TryFrom for PaneId { + type Error = &'static str; + fn try_from(protobuf_pane_id: ProtobufPaneId) -> Result { + match ProtobufPaneType::from_i32(protobuf_pane_id.pane_type) { + Some(ProtobufPaneType::Terminal) => Ok(PaneId::Terminal(protobuf_pane_id.id)), + Some(ProtobufPaneType::Plugin) => Ok(PaneId::Plugin(protobuf_pane_id.id)), + None => Err("Failed to convert PaneId"), + } + } +} + +// note: ProtobufPaneId and ProtobufPaneType are not the same as the ones defined in plugin_command.rs +// this is a duplicate type - we are forced to do this because protobuffs do not support recursive +// imports +impl TryFrom for ProtobufPaneId { + type Error = &'static str; + fn try_from(pane_id: PaneId) -> Result { + match pane_id { + PaneId::Terminal(id) => Ok(ProtobufPaneId { + pane_type: ProtobufPaneType::Terminal as i32, + id, + }), + PaneId::Plugin(id) => Ok(ProtobufPaneId { + pane_type: ProtobufPaneType::Plugin as i32, + id, + }), + } + } +} diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto index 3e3567dd..db25ae6c 100644 --- a/zellij-utils/src/plugin_api/plugin_command.proto +++ b/zellij-utils/src/plugin_api/plugin_command.proto @@ -101,6 +101,7 @@ enum CommandName { Reconfigure = 87; HidePaneWithId = 88; ShowPaneWithId = 89; + OpenCommandPaneBackground = 90; } message PluginCommand { @@ -161,6 +162,7 @@ message PluginCommand { string reconfigure_payload = 63; HidePaneWithIdPayload hide_pane_with_id_payload = 64; ShowPaneWithIdPayload show_pane_with_id_payload = 65; + OpenCommandPanePayload open_command_pane_background_payload = 66; } } @@ -238,6 +240,7 @@ message UnsubscribePayload { message OpenFilePayload { file.File file_to_open = 1; optional FloatingPaneCoordinates floating_pane_coordinates = 2; + repeated ContextItem context = 3; } message OpenCommandPanePayload { diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs index e72001e2..d07fab62 100644 --- a/zellij-utils/src/plugin_api/plugin_command.rs +++ b/zellij-utils/src/plugin_api/plugin_command.rs @@ -227,7 +227,14 @@ impl TryFrom for PluginCommand { Some(CommandName::OpenFile) => match protobuf_plugin_command.payload { Some(Payload::OpenFilePayload(file_to_open_payload)) => { match file_to_open_payload.file_to_open { - Some(file_to_open) => Ok(PluginCommand::OpenFile(file_to_open.try_into()?)), + Some(file_to_open) => { + let context: BTreeMap = file_to_open_payload + .context + .into_iter() + .map(|e| (e.name, e.value)) + .collect(); + Ok(PluginCommand::OpenFile(file_to_open.try_into()?, context)) + }, None => Err("Malformed open file payload"), } }, @@ -238,10 +245,16 @@ impl TryFrom for PluginCommand { let floating_pane_coordinates = file_to_open_payload .floating_pane_coordinates .map(|f| f.into()); + let context: BTreeMap = file_to_open_payload + .context + .into_iter() + .map(|e| (e.name, e.value)) + .collect(); match file_to_open_payload.file_to_open { Some(file_to_open) => Ok(PluginCommand::OpenFileFloating( file_to_open.try_into()?, floating_pane_coordinates, + context, )), None => Err("Malformed open file payload"), } @@ -725,7 +738,15 @@ impl TryFrom for PluginCommand { Some(Payload::OpenFileInPlacePayload(file_to_open_payload)) => { match file_to_open_payload.file_to_open { Some(file_to_open) => { - Ok(PluginCommand::OpenFileInPlace(file_to_open.try_into()?)) + let context: BTreeMap = file_to_open_payload + .context + .into_iter() + .map(|e| (e.name, e.value)) + .collect(); + Ok(PluginCommand::OpenFileInPlace( + file_to_open.try_into()?, + context, + )) }, None => Err("Malformed open file in place payload"), } @@ -942,6 +963,25 @@ impl TryFrom for PluginCommand { }, _ => Err("Mismatched payload for ShowPaneWithId"), }, + Some(CommandName::OpenCommandPaneBackground) => match protobuf_plugin_command.payload { + Some(Payload::OpenCommandPaneBackgroundPayload(command_to_run_payload)) => { + match command_to_run_payload.command_to_run { + Some(command_to_run) => { + let context: BTreeMap = command_to_run_payload + .context + .into_iter() + .map(|e| (e.name, e.value)) + .collect(); + Ok(PluginCommand::OpenCommandPaneBackground( + command_to_run.try_into()?, + context, + )) + }, + None => Err("Malformed open command pane background payload"), + } + }, + _ => Err("Mismatched payload for OpenCommandPaneBackground"), + }, None => Err("Unrecognized plugin command"), } } @@ -981,19 +1021,27 @@ impl TryFrom for ProtobufPluginCommand { name: CommandName::GetZellijVersion as i32, payload: None, }), - PluginCommand::OpenFile(file_to_open) => Ok(ProtobufPluginCommand { + PluginCommand::OpenFile(file_to_open, context) => Ok(ProtobufPluginCommand { name: CommandName::OpenFile as i32, payload: Some(Payload::OpenFilePayload(OpenFilePayload { file_to_open: Some(file_to_open.try_into()?), floating_pane_coordinates: None, + context: context + .into_iter() + .map(|(name, value)| ContextItem { name, value }) + .collect(), })), }), - PluginCommand::OpenFileFloating(file_to_open, floating_pane_coordinates) => { + PluginCommand::OpenFileFloating(file_to_open, floating_pane_coordinates, context) => { Ok(ProtobufPluginCommand { name: CommandName::OpenFileFloating as i32, payload: Some(Payload::OpenFileFloatingPayload(OpenFilePayload { file_to_open: Some(file_to_open.try_into()?), floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()), + context: context + .into_iter() + .map(|(name, value)| ContextItem { name, value }) + .collect(), })), }) }, @@ -1002,6 +1050,7 @@ impl TryFrom for ProtobufPluginCommand { payload: Some(Payload::OpenTerminalPayload(OpenFilePayload { file_to_open: Some(cwd.try_into()?), floating_pane_coordinates: None, + context: vec![], // will be added in the future })), }), PluginCommand::OpenTerminalFloating(cwd, floating_pane_coordinates) => { @@ -1010,6 +1059,7 @@ impl TryFrom for ProtobufPluginCommand { payload: Some(Payload::OpenTerminalFloatingPayload(OpenFilePayload { file_to_open: Some(cwd.try_into()?), floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()), + context: vec![], // will be added in the future })), }) }, @@ -1332,13 +1382,18 @@ impl TryFrom for ProtobufPluginCommand { payload: Some(Payload::OpenTerminalInPlacePayload(OpenFilePayload { file_to_open: Some(cwd.try_into()?), floating_pane_coordinates: None, + context: vec![], // will be added in the future })), }), - PluginCommand::OpenFileInPlace(file_to_open) => Ok(ProtobufPluginCommand { + PluginCommand::OpenFileInPlace(file_to_open, context) => Ok(ProtobufPluginCommand { name: CommandName::OpenFileInPlace as i32, payload: Some(Payload::OpenFileInPlacePayload(OpenFilePayload { file_to_open: Some(file_to_open.try_into()?), floating_pane_coordinates: None, + context: context + .into_iter() + .map(|(name, value)| ContextItem { name, value }) + .collect(), })), }), PluginCommand::OpenCommandPaneInPlace(command_to_run, context) => { @@ -1516,6 +1571,22 @@ impl TryFrom for ProtobufPluginCommand { })), }) }, + PluginCommand::OpenCommandPaneBackground(command_to_run, context) => { + let context: Vec<_> = context + .into_iter() + .map(|(name, value)| ContextItem { name, value }) + .collect(); + Ok(ProtobufPluginCommand { + name: CommandName::OpenCommandPaneBackground as i32, + payload: Some(Payload::OpenCommandPaneBackgroundPayload( + OpenCommandPanePayload { + command_to_run: Some(command_to_run.try_into()?), + floating_pane_coordinates: None, + context, + }, + )), + }) + }, } } } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap index 0f919210..45657125 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -234,6 +234,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -731,6 +732,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -990,6 +992,7 @@ Config { Down, ), None, + false, ), SwitchToMode( Normal, @@ -1160,6 +1163,7 @@ Config { NewPane( None, None, + false, ), SwitchToMode( Normal, @@ -1188,6 +1192,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -1255,6 +1260,7 @@ Config { Right, ), None, + false, ), SwitchToMode( Normal, @@ -1786,6 +1792,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -2255,6 +2262,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -2608,6 +2616,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3043,6 +3052,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3411,6 +3421,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3742,6 +3753,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4097,6 +4109,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4541,6 +4554,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4875,6 +4889,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -5068,6 +5083,7 @@ Config { Down, ), None, + false, ), SwitchToMode( Normal, @@ -5084,6 +5100,7 @@ Config { Right, ), None, + false, ), SwitchToMode( Normal, @@ -5392,6 +5409,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap index 04174c5f..95e4ff27 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -234,6 +234,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -731,6 +732,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -990,6 +992,7 @@ Config { Down, ), None, + false, ), SwitchToMode( Normal, @@ -1160,6 +1163,7 @@ Config { NewPane( None, None, + false, ), SwitchToMode( Normal, @@ -1188,6 +1192,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -1255,6 +1260,7 @@ Config { Right, ), None, + false, ), SwitchToMode( Normal, @@ -1786,6 +1792,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -2255,6 +2262,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -2608,6 +2616,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3043,6 +3052,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3411,6 +3421,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3742,6 +3753,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4097,6 +4109,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4541,6 +4554,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4875,6 +4889,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -5068,6 +5083,7 @@ Config { Down, ), None, + false, ), SwitchToMode( Normal, @@ -5084,6 +5100,7 @@ Config { Right, ), None, + false, ), SwitchToMode( Normal, @@ -5392,6 +5409,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap index ef40f8c5..56cea06e 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap @@ -234,6 +234,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -731,6 +732,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -990,6 +992,7 @@ Config { Down, ), None, + false, ), SwitchToMode( Normal, @@ -1160,6 +1163,7 @@ Config { NewPane( None, None, + false, ), SwitchToMode( Normal, @@ -1188,6 +1192,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -1255,6 +1260,7 @@ Config { Right, ), None, + false, ), SwitchToMode( Normal, @@ -1786,6 +1792,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -2255,6 +2262,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -2608,6 +2616,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3043,6 +3052,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3411,6 +3421,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3742,6 +3753,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4097,6 +4109,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4541,6 +4554,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4875,6 +4889,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -5068,6 +5083,7 @@ Config { Down, ), None, + false, ), SwitchToMode( Normal, @@ -5084,6 +5100,7 @@ Config { Right, ), None, + false, ), SwitchToMode( Normal, @@ -5392,6 +5409,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap index 795d0cf9..5233e9e4 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -234,6 +234,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -731,6 +732,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -990,6 +992,7 @@ Config { Down, ), None, + false, ), SwitchToMode( Normal, @@ -1160,6 +1163,7 @@ Config { NewPane( None, None, + false, ), SwitchToMode( Normal, @@ -1188,6 +1192,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -1255,6 +1260,7 @@ Config { Right, ), None, + false, ), SwitchToMode( Normal, @@ -1786,6 +1792,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -2255,6 +2262,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -2608,6 +2616,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3043,6 +3052,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3411,6 +3421,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -3742,6 +3753,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4097,6 +4109,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4541,6 +4554,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -4875,6 +4889,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier { @@ -5068,6 +5083,7 @@ Config { Down, ), None, + false, ), SwitchToMode( Normal, @@ -5084,6 +5100,7 @@ Config { Right, ), None, + false, ), SwitchToMode( Normal, @@ -5392,6 +5409,7 @@ Config { NewPane( None, None, + false, ), ], KeyWithModifier {