feat(panes): in place run (#2795)

* prototype

* fix tests

* add to all the things except plugins

* add in-place to plugin commands

* fix launch-or-focus should_float and in place behavior

* various cleanups

* style(fmt): rustfmt
This commit is contained in:
Aram Drevekenin 2023-09-18 16:28:06 +02:00 committed by GitHub
parent 74a3b63635
commit e392a66833
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 1034 additions and 112 deletions

View file

@ -245,7 +245,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
action_key(&km, &[A::SearchToggleOption(SOpt::WholeWord)])),
]} else if mi.mode == IM::Session { vec![
(s("Detach"), s("Detach"), action_key(&km, &[Action::Detach])),
(s("Session Manager"), s("Manager"), action_key(&km, &[A::LaunchOrFocusPlugin(Default::default(), true, true), TO_NORMAL])), // not entirely accurate
(s("Session Manager"), s("Manager"), action_key(&km, &[A::LaunchOrFocusPlugin(Default::default(), true, true, false), TO_NORMAL])), // not entirely accurate
(s("Select pane"), s("Select"), to_normal_key),
]} else if mi.mode == IM::Tmux { vec![
(s("Move focus"), s("Move"), action_key_group(&km, &[

View file

@ -25,6 +25,7 @@ fn main() {
direction,
cwd,
floating,
in_place,
name,
close_on_exit,
start_suspended,
@ -36,6 +37,7 @@ fn main() {
direction,
cwd,
floating,
in_place,
name,
close_on_exit,
start_suspended,
@ -49,6 +51,7 @@ fn main() {
direction,
line_number,
floating,
in_place,
cwd,
})) = opts.command
{
@ -64,6 +67,7 @@ fn main() {
direction,
line_number,
floating,
in_place,
cwd,
};
commands::send_action_to_session(command_cli_action, opts.session, config);

View file

@ -18,8 +18,11 @@ pub fn start_cli_client(os_input: Box<dyn ClientOsApi>, session_name: &str, acti
sock_dir
};
os_input.connect_to_server(&*zellij_ipc_pipe);
let pane_id = os_input
.env_variable("ZELLIJ_PANE_ID")
.and_then(|e| e.trim().parse().ok());
for action in actions {
let msg = ClientToServerMsg::Action(action, None);
let msg = ClientToServerMsg::Action(action, pane_id, None);
os_input.send_to_server(msg);
}
loop {

View file

@ -283,13 +283,13 @@ impl InputHandler {
Action::NoOp => {},
Action::Quit => {
self.os_input
.send_to_server(ClientToServerMsg::Action(action, client_id));
.send_to_server(ClientToServerMsg::Action(action, None, client_id));
self.exit(ExitReason::Normal);
should_break = true;
},
Action::Detach => {
self.os_input
.send_to_server(ClientToServerMsg::Action(action, client_id));
.send_to_server(ClientToServerMsg::Action(action, None, client_id));
self.exit(ExitReason::NormalDetached);
should_break = true;
},
@ -298,7 +298,7 @@ impl InputHandler {
// server later that atomically changes the mode as well
self.mode = mode;
self.os_input
.send_to_server(ClientToServerMsg::Action(action, None));
.send_to_server(ClientToServerMsg::Action(action, None, None));
},
Action::CloseFocus
| Action::ClearScreen
@ -318,7 +318,7 @@ impl InputHandler {
| Action::MoveFocusOrTab(_) => {
self.command_is_executing.blocking_input_thread();
self.os_input
.send_to_server(ClientToServerMsg::Action(action, client_id));
.send_to_server(ClientToServerMsg::Action(action, None, client_id));
self.command_is_executing
.wait_until_input_thread_is_unblocked();
},
@ -333,7 +333,7 @@ impl InputHandler {
},
_ => self
.os_input
.send_to_server(ClientToServerMsg::Action(action, client_id)),
.send_to_server(ClientToServerMsg::Action(action, None, client_id)),
}
should_break

View file

@ -316,6 +316,7 @@ pub fn start_client(
os_api.send_to_server(ClientToServerMsg::Action(
on_force_close.into(),
None,
None,
));
}
}),

View file

@ -114,6 +114,9 @@ pub trait ClientOsApi: Send + Sync {
fn disable_mouse(&self) -> Result<()>;
// Repeatedly send action, until stdin is readable again
fn stdin_poller(&self) -> StdinPoller;
fn env_variable(&self, _name: &str) -> Option<String> {
None
}
}
impl ClientOsApi for ClientOsInputOutput {
@ -282,6 +285,10 @@ impl ClientOsApi for ClientOsInputOutput {
fn stdin_poller(&self) -> StdinPoller {
StdinPoller::default()
}
fn env_variable(&self, name: &str) -> Option<String> {
std::env::var(name).ok()
}
}
impl Clone for Box<dyn ClientOsApi> {

View file

@ -199,7 +199,7 @@ fn extract_actions_sent_to_server(
) -> Vec<Action> {
let events_sent_to_server = events_sent_to_server.lock().unwrap();
events_sent_to_server.iter().fold(vec![], |mut acc, event| {
if let ClientToServerMsg::Action(action, None) = event {
if let ClientToServerMsg::Action(action, None, None) = event {
acc.push(action.clone());
}
acc

View file

@ -183,6 +183,7 @@ fn handle_openpty(
}
command
.args(&cmd.args)
.env("ZELLIJ_PANE_ID", &format!("{}", terminal_id))
.pre_exec(move || -> std::io::Result<()> {
if libc::login_tty(pid_secondary) != 0 {
panic!("failed to set controlling terminal");

View file

@ -157,6 +157,7 @@ impl FloatingPanes {
// move clients from the previously active pane to the new pane we just inserted
self.move_clients_between_panes(pane_id, with_pane_id);
self.set_pane_frames();
removed_pane
}
pub fn remove_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
@ -295,7 +296,7 @@ impl FloatingPanes {
pane.render_full_viewport();
}
}
pub fn set_pane_frames(&mut self, _os_api: &mut Box<dyn ServerOsApi>) -> Result<()> {
pub fn set_pane_frames(&mut self) -> Result<()> {
let err_context =
|pane_id: &PaneId| format!("failed to activate frame on pane {pane_id:?}");
@ -640,7 +641,7 @@ impl FloatingPanes {
current_position.set_geom_override(geom);
}
current_position.set_should_render(true);
let _ = self.set_pane_frames(os_api);
let _ = self.set_pane_frames();
}
}
pub fn move_clients_out_of_pane(&mut self, pane_id: PaneId) {

View file

@ -159,6 +159,7 @@ impl TiledPanes {
// move clients from the previously active pane to the new pane we just inserted
self.move_clients_between_panes(pane_id, with_pane_id);
self.reapply_pane_frames();
removed_pane
}
pub fn insert_pane(&mut self, pane_id: PaneId, pane: Box<dyn Pane>) {

View file

@ -12,6 +12,7 @@ use std::{
};
use wasmer::Store;
use crate::panes::PaneId;
use crate::screen::ScreenInstruction;
use crate::{pty::PtyInstruction, thread_bus::Bus, ClientId, ServerInstruction};
@ -38,9 +39,11 @@ pub type PluginId = u32;
pub enum PluginInstruction {
Load(
Option<bool>, // should float
bool, // should be opened in place
Option<String>, // pane title
RunPlugin,
usize, // tab index
Option<PaneId>, // pane id to replace if this is to be opened "in-place"
ClientId,
Size,
),
@ -156,21 +159,31 @@ pub(crate) fn plugin_thread_main(
let (event, mut err_ctx) = bus.recv().expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Plugin((&event).into()));
match event {
PluginInstruction::Load(should_float, pane_title, run, tab_index, client_id, size) => {
match wasm_bridge.load_plugin(&run, tab_index, size, Some(client_id)) {
PluginInstruction::Load(
should_float,
should_be_open_in_place,
pane_title,
run,
tab_index,
pane_id_to_replace,
client_id,
size,
) => match wasm_bridge.load_plugin(&run, tab_index, size, Some(client_id)) {
Ok(plugin_id) => {
drop(bus.senders.send_to_screen(ScreenInstruction::AddPlugin(
should_float,
should_be_open_in_place,
run,
pane_title,
tab_index,
plugin_id,
pane_id_to_replace,
Some(client_id),
)));
},
Err(e) => {
log::error!("Failed to load plugin: {e}");
},
}
},
PluginInstruction::Update(updates) => {
wasm_bridge.update_plugins(updates)?;
@ -192,12 +205,16 @@ pub(crate) fn plugin_thread_main(
// the cli who spawned the command and is not an existing client_id
match wasm_bridge.load_plugin(&run, tab_index, size, None) {
Ok(plugin_id) => {
let should_be_open_in_place = false;
drop(bus.senders.send_to_screen(ScreenInstruction::AddPlugin(
should_float,
should_be_open_in_place,
run,
pane_title,
tab_index,
plugin_id,
None,
None,
)));
},
Err(e) => {

View file

@ -533,9 +533,11 @@ pub fn load_new_plugin_from_hd() {
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,
));
@ -601,9 +603,11 @@ pub fn plugin_workers() {
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,
));
@ -672,9 +676,11 @@ pub fn plugin_workers_persist_state() {
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,
));
@ -747,9 +753,11 @@ pub fn can_subscribe_to_hd_events() {
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,
));
@ -817,9 +825,11 @@ pub fn switch_to_mode_plugin_command() {
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,
));
@ -885,9 +895,11 @@ pub fn switch_to_mode_plugin_command_permission_denied() {
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,
));
@ -953,9 +965,11 @@ pub fn new_tabs_with_layout_plugin_command() {
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,
));
@ -1035,9 +1049,11 @@ pub fn new_tab_plugin_command() {
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,
));
@ -1103,9 +1119,11 @@ pub fn go_to_next_tab_plugin_command() {
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,
));
@ -1170,9 +1188,11 @@ pub fn go_to_previous_tab_plugin_command() {
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,
));
@ -1237,9 +1257,11 @@ pub fn resize_focused_pane_plugin_command() {
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,
));
@ -1304,9 +1326,11 @@ pub fn resize_focused_pane_with_direction_plugin_command() {
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,
));
@ -1371,9 +1395,11 @@ pub fn focus_next_pane_plugin_command() {
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,
));
@ -1438,9 +1464,11 @@ pub fn focus_previous_pane_plugin_command() {
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,
));
@ -1505,9 +1533,11 @@ pub fn move_focus_plugin_command() {
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,
));
@ -1572,9 +1602,11 @@ pub fn move_focus_or_tab_plugin_command() {
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,
));
@ -1639,9 +1671,11 @@ pub fn edit_scrollback_plugin_command() {
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,
));
@ -1706,9 +1740,11 @@ pub fn write_plugin_command() {
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,
));
@ -1773,9 +1809,11 @@ pub fn write_chars_plugin_command() {
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,
));
@ -1840,9 +1878,11 @@ pub fn toggle_tab_plugin_command() {
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,
));
@ -1907,9 +1947,11 @@ pub fn move_pane_plugin_command() {
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,
));
@ -1974,9 +2016,11 @@ pub fn move_pane_with_direction_plugin_command() {
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,
));
@ -2042,9 +2086,11 @@ pub fn clear_screen_plugin_command() {
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,
));
@ -2110,9 +2156,11 @@ pub fn scroll_up_plugin_command() {
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,
));
@ -2177,9 +2225,11 @@ pub fn scroll_down_plugin_command() {
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,
));
@ -2244,9 +2294,11 @@ pub fn scroll_to_top_plugin_command() {
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,
));
@ -2311,9 +2363,11 @@ pub fn scroll_to_bottom_plugin_command() {
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,
));
@ -2378,9 +2432,11 @@ pub fn page_scroll_up_plugin_command() {
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,
));
@ -2445,9 +2501,11 @@ pub fn page_scroll_down_plugin_command() {
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,
));
@ -2512,9 +2570,11 @@ pub fn toggle_focus_fullscreen_plugin_command() {
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,
));
@ -2579,9 +2639,11 @@ pub fn toggle_pane_frames_plugin_command() {
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,
));
@ -2646,9 +2708,11 @@ pub fn toggle_pane_embed_or_eject_plugin_command() {
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,
));
@ -2713,9 +2777,11 @@ pub fn undo_rename_pane_plugin_command() {
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,
));
@ -2780,9 +2846,11 @@ pub fn close_focus_plugin_command() {
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,
));
@ -2847,9 +2915,11 @@ pub fn toggle_active_tab_sync_plugin_command() {
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,
));
@ -2914,9 +2984,11 @@ pub fn close_focused_tab_plugin_command() {
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,
));
@ -2981,9 +3053,11 @@ pub fn undo_rename_tab_plugin_command() {
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,
));
@ -3048,9 +3122,11 @@ pub fn previous_swap_layout_plugin_command() {
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,
));
@ -3115,9 +3191,11 @@ pub fn next_swap_layout_plugin_command() {
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,
));
@ -3182,9 +3260,11 @@ pub fn go_to_tab_name_plugin_command() {
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,
));
@ -3249,9 +3329,11 @@ pub fn focus_or_create_tab_plugin_command() {
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,
));
@ -3316,9 +3398,11 @@ pub fn go_to_tab() {
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,
));
@ -3383,9 +3467,11 @@ pub fn start_or_reload_plugin() {
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,
));
@ -3457,9 +3543,11 @@ pub fn quit_zellij_plugin_command() {
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,
));
@ -3531,9 +3619,11 @@ pub fn detach_plugin_command() {
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,
));
@ -3605,9 +3695,11 @@ pub fn open_file_floating_plugin_command() {
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,
));
@ -3679,9 +3771,11 @@ pub fn open_file_plugin_command() {
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,
));
@ -3754,9 +3848,11 @@ pub fn open_file_with_line_plugin_command() {
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,
));
@ -3828,9 +3924,11 @@ pub fn open_file_with_line_floating_plugin_command() {
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,
));
@ -3902,9 +4000,11 @@ pub fn open_terminal_plugin_command() {
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,
));
@ -3976,9 +4076,11 @@ pub fn open_terminal_floating_plugin_command() {
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,
));
@ -4050,9 +4152,11 @@ pub fn open_command_pane_plugin_command() {
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,
));
@ -4124,9 +4228,11 @@ pub fn open_command_pane_floating_plugin_command() {
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,
));
@ -4191,9 +4297,11 @@ pub fn switch_to_tab_plugin_command() {
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,
));
@ -4253,9 +4361,11 @@ pub fn hide_self_plugin_command() {
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,
));
@ -4315,9 +4425,11 @@ pub fn show_self_plugin_command() {
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,
));
@ -4382,9 +4494,11 @@ pub fn close_terminal_pane_plugin_command() {
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,
));
@ -4449,9 +4563,11 @@ pub fn close_plugin_pane_plugin_command() {
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,
));
@ -4516,9 +4632,11 @@ pub fn focus_terminal_pane_plugin_command() {
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,
));
@ -4583,9 +4701,11 @@ pub fn focus_plugin_pane_plugin_command() {
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,
));
@ -4650,9 +4770,11 @@ pub fn rename_terminal_pane_plugin_command() {
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,
));
@ -4717,9 +4839,11 @@ pub fn rename_plugin_pane_plugin_command() {
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,
));
@ -4784,9 +4908,11 @@ pub fn rename_tab_plugin_command() {
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,
));
@ -4860,9 +4986,11 @@ pub fn send_configuration_to_plugins() {
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,
));
@ -4924,9 +5052,11 @@ pub fn request_plugin_permissions() {
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,
));
@ -5012,9 +5142,11 @@ pub fn granted_permission_request_result() {
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
plugin_should_float,
false,
plugin_title,
run_plugin.clone(),
tab_index,
None,
client_id,
size,
));
@ -5098,9 +5230,11 @@ pub fn denied_permission_request_result() {
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
plugin_should_float,
false,
plugin_title,
run_plugin.clone(),
tab_index,
None,
client_id,
size,
));

View file

@ -51,6 +51,7 @@ macro_rules! apply_action {
if let Err(e) = route_action(
$action,
$env.plugin_env.client_id,
Some(PaneId::Plugin($env.plugin_env.plugin_id)),
$env.plugin_env.senders.clone(),
$env.plugin_env.capabilities.clone(),
$env.plugin_env.client_attributes.clone(),
@ -214,6 +215,15 @@ fn host_run_plugin_command(env: &ForeignFunctionEnv) {
connect_to_session.tab_position,
connect_to_session.pane_id,
)?,
PluginCommand::OpenFileInPlace(file_to_open) => {
open_file_in_place(env, file_to_open)
},
PluginCommand::OpenTerminalInPlace(cwd) => {
open_terminal_in_place(env, cwd.path.try_into()?)
},
PluginCommand::OpenCommandPaneInPlace(command_to_run) => {
open_command_pane_in_place(env, command_to_run)
},
},
(PermissionStatus::Denied, permission) => {
log::error!(
@ -347,12 +357,14 @@ fn get_zellij_version(env: &ForeignFunctionEnv) {
fn open_file(env: &ForeignFunctionEnv, file_to_open: FileToOpen) {
let error_msg = || format!("failed to open file in plugin {}", env.plugin_env.name());
let floating = false;
let in_place = false;
let action = Action::EditFile(
file_to_open.path,
file_to_open.line_number,
file_to_open.cwd,
None,
floating,
in_place,
);
apply_action!(action, error_msg, env);
}
@ -360,12 +372,29 @@ fn open_file(env: &ForeignFunctionEnv, file_to_open: FileToOpen) {
fn open_file_floating(env: &ForeignFunctionEnv, file_to_open: FileToOpen) {
let error_msg = || format!("failed to open file in plugin {}", env.plugin_env.name());
let floating = true;
let in_place = false;
let action = Action::EditFile(
file_to_open.path,
file_to_open.line_number,
file_to_open.cwd,
None,
floating,
in_place,
);
apply_action!(action, error_msg, env);
}
fn open_file_in_place(env: &ForeignFunctionEnv, file_to_open: FileToOpen) {
let error_msg = || format!("failed to open file in plugin {}", env.plugin_env.name());
let floating = false;
let in_place = true;
let action = Action::EditFile(
file_to_open.path,
file_to_open.line_number,
file_to_open.cwd,
None,
floating,
in_place,
);
apply_action!(action, error_msg, env);
}
@ -402,6 +431,22 @@ fn open_terminal_floating(env: &ForeignFunctionEnv, cwd: PathBuf) {
apply_action!(action, error_msg, env);
}
fn open_terminal_in_place(env: &ForeignFunctionEnv, cwd: PathBuf) {
let error_msg = || format!("failed to open file in plugin {}", env.plugin_env.name());
let mut default_shell = env
.plugin_env
.default_shell
.clone()
.unwrap_or_else(|| TerminalAction::RunCommand(RunCommand::default()));
default_shell.change_cwd(cwd);
let run_command_action: Option<RunCommandAction> = match default_shell {
TerminalAction::RunCommand(run_command) => Some(run_command.into()),
_ => None,
};
let action = Action::NewInPlacePane(run_command_action, None);
apply_action!(action, error_msg, env);
}
fn open_command_pane(env: &ForeignFunctionEnv, command_to_run: CommandToRun) {
let error_msg = || format!("failed to open command in plugin {}", env.plugin_env.name());
let command = command_to_run.path;
@ -444,6 +489,27 @@ fn open_command_pane_floating(env: &ForeignFunctionEnv, command_to_run: CommandT
apply_action!(action, error_msg, env);
}
fn open_command_pane_in_place(env: &ForeignFunctionEnv, command_to_run: CommandToRun) {
let error_msg = || format!("failed to open command in plugin {}", env.plugin_env.name());
let command = command_to_run.path;
let cwd = command_to_run.cwd;
let args = command_to_run.args;
let direction = None;
let hold_on_close = true;
let hold_on_start = false;
let name = None;
let run_command_action = RunCommandAction {
command,
args,
cwd,
direction,
hold_on_close,
hold_on_start,
};
let action = Action::NewInPlacePane(Some(run_command_action), name);
apply_action!(action, error_msg, env);
}
fn switch_tab_to(env: &ForeignFunctionEnv, tab_idx: u32) {
env.plugin_env
.senders
@ -1090,14 +1156,16 @@ fn check_command_permission(
return (PermissionStatus::Granted, None);
}
let permission = match command {
PluginCommand::OpenFile(..) | PluginCommand::OpenFileFloating(..) => {
PermissionType::OpenFiles
},
PluginCommand::OpenFile(..)
| PluginCommand::OpenFileFloating(..)
| PluginCommand::OpenFileInPlace(..) => PermissionType::OpenFiles,
PluginCommand::OpenTerminal(..)
| PluginCommand::StartOrReloadPlugin(..)
| PluginCommand::OpenTerminalFloating(..) => PermissionType::OpenTerminalsOrPlugins,
| PluginCommand::OpenTerminalFloating(..)
| PluginCommand::OpenTerminalInPlace(..) => PermissionType::OpenTerminalsOrPlugins,
PluginCommand::OpenCommandPane(..)
| PluginCommand::OpenCommandPaneFloating(..)
| PluginCommand::OpenCommandPaneInPlace(..)
| PluginCommand::ExecCmd(..) => PermissionType::RunCommands,
PluginCommand::Write(..) | PluginCommand::WriteChars(..) => PermissionType::WriteToStdin,
PluginCommand::SwitchTabTo(..)

View file

@ -26,9 +26,10 @@ pub type VteBytes = Vec<u8>;
pub type TabIndex = u32;
#[derive(Clone, Copy, Debug)]
pub enum ClientOrTabIndex {
pub enum ClientTabIndexOrPaneId {
ClientId(ClientId),
TabIndex(usize),
PaneId(PaneId),
}
/// Instructions related to PTYs (pseudoterminals).
@ -38,7 +39,7 @@ pub enum PtyInstruction {
Option<TerminalAction>,
Option<bool>,
Option<String>,
ClientOrTabIndex,
ClientTabIndexOrPaneId,
), // bool (if Some) is
// should_float, String is an optional pane name
OpenInPlaceEditor(PathBuf, Option<usize>, ClientId), // Option<usize> is the optional line number
@ -62,6 +63,11 @@ pub enum PtyInstruction {
ClosePane(PaneId),
CloseTab(Vec<PaneId>),
ReRunCommandInPane(PaneId, RunCommand),
SpawnInPlaceTerminal(
Option<TerminalAction>,
Option<String>,
ClientTabIndexOrPaneId,
), // String is an optional pane name
Exit,
}
@ -78,6 +84,7 @@ impl From<&PtyInstruction> for PtyContext {
PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
PtyInstruction::NewTab(..) => PtyContext::NewTab,
PtyInstruction::ReRunCommandInPane(..) => PtyContext::ReRunCommandInPane,
PtyInstruction::SpawnInPlaceTerminal(..) => PtyContext::SpawnInPlaceTerminal,
PtyInstruction::Exit => PtyContext::Exit,
}
}
@ -164,13 +171,80 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
},
}
},
PtyInstruction::SpawnInPlaceTerminal(
terminal_action,
name,
client_id_tab_index_or_pane_id,
) => {
let err_context = || {
format!(
"failed to spawn terminal for {:?}",
client_id_tab_index_or_pane_id
)
};
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),
};
match pty
.spawn_terminal(terminal_action, client_id_tab_index_or_pane_id)
.with_context(err_context)
{
Ok((pid, starts_held)) => {
let hold_for_command = if starts_held { run_command } else { None };
pty.bus
.senders
.send_to_screen(ScreenInstruction::ReplacePane(
PaneId::Terminal(pid),
hold_for_command,
pane_title,
client_id_tab_index_or_pane_id,
))
.with_context(err_context)?;
},
Err(err) => match err.downcast_ref::<ZellijError>() {
Some(ZellijError::CommandNotFound { terminal_id, .. }) => {
if hold_on_close {
let hold_for_command = None; // we do not hold an "error" pane
pty.bus
.senders
.send_to_screen(ScreenInstruction::ReplacePane(
PaneId::Terminal(*terminal_id),
hold_for_command,
pane_title,
client_id_tab_index_or_pane_id,
))
.with_context(err_context)?;
if let Some(run_command) = run_command {
send_command_not_found_to_screen(
pty.bus.senders.clone(),
*terminal_id,
run_command.clone(),
None,
)
.with_context(err_context)?;
}
} else {
log::error!("Failed to spawn terminal: {:?}", err);
pty.close_pane(PaneId::Terminal(*terminal_id))
.with_context(err_context)?;
}
},
_ => Err::<(), _>(err).non_fatal(),
},
}
},
PtyInstruction::OpenInPlaceEditor(temp_file, line_number, client_id) => {
let err_context =
|| format!("failed to open in-place editor for client {}", client_id);
match pty.spawn_terminal(
Some(TerminalAction::OpenFile(temp_file, line_number, None)),
ClientOrTabIndex::ClientId(client_id),
ClientTabIndexOrPaneId::ClientId(client_id),
) {
Ok((pid, _starts_held)) => {
pty.bus
@ -199,7 +273,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
_ => (false, None, name),
};
match pty
.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id))
.spawn_terminal(terminal_action, ClientTabIndexOrPaneId::ClientId(client_id))
.with_context(err_context)
{
Ok((pid, starts_held)) => {
@ -270,7 +344,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
_ => (false, None, name),
};
match pty
.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id))
.spawn_terminal(terminal_action, ClientTabIndexOrPaneId::ClientId(client_id))
.with_context(err_context)
{
Ok((pid, starts_held)) => {
@ -501,25 +575,45 @@ impl Pty {
};
};
}
fn fill_cwd_from_pane_id(&self, terminal_action: &mut TerminalAction, pane_id: &u32) {
if let TerminalAction::RunCommand(run_command) = terminal_action {
if run_command.cwd.is_none() {
run_command.cwd = self.id_to_child_pid.get(pane_id).and_then(|&id| {
self.bus
.os_input
.as_ref()
.and_then(|input| input.get_cwd(Pid::from_raw(id)))
});
};
};
}
pub fn spawn_terminal(
&mut self,
terminal_action: Option<TerminalAction>,
client_or_tab_index: ClientOrTabIndex,
client_or_tab_index: ClientTabIndexOrPaneId,
) -> Result<(u32, bool)> {
// bool is starts_held
let err_context = || format!("failed to spawn terminal for {:?}", client_or_tab_index);
// returns the terminal id
let terminal_action = match client_or_tab_index {
ClientOrTabIndex::ClientId(client_id) => {
ClientTabIndexOrPaneId::ClientId(client_id) => {
let mut terminal_action =
terminal_action.unwrap_or_else(|| self.get_default_terminal(None, None));
self.fill_cwd(&mut terminal_action, client_id);
terminal_action
},
ClientOrTabIndex::TabIndex(_) => {
ClientTabIndexOrPaneId::TabIndex(_) => {
terminal_action.unwrap_or_else(|| self.get_default_terminal(None, None))
},
ClientTabIndexOrPaneId::PaneId(pane_id) => {
let mut terminal_action =
terminal_action.unwrap_or_else(|| self.get_default_terminal(None, None));
if let PaneId::Terminal(terminal_pane_id) = pane_id {
self.fill_cwd_from_pane_id(&mut terminal_action, &terminal_pane_id);
}
terminal_action
},
};
let (hold_on_start, hold_on_close) = match &terminal_action {
TerminalAction::RunCommand(run_command) => {

View file

@ -6,7 +6,7 @@ use crate::{
os_input_output::ServerOsApi,
panes::PaneId,
plugins::PluginInstruction,
pty::{ClientOrTabIndex, PtyInstruction},
pty::{ClientTabIndexOrPaneId, PtyInstruction},
screen::ScreenInstruction,
ServerInstruction, SessionMetaData, SessionState,
};
@ -30,6 +30,7 @@ use crate::ClientId;
pub(crate) fn route_action(
action: Action,
client_id: ClientId,
pane_id: Option<PaneId>,
senders: ThreadSenders,
capabilities: PluginCapabilities,
client_attributes: ClientAttributes,
@ -257,37 +258,58 @@ pub(crate) fn route_action(
shell,
None,
name,
ClientOrTabIndex::ClientId(client_id),
ClientTabIndexOrPaneId::ClientId(client_id),
),
};
senders.send_to_pty(pty_instr).with_context(err_context)?;
},
Action::EditFile(path_to_file, line_number, cwd, split_direction, should_float) => {
Action::EditFile(
path_to_file,
line_number,
cwd,
split_direction,
should_float,
should_open_in_place,
) => {
let title = format!("Editing: {}", path_to_file.display());
let open_file = TerminalAction::OpenFile(path_to_file, line_number, cwd);
let pty_instr = match (split_direction, should_float) {
(Some(Direction::Left), false) => {
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)
},
(Some(Direction::Right), false) => {
(Some(Direction::Right), false, false) => {
PtyInstruction::SpawnTerminalVertically(Some(open_file), Some(title), client_id)
},
(Some(Direction::Up), false) => PtyInstruction::SpawnTerminalHorizontally(
(Some(Direction::Up), false, false) => PtyInstruction::SpawnTerminalHorizontally(
Some(open_file),
Some(title),
client_id,
),
(Some(Direction::Down), false) => PtyInstruction::SpawnTerminalHorizontally(
(Some(Direction::Down), false, false) => PtyInstruction::SpawnTerminalHorizontally(
Some(open_file),
Some(title),
client_id,
),
// No direction specified or should float - defer placement to screen
(None, _) | (_, true) => PtyInstruction::SpawnTerminal(
// open terminal in place
(_, _, true) => match pane_id {
Some(pane_id) => PtyInstruction::SpawnInPlaceTerminal(
Some(open_file),
Some(title),
ClientTabIndexOrPaneId::PaneId(pane_id),
),
None => PtyInstruction::SpawnInPlaceTerminal(
Some(open_file),
Some(title),
ClientTabIndexOrPaneId::ClientId(client_id),
),
},
// Open either floating terminal if we were asked with should_float or defer
// placement to screen
(None, _, _) | (_, true, _) => PtyInstruction::SpawnTerminal(
Some(open_file),
Some(should_float),
Some(title),
ClientOrTabIndex::ClientId(client_id),
ClientTabIndexOrPaneId::ClientId(client_id),
),
};
senders.send_to_pty(pty_instr).with_context(err_context)?;
@ -319,10 +341,35 @@ pub(crate) fn route_action(
run_cmd,
Some(should_float),
name,
ClientOrTabIndex::ClientId(client_id),
ClientTabIndexOrPaneId::ClientId(client_id),
))
.with_context(err_context)?;
},
Action::NewInPlacePane(run_command, name) => {
let run_cmd = run_command
.map(|cmd| TerminalAction::RunCommand(cmd.into()))
.or_else(|| default_shell.clone());
match pane_id {
Some(pane_id) => {
senders
.send_to_pty(PtyInstruction::SpawnInPlaceTerminal(
run_cmd,
name,
ClientTabIndexOrPaneId::PaneId(pane_id),
))
.with_context(err_context)?;
},
None => {
senders
.send_to_pty(PtyInstruction::SpawnInPlaceTerminal(
run_cmd,
name,
ClientTabIndexOrPaneId::ClientId(client_id),
))
.with_context(err_context)?;
},
}
},
Action::NewTiledPane(direction, run_command, name) => {
let should_float = false;
let run_cmd = run_command
@ -346,7 +393,7 @@ pub(crate) fn route_action(
run_cmd,
Some(should_float),
name,
ClientOrTabIndex::ClientId(client_id),
ClientTabIndexOrPaneId::ClientId(client_id),
),
};
senders.send_to_pty(pty_instr).with_context(err_context)?;
@ -394,7 +441,7 @@ pub(crate) fn route_action(
run_cmd,
None,
None,
ClientOrTabIndex::ClientId(client_id),
ClientTabIndexOrPaneId::ClientId(client_id),
),
};
senders.send_to_pty(pty_instr).with_context(err_context)?;
@ -616,17 +663,35 @@ pub(crate) fn route_action(
))
.with_context(err_context)?;
},
Action::NewInPlacePluginPane(run_plugin, name) => {
if let Some(pane_id) = pane_id {
senders
.send_to_screen(ScreenInstruction::NewInPlacePluginPane(
run_plugin, name, pane_id, client_id,
))
.with_context(err_context)?;
} else {
log::error!("Must have pane_id in order to open in place pane");
}
},
Action::StartOrReloadPlugin(run_plugin) => {
senders
.send_to_screen(ScreenInstruction::StartOrReloadPluginPane(run_plugin, None))
.with_context(err_context)?;
},
Action::LaunchOrFocusPlugin(run_plugin, should_float, move_to_focused_tab) => {
Action::LaunchOrFocusPlugin(
run_plugin,
should_float,
move_to_focused_tab,
should_open_in_place,
) => {
senders
.send_to_screen(ScreenInstruction::LaunchOrFocusPlugin(
run_plugin,
should_float,
move_to_focused_tab,
should_open_in_place,
pane_id,
client_id,
))
.with_context(err_context)?;
@ -752,7 +817,7 @@ pub(crate) fn route_thread_main(
-> Result<bool> {
let mut should_break = false;
match instruction {
ClientToServerMsg::Action(action, maybe_client_id) => {
ClientToServerMsg::Action(action, maybe_pane_id, maybe_client_id) => {
let client_id = maybe_client_id.unwrap_or(client_id);
if let Some(rlocked_sessions) = rlocked_sessions.as_ref() {
if let Action::SwitchToMode(input_mode) = action {
@ -769,6 +834,7 @@ pub(crate) fn route_thread_main(
if route_action(
action,
client_id,
maybe_pane_id.map(|p| PaneId::Terminal(p)),
rlocked_sessions.senders.clone(),
rlocked_sessions.capabilities.clone(),
rlocked_sessions.client_attributes.clone(),

View file

@ -32,7 +32,7 @@ use crate::{
panes::sixel::SixelImageStore,
panes::PaneId,
plugins::PluginInstruction,
pty::{ClientOrTabIndex, PtyInstruction, VteBytes},
pty::{ClientTabIndexOrPaneId, PtyInstruction, VteBytes},
tab::Tab,
thread_bus::Bus,
ui::{
@ -141,7 +141,7 @@ pub enum ScreenInstruction {
Option<InitialTitle>,
Option<ShouldFloat>,
HoldForCommand,
ClientOrTabIndex,
ClientTabIndexOrPaneId,
),
OpenInPlaceEditor(PaneId, ClientId),
TogglePaneEmbedOrFloating(ClientId),
@ -268,20 +268,24 @@ pub enum ScreenInstruction {
NewTiledPluginPane(RunPlugin, Option<String>, ClientId), // Option<String> is
// optional pane title
NewFloatingPluginPane(RunPlugin, Option<String>, ClientId), // Option<String> is an
NewInPlacePluginPane(RunPlugin, Option<String>, PaneId, ClientId), // Option<String> is an
// optional pane title
StartOrReloadPluginPane(RunPlugin, Option<String>),
AddPlugin(
Option<bool>, // should_float
bool, // should be opened in place
RunPlugin,
Option<String>, // pane title
usize, // tab index
u32, // plugin id
Option<PaneId>,
Option<ClientId>,
),
UpdatePluginLoadingStage(u32, LoadingIndication), // u32 - plugin_id
StartPluginLoadingIndication(u32, LoadingIndication), // u32 - plugin_id
ProgressPluginLoadingOffset(u32), // u32 - plugin id
RequestStateUpdateForPlugins,
LaunchOrFocusPlugin(RunPlugin, bool, bool, ClientId), // bools are: should_float, move_to_focused_tab
LaunchOrFocusPlugin(RunPlugin, bool, bool, bool, Option<PaneId>, ClientId), // bools are: should_float, move_to_focused_tab, should_open_in_place Option<PaneId> is the pane id to replace
SuppressPane(PaneId, ClientId), // bool is should_float
FocusPaneWithId(PaneId, bool, ClientId), // bool is should_float
RenamePane(PaneId, Vec<u8>),
@ -294,6 +298,12 @@ pub enum ScreenInstruction {
BreakPaneRight(ClientId),
BreakPaneLeft(ClientId),
UpdateSessionInfos(BTreeMap<String, SessionInfo>), // String is the session name
ReplacePane(
PaneId,
HoldForCommand,
Option<InitialTitle>,
ClientTabIndexOrPaneId,
),
}
impl From<&ScreenInstruction> for ScreenContext {
@ -468,6 +478,8 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::BreakPaneRight(..) => ScreenContext::BreakPaneRight,
ScreenInstruction::BreakPaneLeft(..) => ScreenContext::BreakPaneLeft,
ScreenInstruction::UpdateSessionInfos(..) => ScreenContext::UpdateSessionInfos,
ScreenInstruction::ReplacePane(..) => ScreenContext::ReplacePane,
ScreenInstruction::NewInPlacePluginPane(..) => ScreenContext::NewInPlacePluginPane,
}
}
}
@ -1822,7 +1834,63 @@ impl Screen {
self.render()?;
Ok(())
}
pub fn replace_pane(
&mut self,
new_pane_id: PaneId,
hold_for_command: HoldForCommand,
run_plugin: Option<Run>,
pane_title: Option<InitialTitle>,
client_id_tab_index_or_pane_id: ClientTabIndexOrPaneId,
) -> Result<()> {
let err_context = || format!("failed to replace pane");
let suppress_pane = |tab: &mut Tab, pane_id: PaneId, new_pane_id: PaneId| {
tab.suppress_pane_and_replace_with_pid(pane_id, new_pane_id, run_plugin);
if let Some(pane_title) = pane_title {
tab.rename_pane(pane_title.as_bytes().to_vec(), new_pane_id);
}
if let Some(hold_for_command) = hold_for_command {
let is_first_run = true;
tab.hold_pane(new_pane_id, None, is_first_run, hold_for_command)
}
};
match client_id_tab_index_or_pane_id {
ClientTabIndexOrPaneId::ClientId(client_id) => {
active_tab!(self, client_id, |tab: &mut Tab| {
match tab.get_active_pane_id(client_id) {
Some(pane_id) => {
suppress_pane(tab, pane_id, new_pane_id);
},
None => {
log::error!(
"Failed to find active pane for client id: {:?}",
client_id
);
},
}
})
},
ClientTabIndexOrPaneId::PaneId(pane_id) => {
let tab_index = self
.tabs
.iter()
.find(|(_tab_index, tab)| tab.has_pane_with_pid(&pane_id))
.map(|(tab_index, _tab)| *tab_index);
match tab_index {
Some(tab_index) => {
let tab = self.tabs.get_mut(&tab_index).with_context(err_context)?;
suppress_pane(tab, pane_id, new_pane_id);
},
None => {
log::error!("Could not find pane with id: {:?}", pane_id);
},
};
},
ClientTabIndexOrPaneId::TabIndex(tab_index) => {
log::error!("Cannot replace pane with tab index");
},
}
Ok(())
}
fn unblock_input(&self) -> Result<()> {
self.bus
.senders
@ -1919,7 +1987,7 @@ pub(crate) fn screen_thread_main(
client_or_tab_index,
) => {
match client_or_tab_index {
ClientOrTabIndex::ClientId(client_id) => {
ClientTabIndexOrPaneId::ClientId(client_id) => {
active_tab_and_connected_client_id!(screen, client_id, |tab: &mut Tab, client_id: ClientId| {
tab.new_pane(pid,
initial_pane_title,
@ -1942,7 +2010,7 @@ pub(crate) fn screen_thread_main(
)
}
},
ClientOrTabIndex::TabIndex(tab_index) => {
ClientTabIndexOrPaneId::TabIndex(tab_index) => {
if let Some(active_tab) = screen.tabs.get_mut(&tab_index) {
active_tab.new_pane(
pid,
@ -1959,6 +2027,9 @@ pub(crate) fn screen_thread_main(
log::error!("Tab index not found: {:?}", tab_index);
}
},
ClientTabIndexOrPaneId::PaneId(pane_id) => {
log::error!("cannot open a pane with a pane id??");
},
};
screen.unblock_input()?;
screen.log_and_report_session_state()?;
@ -1967,7 +2038,7 @@ pub(crate) fn screen_thread_main(
},
ScreenInstruction::OpenInPlaceEditor(pid, client_id) => {
active_tab!(screen, client_id, |tab: &mut Tab| tab
.suppress_active_pane(pid, client_id), ?);
.replace_active_pane_with_editor_pane(pid, client_id), ?);
screen.unblock_input()?;
screen.log_and_report_session_state()?;
@ -2892,11 +2963,14 @@ pub(crate) fn screen_thread_main(
let tab_index = screen.active_tab_indices.values().next().unwrap_or(&1);
let size = Size::default();
let should_float = Some(false);
let should_be_opened_in_place = false;
screen.bus.senders.send_to_plugin(PluginInstruction::Load(
should_float,
should_be_opened_in_place,
pane_title,
run_plugin,
*tab_index,
None, // pane it to replace
client_id,
size,
))?;
@ -2906,11 +2980,14 @@ pub(crate) fn screen_thread_main(
Some(tab_index) => {
let size = Size::default();
let should_float = Some(true);
let should_be_opened_in_place = false;
screen.bus.senders.send_to_plugin(PluginInstruction::Load(
should_float,
should_be_opened_in_place,
pane_title,
run_plugin,
*tab_index,
None, // pane id to replace
client_id,
size,
))?;
@ -2922,6 +2999,33 @@ pub(crate) fn screen_thread_main(
},
}
},
ScreenInstruction::NewInPlacePluginPane(
run_plugin,
pane_title,
pane_id_to_replace,
client_id,
) => match screen.active_tab_indices.values().next() {
Some(tab_index) => {
let size = Size::default();
let should_float = None;
let should_be_in_place = true;
screen.bus.senders.send_to_plugin(PluginInstruction::Load(
should_float,
should_be_in_place,
pane_title,
run_plugin,
*tab_index,
Some(pane_id_to_replace),
client_id,
size,
))?;
},
None => {
log::error!(
"Could not find an active tab - is there at least 1 connected user?"
);
},
},
ScreenInstruction::StartOrReloadPluginPane(run_plugin, pane_title) => {
let tab_index = screen.active_tab_indices.values().next().unwrap_or(&1);
let size = Size::default();
@ -2939,15 +3043,43 @@ pub(crate) fn screen_thread_main(
},
ScreenInstruction::AddPlugin(
should_float,
should_be_in_place,
run_plugin_location,
pane_title,
tab_index,
plugin_id,
pane_id_to_replace,
client_id,
) => {
let pane_title =
pane_title.unwrap_or_else(|| run_plugin_location.location.to_string());
let run_plugin = Run::Plugin(run_plugin_location);
if let Some(active_tab) = screen.tabs.get_mut(&tab_index) {
if should_be_in_place {
if let Some(pane_id_to_replace) = pane_id_to_replace {
let client_tab_index_or_pane_id =
ClientTabIndexOrPaneId::PaneId(pane_id_to_replace);
screen.replace_pane(
PaneId::Plugin(plugin_id),
None,
Some(run_plugin),
Some(pane_title),
client_tab_index_or_pane_id,
)?;
} else if let Some(client_id) = client_id {
let client_tab_index_or_pane_id =
ClientTabIndexOrPaneId::ClientId(client_id);
screen.replace_pane(
PaneId::Plugin(plugin_id),
None,
Some(run_plugin),
Some(pane_title),
client_tab_index_or_pane_id,
)?;
} else {
log::error!("Must have pane id to replace or connected client_id if replacing a pane");
}
} else if let Some(active_tab) = screen.tabs.get_mut(&tab_index) {
active_tab.new_pane(
PaneId::Plugin(plugin_id),
Some(pane_title),
@ -3003,8 +3135,32 @@ pub(crate) fn screen_thread_main(
run_plugin,
should_float,
move_to_focused_tab,
should_open_in_place,
pane_id_to_replace,
client_id,
) => {
match pane_id_to_replace {
Some(pane_id_to_replace) => match screen.active_tab_indices.values().next() {
Some(tab_index) => {
let size = Size::default();
screen.bus.senders.send_to_plugin(PluginInstruction::Load(
Some(should_float),
should_open_in_place,
None,
run_plugin,
*tab_index,
Some(pane_id_to_replace),
client_id,
size,
))?;
},
None => {
log::error!(
"Could not find an active tab - is there at least 1 connected user?"
);
},
},
None => {
let client_id = if screen.active_tab_indices.contains_key(&client_id) {
Some(client_id)
} else {
@ -3029,15 +3185,21 @@ pub(crate) fn screen_thread_main(
} else {
screen.bus.senders.send_to_plugin(PluginInstruction::Load(
Some(should_float),
should_open_in_place,
None,
run_plugin,
tab_index,
None, // pane id to replace
client_id,
Size::default(),
))?;
}
},
None => log::error!("No connected clients found - cannot load or focus plugin"),
None => log::error!(
"No connected clients found - cannot load or focus plugin"
),
}
},
}
},
ScreenInstruction::SuppressPane(pane_id, client_id) => {
@ -3109,6 +3271,26 @@ pub(crate) fn screen_thread_main(
ScreenInstruction::UpdateSessionInfos(new_session_infos) => {
screen.update_session_infos(new_session_infos)?;
},
ScreenInstruction::ReplacePane(
new_pane_id,
hold_for_command,
pane_title,
client_id_tab_index_or_pane_id,
) => {
let err_context = || format!("Failed to replace pane");
screen.replace_pane(
new_pane_id,
hold_for_command,
None,
pane_title,
client_id_tab_index_or_pane_id,
)?;
screen.unblock_input()?;
screen.log_and_report_session_state()?;
screen.render()?;
},
}
}
Ok(())

View file

@ -32,7 +32,7 @@ use crate::{
panes::{FloatingPanes, TiledPanes},
panes::{LinkHandler, PaneId, PluginPane, TerminalPane},
plugins::PluginInstruction,
pty::{ClientOrTabIndex, PtyInstruction, VteBytes},
pty::{ClientTabIndexOrPaneId, PtyInstruction, VteBytes},
thread_bus::ThreadSenders,
ClientId, ServerInstruction,
};
@ -991,8 +991,8 @@ impl Tab {
let name = None;
let should_float = true;
let client_id_or_tab_index = match client_id {
Some(client_id) => ClientOrTabIndex::ClientId(client_id),
None => ClientOrTabIndex::TabIndex(self.index),
Some(client_id) => ClientTabIndexOrPaneId::ClientId(client_id),
None => ClientTabIndexOrPaneId::TabIndex(self.index),
};
let instruction = PtyInstruction::SpawnTerminal(
default_shell,
@ -1074,7 +1074,11 @@ impl Tab {
self.add_tiled_pane(new_pane, pid, client_id)
}
}
pub fn suppress_active_pane(&mut self, pid: PaneId, client_id: ClientId) -> Result<()> {
pub fn replace_active_pane_with_editor_pane(
&mut self,
pid: PaneId,
client_id: ClientId,
) -> Result<()> {
// this method creates a new pane from pid and replaces it with the active pane
// the active pane is then suppressed (hidden and not rendered) until the current
// created pane is closed, in which case it will be replaced back by it
@ -1140,6 +1144,116 @@ impl Tab {
}
Ok(())
}
pub fn suppress_pane_and_replace_with_pid(
&mut self,
old_pane_id: PaneId,
new_pane_id: PaneId,
run_plugin: Option<Run>,
) -> Result<()> {
// this method creates a new pane from pid and replaces it with the active pane
// the active pane is then suppressed (hidden and not rendered) until the current
// created pane is closed, in which case it will be replaced back by it
let err_context = || format!("failed to suppress active pane");
match new_pane_id {
PaneId::Terminal(new_pane_id) => {
let next_terminal_position = self.get_next_terminal_position(); // TODO: this is not accurate in this case
let mut new_pane = TerminalPane::new(
new_pane_id,
PaneGeom::default(), // the initial size will be set later
self.style,
next_terminal_position,
String::new(),
self.link_handler.clone(),
self.character_cell_size.clone(),
self.sixel_image_store.clone(),
self.terminal_emulator_colors.clone(),
self.terminal_emulator_color_codes.clone(),
None,
None,
self.debug,
);
let replaced_pane = if self.floating_panes.panes_contain(&old_pane_id) {
self.floating_panes
.replace_pane(old_pane_id, Box::new(new_pane))
.ok()
} else {
self.tiled_panes
.replace_pane(old_pane_id, Box::new(new_pane))
};
match replaced_pane {
Some(replaced_pane) => {
resize_pty!(
replaced_pane,
self.os_api,
self.senders,
self.character_cell_size
);
self.suppressed_panes
.insert(PaneId::Terminal(new_pane_id), replaced_pane);
},
None => {
Err::<(), _>(anyhow!(
"Could not find editor pane to replace - is no pane focused?"
))
.with_context(err_context)
.non_fatal();
},
}
},
PaneId::Plugin(plugin_pid) => {
// TBD, currently unsupported
let mut new_pane = PluginPane::new(
plugin_pid,
PaneGeom::default(), // this will be filled out later
self.senders
.to_plugin
.as_ref()
.with_context(err_context)?
.clone(),
String::new(),
String::new(),
self.sixel_image_store.clone(),
self.terminal_emulator_colors.clone(),
self.terminal_emulator_color_codes.clone(),
self.link_handler.clone(),
self.character_cell_size.clone(),
self.connected_clients.borrow().iter().copied().collect(),
self.style,
run_plugin,
self.debug,
);
let replaced_pane = if self.floating_panes.panes_contain(&old_pane_id) {
self.floating_panes
.replace_pane(old_pane_id, Box::new(new_pane))
.ok()
} else {
self.tiled_panes
.replace_pane(old_pane_id, Box::new(new_pane))
};
match replaced_pane {
Some(replaced_pane) => {
resize_pty!(
replaced_pane,
self.os_api,
self.senders,
self.character_cell_size
);
self.suppressed_panes
.insert(PaneId::Plugin(plugin_pid), replaced_pane);
},
None => {
Err::<(), _>(anyhow!(
"Could not find editor pane to replace - is no pane focused?"
))
.with_context(err_context)
.non_fatal();
},
}
},
}
Ok(())
}
pub fn horizontal_split(
&mut self,
pid: PaneId,
@ -3427,6 +3541,10 @@ impl Tab {
})
}
pub fn suppress_pane(&mut self, pane_id: PaneId, client_id: ClientId) {
// this method places a pane in the suppressed pane with its own ID - this means we'll
// 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)) {
self.suppressed_panes.insert(pane_id, pane);
}

View file

@ -2094,7 +2094,8 @@ fn suppress_tiled_pane() {
let mut tab = create_new_tab(size, ModeInfo::default());
let new_pane_id = PaneId::Terminal(2);
let mut output = Output::default();
tab.suppress_active_pane(new_pane_id, client_id).unwrap();
tab.replace_active_pane_with_editor_pane(new_pane_id, client_id)
.unwrap();
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes()))
.unwrap();
tab.render(&mut output).unwrap();
@ -2122,7 +2123,8 @@ fn suppress_floating_pane() {
tab.toggle_floating_panes(Some(client_id), None).unwrap();
tab.new_pane(new_pane_id, None, None, None, Some(client_id))
.unwrap();
tab.suppress_active_pane(editor_pane_id, client_id).unwrap();
tab.replace_active_pane_with_editor_pane(editor_pane_id, client_id)
.unwrap();
tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes()))
.unwrap();
tab.render(&mut output).unwrap();
@ -2145,7 +2147,8 @@ fn close_suppressing_tiled_pane() {
let mut tab = create_new_tab(size, ModeInfo::default());
let new_pane_id = PaneId::Terminal(2);
let mut output = Output::default();
tab.suppress_active_pane(new_pane_id, client_id).unwrap();
tab.replace_active_pane_with_editor_pane(new_pane_id, client_id)
.unwrap();
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes()))
.unwrap();
tab.handle_pty_bytes(1, Vec::from("\n\n\nI am the original pane".as_bytes()))
@ -2176,7 +2179,8 @@ fn close_suppressing_floating_pane() {
tab.toggle_floating_panes(Some(client_id), None).unwrap();
tab.new_pane(new_pane_id, None, None, None, Some(client_id))
.unwrap();
tab.suppress_active_pane(editor_pane_id, client_id).unwrap();
tab.replace_active_pane_with_editor_pane(editor_pane_id, client_id)
.unwrap();
tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes()))
.unwrap();
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am the original pane".as_bytes()))
@ -2202,7 +2206,8 @@ fn suppress_tiled_pane_float_it_and_close() {
let mut tab = create_new_tab(size, ModeInfo::default());
let new_pane_id = PaneId::Terminal(2);
let mut output = Output::default();
tab.suppress_active_pane(new_pane_id, client_id).unwrap();
tab.replace_active_pane_with_editor_pane(new_pane_id, client_id)
.unwrap();
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes()))
.unwrap();
tab.handle_pty_bytes(1, Vec::from("\n\n\nI am the original pane".as_bytes()))
@ -2234,7 +2239,8 @@ fn suppress_floating_pane_embed_it_and_close_it() {
tab.toggle_floating_panes(Some(client_id), None).unwrap();
tab.new_pane(new_pane_id, None, None, None, Some(client_id))
.unwrap();
tab.suppress_active_pane(editor_pane_id, client_id).unwrap();
tab.replace_active_pane_with_editor_pane(editor_pane_id, client_id)
.unwrap();
tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes()))
.unwrap();
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am the original pane".as_bytes()))
@ -2261,7 +2267,8 @@ fn resize_whole_tab_while_tiled_pane_is_suppressed() {
let mut tab = create_new_tab(size, ModeInfo::default());
let new_pane_id = PaneId::Terminal(2);
let mut output = Output::default();
tab.suppress_active_pane(new_pane_id, client_id).unwrap();
tab.replace_active_pane_with_editor_pane(new_pane_id, client_id)
.unwrap();
tab.handle_pty_bytes(2, Vec::from("\n\n\nI am an editor pane".as_bytes()))
.unwrap();
tab.resize_whole_tab(Size {
@ -2294,7 +2301,8 @@ fn resize_whole_tab_while_floting_pane_is_suppressed() {
tab.toggle_floating_panes(Some(client_id), None).unwrap();
tab.new_pane(new_pane_id, None, None, None, Some(client_id))
.unwrap();
tab.suppress_active_pane(editor_pane_id, client_id).unwrap();
tab.replace_active_pane_with_editor_pane(editor_pane_id, client_id)
.unwrap();
tab.handle_pty_bytes(3, Vec::from("\n\n\nI am an editor pane".as_bytes()))
.unwrap();
tab.resize_whole_tab(Size {

View file

@ -330,7 +330,8 @@ fn write_to_suppressed_pane() {
tab.vertical_split(PaneId::Terminal(2), None, 1).unwrap();
// Suppress pane 2 and remove it from active panes
tab.suppress_active_pane(PaneId::Terminal(2), 1).unwrap();
tab.replace_active_pane_with_editor_pane(PaneId::Terminal(2), 1)
.unwrap();
tab.tiled_panes.remove_pane(PaneId::Terminal(2));
// Make sure it's suppressed now

View file

@ -114,6 +114,7 @@ fn send_cli_action_to_server(
route_action(
action,
client_id,
None,
senders.clone(),
capabilities,
client_attributes.clone(),
@ -1866,6 +1867,7 @@ pub fn send_cli_new_pane_action_with_default_parameters() {
plugin: None,
cwd: None,
floating: false,
in_place: false,
name: None,
close_on_exit: false,
start_suspended: false,
@ -1903,6 +1905,7 @@ pub fn send_cli_new_pane_action_with_split_direction() {
plugin: None,
cwd: None,
floating: false,
in_place: false,
name: None,
close_on_exit: false,
start_suspended: false,
@ -1940,6 +1943,7 @@ pub fn send_cli_new_pane_action_with_command_and_cwd() {
plugin: None,
cwd: Some("/some/folder".into()),
floating: false,
in_place: false,
name: None,
close_on_exit: false,
start_suspended: false,
@ -1976,6 +1980,7 @@ pub fn send_cli_edit_action_with_default_parameters() {
direction: None,
line_number: None,
floating: false,
in_place: false,
cwd: None,
};
send_cli_action_to_server(&session_metadata, cli_edit_action, client_id);
@ -2009,6 +2014,7 @@ pub fn send_cli_edit_action_with_line_number() {
direction: None,
line_number: Some(100),
floating: false,
in_place: false,
cwd: None,
};
send_cli_action_to_server(&session_metadata, cli_edit_action, client_id);
@ -2042,6 +2048,7 @@ pub fn send_cli_edit_action_with_split_direction() {
direction: Some(Direction::Down),
line_number: None,
floating: false,
in_place: false,
cwd: None,
};
send_cli_action_to_server(&session_metadata, cli_edit_action, client_id);
@ -2567,6 +2574,7 @@ pub fn send_cli_launch_or_focus_plugin_action() {
);
let cli_action = CliAction::LaunchOrFocusPlugin {
floating: true,
in_place: false,
move_to_focused_tab: true,
url: url::Url::parse("file:/path/to/fake/plugin").unwrap(),
configuration: Default::default(),
@ -2625,6 +2633,7 @@ pub fn send_cli_launch_or_focus_plugin_action_when_plugin_is_already_loaded() {
);
let cli_action = CliAction::LaunchOrFocusPlugin {
floating: true,
in_place: false,
move_to_focused_tab: true,
url: url::Url::parse("file:/path/to/fake/plugin").unwrap(),
configuration: Default::default(),

View file

@ -1,6 +1,6 @@
---
source: zellij-server/src/./unit/screen_tests.rs
assertion_line: 2572
assertion_line: 2596
expression: "format!(\"{:#?}\", plugin_load_instruction)"
---
Some(
@ -8,6 +8,7 @@ Some(
Some(
true,
),
false,
None,
RunPlugin {
_allow_exec_host_cmd: false,
@ -19,6 +20,7 @@ Some(
),
},
0,
None,
1,
Size {
rows: 0,

View file

@ -87,6 +87,14 @@ pub fn open_file_floating(file_to_open: FileToOpen) {
unsafe { host_run_plugin_command() };
}
/// Open a file in the user's default `$EDITOR` in a new floating pane
pub fn open_file_in_place(file_to_open: FileToOpen) {
let plugin_command = PluginCommand::OpenFileInPlace(file_to_open);
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 new terminal pane to the specified location on the host filesystem
pub fn open_terminal<P: AsRef<Path>>(path: P) {
let file_to_open = FileToOpen::new(path.as_ref().to_path_buf());
@ -105,8 +113,16 @@ pub fn open_terminal_floating<P: AsRef<Path>>(path: P) {
unsafe { host_run_plugin_command() };
}
/// Open a new floating terminal pane to the specified location on the host filesystem
pub fn open_terminal_in_place<P: AsRef<Path>>(path: P) {
let file_to_open = FileToOpen::new(path.as_ref().to_path_buf());
let plugin_command = PluginCommand::OpenTerminalInPlace(file_to_open);
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 new 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<P: AsRef<Path>, A: AsRef<str>>(path: P, args: Vec<A>) {
pub fn open_command_pane(command_to_run: CommandToRun) {
let plugin_command = PluginCommand::OpenCommandPane(command_to_run);
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
@ -115,7 +131,6 @@ pub fn open_command_pane(command_to_run: CommandToRun) {
}
/// Open a new floating 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_floating<P: AsRef<Path>, A: AsRef<str>>(path: P, args: Vec<A>) {
pub fn open_command_pane_floating(command_to_run: CommandToRun) {
let plugin_command = PluginCommand::OpenCommandPaneFloating(command_to_run);
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
@ -123,6 +138,14 @@ pub fn open_command_pane_floating(command_to_run: CommandToRun) {
unsafe { host_run_plugin_command() };
}
/// Open a new floating 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_in_place(command_to_run: CommandToRun) {
let plugin_command = PluginCommand::OpenCommandPaneInPlace(command_to_run);
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);

View file

@ -137,6 +137,8 @@ pub struct LaunchOrFocusPluginPayload {
pub plugin_configuration: ::core::option::Option<PluginConfiguration>,
#[prost(bool, tag = "4")]
pub move_to_focused_tab: bool,
#[prost(bool, tag = "5")]
pub should_open_in_place: bool,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]

View file

@ -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"
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"
)]
pub payload: ::core::option::Option<plugin_command::Payload>,
}
@ -90,6 +90,12 @@ pub mod plugin_command {
RequestPluginPermissionPayload(super::RequestPluginPermissionPayload),
#[prost(message, tag = "39")]
SwitchSessionPayload(super::SwitchSessionPayload),
#[prost(message, tag = "40")]
OpenFileInPlacePayload(super::OpenFilePayload),
#[prost(message, tag = "41")]
OpenTerminalInPlacePayload(super::OpenFilePayload),
#[prost(message, tag = "42")]
OpenCommandPaneInPlacePayload(super::OpenCommandPanePayload),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
@ -254,6 +260,9 @@ pub enum CommandName {
ReportCrash = 65,
RequestPluginPermissions = 66,
SwitchSession = 67,
OpenTerminalInPlace = 68,
OpenCommandInPlace = 69,
OpenFileInPlace = 70,
}
impl CommandName {
/// String value of the enum field names used in the ProtoBuf definition.
@ -330,6 +339,9 @@ impl CommandName {
CommandName::ReportCrash => "ReportCrash",
CommandName::RequestPluginPermissions => "RequestPluginPermissions",
CommandName::SwitchSession => "SwitchSession",
CommandName::OpenTerminalInPlace => "OpenTerminalInPlace",
CommandName::OpenCommandInPlace => "OpenCommandInPlace",
CommandName::OpenFileInPlace => "OpenFileInPlace",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@ -403,6 +415,9 @@ impl CommandName {
"ReportCrash" => Some(Self::ReportCrash),
"RequestPluginPermissions" => Some(Self::RequestPluginPermissions),
"SwitchSession" => Some(Self::SwitchSession),
"OpenTerminalInPlace" => Some(Self::OpenTerminalInPlace),
"OpenCommandInPlace" => Some(Self::OpenCommandInPlace),
"OpenFileInPlace" => Some(Self::OpenFileInPlace),
_ => None,
}
}

View file

@ -158,6 +158,18 @@ pub enum Sessions {
#[clap(short, long, value_parser, default_value("false"), takes_value(false))]
floating: bool,
/// Open the new pane in place of the current pane, temporarily suspending it
#[clap(
short,
long,
value_parser,
default_value("false"),
takes_value(false),
conflicts_with("floating"),
conflicts_with("direction")
)]
in_place: bool,
/// Name of the new pane
#[clap(short, long, value_parser)]
name: Option<String>,
@ -183,6 +195,18 @@ pub enum Sessions {
#[clap(short, long, value_parser, conflicts_with("floating"))]
direction: Option<Direction>,
/// Open the new pane in place of the current pane, temporarily suspending it
#[clap(
short,
long,
value_parser,
default_value("false"),
takes_value(false),
conflicts_with("floating"),
conflicts_with("direction")
)]
in_place: bool,
/// Open the new pane in floating mode
#[clap(short, long, value_parser, default_value("false"), takes_value(false))]
floating: bool,
@ -292,6 +316,18 @@ pub enum CliAction {
#[clap(short, long, value_parser, default_value("false"), takes_value(false))]
floating: bool,
/// Open the new pane in place of the current pane, temporarily suspending it
#[clap(
short,
long,
value_parser,
default_value("false"),
takes_value(false),
conflicts_with("floating"),
conflicts_with("direction")
)]
in_place: bool,
/// Name of the new pane
#[clap(short, long, value_parser)]
name: Option<String>,
@ -335,6 +371,18 @@ pub enum CliAction {
#[clap(short, long, value_parser, default_value("false"), takes_value(false))]
floating: bool,
/// Open the new pane in place of the current pane, temporarily suspending it
#[clap(
short,
long,
value_parser,
default_value("false"),
takes_value(false),
conflicts_with("floating"),
conflicts_with("direction")
)]
in_place: bool,
/// Change the working directory of the editor
#[clap(long, value_parser)]
cwd: Option<PathBuf>,
@ -409,6 +457,8 @@ pub enum CliAction {
#[clap(short, long, value_parser)]
floating: bool,
#[clap(short, long, value_parser)]
in_place: bool,
#[clap(short, long, value_parser)]
move_to_focused_tab: bool,
url: Url,
#[clap(short, long, value_parser)]

View file

@ -1061,4 +1061,7 @@ pub enum PluginCommand {
ReportPanic(String), // stringified panic
RequestPluginPermissions(Vec<PermissionType>),
SwitchSession(ConnectToSession),
OpenTerminalInPlace(FileToOpen), // only used for the path as cwd
OpenFileInPlace(FileToOpen),
OpenCommandPaneInPlace(CommandToRun),
}

View file

@ -343,6 +343,8 @@ pub enum ScreenContext {
BreakPaneRight,
BreakPaneLeft,
UpdateSessionInfos,
ReplacePane,
NewInPlacePluginPane,
}
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
@ -358,6 +360,7 @@ pub enum PtyContext {
ClosePane,
CloseTab,
ReRunCommandInPane,
SpawnInPlaceTerminal,
Exit,
}

View file

@ -161,11 +161,14 @@ pub enum Action {
Option<PathBuf>,
Option<Direction>,
bool,
), // usize is an optional line number, Option<PathBuf> is an optional cwd, bool is floating true/false
bool,
), // usize is an optional line number, Option<PathBuf> is an optional cwd, bool is floating true/false, second bool is in_place
/// Open a new floating pane
NewFloatingPane(Option<RunCommandAction>, Option<String>), // String is an optional pane name
/// Open a new tiled (embedded, non-floating) pane
NewTiledPane(Option<Direction>, Option<RunCommandAction>, Option<String>), // String is an
/// Open a new pane in place of the focused one, suppressing it instead
NewInPlacePane(Option<RunCommandAction>, Option<String>), // String is an
// optional pane
// name
/// Embed focused pane in tab if floating or float focused pane if embedded
@ -204,7 +207,8 @@ pub enum Action {
LeftClick(Position),
RightClick(Position),
MiddleClick(Position),
LaunchOrFocusPlugin(RunPlugin, bool, bool), // bools => should float, move_to_focused_tab
LaunchOrFocusPlugin(RunPlugin, bool, bool, bool), // bools => should float,
// move_to_focused_tab, should_open_in_place
LeftMouseRelease(Position),
RightMouseRelease(Position),
MiddleMouseRelease(Position),
@ -232,6 +236,7 @@ pub enum Action {
/// Open a new tiled (embedded, non-floating) plugin pane
NewTiledPluginPane(RunPlugin, Option<String>), // String is an optional name
NewFloatingPluginPane(RunPlugin, Option<String>), // String is an optional name
NewInPlacePluginPane(RunPlugin, Option<String>), // String is an optional name
StartOrReloadPlugin(RunPlugin),
CloseTerminalPane(u32),
ClosePluginPane(u32),
@ -293,6 +298,7 @@ impl Action {
plugin,
cwd,
floating,
in_place,
name,
close_on_exit,
start_suspended,
@ -313,6 +319,8 @@ impl Action {
};
if floating {
Ok(vec![Action::NewFloatingPluginPane(plugin, name)])
} else if in_place {
Ok(vec![Action::NewInPlacePluginPane(plugin, name)])
} else {
// it is intentional that a new tiled plugin pane cannot include a
// direction
@ -342,6 +350,8 @@ impl Action {
Some(run_command_action),
name,
)])
} else if in_place {
Ok(vec![Action::NewInPlacePane(Some(run_command_action), name)])
} else {
Ok(vec![Action::NewTiledPane(
direction,
@ -352,6 +362,8 @@ impl Action {
} else {
if floating {
Ok(vec![Action::NewFloatingPane(None, name)])
} else if in_place {
Ok(vec![Action::NewInPlacePane(None, name)])
} else {
Ok(vec![Action::NewTiledPane(direction, None, name)])
}
@ -362,6 +374,7 @@ impl Action {
file,
line_number,
floating,
in_place,
cwd,
} => {
let mut file = file;
@ -380,6 +393,7 @@ impl Action {
cwd,
direction,
floating,
in_place,
)])
},
CliAction::SwitchMode { input_mode } => {
@ -501,6 +515,7 @@ impl Action {
CliAction::LaunchOrFocusPlugin {
url,
floating,
in_place,
move_to_focused_tab,
configuration,
} => {
@ -516,6 +531,7 @@ impl Action {
run_plugin,
floating,
move_to_focused_tab,
in_place,
)])
},
}

View file

@ -84,7 +84,7 @@ pub enum ClientToServerMsg {
Option<usize>, // tab position to focus
Option<(u32, bool)>, // (pane_id, is_plugin) => pane id to focus
),
Action(Action, Option<ClientId>),
Action(Action, Option<u32>, Option<ClientId>), // u32 is the terminal id
ClientExited,
KillSession,
ConnStatus,

View file

@ -886,6 +886,9 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
let floating = command_metadata
.and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "floating"))
.unwrap_or(false);
let in_place = command_metadata
.and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "in_place"))
.unwrap_or(false);
let run_command_action = RunCommandAction {
command: PathBuf::from(command),
args,
@ -896,6 +899,8 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
};
if floating {
Ok(Action::NewFloatingPane(Some(run_command_action), name))
} else if in_place {
Ok(Action::NewInPlacePane(Some(run_command_action), name))
} else {
Ok(Action::NewTiledPane(
direction,
@ -923,6 +928,9 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
let move_to_focused_tab = command_metadata
.and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "move_to_focused_tab"))
.unwrap_or(false);
let should_open_in_place = command_metadata
.and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "in_place"))
.unwrap_or(false);
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
let location = RunPluginLocation::parse(&plugin_path, Some(current_dir))?;
let configuration = KdlLayoutParser::parse_plugin_user_configuration(&kdl_action)?;
@ -935,6 +943,7 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
run_plugin,
should_float,
move_to_focused_tab,
should_open_in_place,
))
},
"PreviousSwapLayout" => Ok(Action::PreviousSwapLayout),

View file

@ -85,6 +85,7 @@ message LaunchOrFocusPluginPayload {
bool should_float = 2;
optional PluginConfiguration plugin_configuration = 3;
bool move_to_focused_tab = 4;
bool should_open_in_place = 5;
}
message GoToTabNamePayload {

View file

@ -232,12 +232,14 @@ impl TryFrom<ProtobufAction> for Action {
.and_then(|d| ProtobufResizeDirection::from_i32(d))
.and_then(|d| d.try_into().ok());
let should_float = payload.should_float;
let should_be_in_place = false;
Ok(Action::EditFile(
file_to_edit,
line_number,
cwd,
direction,
should_float,
should_be_in_place,
))
},
_ => Err("Wrong payload for Action::NewPane"),
@ -400,10 +402,12 @@ impl TryFrom<ProtobufAction> for Action {
};
let should_float = payload.should_float;
let move_to_focused_tab = payload.move_to_focused_tab;
let should_open_in_place = payload.should_open_in_place;
Ok(Action::LaunchOrFocusPlugin(
run_plugin,
should_float,
move_to_focused_tab,
should_open_in_place,
))
},
_ => Err("Wrong payload for Action::LaunchOrFocusPlugin"),
@ -814,7 +818,14 @@ impl TryFrom<Action> for ProtobufAction {
})),
})
},
Action::EditFile(path_to_file, line_number, cwd, direction, should_float) => {
Action::EditFile(
path_to_file,
line_number,
cwd,
direction,
should_float,
_should_be_in_place,
) => {
let file_to_edit = path_to_file.display().to_string();
let cwd = cwd.map(|cwd| cwd.display().to_string());
let direction: Option<i32> = direction
@ -959,7 +970,12 @@ impl TryFrom<Action> for ProtobufAction {
optional_payload: Some(OptionalPayload::MiddleClickPayload(position)),
})
},
Action::LaunchOrFocusPlugin(run_plugin, should_float, move_to_focused_tab) => {
Action::LaunchOrFocusPlugin(
run_plugin,
should_float,
move_to_focused_tab,
should_open_in_place,
) => {
let url: Url = Url::from(&run_plugin.location);
Ok(ProtobufAction {
name: ProtobufActionName::LaunchOrFocusPlugin as i32,
@ -968,6 +984,7 @@ impl TryFrom<Action> for ProtobufAction {
plugin_url: url.into(),
should_float,
move_to_focused_tab,
should_open_in_place,
plugin_configuration: Some(run_plugin.configuration.try_into()?),
},
)),
@ -1149,6 +1166,8 @@ impl TryFrom<Action> for ProtobufAction {
}),
Action::NoOp
| Action::Confirm
| Action::NewInPlacePane(..)
| Action::NewInPlacePluginPane(..)
| Action::Deny
| Action::Copy
| Action::SkipConfirm(..) => Err("Unsupported action"),

View file

@ -79,6 +79,9 @@ enum CommandName {
ReportCrash = 65;
RequestPluginPermissions = 66;
SwitchSession = 67;
OpenTerminalInPlace = 68;
OpenCommandInPlace = 69;
OpenFileInPlace = 70;
}
message PluginCommand {
@ -122,6 +125,9 @@ message PluginCommand {
string report_crash_payload = 37;
RequestPluginPermissionPayload request_plugin_permission_payload = 38;
SwitchSessionPayload switch_session_payload = 39;
OpenFilePayload open_file_in_place_payload = 40;
OpenFilePayload open_terminal_in_place_payload = 41;
OpenCommandPanePayload open_command_pane_in_place_payload = 42;
}
}

View file

@ -82,7 +82,7 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
None => Err("Malformed open file payload"),
}
},
_ => Err("Mismatched payload for OpenFile"),
_ => Err("Mismatched payload for OpenFileFloating"),
},
Some(CommandName::OpenTerminal) => match protobuf_plugin_command.payload {
Some(Payload::OpenTerminalPayload(file_to_open_payload)) => {
@ -520,6 +520,39 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
},
_ => Err("Mismatched payload for SwitchSession"),
},
Some(CommandName::OpenTerminalInPlace) => match protobuf_plugin_command.payload {
Some(Payload::OpenTerminalInPlacePayload(file_to_open_payload)) => {
match file_to_open_payload.file_to_open {
Some(file_to_open) => {
Ok(PluginCommand::OpenTerminalInPlace(file_to_open.try_into()?))
},
None => Err("Malformed open terminal in-place payload"),
}
},
_ => Err("Mismatched payload for OpenTerminalInPlace"),
},
Some(CommandName::OpenFileInPlace) => match protobuf_plugin_command.payload {
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()?))
},
None => Err("Malformed open file in place payload"),
}
},
_ => Err("Mismatched payload for OpenFileInPlace"),
},
Some(CommandName::OpenCommandInPlace) => match protobuf_plugin_command.payload {
Some(Payload::OpenCommandPaneInPlacePayload(command_to_run_payload)) => {
match command_to_run_payload.command_to_run {
Some(command_to_run) => Ok(PluginCommand::OpenCommandPaneInPlace(
command_to_run.try_into()?,
)),
None => Err("Malformed open command pane in-place payload"),
}
},
_ => Err("Mismatched payload for OpenCommandPaneInPlace"),
},
None => Err("Unrecognized plugin command"),
}
}
@ -875,6 +908,26 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
pane_id_is_plugin: switch_to_session.pane_id.map(|p| p.1),
})),
}),
PluginCommand::OpenTerminalInPlace(cwd) => Ok(ProtobufPluginCommand {
name: CommandName::OpenTerminalInPlace as i32,
payload: Some(Payload::OpenTerminalInPlacePayload(OpenFilePayload {
file_to_open: Some(cwd.try_into()?),
})),
}),
PluginCommand::OpenFileInPlace(file_to_open) => Ok(ProtobufPluginCommand {
name: CommandName::OpenFileInPlace as i32,
payload: Some(Payload::OpenFileInPlacePayload(OpenFilePayload {
file_to_open: Some(file_to_open.try_into()?),
})),
}),
PluginCommand::OpenCommandPaneInPlace(command_to_run) => Ok(ProtobufPluginCommand {
name: CommandName::OpenCommandInPlace as i32,
payload: Some(Payload::OpenCommandPaneInPlacePayload(
OpenCommandPanePayload {
command_to_run: Some(command_to_run.try_into()?),
},
)),
}),
}
}
}

View file

@ -2506,6 +2506,7 @@ Config {
},
true,
true,
false,
),
SwitchToMode(
Normal,

View file

@ -2506,6 +2506,7 @@ Config {
},
true,
true,
false,
),
SwitchToMode(
Normal,

View file

@ -2506,6 +2506,7 @@ Config {
},
true,
true,
false,
),
SwitchToMode(
Normal,

View file

@ -2506,6 +2506,7 @@ Config {
},
true,
true,
false,
),
SwitchToMode(
Normal,

View file

@ -2506,6 +2506,7 @@ Config {
},
true,
true,
false,
),
SwitchToMode(
Normal,