aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-utils/src/plugin_api/event.rs
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2025-12-11 14:25:42 +0100
committerGitHub <noreply@github.com>2025-12-11 14:25:42 +0100
commit693fa082def5ef0fe030cd7fdc3b1df051b35dc6 (patch)
treeb3f651462bbc1cd2fa5b2d79207343413ef6b83c /zellij-utils/src/plugin_api/event.rs
parentfix(zellij-server): unblock building on 32bit targets (#4484) (diff)
downloadsl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.tar.gz
sl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.tar.xz
sl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.zip
Command Sequences, Blocking CLI Commands, New Plugin APIs (#4546)
* fix deadlocks * initial logging to plugin implementation * only log actions, include keys as Action::Write * send actions to plugins as a new Event::UserAction * add Action::Write metadata to plugin API * run_action plugin command and permission * block-until-exit/success/failure in the cli * fix tests * add NewBlockingPane to the plugin API * run actions in a different thread so as not to block plugin execution * add custom context to run_action flow * fixes for floating pane z_index sorting and unknown protobuf events not crashing plugins * always render the frame of pinned panes * macros module * add macros to config * KDL macro parsing * thread macros to the plugin thread * get/update/delete macros from plugins * add macros updated event * add ability to rename macros * fix deadlock * fix z_indices with pinned unselectable panes bug * allow plugins to opt-in to show_cursor * change show_cursor(bool) to show_cursor(Option<coordinates>) * test * upgrade strum to allow proper enum iteration and case insensitive fromstr implementation for Actions * better cwd tracking for exited command panes * add initial panes to NewTab Action (no-op for now) * add initial panes to cli newtab action * make initial panes work * add close_on_exit and start_suspended to the cli * properly convert newtab action (potentially breaking for plugins) * allow first pane in tab to block * make distinction between opening panes with the last active client and the current pane in the cli * improve some apis * fix fixed size closing pane bug * fix plugin cursor coordinates * fix issues with launch-or-focus-plugin having to do with in-place and suppressed panes * fix: set affected pane in replace pane * allow setting a pane in place when it is suppressed and being focused (tests failing) * fix tests * some fixes for unsuppression of panes * some fixes for completion_tx in new tab action * allow replace/suppressed rather than replace/close only * fixes for inserting suppressed panes without conflicts * pane focus history and explicit unsuppress plugin api * fix: prefer explicitly provided pane id in placement in actions that provide it * fix floating pane coordinate change and z_index * fix plugin cache dir to persist between sessions * fix some stack focusing issues when adding panes without a client id * make sure pinned panes are always above non-pinned floating panes * allow plugins to copy to clipboard * initial text styling additions implementations - buggy * fix dimming issues * remove extras, stay with just dim and unbold * unbold and dim as indices * allow ui components to render success/error colors * allow plugins to send sigint/sigkill to processes, as well as receive their pid * ui component fixes and send originating plugin context with run_action * originating plugin for new tab too * make plugn show pane command also expand stacked panes * add sequence plugin * remove macros * remove snapshots * add shortcut for sequence plugin * fix plugin tests * plugin tests for new apis * update snapshots for sequence shortcut * cleanups and formatting * Event::CwdChanged * moar cleanups * remove some warnings * remove mroe warnings * remove dead code * remove more dead code * fix minor bugs in sequence plugin * update sequence help and format codebase * fix conditional blocking cli commands * fix initial-command new-tab cli flag * add client_ids to CwdChanged Event * last fixes * add pr
Diffstat (limited to 'zellij-utils/src/plugin_api/event.rs')
-rw-r--r--zellij-utils/src/plugin_api/event.rs173
1 files changed, 161 insertions, 12 deletions
diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs
index 02ef1842..4e430b0b 100644
--- a/zellij-utils/src/plugin_api/event.rs
+++ b/zellij-utils/src/plugin_api/event.rs
@@ -2,20 +2,24 @@ pub use super::generated_api::api::{
action::{Action as ProtobufAction, Position as ProtobufPosition},
event::{
event::Payload as ProtobufEventPayload, pane_scrollback_response,
- ClientInfo as ProtobufClientInfo, ClientTabHistory as ProtobufClientTabHistory,
- CopyDestination as ProtobufCopyDestination, Event as ProtobufEvent,
- EventNameList as ProtobufEventNameList, EventType as ProtobufEventType,
- FileMetadata as ProtobufFileMetadata, InputModeKeybinds as ProtobufInputModeKeybinds,
- KeyBind as ProtobufKeyBind, LayoutInfo as ProtobufLayoutInfo,
- ModeUpdatePayload as ProtobufModeUpdatePayload, PaneContents as ProtobufPaneContents,
- PaneContentsEntry as ProtobufPaneContentsEntry, PaneId as ProtobufPaneId,
- PaneInfo as ProtobufPaneInfo, PaneManifest as ProtobufPaneManifest,
+ ActionCompletePayload as ProtobufActionCompletePayload, ClientInfo as ProtobufClientInfo,
+ ClientPaneHistory as ProtobufClientPaneHistory,
+ ClientTabHistory as ProtobufClientTabHistory, ContextItem as ProtobufContextItem,
+ CopyDestination as ProtobufCopyDestination, CwdChangedPayload as ProtobufCwdChangedPayload,
+ Event as ProtobufEvent, EventNameList as ProtobufEventNameList,
+ EventType as ProtobufEventType, FileMetadata as ProtobufFileMetadata,
+ InputModeKeybinds as ProtobufInputModeKeybinds, KeyBind as ProtobufKeyBind,
+ LayoutInfo as ProtobufLayoutInfo, ModeUpdatePayload as ProtobufModeUpdatePayload,
+ PaneContents as ProtobufPaneContents, PaneContentsEntry as ProtobufPaneContentsEntry,
+ PaneId as ProtobufPaneId, PaneInfo as ProtobufPaneInfo,
+ PaneManifest as ProtobufPaneManifest,
PaneRenderReportPayload as ProtobufPaneRenderReportPayload,
PaneScrollbackResponse as ProtobufPaneScrollbackResponse, PaneType as ProtobufPaneType,
PluginInfo as ProtobufPluginInfo, ResurrectableSession as ProtobufResurrectableSession,
SelectedText as ProtobufSelectedText, SessionManifest as ProtobufSessionManifest,
- TabInfo as ProtobufTabInfo, WebServerStatusPayload as ProtobufWebServerStatusPayload,
- WebSharing as ProtobufWebSharing, *,
+ TabInfo as ProtobufTabInfo, UserActionPayload as ProtobufUserActionPayload,
+ WebServerStatusPayload as ProtobufWebServerStatusPayload, WebSharing as ProtobufWebSharing,
+ *,
},
input_mode::InputMode as ProtobufInputMode,
key::Key as ProtobufKey,
@@ -23,8 +27,8 @@ pub use super::generated_api::api::{
};
#[allow(hidden_glob_reexports)]
use crate::data::{
- ClientInfo, CopyDestination, Event, EventType, FileMetadata, InputMode, KeyWithModifier,
- LayoutInfo, ModeInfo, Mouse, PaneContents, PaneId, PaneInfo, PaneManifest,
+ ClientId, ClientInfo, CopyDestination, Event, EventType, FileMetadata, InputMode,
+ KeyWithModifier, LayoutInfo, ModeInfo, Mouse, PaneContents, PaneId, PaneInfo, PaneManifest,
PaneScrollbackResponse, PermissionStatus, PluginCapabilities, PluginInfo, SelectedText,
SessionInfo, Style, TabInfo, WebServerStatus, WebSharing,
};
@@ -397,6 +401,63 @@ impl TryFrom<ProtobufEvent> for Event {
},
_ => Err("Malformed payload for the PaneRenderReport Event"),
},
+ Some(ProtobufEventType::UserAction) => match protobuf_event.payload {
+ Some(ProtobufEventPayload::UserActionPayload(protobuf_payload)) => {
+ let action: Action = protobuf_payload
+ .action
+ .ok_or("Missing action in UserAction payload")?
+ .try_into()
+ .map_err(|_| "Failed to convert Action in UserAction payload")?;
+ let client_id = protobuf_payload.client_id as u16;
+ let terminal_id = protobuf_payload.terminal_id;
+ let cli_client_id = protobuf_payload.cli_client_id.map(|id| id as u16);
+ Ok(Event::UserAction(
+ action,
+ client_id,
+ terminal_id,
+ cli_client_id,
+ ))
+ },
+ _ => Err("Malformed payload for the UserAction Event"),
+ },
+ Some(ProtobufEventType::ActionComplete) => match protobuf_event.payload {
+ Some(ProtobufEventPayload::ActionCompletePayload(protobuf_payload)) => {
+ let action: Action = protobuf_payload
+ .action
+ .ok_or("Missing action in ActionComplete payload")?
+ .try_into()
+ .map_err(|_| "Failed to convert Action in ActionComplete payload")?;
+ let pane_id = protobuf_payload
+ .pane_id
+ .map(|id| id.try_into())
+ .transpose()
+ .map_err(|_| "Failed to convert PaneId in ActionComplete payload")?;
+ let context: BTreeMap<String, String> = protobuf_payload
+ .context
+ .into_iter()
+ .map(|item| (item.name, item.value))
+ .collect();
+ Ok(Event::ActionComplete(action, pane_id, context))
+ },
+ _ => Err("Malformed payload for the ActionComplete Event"),
+ },
+ Some(ProtobufEventType::CwdChanged) => match protobuf_event.payload {
+ Some(ProtobufEventPayload::CwdChangedPayload(protobuf_payload)) => {
+ let pane_id: PaneId = protobuf_payload
+ .pane_id
+ .ok_or("Missing pane_id in CwdChanged payload")?
+ .try_into()
+ .map_err(|_| "Failed to convert PaneId in CwdChanged payload")?;
+ let new_cwd = PathBuf::from(protobuf_payload.new_cwd);
+ let focused_client_ids: Vec<ClientId> = protobuf_payload
+ .focused_client_ids
+ .into_iter()
+ .map(|id| id as u16)
+ .collect();
+ Ok(Event::CwdChanged(pane_id, new_cwd, focused_client_ids))
+ },
+ _ => Err("Malformed payload for the CwdChanged Event"),
+ },
None => Err("Unknown Protobuf Event"),
}
}
@@ -797,6 +858,58 @@ impl TryFrom<Event> for ProtobufEvent {
pane_contents_map.try_into()?,
)),
}),
+ Event::UserAction(action, client_id, terminal_id, cli_client_id) => {
+ let protobuf_action: ProtobufAction = action
+ .try_into()
+ .map_err(|_| "Failed to convert Action to protobuf")?;
+ let protobuf_payload = ProtobufUserActionPayload {
+ action: Some(protobuf_action),
+ client_id: client_id as u32,
+ terminal_id,
+ cli_client_id: cli_client_id.map(|id| id as u32),
+ };
+ Ok(ProtobufEvent {
+ name: ProtobufEventType::UserAction as i32,
+ payload: Some(event::Payload::UserActionPayload(protobuf_payload)),
+ })
+ },
+ Event::ActionComplete(action, pane_id, context) => {
+ let protobuf_action = action.try_into()?;
+ let protobuf_pane_id = pane_id.map(|id| id.try_into()).transpose()?;
+ let context_items: Vec<ProtobufContextItem> = context
+ .into_iter()
+ .map(|(name, value)| ProtobufContextItem { name, value })
+ .collect();
+ let action_complete_payload = ProtobufActionCompletePayload {
+ action: Some(protobuf_action),
+ pane_id: protobuf_pane_id,
+ context: context_items,
+ };
+ Ok(ProtobufEvent {
+ name: ProtobufEventType::ActionComplete as i32,
+ payload: Some(event::Payload::ActionCompletePayload(
+ action_complete_payload,
+ )),
+ })
+ },
+ Event::CwdChanged(pane_id, new_cwd, focused_client_ids) => {
+ let protobuf_pane_id: ProtobufPaneId = pane_id.try_into()?;
+ let new_cwd_string = new_cwd
+ .to_str()
+ .ok_or("Failed to convert PathBuf to string")?
+ .to_string();
+ let focused_client_ids_u32: Vec<u32> =
+ focused_client_ids.into_iter().map(|id| id as u32).collect();
+ let cwd_changed_payload = ProtobufCwdChangedPayload {
+ pane_id: Some(protobuf_pane_id),
+ new_cwd: new_cwd_string,
+ focused_client_ids: focused_client_ids_u32,
+ };
+ Ok(ProtobufEvent {
+ name: ProtobufEventType::CwdChanged as i32,
+ payload: Some(event::Payload::CwdChangedPayload(cwd_changed_payload)),
+ })
+ },
}
}
}
@@ -842,6 +955,11 @@ impl TryFrom<SessionInfo> for ProtobufSessionManifest {
.into_iter()
.map(|t| ProtobufClientTabHistory::from(t))
.collect(),
+ pane_history: session_info
+ .pane_history
+ .into_iter()
+ .map(|p| ProtobufClientPaneHistory::from(p))
+ .collect(),
})
}
}
@@ -854,6 +972,18 @@ impl From<(u16, Vec<usize>)> for ProtobufClientTabHistory {
}
}
}
+
+impl From<(u16, Vec<PaneId>)> for ProtobufClientPaneHistory {
+ fn from((client_id, pane_history): (u16, Vec<PaneId>)) -> ProtobufClientPaneHistory {
+ ProtobufClientPaneHistory {
+ client_id: client_id as u32,
+ pane_history: pane_history
+ .into_iter()
+ .filter_map(|p| p.try_into().ok())
+ .collect(),
+ }
+ }
+}
impl From<(u32, PluginInfo)> for ProtobufPluginInfo {
fn from((plugin_id, plugin_info): (u32, PluginInfo)) -> ProtobufPluginInfo {
ProtobufPluginInfo {
@@ -910,6 +1040,16 @@ impl TryFrom<ProtobufSessionManifest> for SessionInfo {
.collect();
tab_history.insert(client_id as u16, tab_history_for_client);
}
+ let mut pane_history = BTreeMap::new();
+ for client_pane_history in protobuf_session_manifest.pane_history.into_iter() {
+ let client_id = client_pane_history.client_id;
+ let pane_history_for_client = client_pane_history
+ .pane_history
+ .into_iter()
+ .filter_map(|p| p.try_into().ok())
+ .collect();
+ pane_history.insert(client_id as u16, pane_history_for_client);
+ }
Ok(SessionInfo {
name: protobuf_session_manifest.name,
tabs: protobuf_session_manifest
@@ -929,6 +1069,7 @@ impl TryFrom<ProtobufSessionManifest> for SessionInfo {
web_clients_allowed: protobuf_session_manifest.web_clients_allowed,
web_client_count: protobuf_session_manifest.web_client_count as usize,
tab_history,
+ pane_history,
})
}
}
@@ -1476,6 +1617,9 @@ impl TryFrom<ProtobufEventType> for EventType {
ProtobufEventType::FailedToStartWebServer => EventType::FailedToStartWebServer,
ProtobufEventType::InterceptedKeyPress => EventType::InterceptedKeyPress,
ProtobufEventType::PaneRenderReport => EventType::PaneRenderReport,
+ ProtobufEventType::UserAction => EventType::UserAction,
+ ProtobufEventType::ActionComplete => EventType::ActionComplete,
+ ProtobufEventType::CwdChanged => EventType::CwdChanged,
})
}
}
@@ -1520,6 +1664,9 @@ impl TryFrom<EventType> for ProtobufEventType {
EventType::FailedToStartWebServer => ProtobufEventType::FailedToStartWebServer,
EventType::InterceptedKeyPress => ProtobufEventType::InterceptedKeyPress,
EventType::PaneRenderReport => ProtobufEventType::PaneRenderReport,
+ EventType::UserAction => ProtobufEventType::UserAction,
+ EventType::ActionComplete => ProtobufEventType::ActionComplete,
+ EventType::CwdChanged => ProtobufEventType::CwdChanged,
})
}
}
@@ -2129,6 +2276,7 @@ fn serialize_session_update_event_with_non_default_values() {
web_clients_allowed: false,
web_client_count: 1,
tab_history,
+ pane_history: Default::default(),
};
let session_info_2 = SessionInfo {
name: "session 2".to_owned(),
@@ -2147,6 +2295,7 @@ fn serialize_session_update_event_with_non_default_values() {
web_clients_allowed: false,
web_client_count: 0,
tab_history: Default::default(),
+ pane_history: Default::default(),
};
let session_infos = vec![session_info_1, session_info_2];
let resurrectable_sessions = vec![];