feat(plugins): add API to list clients, their focused panes and running commands/plugins (#3687)
* fix(list-clients): properly show client info after a tab was closed * feat(plugins): add API to list clients, their focused panes and running commands/plugins * style(fmt): rustfmt
This commit is contained in:
parent
912c9f599f
commit
d671ab650e
17 changed files with 339 additions and 16 deletions
|
|
@ -525,6 +525,9 @@ impl ZellijPlugin for State {
|
||||||
];
|
];
|
||||||
rebind_keys(keys_to_unbind, keys_to_rebind, write_to_disk);
|
rebind_keys(keys_to_unbind, keys_to_rebind, write_to_disk);
|
||||||
},
|
},
|
||||||
|
BareKey::Char('z') if key.has_modifiers(&[KeyModifier::Alt]) => {
|
||||||
|
list_clients();
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
},
|
},
|
||||||
Event::CustomMessage(message, payload) => {
|
Event::CustomMessage(message, payload) => {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use wasm_bridge::WasmBridge;
|
||||||
use zellij_utils::{
|
use zellij_utils::{
|
||||||
async_std::{channel, future::timeout, task},
|
async_std::{channel, future::timeout, task},
|
||||||
data::{
|
data::{
|
||||||
Event, EventType, InputMode, MessageToPlugin, PermissionStatus, PermissionType,
|
ClientInfo, Event, EventType, InputMode, MessageToPlugin, PermissionStatus, PermissionType,
|
||||||
PipeMessage, PipeSource, PluginCapabilities,
|
PipeMessage, PipeSource, PluginCapabilities,
|
||||||
},
|
},
|
||||||
errors::{prelude::*, ContextType, PluginContext},
|
errors::{prelude::*, ContextType, PluginContext},
|
||||||
|
|
@ -158,6 +158,7 @@ pub enum PluginInstruction {
|
||||||
file_path: Option<PathBuf>,
|
file_path: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
WatchFilesystem,
|
WatchFilesystem,
|
||||||
|
ListClientsToPlugin(SessionLayoutMetadata, PluginId, ClientId),
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,6 +204,7 @@ impl From<&PluginInstruction> for PluginContext {
|
||||||
PluginInstruction::FailedToWriteConfigToDisk { .. } => {
|
PluginInstruction::FailedToWriteConfigToDisk { .. } => {
|
||||||
PluginContext::FailedToWriteConfigToDisk
|
PluginContext::FailedToWriteConfigToDisk
|
||||||
},
|
},
|
||||||
|
PluginInstruction::ListClientsToPlugin(..) => PluginContext::ListClientsToPlugin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -607,6 +609,35 @@ pub(crate) fn plugin_thread_main(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
PluginInstruction::ListClientsToPlugin(
|
||||||
|
mut session_layout_metadata,
|
||||||
|
plugin_id,
|
||||||
|
client_id,
|
||||||
|
) => {
|
||||||
|
populate_session_layout_metadata(
|
||||||
|
&mut session_layout_metadata,
|
||||||
|
&wasm_bridge,
|
||||||
|
&plugin_aliases,
|
||||||
|
);
|
||||||
|
let mut clients_metadata = session_layout_metadata.all_clients_metadata();
|
||||||
|
let mut client_list_for_plugin = vec![];
|
||||||
|
let default_editor = session_layout_metadata.default_editor.clone();
|
||||||
|
for (client_metadata_id, client_metadata) in clients_metadata.iter_mut() {
|
||||||
|
let is_current_client = client_metadata_id == &client_id;
|
||||||
|
client_list_for_plugin.push(ClientInfo::new(
|
||||||
|
*client_metadata_id,
|
||||||
|
client_metadata.get_pane_id().into(),
|
||||||
|
client_metadata.stringify_command(&default_editor),
|
||||||
|
is_current_client,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let updates = vec![(
|
||||||
|
Some(plugin_id),
|
||||||
|
Some(client_id),
|
||||||
|
Event::ListClients(client_list_for_plugin),
|
||||||
|
)];
|
||||||
|
wasm_bridge.update_plugins(updates, shutdown_send.clone())?;
|
||||||
|
},
|
||||||
PluginInstruction::LogLayoutToHd(mut session_layout_metadata) => {
|
PluginInstruction::LogLayoutToHd(mut session_layout_metadata) => {
|
||||||
populate_session_layout_metadata(
|
populate_session_layout_metadata(
|
||||||
&mut session_layout_metadata,
|
&mut session_layout_metadata,
|
||||||
|
|
|
||||||
|
|
@ -8551,3 +8551,74 @@ pub fn rebind_keys_plugin_command() {
|
||||||
.clone();
|
.clone();
|
||||||
assert_snapshot!(format!("{:#?}", rebind_event));
|
assert_snapshot!(format!("{:#?}", rebind_event));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
pub fn list_clients_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::ListClientsToPlugin,
|
||||||
|
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,
|
||||||
|
Some(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('z')).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 list_clients_instruction = received_screen_instructions
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find_map(|i| {
|
||||||
|
if let ScreenInstruction::ListClientsToPlugin(..) = i {
|
||||||
|
Some(i.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
assert_snapshot!(format!("{:#?}", list_clients_instruction));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||||
|
assertion_line: 8623
|
||||||
|
expression: "format!(\"{:#?}\", list_clients_instruction)"
|
||||||
|
---
|
||||||
|
ListClientsToPlugin(
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
@ -351,6 +351,7 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) {
|
||||||
keys_to_unbind,
|
keys_to_unbind,
|
||||||
write_config_to_disk,
|
write_config_to_disk,
|
||||||
} => rebind_keys(env, keys_to_rebind, keys_to_unbind, write_config_to_disk)?,
|
} => rebind_keys(env, keys_to_rebind, keys_to_unbind, write_config_to_disk)?,
|
||||||
|
PluginCommand::ListClients => list_clients(env),
|
||||||
},
|
},
|
||||||
(PermissionStatus::Denied, permission) => {
|
(PermissionStatus::Denied, permission) => {
|
||||||
log::error!(
|
log::error!(
|
||||||
|
|
@ -1474,6 +1475,15 @@ fn dump_session_layout(env: &PluginEnv) {
|
||||||
.map(|sender| sender.send(ScreenInstruction::DumpLayoutToPlugin(env.plugin_id)));
|
.map(|sender| sender.send(ScreenInstruction::DumpLayoutToPlugin(env.plugin_id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn list_clients(env: &PluginEnv) {
|
||||||
|
let _ = env.senders.to_screen.as_ref().map(|sender| {
|
||||||
|
sender.send(ScreenInstruction::ListClientsToPlugin(
|
||||||
|
env.plugin_id,
|
||||||
|
env.client_id,
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn scan_host_folder(env: &PluginEnv, folder_to_scan: PathBuf) {
|
fn scan_host_folder(env: &PluginEnv, folder_to_scan: PathBuf) {
|
||||||
if !folder_to_scan.starts_with("/host") {
|
if !folder_to_scan.starts_with("/host") {
|
||||||
log::error!(
|
log::error!(
|
||||||
|
|
@ -1897,7 +1907,9 @@ fn check_command_permission(
|
||||||
| PluginCommand::BlockCliPipeInput(..)
|
| PluginCommand::BlockCliPipeInput(..)
|
||||||
| PluginCommand::CliPipeOutput(..) => PermissionType::ReadCliPipes,
|
| PluginCommand::CliPipeOutput(..) => PermissionType::ReadCliPipes,
|
||||||
PluginCommand::MessageToPlugin(..) => PermissionType::MessageAndLaunchOtherPlugins,
|
PluginCommand::MessageToPlugin(..) => PermissionType::MessageAndLaunchOtherPlugins,
|
||||||
PluginCommand::DumpSessionLayout => PermissionType::ReadApplicationState,
|
PluginCommand::ListClients | PluginCommand::DumpSessionLayout => {
|
||||||
|
PermissionType::ReadApplicationState
|
||||||
|
},
|
||||||
PluginCommand::RebindKeys { .. } | PluginCommand::Reconfigure(..) => {
|
PluginCommand::RebindKeys { .. } | PluginCommand::Reconfigure(..) => {
|
||||||
PermissionType::Reconfigure
|
PermissionType::Reconfigure
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ pub enum PtyInstruction {
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
default_editor: Option<PathBuf>,
|
default_editor: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
|
ListClientsToPlugin(SessionLayoutMetadata, PluginId, ClientId),
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,6 +126,7 @@ impl From<&PtyInstruction> for PtyContext {
|
||||||
PtyInstruction::FillPluginCwd(..) => PtyContext::FillPluginCwd,
|
PtyInstruction::FillPluginCwd(..) => PtyContext::FillPluginCwd,
|
||||||
PtyInstruction::ListClientsMetadata(..) => PtyContext::ListClientsMetadata,
|
PtyInstruction::ListClientsMetadata(..) => PtyContext::ListClientsMetadata,
|
||||||
PtyInstruction::Reconfigure { .. } => PtyContext::Reconfigure,
|
PtyInstruction::Reconfigure { .. } => PtyContext::Reconfigure,
|
||||||
|
PtyInstruction::ListClientsToPlugin(..) => PtyContext::ListClientsToPlugin,
|
||||||
PtyInstruction::Exit => PtyContext::Exit,
|
PtyInstruction::Exit => PtyContext::Exit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -724,6 +726,23 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
|
||||||
.with_context(err_context)
|
.with_context(err_context)
|
||||||
.non_fatal();
|
.non_fatal();
|
||||||
},
|
},
|
||||||
|
PtyInstruction::ListClientsToPlugin(
|
||||||
|
mut session_layout_metadata,
|
||||||
|
plugin_id,
|
||||||
|
client_id,
|
||||||
|
) => {
|
||||||
|
let err_context = || format!("Failed to dump layout");
|
||||||
|
pty.populate_session_layout_metadata(&mut session_layout_metadata);
|
||||||
|
pty.bus
|
||||||
|
.senders
|
||||||
|
.send_to_plugin(PluginInstruction::ListClientsToPlugin(
|
||||||
|
session_layout_metadata,
|
||||||
|
plugin_id,
|
||||||
|
client_id,
|
||||||
|
))
|
||||||
|
.with_context(err_context)
|
||||||
|
.non_fatal();
|
||||||
|
},
|
||||||
PtyInstruction::LogLayoutToHd(mut session_layout_metadata) => {
|
PtyInstruction::LogLayoutToHd(mut session_layout_metadata) => {
|
||||||
let err_context = || format!("Failed to dump layout");
|
let err_context = || format!("Failed to dump layout");
|
||||||
pty.populate_session_layout_metadata(&mut session_layout_metadata);
|
pty.populate_session_layout_metadata(&mut session_layout_metadata);
|
||||||
|
|
|
||||||
|
|
@ -410,6 +410,7 @@ pub enum ScreenInstruction {
|
||||||
should_change_focus_to_new_tab: bool,
|
should_change_focus_to_new_tab: bool,
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
},
|
},
|
||||||
|
ListClientsToPlugin(PluginId, ClientId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&ScreenInstruction> for ScreenContext {
|
impl From<&ScreenInstruction> for ScreenContext {
|
||||||
|
|
@ -621,6 +622,7 @@ impl From<&ScreenInstruction> for ScreenContext {
|
||||||
ScreenInstruction::BreakPanesToTabWithIndex { .. } => {
|
ScreenInstruction::BreakPanesToTabWithIndex { .. } => {
|
||||||
ScreenContext::BreakPanesToTabWithIndex
|
ScreenContext::BreakPanesToTabWithIndex
|
||||||
},
|
},
|
||||||
|
ScreenInstruction::ListClientsToPlugin(..) => ScreenContext::ListClientsToPlugin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2506,7 +2508,7 @@ impl Screen {
|
||||||
let active_tab_index =
|
let active_tab_index =
|
||||||
first_client_id.and_then(|client_id| self.active_tab_indices.get(&client_id));
|
first_client_id.and_then(|client_id| self.active_tab_indices.get(&client_id));
|
||||||
|
|
||||||
for (tab_index, tab) in self.tabs.values().enumerate() {
|
for (tab_index, tab) in self.tabs.iter() {
|
||||||
let tab_is_focused = active_tab_index == Some(&tab_index);
|
let tab_is_focused = active_tab_index == Some(&tab_index);
|
||||||
let hide_floating_panes = !tab.are_floating_panes_visible();
|
let hide_floating_panes = !tab.are_floating_panes_visible();
|
||||||
let mut suppressed_panes = HashMap::new();
|
let mut suppressed_panes = HashMap::new();
|
||||||
|
|
@ -3110,6 +3112,21 @@ pub(crate) fn screen_thread_main(
|
||||||
.with_context(err_context)
|
.with_context(err_context)
|
||||||
.non_fatal();
|
.non_fatal();
|
||||||
},
|
},
|
||||||
|
ScreenInstruction::ListClientsToPlugin(plugin_id, client_id) => {
|
||||||
|
let err_context = || format!("Failed to dump layout");
|
||||||
|
let session_layout_metadata =
|
||||||
|
screen.get_layout_metadata(screen.default_shell.clone());
|
||||||
|
screen
|
||||||
|
.bus
|
||||||
|
.senders
|
||||||
|
.send_to_pty(PtyInstruction::ListClientsToPlugin(
|
||||||
|
session_layout_metadata,
|
||||||
|
plugin_id,
|
||||||
|
client_id,
|
||||||
|
))
|
||||||
|
.with_context(err_context)
|
||||||
|
.non_fatal();
|
||||||
|
},
|
||||||
ScreenInstruction::EditScrollback(client_id) => {
|
ScreenInstruction::EditScrollback(client_id) => {
|
||||||
active_tab_and_connected_client_id!(
|
active_tab_and_connected_client_id!(
|
||||||
screen,
|
screen,
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,28 @@ impl SessionLayoutMetadata {
|
||||||
|
|
||||||
ClientMetadata::render_many(clients_metadata, &self.default_editor)
|
ClientMetadata::render_many(clients_metadata, &self.default_editor)
|
||||||
}
|
}
|
||||||
|
pub fn all_clients_metadata(&self) -> BTreeMap<ClientId, ClientMetadata> {
|
||||||
|
let mut clients_metadata: BTreeMap<ClientId, ClientMetadata> = BTreeMap::new();
|
||||||
|
for tab in &self.tabs {
|
||||||
|
let panes = if tab.hide_floating_panes {
|
||||||
|
&tab.tiled_panes
|
||||||
|
} else {
|
||||||
|
&tab.floating_panes
|
||||||
|
};
|
||||||
|
for pane in panes {
|
||||||
|
for focused_client in &pane.focused_clients {
|
||||||
|
clients_metadata.insert(
|
||||||
|
*focused_client,
|
||||||
|
ClientMetadata {
|
||||||
|
pane_id: pane.id.clone(),
|
||||||
|
command: pane.run.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clients_metadata
|
||||||
|
}
|
||||||
pub fn is_dirty(&self) -> bool {
|
pub fn is_dirty(&self) -> bool {
|
||||||
// here we check to see if the serialized layout would be different than the base one, and
|
// here we check to see if the serialized layout would be different than the base one, and
|
||||||
// thus is "dirty". A layout is considered dirty if one of the following is true:
|
// thus is "dirty". A layout is considered dirty if one of the following is true:
|
||||||
|
|
@ -372,7 +394,7 @@ impl PaneLayoutMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ClientMetadata {
|
pub struct ClientMetadata {
|
||||||
pane_id: PaneId,
|
pane_id: PaneId,
|
||||||
command: Option<Run>,
|
command: Option<Run>,
|
||||||
}
|
}
|
||||||
|
|
@ -404,6 +426,9 @@ impl ClientMetadata {
|
||||||
};
|
};
|
||||||
stringified.unwrap_or("N/A".to_owned())
|
stringified.unwrap_or("N/A".to_owned())
|
||||||
}
|
}
|
||||||
|
pub fn get_pane_id(&self) -> PaneId {
|
||||||
|
self.pane_id
|
||||||
|
}
|
||||||
pub fn render_many(
|
pub fn render_many(
|
||||||
clients_metadata: BTreeMap<ClientId, ClientMetadata>,
|
clients_metadata: BTreeMap<ClientId, ClientMetadata>,
|
||||||
default_editor: &Option<PathBuf>,
|
default_editor: &Option<PathBuf>,
|
||||||
|
|
|
||||||
|
|
@ -854,6 +854,15 @@ pub fn dump_session_layout() {
|
||||||
unsafe { host_run_plugin_command() };
|
unsafe { host_run_plugin_command() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a list of clients, their focused pane and running command or focused plugin back as an
|
||||||
|
/// Event::ListClients (note: this event must be subscribed to)
|
||||||
|
pub fn list_clients() {
|
||||||
|
let plugin_command = PluginCommand::ListClients;
|
||||||
|
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 configuration for the current user
|
/// Change configuration for the current user
|
||||||
pub fn reconfigure(new_config: String, save_configuration_file: bool) {
|
pub fn reconfigure(new_config: String, save_configuration_file: bool) {
|
||||||
let plugin_command = PluginCommand::Reconfigure(new_config, save_configuration_file);
|
let plugin_command = PluginCommand::Reconfigure(new_config, save_configuration_file);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ pub struct Event {
|
||||||
pub name: i32,
|
pub name: i32,
|
||||||
#[prost(
|
#[prost(
|
||||||
oneof = "event::Payload",
|
oneof = "event::Payload",
|
||||||
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22"
|
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23"
|
||||||
)]
|
)]
|
||||||
pub payload: ::core::option::Option<event::Payload>,
|
pub payload: ::core::option::Option<event::Payload>,
|
||||||
}
|
}
|
||||||
|
|
@ -62,10 +62,30 @@ pub mod event {
|
||||||
CommandPaneRerunPayload(super::CommandPaneReRunPayload),
|
CommandPaneRerunPayload(super::CommandPaneReRunPayload),
|
||||||
#[prost(message, tag = "22")]
|
#[prost(message, tag = "22")]
|
||||||
FailedToWriteConfigToDiskPayload(super::FailedToWriteConfigToDiskPayload),
|
FailedToWriteConfigToDiskPayload(super::FailedToWriteConfigToDiskPayload),
|
||||||
|
#[prost(message, tag = "23")]
|
||||||
|
ListClientsPayload(super::ListClientsPayload),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct ListClientsPayload {
|
||||||
|
#[prost(message, repeated, tag = "1")]
|
||||||
|
pub client_info: ::prost::alloc::vec::Vec<ClientInfo>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct ClientInfo {
|
||||||
|
#[prost(uint32, tag = "1")]
|
||||||
|
pub client_id: u32,
|
||||||
|
#[prost(message, optional, tag = "2")]
|
||||||
|
pub pane_id: ::core::option::Option<PaneId>,
|
||||||
|
#[prost(string, tag = "3")]
|
||||||
|
pub running_command: ::prost::alloc::string::String,
|
||||||
|
#[prost(bool, tag = "4")]
|
||||||
|
pub is_current_client: bool,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct FailedToWriteConfigToDiskPayload {
|
pub struct FailedToWriteConfigToDiskPayload {
|
||||||
#[prost(string, optional, tag = "1")]
|
#[prost(string, optional, tag = "1")]
|
||||||
pub file_path: ::core::option::Option<::prost::alloc::string::String>,
|
pub file_path: ::core::option::Option<::prost::alloc::string::String>,
|
||||||
|
|
@ -447,6 +467,7 @@ pub enum EventType {
|
||||||
EditPaneExited = 23,
|
EditPaneExited = 23,
|
||||||
CommandPaneReRun = 24,
|
CommandPaneReRun = 24,
|
||||||
FailedToWriteConfigToDisk = 25,
|
FailedToWriteConfigToDisk = 25,
|
||||||
|
ListClients = 26,
|
||||||
}
|
}
|
||||||
impl EventType {
|
impl EventType {
|
||||||
/// String value of the enum field names used in the ProtoBuf definition.
|
/// String value of the enum field names used in the ProtoBuf definition.
|
||||||
|
|
@ -481,6 +502,7 @@ impl EventType {
|
||||||
EventType::EditPaneExited => "EditPaneExited",
|
EventType::EditPaneExited => "EditPaneExited",
|
||||||
EventType::CommandPaneReRun => "CommandPaneReRun",
|
EventType::CommandPaneReRun => "CommandPaneReRun",
|
||||||
EventType::FailedToWriteConfigToDisk => "FailedToWriteConfigToDisk",
|
EventType::FailedToWriteConfigToDisk => "FailedToWriteConfigToDisk",
|
||||||
|
EventType::ListClients => "ListClients",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
|
|
@ -512,6 +534,7 @@ impl EventType {
|
||||||
"EditPaneExited" => Some(Self::EditPaneExited),
|
"EditPaneExited" => Some(Self::EditPaneExited),
|
||||||
"CommandPaneReRun" => Some(Self::CommandPaneReRun),
|
"CommandPaneReRun" => Some(Self::CommandPaneReRun),
|
||||||
"FailedToWriteConfigToDisk" => Some(Self::FailedToWriteConfigToDisk),
|
"FailedToWriteConfigToDisk" => Some(Self::FailedToWriteConfigToDisk),
|
||||||
|
"ListClients" => Some(Self::ListClients),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -715,6 +715,7 @@ pub enum CommandName {
|
||||||
ReloadPlugin = 110,
|
ReloadPlugin = 110,
|
||||||
LoadNewPlugin = 111,
|
LoadNewPlugin = 111,
|
||||||
RebindKeys = 112,
|
RebindKeys = 112,
|
||||||
|
ListClients = 113,
|
||||||
}
|
}
|
||||||
impl CommandName {
|
impl CommandName {
|
||||||
/// String value of the enum field names used in the ProtoBuf definition.
|
/// String value of the enum field names used in the ProtoBuf definition.
|
||||||
|
|
@ -838,6 +839,7 @@ impl CommandName {
|
||||||
CommandName::ReloadPlugin => "ReloadPlugin",
|
CommandName::ReloadPlugin => "ReloadPlugin",
|
||||||
CommandName::LoadNewPlugin => "LoadNewPlugin",
|
CommandName::LoadNewPlugin => "LoadNewPlugin",
|
||||||
CommandName::RebindKeys => "RebindKeys",
|
CommandName::RebindKeys => "RebindKeys",
|
||||||
|
CommandName::ListClients => "ListClients",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
|
|
@ -958,6 +960,7 @@ impl CommandName {
|
||||||
"ReloadPlugin" => Some(Self::ReloadPlugin),
|
"ReloadPlugin" => Some(Self::ReloadPlugin),
|
||||||
"LoadNewPlugin" => Some(Self::LoadNewPlugin),
|
"LoadNewPlugin" => Some(Self::LoadNewPlugin),
|
||||||
"RebindKeys" => Some(Self::RebindKeys),
|
"RebindKeys" => Some(Self::RebindKeys),
|
||||||
|
"ListClients" => Some(Self::ListClients),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -921,6 +921,7 @@ pub enum Event {
|
||||||
EditPaneExited(u32, Option<i32>, Context), // u32 - terminal_pane_id, Option<i32> - exit code
|
EditPaneExited(u32, Option<i32>, Context), // u32 - terminal_pane_id, Option<i32> - exit code
|
||||||
CommandPaneReRun(u32, Context), // u32 - terminal_pane_id, Option<i32> -
|
CommandPaneReRun(u32, Context), // u32 - terminal_pane_id, Option<i32> -
|
||||||
FailedToWriteConfigToDisk(Option<String>), // String -> the file path we failed to write
|
FailedToWriteConfigToDisk(Option<String>), // String -> the file path we failed to write
|
||||||
|
ListClients(Vec<ClientInfo>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
|
@ -1365,6 +1366,29 @@ pub struct PaneInfo {
|
||||||
/// (eg. the default `status-bar` or `tab-bar`).
|
/// (eg. the default `status-bar` or `tab-bar`).
|
||||||
pub is_selectable: bool,
|
pub is_selectable: bool,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
|
pub struct ClientInfo {
|
||||||
|
pub client_id: ClientId,
|
||||||
|
pub pane_id: PaneId,
|
||||||
|
pub running_command: String,
|
||||||
|
pub is_current_client: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientInfo {
|
||||||
|
pub fn new(
|
||||||
|
client_id: ClientId,
|
||||||
|
pane_id: PaneId,
|
||||||
|
running_command: String,
|
||||||
|
is_current_client: bool,
|
||||||
|
) -> Self {
|
||||||
|
ClientInfo {
|
||||||
|
client_id,
|
||||||
|
pane_id,
|
||||||
|
running_command,
|
||||||
|
is_current_client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||||
pub struct PluginIds {
|
pub struct PluginIds {
|
||||||
|
|
@ -1878,4 +1902,5 @@ pub enum PluginCommand {
|
||||||
keys_to_unbind: Vec<(InputMode, KeyWithModifier)>,
|
keys_to_unbind: Vec<(InputMode, KeyWithModifier)>,
|
||||||
write_config_to_disk: bool,
|
write_config_to_disk: bool,
|
||||||
},
|
},
|
||||||
|
ListClients,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -372,6 +372,7 @@ pub enum ScreenContext {
|
||||||
CloseTabWithIndex,
|
CloseTabWithIndex,
|
||||||
BreakPanesToNewTab,
|
BreakPanesToNewTab,
|
||||||
BreakPanesToTabWithIndex,
|
BreakPanesToTabWithIndex,
|
||||||
|
ListClientsToPlugin,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
|
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
|
||||||
|
|
@ -395,6 +396,7 @@ pub enum PtyContext {
|
||||||
DumpLayoutToPlugin,
|
DumpLayoutToPlugin,
|
||||||
ListClientsMetadata,
|
ListClientsMetadata,
|
||||||
Reconfigure,
|
Reconfigure,
|
||||||
|
ListClientsToPlugin,
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -432,6 +434,7 @@ pub enum PluginContext {
|
||||||
ListClientsMetadata,
|
ListClientsMetadata,
|
||||||
Reconfigure,
|
Reconfigure,
|
||||||
FailedToWriteConfigToDisk,
|
FailedToWriteConfigToDisk,
|
||||||
|
ListClientsToPlugin,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stack call representations corresponding to the different types of [`ClientInstruction`]s.
|
/// Stack call representations corresponding to the different types of [`ClientInstruction`]s.
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ enum EventType {
|
||||||
EditPaneExited = 23;
|
EditPaneExited = 23;
|
||||||
CommandPaneReRun = 24;
|
CommandPaneReRun = 24;
|
||||||
FailedToWriteConfigToDisk = 25;
|
FailedToWriteConfigToDisk = 25;
|
||||||
|
ListClients = 26;
|
||||||
}
|
}
|
||||||
|
|
||||||
message EventNameList {
|
message EventNameList {
|
||||||
|
|
@ -79,9 +80,21 @@ message Event {
|
||||||
EditPaneExitedPayload edit_pane_exited_payload = 20;
|
EditPaneExitedPayload edit_pane_exited_payload = 20;
|
||||||
CommandPaneReRunPayload command_pane_rerun_payload = 21;
|
CommandPaneReRunPayload command_pane_rerun_payload = 21;
|
||||||
FailedToWriteConfigToDiskPayload failed_to_write_config_to_disk_payload = 22;
|
FailedToWriteConfigToDiskPayload failed_to_write_config_to_disk_payload = 22;
|
||||||
|
ListClientsPayload list_clients_payload = 23;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ListClientsPayload {
|
||||||
|
repeated ClientInfo client_info = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ClientInfo {
|
||||||
|
uint32 client_id = 1;
|
||||||
|
PaneId pane_id = 2;
|
||||||
|
string running_command = 3;
|
||||||
|
bool is_current_client = 4;
|
||||||
|
}
|
||||||
|
|
||||||
message FailedToWriteConfigToDiskPayload {
|
message FailedToWriteConfigToDiskPayload {
|
||||||
optional string file_path = 1;
|
optional string file_path = 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
pub use super::generated_api::api::{
|
pub use super::generated_api::api::{
|
||||||
action::{Action as ProtobufAction, Position as ProtobufPosition},
|
action::{Action as ProtobufAction, Position as ProtobufPosition},
|
||||||
event::{
|
event::{
|
||||||
event::Payload as ProtobufEventPayload, CopyDestination as ProtobufCopyDestination,
|
event::Payload as ProtobufEventPayload, ClientInfo as ProtobufClientInfo,
|
||||||
Event as ProtobufEvent, EventNameList as ProtobufEventNameList,
|
CopyDestination as ProtobufCopyDestination, Event as ProtobufEvent,
|
||||||
EventType as ProtobufEventType, FileMetadata as ProtobufFileMetadata,
|
EventNameList as ProtobufEventNameList, EventType as ProtobufEventType,
|
||||||
InputModeKeybinds as ProtobufInputModeKeybinds, KeyBind as ProtobufKeyBind,
|
FileMetadata as ProtobufFileMetadata, InputModeKeybinds as ProtobufInputModeKeybinds,
|
||||||
LayoutInfo as ProtobufLayoutInfo, ModeUpdatePayload as ProtobufModeUpdatePayload,
|
KeyBind as ProtobufKeyBind, LayoutInfo as ProtobufLayoutInfo,
|
||||||
PaneId as ProtobufPaneId, PaneInfo as ProtobufPaneInfo,
|
ModeUpdatePayload as ProtobufModeUpdatePayload, PaneId as ProtobufPaneId,
|
||||||
PaneManifest as ProtobufPaneManifest, PaneType as ProtobufPaneType,
|
PaneInfo as ProtobufPaneInfo, PaneManifest as ProtobufPaneManifest,
|
||||||
PluginInfo as ProtobufPluginInfo, ResurrectableSession as ProtobufResurrectableSession,
|
PaneType as ProtobufPaneType, PluginInfo as ProtobufPluginInfo,
|
||||||
|
ResurrectableSession as ProtobufResurrectableSession,
|
||||||
SessionManifest as ProtobufSessionManifest, TabInfo as ProtobufTabInfo, *,
|
SessionManifest as ProtobufSessionManifest, TabInfo as ProtobufTabInfo, *,
|
||||||
},
|
},
|
||||||
input_mode::InputMode as ProtobufInputMode,
|
input_mode::InputMode as ProtobufInputMode,
|
||||||
|
|
@ -17,9 +18,9 @@ pub use super::generated_api::api::{
|
||||||
};
|
};
|
||||||
#[allow(hidden_glob_reexports)]
|
#[allow(hidden_glob_reexports)]
|
||||||
use crate::data::{
|
use crate::data::{
|
||||||
CopyDestination, Event, EventType, FileMetadata, InputMode, KeyWithModifier, LayoutInfo,
|
ClientInfo, CopyDestination, Event, EventType, FileMetadata, InputMode, KeyWithModifier,
|
||||||
ModeInfo, Mouse, PaneId, PaneInfo, PaneManifest, PermissionStatus, PluginCapabilities,
|
LayoutInfo, ModeInfo, Mouse, PaneId, PaneInfo, PaneManifest, PermissionStatus,
|
||||||
PluginInfo, SessionInfo, Style, TabInfo,
|
PluginCapabilities, PluginInfo, SessionInfo, Style, TabInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::errors::prelude::*;
|
use crate::errors::prelude::*;
|
||||||
|
|
@ -320,11 +321,50 @@ impl TryFrom<ProtobufEvent> for Event {
|
||||||
)),
|
)),
|
||||||
_ => Err("Malformed payload for the FailedToWriteConfigToDisk Event"),
|
_ => Err("Malformed payload for the FailedToWriteConfigToDisk Event"),
|
||||||
},
|
},
|
||||||
|
Some(ProtobufEventType::ListClients) => match protobuf_event.payload {
|
||||||
|
Some(ProtobufEventPayload::ListClientsPayload(mut list_clients_payload)) => {
|
||||||
|
Ok(Event::ListClients(
|
||||||
|
list_clients_payload
|
||||||
|
.client_info
|
||||||
|
.drain(..)
|
||||||
|
.filter_map(|c| c.try_into().ok())
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
_ => Err("Malformed payload for the FailedToWriteConfigToDisk Event"),
|
||||||
|
},
|
||||||
None => Err("Unknown Protobuf Event"),
|
None => Err("Unknown Protobuf Event"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ProtobufClientInfo> for ClientInfo {
|
||||||
|
type Error = &'static str;
|
||||||
|
fn try_from(protobuf_client_info: ProtobufClientInfo) -> Result<Self, &'static str> {
|
||||||
|
Ok(ClientInfo::new(
|
||||||
|
protobuf_client_info.client_id as u16,
|
||||||
|
protobuf_client_info
|
||||||
|
.pane_id
|
||||||
|
.ok_or("No pane id found")?
|
||||||
|
.try_into()?,
|
||||||
|
protobuf_client_info.running_command,
|
||||||
|
protobuf_client_info.is_current_client,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ClientInfo> for ProtobufClientInfo {
|
||||||
|
type Error = &'static str;
|
||||||
|
fn try_from(client_info: ClientInfo) -> Result<Self, &'static str> {
|
||||||
|
Ok(ProtobufClientInfo {
|
||||||
|
client_id: client_info.client_id as u32,
|
||||||
|
pane_id: Some(client_info.pane_id.try_into()?),
|
||||||
|
running_command: client_info.running_command,
|
||||||
|
is_current_client: client_info.is_current_client,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<Event> for ProtobufEvent {
|
impl TryFrom<Event> for ProtobufEvent {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
fn try_from(event: Event) -> Result<Self, &'static str> {
|
fn try_from(event: Event) -> Result<Self, &'static str> {
|
||||||
|
|
@ -634,6 +674,15 @@ impl TryFrom<Event> for ProtobufEvent {
|
||||||
FailedToWriteConfigToDiskPayload { file_path },
|
FailedToWriteConfigToDiskPayload { file_path },
|
||||||
)),
|
)),
|
||||||
}),
|
}),
|
||||||
|
Event::ListClients(mut client_info_list) => Ok(ProtobufEvent {
|
||||||
|
name: ProtobufEventType::ListClients as i32,
|
||||||
|
payload: Some(event::Payload::ListClientsPayload(ListClientsPayload {
|
||||||
|
client_info: client_info_list
|
||||||
|
.drain(..)
|
||||||
|
.filter_map(|c| c.try_into().ok())
|
||||||
|
.collect(),
|
||||||
|
})),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1178,6 +1227,7 @@ impl TryFrom<ProtobufEventType> for EventType {
|
||||||
ProtobufEventType::EditPaneExited => EventType::EditPaneExited,
|
ProtobufEventType::EditPaneExited => EventType::EditPaneExited,
|
||||||
ProtobufEventType::CommandPaneReRun => EventType::CommandPaneReRun,
|
ProtobufEventType::CommandPaneReRun => EventType::CommandPaneReRun,
|
||||||
ProtobufEventType::FailedToWriteConfigToDisk => EventType::FailedToWriteConfigToDisk,
|
ProtobufEventType::FailedToWriteConfigToDisk => EventType::FailedToWriteConfigToDisk,
|
||||||
|
ProtobufEventType::ListClients => EventType::ListClients,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1212,6 +1262,7 @@ impl TryFrom<EventType> for ProtobufEventType {
|
||||||
EventType::EditPaneExited => ProtobufEventType::EditPaneExited,
|
EventType::EditPaneExited => ProtobufEventType::EditPaneExited,
|
||||||
EventType::CommandPaneReRun => ProtobufEventType::CommandPaneReRun,
|
EventType::CommandPaneReRun => ProtobufEventType::CommandPaneReRun,
|
||||||
EventType::FailedToWriteConfigToDisk => ProtobufEventType::FailedToWriteConfigToDisk,
|
EventType::FailedToWriteConfigToDisk => ProtobufEventType::FailedToWriteConfigToDisk,
|
||||||
|
EventType::ListClients => ProtobufEventType::ListClients,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ enum CommandName {
|
||||||
ReloadPlugin = 110;
|
ReloadPlugin = 110;
|
||||||
LoadNewPlugin = 111;
|
LoadNewPlugin = 111;
|
||||||
RebindKeys = 112;
|
RebindKeys = 112;
|
||||||
|
ListClients = 113;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PluginCommand {
|
message PluginCommand {
|
||||||
|
|
|
||||||
|
|
@ -1299,6 +1299,10 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
|
||||||
},
|
},
|
||||||
_ => Err("Mismatched payload for RebindKeys"),
|
_ => Err("Mismatched payload for RebindKeys"),
|
||||||
},
|
},
|
||||||
|
Some(CommandName::ListClients) => match protobuf_plugin_command.payload {
|
||||||
|
Some(_) => Err("ListClients should have no payload, found a payload"),
|
||||||
|
None => Ok(PluginCommand::ListClients),
|
||||||
|
},
|
||||||
None => Err("Unrecognized plugin command"),
|
None => Err("Unrecognized plugin command"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2122,6 +2126,10 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
|
||||||
write_config_to_disk,
|
write_config_to_disk,
|
||||||
})),
|
})),
|
||||||
}),
|
}),
|
||||||
|
PluginCommand::ListClients => Ok(ProtobufPluginCommand {
|
||||||
|
name: CommandName::ListClients as i32,
|
||||||
|
payload: None,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue