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:
Aram Drevekenin 2024-10-22 15:27:40 +02:00 committed by GitHub
parent 912c9f599f
commit d671ab650e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 339 additions and 16 deletions

View file

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

View file

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

View file

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

View file

@ -0,0 +1,9 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
assertion_line: 8623
expression: "format!(\"{:#?}\", list_clients_instruction)"
---
ListClientsToPlugin(
0,
1,
)

View file

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

View file

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

View file

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

View file

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

View file

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

View 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,
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -126,6 +126,7 @@ enum CommandName {
ReloadPlugin = 110; ReloadPlugin = 110;
LoadNewPlugin = 111; LoadNewPlugin = 111;
RebindKeys = 112; RebindKeys = 112;
ListClients = 113;
} }
message PluginCommand { message PluginCommand {

View file

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