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
This commit is contained in:
Aram Drevekenin 2024-07-29 11:40:01 +02:00 committed by GitHub
parent a6fe5ff1d5
commit 355463383a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
53 changed files with 3082 additions and 621 deletions

View File

@ -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) => {

View File

@ -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<KeyWithModifier
}
if mi.mode == IM::Pane { vec![
(s("New"), s("New"), single_action_key(&km, &[A::NewPane(None, None), TO_NORMAL])),
(s("New"), s("New"), single_action_key(&km, &[A::NewPane(None, None, false), TO_NORMAL])),
(s("Change Focus"), s("Move"),
action_key_group(&km, &[&[A::MoveFocus(Dir::Left)], &[A::MoveFocus(Dir::Down)],
&[A::MoveFocus(Dir::Up)], &[A::MoveFocus(Dir::Right)]])),
@ -1271,8 +1271,8 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<KeyWithModifier
(s("Move focus"), s("Move"), action_key_group(&km, &[
&[A::MoveFocus(Dir::Left)], &[A::MoveFocus(Dir::Down)],
&[A::MoveFocus(Dir::Up)], &[A::MoveFocus(Dir::Right)]])),
(s("Split down"), s("Down"), action_key(&km, &[A::NewPane(Some(Dir::Down), None), TO_NORMAL])),
(s("Split right"), s("Right"), action_key(&km, &[A::NewPane(Some(Dir::Right), None), TO_NORMAL])),
(s("Split down"), s("Down"), action_key(&km, &[A::NewPane(Some(Dir::Down), None, false), TO_NORMAL])),
(s("Split right"), s("Right"), action_key(&km, &[A::NewPane(Some(Dir::Right), None, false), TO_NORMAL])),
(s("Fullscreen"), s("Fullscreen"), action_key(&km, &[A::ToggleFocusFullscreen, TO_NORMAL])),
(s("New tab"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None, None, None), TO_NORMAL])),
(s("Rename tab"), s("Rename"),

View File

@ -149,7 +149,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<KeyWithModifier
}
if mi.mode == IM::Pane { vec![
(s("New"), s("New"), action_key(&km, &[A::NewPane(None, None), TO_NORMAL])),
(s("New"), s("New"), action_key(&km, &[A::NewPane(None, None, false), TO_NORMAL])),
(s("Change Focus"), s("Move"),
action_key_group(&km, &[&[A::MoveFocus(Dir::Left)], &[A::MoveFocus(Dir::Down)],
&[A::MoveFocus(Dir::Up)], &[A::MoveFocus(Dir::Right)]])),
@ -256,8 +256,8 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<KeyWithModifier
(s("Move focus"), s("Move"), action_key_group(&km, &[
&[A::MoveFocus(Dir::Left)], &[A::MoveFocus(Dir::Down)],
&[A::MoveFocus(Dir::Up)], &[A::MoveFocus(Dir::Right)]])),
(s("Split down"), s("Down"), action_key(&km, &[A::NewPane(Some(Dir::Down), None), TO_NORMAL])),
(s("Split right"), s("Right"), action_key(&km, &[A::NewPane(Some(Dir::Right), None), TO_NORMAL])),
(s("Split down"), s("Down"), action_key(&km, &[A::NewPane(Some(Dir::Down), None, false), TO_NORMAL])),
(s("Split right"), s("Right"), action_key(&km, &[A::NewPane(Some(Dir::Right), None, false), TO_NORMAL])),
(s("Fullscreen"), s("Fullscreen"), action_key(&km, &[A::ToggleFocusFullscreen, TO_NORMAL])),
(s("New tab"), s("New"), action_key(&km, &[A::NewTab(None, vec![], None, None, None), TO_NORMAL])),
(s("Rename tab"), s("Rename"),
@ -713,7 +713,7 @@ mod tests {
),
(
KeyWithModifier::new(BareKey::Char('n')),
vec![Action::NewPane(None, None), TO_NORMAL],
vec![Action::NewPane(None, None, false), TO_NORMAL],
),
(
KeyWithModifier::new(BareKey::Char('x')),
@ -763,7 +763,7 @@ mod tests {
),
(
KeyWithModifier::new(BareKey::Char('n')),
vec![Action::NewPane(None, None), TO_NORMAL],
vec![Action::NewPane(None, None, false), TO_NORMAL],
),
(
KeyWithModifier::new(BareKey::Char('x')),
@ -809,7 +809,7 @@ mod tests {
),
(
KeyWithModifier::new(BareKey::Backspace),
vec![Action::NewPane(None, None), TO_NORMAL],
vec![Action::NewPane(None, None, false), TO_NORMAL],
),
(
KeyWithModifier::new(BareKey::Esc),

View File

@ -62,7 +62,7 @@ struct Keygroups<'a> {
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 {

View File

@ -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 {

View File

@ -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()

View File

@ -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,

View File

@ -1,4 +1,4 @@
mod floating_pane_grid;
pub mod floating_pane_grid;
use zellij_utils::{
data::{Direction, PaneInfo, ResizeStrategy},
position::Position,

View File

@ -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;

View File

@ -98,6 +98,15 @@ impl From<ZellijUtilsPaneId> for PaneId {
}
}
impl Into<ZellijUtilsPaneId> 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

View File

@ -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<RunPluginOrAlias, Vec<PluginId>> = HashMap::new();
tab_layout = tab_layout.or_else(|| Some(layout.new_tab().0));
tab_layout

View File

@ -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));
}

View File

@ -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,
),
)

View File

@ -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,
),
),
)

View File

@ -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,
),

View File

@ -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,
),

View File

@ -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,
),

View File

@ -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,
),

View File

@ -24,6 +24,7 @@ Some(
),
None,
None,
false,
ClientId(
1,
),

View File

@ -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,
),
)

View File

@ -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<ClientId> {
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),
};

View File

@ -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<PermissionType>) -> 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<String, String>) {
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<FloatingPaneCoordinates>,
context: BTreeMap<String, String>,
) {
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<String, String>,
) {
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<String, String>,
) {
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,

View File

@ -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<bool>,
Option<String>,
Option<FloatingPaneCoordinates>,
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<TerminalAction>, Option<String>, ClientId), // String is an
// optional pane
// name
// bool is start_suppressed
SpawnTerminalHorizontally(Option<TerminalAction>, Option<String>, ClientId), // String is an
// optional pane
// name
@ -141,29 +143,36 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> 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<Layout>) -> 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<Layout>) -> 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<Layout>) -> 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<Layout>) -> 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<Layout>) -> 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(),
)

View File

@ -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),
),
};

View File

@ -152,6 +152,7 @@ pub enum ScreenInstruction {
HoldForCommand,
Option<Run>, // invoked with
Option<FloatingPaneCoordinates>,
bool, // start suppressed
ClientTabIndexOrPaneId,
),
OpenInPlaceEditor(PaneId, ClientId),
@ -312,6 +313,7 @@ pub enum ScreenInstruction {
u32, // plugin id
Option<PaneId>,
Option<PathBuf>, // cwd
bool, // start suppressed
Option<ClientId>,
),
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 {

View File

@ -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<bool>,
invoked_with: Option<Run>,
floating_pane_coordinates: Option<FloatingPaneCoordinates>,
start_suppressed: bool,
client_id: Option<ClientId>,
) -> 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<dyn Pane>
},
};
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<PaneId> {
// 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<PaneId> {
self.tiled_panes
@ -2494,26 +2529,22 @@ impl Tab {
id: PaneId,
ignore_suppressed_panes: bool,
client_id: Option<ClientId>,
) -> Option<Box<dyn Pane>> {
) {
// 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<ClientId>,
) -> Option<Box<dyn Pane>> {
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));

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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();

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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<String, String>) {
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<FloatingPaneCoordinates>) {
let plugin_command = PluginCommand::OpenFileFloating(file_to_open, coordinates);
pub fn open_file_floating(
file_to_open: FileToOpen,
coordinates: Option<FloatingPaneCoordinates>,
context: BTreeMap<String, String>,
) {
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<String, String>) {
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<String, String>,
) {
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);

View File

@ -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<event::Payload>,
}
@ -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<PaneId>,
}
/// 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<ContextItem>,
}
#[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<i32>,
#[prost(message, repeated, tag = "3")]
pub context: ::prost::alloc::vec::Vec<ContextItem>,
}
#[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<SessionManifest>,
@ -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<Self> {
match value {
"Terminal" => Some(Self::Terminal),
"Plugin" => Some(Self::Plugin),
_ => None,
}
}

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, 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<plugin_command::Payload>,
}
@ -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<super::file::File>,
#[prost(message, optional, tag = "2")]
pub floating_pane_coordinates: ::core::option::Option<FloatingPaneCoordinates>,
#[prost(message, repeated, tag = "3")]
pub context: ::prost::alloc::vec::Vec<ContextItem>,
}
#[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,
}
}

View File

@ -915,7 +915,10 @@ pub enum Event {
// context
CommandPaneOpened(u32, Context), // u32 - terminal_pane_id
CommandPaneExited(u32, Option<i32>, Context), // u32 - terminal_pane_id, Option<i32> -
// exit_code
// exit_code
PaneClosed(PaneId),
EditPaneOpened(u32, Context), // u32 - terminal_pane_id
EditPaneExited(u32, Option<i32>, Context), // u32 - terminal_pane_id, Option<i32> - 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<FloatingPaneCoordinates>),
OpenFile(FileToOpen, Context),
OpenFileFloating(FileToOpen, Option<FloatingPaneCoordinates>, Context),
OpenTerminal(FileToOpen), // only used for the path as cwd
OpenTerminalFloating(FileToOpen, Option<FloatingPaneCoordinates>), // 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<String>, // 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),
}

View File

@ -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<Direction>, Option<String>), // String is an optional pane name
/// Open the file in a new pane using the default editor
NewPane(Option<Direction>, Option<String>, bool), // String is an optional pane name
/// Open the file in a new pane using the default editor, bool -> start suppressed
EditFile(
PathBuf,
Option<usize>,
Option<PathBuf>,
OpenFilePayload,
Option<Direction>,
bool,
bool,
bool,
Option<FloatingPaneCoordinates>,
), // usize is an optional line number, Option<PathBuf> 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<RunCommandAction>,
@ -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),
)])
},

View File

@ -5,16 +5,15 @@ use std::path::PathBuf;
#[derive(Debug, Clone)]
pub enum TerminalAction {
OpenFile(PathBuf, Option<usize>, Option<PathBuf>), // 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<usize>,
pub cwd: Option<PathBuf>,
pub originating_plugin: Option<OriginatingPlugin>,
}
impl OpenFilePayload {
pub fn new(path: PathBuf, line_number: Option<usize>, cwd: Option<PathBuf>) -> 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")]

View File

@ -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" => {

View File

@ -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<ProtobufAction> 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<ProtobufAction> 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<Action> 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<Action> 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<i32> = 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 {

View File

@ -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;

View File

@ -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<ProtobufEvent> 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<Event> 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<ProtobufEventType> 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<EventType> 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<ProtobufPaneId> for PaneId {
type Error = &'static str;
fn try_from(protobuf_pane_id: ProtobufPaneId) -> Result<Self, &'static str> {
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<PaneId> for ProtobufPaneId {
type Error = &'static str;
fn try_from(pane_id: PaneId) -> Result<Self, &'static str> {
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,
}),
}
}
}

View File

@ -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 {

View File

@ -227,7 +227,14 @@ impl TryFrom<ProtobufPluginCommand> 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<String, String> = 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<ProtobufPluginCommand> for PluginCommand {
let floating_pane_coordinates = file_to_open_payload
.floating_pane_coordinates
.map(|f| f.into());
let context: BTreeMap<String, String> = 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<ProtobufPluginCommand> 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<String, String> = 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<ProtobufPluginCommand> 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<String, String> = 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<PluginCommand> 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<PluginCommand> 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<PluginCommand> 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<PluginCommand> 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<PluginCommand> 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,
},
)),
})
},
}
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {