diff options
| author | Aram Drevekenin <aram@poor.dev> | 2026-01-13 11:27:07 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-13 11:27:07 +0100 |
| commit | 4a27ceea0d99b036ca607e05714cef15689d7c26 (patch) | |
| tree | 22c0148718a82082bdb196c5e867697f514cc541 /zellij-utils/src/plugin_api | |
| parent | docs(CONTRIBUTING): Describe rust toolchain versioning policy (#4585) (diff) | |
| download | sl0ppij-4a27ceea0d99b036ca607e05714cef15689d7c26.tar.gz sl0ppij-4a27ceea0d99b036ca607e05714cef15689d7c26.tar.xz sl0ppij-4a27ceea0d99b036ca607e05714cef15689d7c26.zip | |
Layout manager (#4601)
* ad plugin override_layout command
* watch available layouts and report to plugin thread
* Event::AvailableLayoutInfo
* save layout command
* refactor save_layout
* fix api and permissions
* delete_layout API command
* edit layout in place API
* edit layout response through api
* propagate layout parsing errors
* generate random name plugin api
* dump stringified layout API
* synchronicity dump_session_layout and include LayoutMetadata in sync response
* parse_layout plugin api
* fix: default to default layout dir in plugin api
* rename_layout plugin command API
* initial implementation of multi-tab override
* apply to multiple tabs
* allow applying to only the active tab
* add tab/pane names to layout parsing metadata
* indicate is_builtin_plugin in PaneMetadata
* get_layout_dir plugin API command
* consolidate (and fix) is_builtin_plugin logic
* fix bad ret
* allow dumping the layout for just a specific tab in the session
* get focused pane and tab sync api
* color last substring variants
* add plugin
* update assets
* fix tests
* add e2e tests for new plugin apis
* update ui with new shortcut
* rustfmt
* docs(changelog): add PR
Diffstat (limited to 'zellij-utils/src/plugin_api')
| -rw-r--r-- | zellij-utils/src/plugin_api/action.proto | 20 | ||||
| -rw-r--r-- | zellij-utils/src/plugin_api/action.rs | 181 | ||||
| -rw-r--r-- | zellij-utils/src/plugin_api/event.proto | 55 | ||||
| -rw-r--r-- | zellij-utils/src/plugin_api/event.rs | 304 | ||||
| -rw-r--r-- | zellij-utils/src/plugin_api/plugin_command.proto | 137 | ||||
| -rw-r--r-- | zellij-utils/src/plugin_api/plugin_command.rs | 385 |
6 files changed, 941 insertions, 141 deletions
diff --git a/zellij-utils/src/plugin_api/action.proto b/zellij-utils/src/plugin_api/action.proto index de8ef44f..6b899b48 100644 --- a/zellij-utils/src/plugin_api/action.proto +++ b/zellij-utils/src/plugin_api/action.proto @@ -162,14 +162,20 @@ message NewTabPayload { optional UnblockCondition first_pane_unblock_condition = 9; } +message TabLayoutInfo { + uint32 tab_index = 1; + optional string tab_name = 2; + TiledPaneLayout tiled_layout = 3; + repeated FloatingPaneLayout floating_layouts = 4; + repeated SwapTiledLayout swap_tiled_layouts = 5; + repeated SwapFloatingLayout swap_floating_layouts = 6; +} + message OverrideLayoutPayload { - optional TiledPaneLayout tiled_layout = 1; - repeated FloatingPaneLayout floating_layouts = 2; - repeated SwapTiledLayout swap_tiled_layouts = 3; - repeated SwapFloatingLayout swap_floating_layouts = 4; - optional string tab_name = 5; - bool retain_existing_terminal_panes = 10; - bool retain_existing_plugin_panes = 11; + repeated TabLayoutInfo tabs = 1; + bool retain_existing_terminal_panes = 2; + bool retain_existing_plugin_panes = 3; + bool apply_only_to_active_tab = 4; } message Action { diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs index 860b090b..aa4c8b09 100644 --- a/zellij-utils/src/plugin_api/action.rs +++ b/zellij-utils/src/plugin_api/action.rs @@ -62,6 +62,7 @@ pub use super::generated_api::api::{ SwapFloatingLayout as ProtobufSwapFloatingLayout, SwapTiledLayout as ProtobufSwapTiledLayout, SwitchToModePayload, + TabLayoutInfo as ProtobufTabLayoutInfo, TiledPaneLayout as ProtobufTiledPaneLayout, TiledPlacement as ProtobufTiledPlacement, UnblockCondition as ProtobufUnblockCondition, @@ -83,7 +84,7 @@ use crate::input::layout::SplitSize; use crate::input::layout::{ FloatingPaneLayout, LayoutConstraint, PercentOrFixed, PluginAlias, PluginUserConfiguration, Run, RunPlugin, RunPluginLocation, RunPluginOrAlias, SplitDirection, SwapFloatingLayout, - SwapTiledLayout, TiledPaneLayout, + SwapTiledLayout, TabLayoutInfo, TiledPaneLayout, }; use crate::input::mouse::{MouseEvent, MouseEventType}; use crate::position::Position; @@ -723,48 +724,15 @@ impl TryFrom<ProtobufAction> for Action { }, Some(ProtobufActionName::OverrideLayout) => match protobuf_action.optional_payload { Some(OptionalPayload::OverrideLayoutPayload(payload)) => { - let tiled_layout = payload.tiled_layout.map(|l| l.try_into()).transpose()?; - - let floating_layouts = payload - .floating_layouts - .into_iter() - .map(|l| l.try_into()) - .collect::<Result<Vec<_>, _>>()?; - - let swap_tiled_layouts = if payload.swap_tiled_layouts.is_empty() { - None - } else { - Some( - payload - .swap_tiled_layouts - .into_iter() - .map(|l| l.try_into()) - .collect::<Result<Vec<_>, _>>()?, - ) - }; - - let swap_floating_layouts = if payload.swap_floating_layouts.is_empty() { - None - } else { - Some( - payload - .swap_floating_layouts - .into_iter() - .map(|l| l.try_into()) - .collect::<Result<Vec<_>, _>>()?, - ) - }; - - let tab_name = payload.tab_name.filter(|s| !s.is_empty()); - Ok(Action::OverrideLayout { - tiled_layout, - floating_layouts, - swap_tiled_layouts, - swap_floating_layouts, - tab_name, + tabs: payload + .tabs + .into_iter() + .map(|t| t.try_into()) + .collect::<Result<Vec<_>, _>>()?, retain_existing_terminal_panes: payload.retain_existing_terminal_panes, retain_existing_plugin_panes: payload.retain_existing_plugin_panes, + apply_only_to_active_tab: payload.apply_only_to_active_tab, }) }, Some(_) => Err("Mismatched payload for OverrideLayout"), @@ -1556,56 +1524,24 @@ impl TryFrom<Action> for ProtobufAction { optional_payload: None, }), Action::OverrideLayout { - tiled_layout, - floating_layouts, - swap_tiled_layouts, - swap_floating_layouts, - tab_name, + tabs, retain_existing_terminal_panes, retain_existing_plugin_panes, - } => { - let protobuf_tiled_layout = tiled_layout.map(|l| l.try_into()).transpose()?; - - let protobuf_floating_layouts = floating_layouts - .into_iter() - .map(|l| l.try_into()) - .collect::<Result<Vec<_>, _>>()?; - - let protobuf_swap_tiled_layouts = swap_tiled_layouts - .map(|layouts| { - layouts - .into_iter() - .map(|l| l.try_into()) - .collect::<Result<Vec<_>, _>>() - }) - .transpose()? - .unwrap_or_default(); - - let protobuf_swap_floating_layouts = swap_floating_layouts - .map(|layouts| { - layouts + apply_only_to_active_tab, + } => Ok(ProtobufAction { + name: ProtobufActionName::OverrideLayout as i32, + optional_payload: Some(OptionalPayload::OverrideLayoutPayload( + OverrideLayoutPayload { + tabs: tabs .into_iter() - .map(|l| l.try_into()) - .collect::<Result<Vec<_>, _>>() - }) - .transpose()? - .unwrap_or_default(); - - Ok(ProtobufAction { - name: ProtobufActionName::OverrideLayout as i32, - optional_payload: Some(OptionalPayload::OverrideLayoutPayload( - OverrideLayoutPayload { - tiled_layout: protobuf_tiled_layout, - floating_layouts: protobuf_floating_layouts, - swap_tiled_layouts: protobuf_swap_tiled_layouts, - swap_floating_layouts: protobuf_swap_floating_layouts, - tab_name: tab_name.clone(), - retain_existing_terminal_panes, - retain_existing_plugin_panes, - }, - )), - }) - }, + .map(|t| t.try_into()) + .collect::<Result<Vec<_>, _>>()?, + retain_existing_terminal_panes, + retain_existing_plugin_panes, + apply_only_to_active_tab, + }, + )), + }), Action::QueryTabNames => Ok(ProtobufAction { name: ProtobufActionName::QueryTabNames as i32, optional_payload: None, @@ -2739,6 +2675,77 @@ impl TryFrom<CommandOrPlugin> for ProtobufCommandOrPlugin { } } +impl TryFrom<ProtobufTabLayoutInfo> for TabLayoutInfo { + type Error = &'static str; + + fn try_from(protobuf_tab: ProtobufTabLayoutInfo) -> Result<Self, Self::Error> { + Ok(TabLayoutInfo { + tab_index: protobuf_tab.tab_index as usize, + tab_name: protobuf_tab.tab_name.filter(|s| !s.is_empty()), + tiled_layout: protobuf_tab + .tiled_layout + .ok_or("missing tiled_layout")? + .try_into()?, + floating_layouts: protobuf_tab + .floating_layouts + .into_iter() + .map(|l| l.try_into()) + .collect::<Result<Vec<_>, _>>()?, + swap_tiled_layouts: if protobuf_tab.swap_tiled_layouts.is_empty() { + None + } else { + Some( + protobuf_tab + .swap_tiled_layouts + .into_iter() + .map(|l| l.try_into()) + .collect::<Result<Vec<_>, _>>()?, + ) + }, + swap_floating_layouts: if protobuf_tab.swap_floating_layouts.is_empty() { + None + } else { + Some( + protobuf_tab + .swap_floating_layouts + .into_iter() + .map(|l| l.try_into()) + .collect::<Result<Vec<_>, _>>()?, + ) + }, + }) + } +} + +impl TryFrom<TabLayoutInfo> for ProtobufTabLayoutInfo { + type Error = &'static str; + + fn try_from(tab_info: TabLayoutInfo) -> Result<Self, Self::Error> { + Ok(ProtobufTabLayoutInfo { + tab_index: tab_info.tab_index as u32, + tab_name: tab_info.tab_name, + tiled_layout: Some(tab_info.tiled_layout.try_into()?), + floating_layouts: tab_info + .floating_layouts + .into_iter() + .map(|l| l.try_into()) + .collect::<Result<Vec<_>, _>>()?, + swap_tiled_layouts: tab_info + .swap_tiled_layouts + .unwrap_or_default() + .into_iter() + .map(|l| l.try_into()) + .collect::<Result<Vec<_>, _>>()?, + swap_floating_layouts: tab_info + .swap_floating_layouts + .unwrap_or_default() + .into_iter() + .map(|l| l.try_into()) + .collect::<Result<Vec<_>, _>>()?, + }) + } +} + impl TryFrom<ProtobufTiledPaneLayout> for TiledPaneLayout { type Error = &'static str; fn try_from(protobuf: ProtobufTiledPaneLayout) -> Result<Self, Self::Error> { diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto index 3bdeea19..10125387 100644 --- a/zellij-utils/src/plugin_api/event.proto +++ b/zellij-utils/src/plugin_api/event.proto @@ -62,6 +62,7 @@ enum EventType { UserAction = 37; ActionComplete = 38; CwdChanged = 39; + AvailableLayoutInfo = 40; } message EventNameList { @@ -103,6 +104,7 @@ message Event { UserActionPayload user_action_payload = 31; ActionCompletePayload action_complete_payload = 32; CwdChangedPayload cwd_changed_payload = 33; + AvailableLayoutInfoPayload available_layout_info_payload = 34; } } @@ -202,6 +204,11 @@ message SessionUpdatePayload { repeated ResurrectableSession resurrectable_sessions = 2; } +message AvailableLayoutInfoPayload { + repeated LayoutInfo available_layouts = 1; + repeated LayoutWithError layouts_with_errors = 2; +} + message RunCommandResultPayload { optional int32 exit_code = 1; bytes stdout = 2; @@ -318,6 +325,54 @@ message PluginInfo { message LayoutInfo { string name = 1; string source = 2; + optional LayoutMetadata layout_metadata = 5; +} + +message LayoutWithError { + string layout_name = 1; + LayoutParsingError error = 2; +} + +message LayoutParsingError { + oneof error_type { + KdlErrorVariant kdl_error = 1; + SyntaxError syntax_error = 2; + } +} + +message KdlErrorVariant { + KdlError kdl_error = 1; + string file_name = 2; + string source_code = 3; +} + +message SyntaxError { + // Empty message, just a marker +} + +message KdlError { + string error_message = 1; + optional uint64 offset = 2; + optional uint64 len = 3; + optional string help_message = 4; +} + +message LayoutMetadata { + repeated TabMetadata tabs = 1; + string creation_time = 2; + string update_time = 3; + +} + +message TabMetadata { + repeated PaneMetadata pane_metadata = 1; + optional string name = 2; +} + +message PaneMetadata { + optional string name = 1; + bool is_plugin = 2; + bool is_builtin_plugin = 3; } message ResurrectableSession { diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs index 4e430b0b..80d77e56 100644 --- a/zellij-utils/src/plugin_api/event.rs +++ b/zellij-utils/src/plugin_api/event.rs @@ -1,23 +1,29 @@ pub use super::generated_api::api::{ action::{Action as ProtobufAction, Position as ProtobufPosition}, event::{ - event::Payload as ProtobufEventPayload, pane_scrollback_response, - ActionCompletePayload as ProtobufActionCompletePayload, ClientInfo as ProtobufClientInfo, - ClientPaneHistory as ProtobufClientPaneHistory, + event::Payload as ProtobufEventPayload, + layout_parsing_error::ErrorType as ProtobufLayoutParsingErrorType, + pane_scrollback_response, ActionCompletePayload as ProtobufActionCompletePayload, + AvailableLayoutInfoPayload as ProtobufAvailableLayoutInfoPayload, + 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, + InputModeKeybinds as ProtobufInputModeKeybinds, KdlError as ProtobufKdlError, + KdlErrorVariant as ProtobufKdlErrorVariant, KeyBind as ProtobufKeyBind, + LayoutInfo as ProtobufLayoutInfo, LayoutMetadata as ProtobufLayoutMetadata, + LayoutParsingError as ProtobufLayoutParsingError, + LayoutWithError as ProtobufLayoutWithError, ModeUpdatePayload as ProtobufModeUpdatePayload, PaneContents as ProtobufPaneContents, PaneContentsEntry as ProtobufPaneContentsEntry, PaneId as ProtobufPaneId, PaneInfo as ProtobufPaneInfo, - PaneManifest as ProtobufPaneManifest, + PaneManifest as ProtobufPaneManifest, PaneMetadata as ProtobufPaneMetadata, PaneRenderReportPayload as ProtobufPaneRenderReportPayload, PaneScrollbackResponse as ProtobufPaneScrollbackResponse, PaneType as ProtobufPaneType, PluginInfo as ProtobufPluginInfo, ResurrectableSession as ProtobufResurrectableSession, SelectedText as ProtobufSelectedText, SessionManifest as ProtobufSessionManifest, - TabInfo as ProtobufTabInfo, UserActionPayload as ProtobufUserActionPayload, + SyntaxError as ProtobufSyntaxError, TabInfo as ProtobufTabInfo, + TabMetadata as ProtobufTabMetadata, UserActionPayload as ProtobufUserActionPayload, WebServerStatusPayload as ProtobufWebServerStatusPayload, WebSharing as ProtobufWebSharing, *, }, @@ -28,9 +34,10 @@ pub use super::generated_api::api::{ #[allow(hidden_glob_reexports)] use crate::data::{ 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, + KeyWithModifier, LayoutInfo, LayoutMetadata, ModeInfo, Mouse, PaneContents, PaneId, PaneInfo, + PaneManifest, PaneMetadata, PaneScrollbackResponse, PermissionStatus, PluginCapabilities, + PluginInfo, SelectedText, SessionInfo, Style, TabInfo, TabMetadata, WebServerStatus, + WebSharing, }; use crate::errors::prelude::*; @@ -458,6 +465,29 @@ impl TryFrom<ProtobufEvent> for Event { }, _ => Err("Malformed payload for the CwdChanged Event"), }, + Some(ProtobufEventType::AvailableLayoutInfo) => match protobuf_event.payload { + Some(ProtobufEventPayload::AvailableLayoutInfoPayload( + available_layout_info_payload, + )) => { + let mut available_layouts: Vec<LayoutInfo> = vec![]; + let mut layouts_with_errors: Vec<crate::data::LayoutWithError> = vec![]; + + for protobuf_layout_info in available_layout_info_payload.available_layouts { + available_layouts.push(LayoutInfo::try_from(protobuf_layout_info)?); + } + + for protobuf_error in available_layout_info_payload.layouts_with_errors { + layouts_with_errors + .push(crate::data::LayoutWithError::try_from(protobuf_error)?); + } + + Ok(Event::AvailableLayoutInfo( + available_layouts, + layouts_with_errors, + )) + }, + _ => Err("Malformed payload for the AvailableLayoutInfo Event"), + }, None => Err("Unknown Protobuf Event"), } } @@ -910,6 +940,30 @@ impl TryFrom<Event> for ProtobufEvent { payload: Some(event::Payload::CwdChangedPayload(cwd_changed_payload)), }) }, + Event::AvailableLayoutInfo(available_layouts, layouts_with_errors) => { + let mut protobuf_available_layouts = vec![]; + let mut protobuf_layouts_with_errors = vec![]; + + for layout_info in available_layouts { + protobuf_available_layouts.push(layout_info.try_into()?); + } + + for layout_error in layouts_with_errors { + protobuf_layouts_with_errors.push(layout_error.try_into()?); + } + + let available_layout_info_payload = ProtobufAvailableLayoutInfoPayload { + available_layouts: protobuf_available_layouts, + layouts_with_errors: protobuf_layouts_with_errors, + }; + + Ok(ProtobufEvent { + name: ProtobufEventType::AvailableLayoutInfo as i32, + payload: Some(event::Payload::AvailableLayoutInfoPayload( + available_layout_info_payload, + )), + }) + }, } } } @@ -1078,21 +1132,25 @@ impl TryFrom<LayoutInfo> for ProtobufLayoutInfo { type Error = &'static str; fn try_from(layout_info: LayoutInfo) -> Result<Self, &'static str> { match layout_info { - LayoutInfo::File(name) => Ok(ProtobufLayoutInfo { + LayoutInfo::File(name, layout_metadata) => Ok(ProtobufLayoutInfo { source: "file".to_owned(), name, + layout_metadata: Some(layout_metadata.try_into()?), }), LayoutInfo::BuiltIn(name) => Ok(ProtobufLayoutInfo { source: "built-in".to_owned(), name, + layout_metadata: None, }), LayoutInfo::Url(name) => Ok(ProtobufLayoutInfo { source: "url".to_owned(), name, + layout_metadata: None, }), LayoutInfo::Stringified(stringified_layout) => Ok(ProtobufLayoutInfo { source: "stringified".to_owned(), name: stringified_layout.clone(), + layout_metadata: None, }), } } @@ -1102,7 +1160,14 @@ impl TryFrom<ProtobufLayoutInfo> for LayoutInfo { type Error = &'static str; fn try_from(protobuf_layout_info: ProtobufLayoutInfo) -> Result<Self, &'static str> { match protobuf_layout_info.source.as_str() { - "file" => Ok(LayoutInfo::File(protobuf_layout_info.name)), + "file" => { + let layout_metadata = protobuf_layout_info + .layout_metadata + .map(|m| m.try_into()) + .transpose()? + .unwrap_or_default(); + Ok(LayoutInfo::File(protobuf_layout_info.name, layout_metadata)) + }, "built-in" => Ok(LayoutInfo::BuiltIn(protobuf_layout_info.name)), "url" => Ok(LayoutInfo::Url(protobuf_layout_info.name)), "stringified" => Ok(LayoutInfo::Stringified(protobuf_layout_info.name)), @@ -1111,6 +1176,183 @@ impl TryFrom<ProtobufLayoutInfo> for LayoutInfo { } } +impl TryFrom<ProtobufLayoutMetadata> for LayoutMetadata { + type Error = &'static str; + fn try_from(protobuf_metadata: ProtobufLayoutMetadata) -> Result<Self, &'static str> { + let tabs = protobuf_metadata + .tabs + .into_iter() + .map(|t| t.try_into()) + .collect::<Result<Vec<_>, _>>()?; + Ok(LayoutMetadata { + tabs, + creation_time: protobuf_metadata.creation_time, + update_time: protobuf_metadata.update_time, + }) + } +} + +impl TryFrom<LayoutMetadata> for ProtobufLayoutMetadata { + type Error = &'static str; + fn try_from(metadata: LayoutMetadata) -> Result<Self, &'static str> { + let tabs = metadata + .tabs + .into_iter() + .map(|t| t.try_into()) + .collect::<Result<Vec<_>, _>>()?; + Ok(ProtobufLayoutMetadata { + tabs, + creation_time: metadata.creation_time, + update_time: metadata.update_time, + }) + } +} + +impl TryFrom<ProtobufTabMetadata> for TabMetadata { + type Error = &'static str; + fn try_from(protobuf_metadata: ProtobufTabMetadata) -> Result<Self, &'static str> { + let panes = protobuf_metadata + .pane_metadata + .into_iter() + .map(|p| p.try_into()) + .collect::<Result<Vec<_>, _>>()?; + Ok(TabMetadata { + panes, + name: protobuf_metadata.name, + }) + } +} + +impl TryFrom<TabMetadata> for ProtobufTabMetadata { + type Error = &'static str; + fn try_from(metadata: TabMetadata) -> Result<Self, &'static str> { + let pane_metadata = metadata + .panes + .into_iter() + .map(|p| p.try_into()) + .collect::<Result<Vec<_>, _>>()?; + Ok(ProtobufTabMetadata { + pane_metadata, + name: metadata.name, + }) + } +} + +impl TryFrom<ProtobufPaneMetadata> for PaneMetadata { + type Error = &'static str; + fn try_from(protobuf_metadata: ProtobufPaneMetadata) -> Result<Self, &'static str> { + Ok(PaneMetadata { + name: protobuf_metadata.name, + is_plugin: protobuf_metadata.is_plugin, + is_builtin_plugin: protobuf_metadata.is_builtin_plugin, + }) + } +} + +impl TryFrom<PaneMetadata> for ProtobufPaneMetadata { + type Error = &'static str; + fn try_from(metadata: PaneMetadata) -> Result<Self, &'static str> { + Ok(ProtobufPaneMetadata { + name: metadata.name, + is_plugin: metadata.is_plugin, + is_builtin_plugin: metadata.is_builtin_plugin, + }) + } +} + +// LayoutWithError conversions +impl TryFrom<ProtobufLayoutWithError> for crate::data::LayoutWithError { + type Error = &'static str; + fn try_from(protobuf: ProtobufLayoutWithError) -> Result<Self, Self::Error> { + Ok(crate::data::LayoutWithError { + layout_name: protobuf.layout_name, + error: protobuf.error.ok_or("Missing error field")?.try_into()?, + }) + } +} + +impl TryFrom<crate::data::LayoutWithError> for ProtobufLayoutWithError { + type Error = &'static str; + fn try_from(layout_error: crate::data::LayoutWithError) -> Result<Self, Self::Error> { + Ok(ProtobufLayoutWithError { + layout_name: layout_error.layout_name, + error: Some(layout_error.error.try_into()?), + }) + } +} + +// LayoutParsingError conversions +impl TryFrom<ProtobufLayoutParsingError> for crate::data::LayoutParsingError { + type Error = &'static str; + fn try_from(protobuf: ProtobufLayoutParsingError) -> Result<Self, Self::Error> { + match protobuf.error_type.ok_or("Missing error_type")? { + ProtobufLayoutParsingErrorType::KdlError(kdl_variant) => { + Ok(crate::data::LayoutParsingError::KdlError { + kdl_error: kdl_variant + .kdl_error + .ok_or("Missing kdl_error")? + .try_into()?, + file_name: kdl_variant.file_name, + source_code: kdl_variant.source_code, + }) + }, + ProtobufLayoutParsingErrorType::SyntaxError(_) => { + Ok(crate::data::LayoutParsingError::SyntaxError) + }, + } + } +} + +impl TryFrom<crate::data::LayoutParsingError> for ProtobufLayoutParsingError { + type Error = &'static str; + fn try_from(error: crate::data::LayoutParsingError) -> Result<Self, Self::Error> { + let error_type = match error { + crate::data::LayoutParsingError::KdlError { + kdl_error, + file_name, + source_code, + } => ProtobufLayoutParsingErrorType::KdlError(ProtobufKdlErrorVariant { + kdl_error: Some(kdl_error.try_into()?), + file_name, + source_code, + }), + crate::data::LayoutParsingError::SyntaxError => { + ProtobufLayoutParsingErrorType::SyntaxError(ProtobufSyntaxError {}) + }, + }; + Ok(ProtobufLayoutParsingError { + error_type: Some(error_type), + }) + } +} + +// KdlError conversions +impl TryFrom<ProtobufKdlError> for crate::input::config::KdlError { + type Error = &'static str; + fn try_from(protobuf: ProtobufKdlError) -> Result<Self, Self::Error> { + Ok(crate::input::config::KdlError { + error_message: protobuf.error_message, + src: None, // We don't serialize NamedSource + offset: protobuf.offset.map(|o| o as usize), + len: protobuf.len.map(|l| l as usize), + help_message: protobuf.help_message, + }) + } +} + +impl TryFrom<crate::input::config::KdlError> for ProtobufKdlError { + type Error = &'static str; + fn try_from(kdl: crate::input::config::KdlError) -> Result<Self, Self::Error> { + Ok(ProtobufKdlError { + error_message: kdl.error_message, + // src is not serialized + offset: kdl.offset.map(|o| o as u64), + len: kdl.len.map(|l| l as u64), + help_message: kdl.help_message, + }) + } +} + impl TryFrom<CopyDestination> for ProtobufCopyDestination { type Error = &'static str; fn try_from(copy_destination: CopyDestination) -> Result<Self, &'static str> { @@ -1620,6 +1862,7 @@ impl TryFrom<ProtobufEventType> for EventType { ProtobufEventType::UserAction => EventType::UserAction, ProtobufEventType::ActionComplete => EventType::ActionComplete, ProtobufEventType::CwdChanged => EventType::CwdChanged, + ProtobufEventType::AvailableLayoutInfo => EventType::AvailableLayoutInfo, }) } } @@ -1667,6 +1910,7 @@ impl TryFrom<EventType> for ProtobufEventType { EventType::UserAction => ProtobufEventType::UserAction, EventType::ActionComplete => ProtobufEventType::ActionComplete, EventType::CwdChanged => ProtobufEventType::CwdChanged, + EventType::AvailableLayoutInfo => ProtobufEventType::AvailableLayoutInfo, }) } } @@ -2268,9 +2512,23 @@ fn serialize_session_update_event_with_non_default_values() { connected_clients: 2, is_current_session: true, available_layouts: vec![ - LayoutInfo::File("layout 1".to_owned()), + LayoutInfo::File( + "layout 1".to_owned(), + LayoutMetadata { + tabs: vec![], + creation_time: "0".to_owned(), + update_time: "0".to_owned(), + }, + ), LayoutInfo::BuiltIn("layout2".to_owned()), - LayoutInfo::File("layout3".to_owned()), + LayoutInfo::File( + "layout3".to_owned(), + LayoutMetadata { + tabs: vec![], + creation_time: "0".to_owned(), + update_time: "0".to_owned(), + }, + ), ], plugins, web_clients_allowed: false, @@ -2287,9 +2545,23 @@ fn serialize_session_update_event_with_non_default_values() { connected_clients: 0, is_current_session: false, available_layouts: vec![ - LayoutInfo::File("layout 1".to_owned()), + LayoutInfo::File( + "layout 1".to_owned(), + LayoutMetadata { + tabs: vec![], + creation_time: "0".to_owned(), + update_time: "0".to_owned(), + }, + ), LayoutInfo::BuiltIn("layout2".to_owned()), - LayoutInfo::File("layout3".to_owned()), + LayoutInfo::File( + "layout3".to_owned(), + LayoutMetadata { + tabs: vec![], + creation_time: "0".to_owned(), + update_time: "0".to_owned(), + }, + ), ], plugins: Default::default(), web_clients_allowed: false, diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto index fb456ccd..0a0ad2b0 100644 --- a/zellij-utils/src/plugin_api/plugin_command.proto +++ b/zellij-utils/src/plugin_api/plugin_command.proto @@ -166,6 +166,16 @@ enum CommandName { SendSigintToPaneId = 171; SendSigkillToPaneId = 172; GetPanePid = 173; + OverrideLayout = 174; + SaveLayout = 175; + DeleteLayout = 176; + EditLayout = 177; + GenerateRandomName = 178; + DumpLayout = 179; + ParseLayout = 180; + RenameLayout = 181; + GetLayoutDir = 182; + GetFocusedPaneInfo = 183; } message PluginCommand { @@ -280,6 +290,17 @@ message PluginCommand { PaneId send_sigint_to_pane_id_payload = 121; PaneId send_sigkill_to_pane_id_payload = 122; GetPanePidPayload get_pane_pid_payload = 123; + OverrideLayoutPayload override_layout_payload = 124; + SaveLayoutPayload save_layout_payload = 125; + DeleteLayoutPayload delete_layout_payload = 126; + EditLayoutPayload edit_layout_payload = 127; + GenerateRandomNamePayload generate_random_name_payload = 128; + DumpLayoutPayload dump_layout_payload = 129; + ParseLayoutPayload parse_layout_payload = 130; + RenameLayoutPayload rename_layout_payload = 131; + GetLayoutDirPayload get_layout_dir_payload = 132; + DumpSessionLayoutPayload dump_session_layout_payload = 133; + GetFocusedPaneInfoPayload get_focused_pane_info_payload = 134; } } @@ -288,6 +309,10 @@ message NewTabPayload { optional string cwd = 2; } +message DumpSessionLayoutPayload { + optional uint32 tab_index = 1; +} + message RunActionPayload { action.Action action = 1; repeated ContextItem context = 2; @@ -755,3 +780,115 @@ message GetPanePidResponse { string error = 2; } } + +message SaveLayoutResponse { + oneof result { + bool success = 1; + string error = 2; + } +} + +message DeleteLayoutResponse { + oneof result { + bool success = 1; + string error = 2; + } +} + +message EditLayoutResponse { + oneof result { + bool success = 1; + string error = 2; + } +} + +message GenerateRandomNameResponse { + string name = 1; +} + +message OverrideLayoutPayload { + event.LayoutInfo layout_info = 1; + repeated ContextItem context = 2; + bool retain_existing_terminal_panes = 3; + bool retain_existing_plugin_panes = 4; + bool apply_only_to_active_tab = 5; +} + +message SaveLayoutPayload { + string layout_name = 1; + string layout_kdl = 2; + bool overwrite = 3; +} + +message DeleteLayoutPayload { + string layout_name = 1; +} + +message EditLayoutPayload { + string layout_name = 1; + repeated ContextItem context = 2; +} + +message GenerateRandomNamePayload {} + +message DumpLayoutPayload { + string layout_name = 1; +} + +message DumpLayoutResponse { + oneof result { + string layout_content = 1; + string error = 2; + } +} + +message DumpSessionLayoutResponse { + oneof result { + string layout_content = 1; + string error = 2; + } + optional api.event.LayoutMetadata metadata = 3; +} + +message ParseLayoutPayload { + string layout_string = 1; +} + +message ParseLayoutResponse { + oneof result { + api.event.LayoutMetadata metadata = 1; + api.event.LayoutParsingError error = 2; + } +} + +message RenameLayoutPayload { + string old_layout_name = 1; + string new_layout_name = 2; +} + +message RenameLayoutResponse { + oneof result { + bool success = 1; + string error = 2; + } +} + +message GetLayoutDirPayload {} + +message GetLayoutDirResponse { + string layout_dir = 1; +} + +message GetFocusedPaneInfoPayload {} + +message GetFocusedPaneInfoResponse { + oneof result { + FocusedPaneInfo focused_pane_info = 1; + string error = 2; + } +} + +message FocusedPaneInfo { + uint32 focused_tab_index = 1; + PaneId focused_pane_id = 2; +} diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs index 542e1ea2..8c4bf451 100644 --- a/zellij-utils/src/plugin_api/plugin_command.rs +++ b/zellij-utils/src/plugin_api/plugin_command.rs @@ -3,35 +3,48 @@ pub use super::generated_api::api::{ event::{EventNameList as ProtobufEventNameList, Header}, input_mode::InputMode as ProtobufInputMode, plugin_command::{ - get_pane_pid_response, plugin_command::Payload, BreakPanesToNewTabPayload, - BreakPanesToTabWithIndexPayload, ChangeFloatingPanesCoordinatesPayload, - ChangeHostFolderPayload, ClearScreenForPaneIdPayload, CliPipeOutputPayload, - CloseMultiplePanesPayload, CloseTabWithIndexPayload, CommandName, ContextItem, - CopyToClipboardPayload, CreateTokenResponse as ProtobufCreateTokenResponse, - CreateTokenResponse, CursorPosition, EditScrollbackForPaneWithIdPayload, - EmbedMultiplePanesPayload, EnvVariable, ExecCmdPayload, + delete_layout_response, dump_layout_response, dump_session_layout_response, + edit_layout_response, get_focused_pane_info_response, get_pane_pid_response, + parse_layout_response, plugin_command::Payload, rename_layout_response, + save_layout_response, BreakPanesToNewTabPayload, BreakPanesToTabWithIndexPayload, + ChangeFloatingPanesCoordinatesPayload, ChangeHostFolderPayload, + ClearScreenForPaneIdPayload, CliPipeOutputPayload, CloseMultiplePanesPayload, + CloseTabWithIndexPayload, CommandName, ContextItem, CopyToClipboardPayload, + CreateTokenResponse as ProtobufCreateTokenResponse, CreateTokenResponse, CursorPosition, + DeleteLayoutPayload, DeleteLayoutResponse as ProtobufDeleteLayoutResponse, + DumpLayoutPayload, DumpLayoutResponse as ProtobufDumpLayoutResponse, + DumpSessionLayoutPayload, DumpSessionLayoutResponse as ProtobufDumpSessionLayoutResponse, + EditLayoutPayload, EditLayoutResponse as ProtobufEditLayoutResponse, + EditScrollbackForPaneWithIdPayload, EmbedMultiplePanesPayload, EnvVariable, ExecCmdPayload, FixedOrPercent as ProtobufFixedOrPercent, FixedOrPercentValue as ProtobufFixedOrPercentValue, FloatMultiplePanesPayload, - FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, GenerateWebLoginTokenPayload, - GetPanePidPayload, GetPanePidResponse as ProtobufGetPanePidResponse, - GetPaneScrollbackPayload, GroupAndUngroupPanesPayload, HidePaneWithIdPayload, - HighlightAndUnhighlightPanesPayload, HttpVerb as ProtobufHttpVerb, IdAndNewName, - KeyToRebind, KeyToUnbind, KillSessionsPayload, ListTokensResponse, LoadNewPluginPayload, - MessageToPluginPayload, MovePaneWithPaneIdInDirectionPayload, MovePaneWithPaneIdPayload, - MovePayload, NewPluginArgs as ProtobufNewPluginArgs, NewTabPayload, - NewTabsWithLayoutInfoPayload, OpenCommandPaneFloatingNearPluginPayload, - OpenCommandPaneInPlaceOfPluginPayload, OpenCommandPaneNearPluginPayload, - OpenCommandPanePayload, OpenFileFloatingNearPluginPayload, OpenFileInPlaceOfPluginPayload, + FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, FocusedPaneInfo, + GenerateRandomNamePayload, + GenerateRandomNameResponse as ProtobufGenerateRandomNameResponse, + GenerateWebLoginTokenPayload, GetFocusedPaneInfoPayload, + GetFocusedPaneInfoResponse as ProtobufGetFocusedPaneInfoResponse, GetLayoutDirPayload, + GetLayoutDirResponse as ProtobufGetLayoutDirResponse, GetPanePidPayload, + GetPanePidResponse as ProtobufGetPanePidResponse, GetPaneScrollbackPayload, + GroupAndUngroupPanesPayload, HidePaneWithIdPayload, HighlightAndUnhighlightPanesPayload, + HttpVerb as ProtobufHttpVerb, IdAndNewName, KeyToRebind, KeyToUnbind, KillSessionsPayload, + ListTokensResponse, LoadNewPluginPayload, MessageToPluginPayload, + MovePaneWithPaneIdInDirectionPayload, MovePaneWithPaneIdPayload, MovePayload, + NewPluginArgs as ProtobufNewPluginArgs, NewTabPayload, NewTabsWithLayoutInfoPayload, + OpenCommandPaneFloatingNearPluginPayload, OpenCommandPaneInPlaceOfPluginPayload, + OpenCommandPaneNearPluginPayload, OpenCommandPanePayload, + OpenFileFloatingNearPluginPayload, OpenFileInPlaceOfPluginPayload, OpenFileNearPluginPayload, OpenFilePayload, OpenTerminalFloatingNearPluginPayload, - OpenTerminalInPlaceOfPluginPayload, OpenTerminalNearPluginPayload, + OpenTerminalInPlaceOfPluginPayload, OpenTerminalNearPluginPayload, OverrideLayoutPayload, PageScrollDownInPaneIdPayload, PageScrollUpInPaneIdPayload, PaneId as ProtobufPaneId, - PaneIdAndFloatingPaneCoordinates, PaneType as ProtobufPaneType, - PluginCommand as ProtobufPluginCommand, PluginMessagePayload, RebindKeysPayload, - ReconfigurePayload, ReloadPluginPayload, RenameWebLoginTokenPayload, - RenameWebTokenResponse, ReplacePaneWithExistingPanePayload, RequestPluginPermissionPayload, - RerunCommandPanePayload, ResizePaneIdWithDirectionPayload, ResizePayload, - RevokeAllWebTokensResponse, RevokeTokenResponse, RevokeWebLoginTokenPayload, - RunActionPayload, RunCommandPayload, ScrollDownInPaneIdPayload, + PaneIdAndFloatingPaneCoordinates, PaneType as ProtobufPaneType, ParseLayoutPayload, + ParseLayoutResponse as ProtobufParseLayoutResponse, PluginCommand as ProtobufPluginCommand, + PluginMessagePayload, RebindKeysPayload, ReconfigurePayload, ReloadPluginPayload, + RenameLayoutPayload, RenameLayoutResponse as ProtobufRenameLayoutResponse, + RenameWebLoginTokenPayload, RenameWebTokenResponse, ReplacePaneWithExistingPanePayload, + RequestPluginPermissionPayload, RerunCommandPanePayload, ResizePaneIdWithDirectionPayload, + ResizePayload, RevokeAllWebTokensResponse, RevokeTokenResponse, RevokeWebLoginTokenPayload, + RunActionPayload, RunCommandPayload, SaveLayoutPayload, + SaveLayoutResponse as ProtobufSaveLayoutResponse, ScrollDownInPaneIdPayload, ScrollToBottomInPaneIdPayload, ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload, SetFloatingPanePinnedPayload, SetSelfMouseSelectionSupportPayload, SetTimeoutPayload, ShowCursorPayload, ShowPaneWithIdPayload, StackPanesPayload, SubscribePayload, @@ -44,8 +57,10 @@ pub use super::generated_api::api::{ }; use crate::data::{ - ConnectToSession, FloatingPaneCoordinates, GetPanePidResponse, HttpVerb, InputMode, - KeyWithModifier, MessageToPlugin, NewPluginArgs, PaneId, PermissionType, PluginCommand, + ConnectToSession, DeleteLayoutResponse, EditLayoutResponse, FloatingPaneCoordinates, + GetFocusedPaneInfoResponse, GetPanePidResponse, HttpVerb, InputMode, KeyWithModifier, + MessageToPlugin, NewPluginArgs, PaneId, PermissionType, PluginCommand, RenameLayoutResponse, + SaveLayoutResponse, }; use crate::input::actions::Action; use crate::input::layout::SplitSize; @@ -227,6 +242,135 @@ impl From<GetPanePidResponse> for ProtobufGetPanePidResponse { } } +impl From<GetFocusedPaneInfoResponse> for ProtobufGetFocusedPaneInfoResponse { + fn from(response: GetFocusedPaneInfoResponse) -> Self { + match response { + GetFocusedPaneInfoResponse::Ok { tab_index, pane_id } => { + let protobuf_pane_id = ProtobufPaneId { + pane_type: match pane_id { + PaneId::Terminal(_) => ProtobufPaneType::Terminal as i32, + PaneId::Plugin(_) => ProtobufPaneType::Plugin as i32, + }, + id: match pane_id { + PaneId::Terminal(id) | PaneId::Plugin(id) => id, + }, + }; + ProtobufGetFocusedPaneInfoResponse { + result: Some(get_focused_pane_info_response::Result::FocusedPaneInfo( + FocusedPaneInfo { + focused_tab_index: tab_index as u32, + focused_pane_id: Some(protobuf_pane_id), + }, + )), + } + }, + GetFocusedPaneInfoResponse::Err(err) => ProtobufGetFocusedPaneInfoResponse { + result: Some(get_focused_pane_info_response::Result::Error(err)), + }, + } + } +} + +impl TryFrom<ProtobufSaveLayoutResponse> for SaveLayoutResponse { + type Error = &'static str; + fn try_from(protobuf_response: ProtobufSaveLayoutResponse) -> Result<Self, &'static str> { + match protobuf_response.result { + Some(save_layout_response::Result::Success(_)) => Ok(SaveLayoutResponse::Ok(())), + Some(save_layout_response::Result::Error(error)) => Ok(SaveLayoutResponse::Err(error)), + None => Err("Empty SaveLayoutResponse"), + } + } +} + +impl From<SaveLayoutResponse> for ProtobufSaveLayoutResponse { + fn from(response: SaveLayoutResponse) -> Self { + match response { + SaveLayoutResponse::Ok(_) => ProtobufSaveLayoutResponse { + result: Some(save_layout_response::Result::Success(true)), + }, + SaveLayoutResponse::Err(error) => ProtobufSaveLayoutResponse { + result: Some(save_layout_response::Result::Error(error)), + }, + } + } +} + +impl TryFrom<ProtobufDeleteLayoutResponse> for DeleteLayoutResponse { + type Error = &'static str; + fn try_from(protobuf_response: ProtobufDeleteLayoutResponse) -> Result<Self, &'static str> { + match protobuf_response.result { + Some(delete_layout_response::Result::Success(_)) => Ok(DeleteLayoutResponse::Ok(())), + Some(delete_layout_response::Result::Error(error)) => { + Ok(DeleteLayoutResponse::Err(error)) + }, + None => Err("Empty DeleteLayoutResponse"), + } + } +} + +impl From<DeleteLayoutResponse> for ProtobufDeleteLayoutResponse { + fn from(response: DeleteLayoutResponse) -> Self { + match response { + DeleteLayoutResponse::Ok(_) => ProtobufDeleteLayoutResponse { + result: Some(delete_layout_response::Result::Success(true)), + }, + DeleteLayoutResponse::Err(error) => ProtobufDeleteLayoutResponse { + result: Some(delete_layout_response::Result::Error(error)), + }, + } + } +} + +impl TryFrom<ProtobufRenameLayoutResponse> for RenameLayoutResponse { + type Error = &'static str; + fn try_from(protobuf_response: ProtobufRenameLayoutResponse) -> Result<Self, &'static str> { + match protobuf_response.result { + Some(rename_layout_response::Result::Success(_)) => Ok(RenameLayoutResponse::Ok(())), + Some(rename_layout_response::Result::Error(error)) => { + Ok(RenameLayoutResponse::Err(error)) + }, + None => Err("Empty RenameLayoutResponse"), + } + } +} + +impl From<RenameLayoutResponse> for ProtobufRenameLayoutResponse { + fn from(response: RenameLayoutResponse) -> Self { + match response { + RenameLayoutResponse::Ok(_) => ProtobufRenameLayoutResponse { + result: Some(rename_layout_response::Result::Success(true)), + }, + RenameLayoutResponse::Err(error) => ProtobufRenameLayoutResponse { + result: Some(rename_layout_response::Result::Error(error)), + }, + } + } +} + +impl TryFrom<ProtobufEditLayoutResponse> for EditLayoutResponse { + type Error = &'static str; + fn try_from(protobuf_response: ProtobufEditLayoutResponse) -> Result<Self, &'static str> { + match protobuf_response.result { + Some(edit_layout_response::Result::Success(_)) => Ok(EditLayoutResponse::Ok(())), + Some(edit_layout_response::Result::Error(error)) => Ok(EditLayoutResponse::Err(error)), + None => Err("Empty EditLayoutResponse"), + } + } +} + +impl From<EditLayoutResponse> for ProtobufEditLayoutResponse { + fn from(response: EditLayoutResponse) -> Self { + match response { + EditLayoutResponse::Ok(_) => ProtobufEditLayoutResponse { + result: Some(edit_layout_response::Result::Success(true)), + }, + EditLayoutResponse::Err(error) => ProtobufEditLayoutResponse { + result: Some(edit_layout_response::Result::Error(error)), + }, + } + } +} + impl TryFrom<(InputMode, KeyWithModifier, Vec<Action>)> for KeyToRebind { type Error = &'static str; fn try_from( @@ -1047,8 +1191,13 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand { None => Ok(PluginCommand::WatchFilesystem), }, Some(CommandName::DumpSessionLayout) => match protobuf_plugin_command.payload { - Some(_) => Err("DumpSessionLayout should have no payload, found a payload"), - None => Ok(PluginCommand::DumpSessionLayout), + Some(Payload::DumpSessionLayoutPayload(payload)) => { + Ok(PluginCommand::DumpSessionLayout { + tab_index: payload.tab_index.map(|i| i as usize), + }) + }, + None => Ok(PluginCommand::DumpSessionLayout { tab_index: None }), + _ => Err("Mismatched payload for DumpSessionLayout"), }, Some(CommandName::CloseSelf) => match protobuf_plugin_command.payload { Some(_) => Err("CloseSelf should have no payload, found a payload"), @@ -1215,6 +1364,70 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand { }, _ => Err("Mismatched payload for GetPanePid"), }, + Some(CommandName::OverrideLayout) => match protobuf_plugin_command.payload { + Some(Payload::OverrideLayoutPayload(override_layout_payload)) => { + let layout_info = override_layout_payload + .layout_info + .ok_or("OverrideLayout missing layout_info")? + .try_into() + .map_err(|_| "Failed to parse LayoutInfo")?; + let context = override_layout_payload + .context + .into_iter() + .map(|c| (c.name, c.value)) + .collect(); + Ok(PluginCommand::OverrideLayout( + layout_info, + override_layout_payload.retain_existing_terminal_panes, + override_layout_payload.retain_existing_plugin_panes, + override_layout_payload.apply_only_to_active_tab, + context, + )) + }, + _ => Err("Mismatched payload for OverrideLayout"), + }, + Some(CommandName::SaveLayout) => match protobuf_plugin_command.payload { + Some(Payload::SaveLayoutPayload(save_layout_payload)) => { + Ok(PluginCommand::SaveLayout { + layout_name: save_layout_payload.layout_name, + layout_kdl: save_layout_payload.layout_kdl, + overwrite: save_layout_payload.overwrite, + }) + }, + _ => Err("Mismatched payload for SaveLayout"), + }, + Some(CommandName::DeleteLayout) => match protobuf_plugin_command.payload { + Some(Payload::DeleteLayoutPayload(delete_layout_payload)) => { + Ok(PluginCommand::DeleteLayout { + layout_name: delete_layout_payload.layout_name, + }) + }, + _ => Err("Mismatched payload for DeleteLayout"), + }, + Some(CommandName::RenameLayout) => match protobuf_plugin_command.payload { + Some(Payload::RenameLayoutPayload(rename_layout_payload)) => { + Ok(PluginCommand::RenameLayout { + old_layout_name: rename_layout_payload.old_layout_name, + new_layout_name: rename_layout_payload.new_layout_name, + }) + }, + _ => Err("Mismatched payload for RenameLayout"), + }, + Some(CommandName::EditLayout) => match protobuf_plugin_command.payload { + Some(Payload::EditLayoutPayload(edit_layout_payload)) => { + let layout_name = edit_layout_payload.layout_name; + let context: BTreeMap<String, String> = edit_layout_payload + .context + .into_iter() + .map(|e| (e.name, e.value)) + .collect(); + Ok(PluginCommand::EditLayout { + layout_name, + context, + }) + }, + _ => Err("Mismatched payload for EditLayout"), + }, Some(CommandName::MovePaneWithPaneId) => match protobuf_plugin_command.payload { Some(Payload::MovePaneWithPaneIdPayload(move_pane_with_pane_id_payload)) => { match move_pane_with_pane_id_payload.pane_id { @@ -1845,6 +2058,21 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand { }, _ => Err("Mismatched payload for CopyToClipboard"), }, + Some(CommandName::GenerateRandomName) => Ok(PluginCommand::GenerateRandomName), + Some(CommandName::DumpLayout) => match protobuf_plugin_command.payload { + Some(Payload::DumpLayoutPayload(payload)) => { + Ok(PluginCommand::DumpLayout(payload.layout_name)) + }, + _ => Err("Mismatched payload for DumpLayout"), + }, + Some(CommandName::ParseLayout) => match protobuf_plugin_command.payload { + Some(Payload::ParseLayoutPayload(payload)) => { + Ok(PluginCommand::ParseLayout(payload.layout_string)) + }, + _ => Err("Mismatched payload for ParseLayout"), + }, + Some(CommandName::GetLayoutDir) => Ok(PluginCommand::GetLayoutDir), + Some(CommandName::GetFocusedPaneInfo) => Ok(PluginCommand::GetFocusedPaneInfo), None => Err("Unrecognized plugin command"), } } @@ -2417,9 +2645,13 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand { name: CommandName::WatchFilesystem as i32, payload: None, }), - PluginCommand::DumpSessionLayout => Ok(ProtobufPluginCommand { + PluginCommand::DumpSessionLayout { tab_index } => Ok(ProtobufPluginCommand { name: CommandName::DumpSessionLayout as i32, - payload: None, + payload: tab_index.map(|idx| { + Payload::DumpSessionLayoutPayload(DumpSessionLayoutPayload { + tab_index: Some(idx as u32), + }) + }), }), PluginCommand::CloseSelf => Ok(ProtobufPluginCommand { name: CommandName::CloseSelf as i32, @@ -2545,6 +2777,69 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand { pane_id: Some(pane_id.try_into()?), })), }), + PluginCommand::OverrideLayout( + layout_info, + retain_existing_terminal_panes, + retain_existing_plugin_panes, + apply_only_to_active_tab, + context, + ) => Ok(ProtobufPluginCommand { + name: CommandName::OverrideLayout as i32, + payload: Some(Payload::OverrideLayoutPayload(OverrideLayoutPayload { + layout_info: layout_info.try_into().ok(), + context: context + .into_iter() + .map(|(name, value)| ContextItem { name, value }) + .collect(), + retain_existing_terminal_panes, + retain_existing_plugin_panes, + apply_only_to_active_tab, + })), + }), + PluginCommand::SaveLayout { + layout_name, + layout_kdl, + overwrite, + } => Ok(ProtobufPluginCommand { + name: CommandName::SaveLayout as i32, + payload: Some(Payload::SaveLayoutPayload(SaveLayoutPayload { + layout_name, + layout_kdl, + overwrite, + })), + }), + PluginCommand::DeleteLayout { layout_name } => Ok(ProtobufPluginCommand { + name: CommandName::DeleteLayout as i32, + payload: Some(Payload::DeleteLayoutPayload(DeleteLayoutPayload { + layout_name, + })), + }), + PluginCommand::RenameLayout { + old_layout_name, + new_layout_name, + } => Ok(ProtobufPluginCommand { + name: CommandName::RenameLayout as i32, + payload: Some(Payload::RenameLayoutPayload(RenameLayoutPayload { + old_layout_name, + new_layout_name, + })), + }), + PluginCommand::EditLayout { + layout_name, + context, + } => { + let context: Vec<_> = context + .into_iter() + .map(|(name, value)| ContextItem { name, value }) + .collect(); + Ok(ProtobufPluginCommand { + name: CommandName::EditLayout as i32, + payload: Some(Payload::EditLayoutPayload(EditLayoutPayload { + layout_name, + context, + })), + }) + }, PluginCommand::MovePaneWithPaneId(pane_id) => Ok(ProtobufPluginCommand { name: CommandName::MovePaneWithPaneId as i32, payload: Some(Payload::MovePaneWithPaneIdPayload( @@ -3073,6 +3368,34 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand { text, })), }), + PluginCommand::GenerateRandomName => Ok(ProtobufPluginCommand { + name: CommandName::GenerateRandomName as i32, + payload: Some(Payload::GenerateRandomNamePayload( + GenerateRandomNamePayload {}, + )), + }), + PluginCommand::DumpLayout(layout_name) => Ok(ProtobufPluginCommand { + name: CommandName::DumpLayout as i32, + payload: Some(Payload::DumpLayoutPayload(DumpLayoutPayload { + layout_name, + })), + }), + PluginCommand::ParseLayout(layout_string) => Ok(ProtobufPluginCommand { + name: CommandName::ParseLayout as i32, + payload: Some(Payload::ParseLayoutPayload(ParseLayoutPayload { + layout_string, + })), + }), + PluginCommand::GetLayoutDir => Ok(ProtobufPluginCommand { + name: CommandName::GetLayoutDir as i32, + payload: Some(Payload::GetLayoutDirPayload(GetLayoutDirPayload {})), + }), + PluginCommand::GetFocusedPaneInfo => Ok(ProtobufPluginCommand { + name: CommandName::GetFocusedPaneInfo as i32, + payload: Some(Payload::GetFocusedPaneInfoPayload( + GetFocusedPaneInfoPayload {}, + )), + }), } } } |
