aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-utils/src/plugin_api/action.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/action.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/action.rs')
-rw-r--r--zellij-utils/src/plugin_api/action.rs1340
1 files changed, 1297 insertions, 43 deletions
diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs
index c442144a..f8de7b76 100644
--- a/zellij-utils/src/plugin_api/action.rs
+++ b/zellij-utils/src/plugin_api/action.rs
@@ -1,26 +1,88 @@
pub use super::generated_api::api::{
action::{
- action::OptionalPayload, Action as ProtobufAction, ActionName as ProtobufActionName,
- DumpScreenPayload, EditFilePayload, GoToTabNamePayload, IdAndName,
- LaunchOrFocusPluginPayload, MouseEventPayload as ProtobufMouseEventPayload,
- MovePanePayload, MoveTabDirection as ProtobufMoveTabDirection,
- NameAndValue as ProtobufNameAndValue, NewFloatingPanePayload, NewPanePayload,
- NewPluginPanePayload, NewTiledPanePayload, PaneIdAndShouldFloat,
- PluginConfiguration as ProtobufPluginConfiguration, Position as ProtobufPosition,
- RunCommandAction as ProtobufRunCommandAction, ScrollAtPayload,
- SearchDirection as ProtobufSearchDirection, SearchOption as ProtobufSearchOption,
- SwitchToModePayload, WriteCharsPayload, WritePayload,
+ action::OptionalPayload,
+ command_or_plugin::CommandOrPluginType,
+ pane_run::RunType,
+ run_plugin_location_data::LocationData,
+ run_plugin_or_alias::PluginType,
+ Action as ProtobufAction,
+ ActionName as ProtobufActionName,
+ BareKey as ProtobufBareKey,
+ // New layout-related types
+ CommandOrPlugin as ProtobufCommandOrPlugin,
+ DumpScreenPayload,
+ EditFilePayload,
+ FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates,
+ FloatingPaneLayout as ProtobufFloatingPaneLayout,
+ FloatingPlacement as ProtobufFloatingPlacement,
+ GoToTabNamePayload,
+ IdAndName,
+ InPlaceConfig as ProtobufInPlaceConfig,
+ KeyModifier as ProtobufKeyModifier,
+ KeyWithModifier as ProtobufKeyWithModifier,
+ LaunchOrFocusPluginPayload,
+ LayoutConstraint as ProtobufLayoutConstraint,
+ LayoutConstraintFloatingPair as ProtobufLayoutConstraintFloatingPair,
+ LayoutConstraintTiledPair as ProtobufLayoutConstraintTiledPair,
+ LayoutConstraintWithValue as ProtobufLayoutConstraintWithValue,
+ MouseEventPayload as ProtobufMouseEventPayload,
+ MovePanePayload,
+ MoveTabDirection as ProtobufMoveTabDirection,
+ NameAndValue as ProtobufNameAndValue,
+ NewBlockingPanePayload,
+ NewFloatingPanePayload,
+ NewInPlacePanePayload,
+ NewPanePayload,
+ NewPanePlacement as ProtobufNewPanePlacement,
+ NewPluginPanePayload,
+ NewTabPayload,
+ NewTiledPanePayload,
+ PaneId as ProtobufPaneId,
+ PaneIdAndShouldFloat,
+ PaneRun as ProtobufPaneRun,
+ PercentOrFixed as ProtobufPercentOrFixed,
+ PluginAlias as ProtobufPluginAlias,
+ PluginConfiguration as ProtobufPluginConfiguration,
+ PluginTag as ProtobufPluginTag,
+ PluginUserConfiguration as ProtobufPluginUserConfiguration,
+ Position as ProtobufPosition,
+ RunCommandAction as ProtobufRunCommandAction,
+ RunEditFileAction as ProtobufRunEditFileAction,
+ RunPlugin as ProtobufRunPlugin,
+ RunPluginLocation as ProtobufRunPluginLocation,
+ RunPluginLocationData as ProtobufRunPluginLocationData,
+ RunPluginOrAlias as ProtobufRunPluginOrAlias,
+ ScrollAtPayload,
+ SearchDirection as ProtobufSearchDirection,
+ SearchOption as ProtobufSearchOption,
+ SplitDirection as ProtobufSplitDirection,
+ SplitSize as ProtobufSplitSize,
+ StackedPlacement as ProtobufStackedPlacement,
+ SwapFloatingLayout as ProtobufSwapFloatingLayout,
+ SwapTiledLayout as ProtobufSwapTiledLayout,
+ SwitchToModePayload,
+ TiledPaneLayout as ProtobufTiledPaneLayout,
+ TiledPlacement as ProtobufTiledPlacement,
+ UnblockCondition as ProtobufUnblockCondition,
+ WriteCharsPayload,
+ WritePayload,
},
input_mode::InputMode as ProtobufInputMode,
resize::{Resize as ProtobufResize, ResizeDirection as ProtobufResizeDirection},
};
-use crate::data::{Direction, InputMode, ResizeStrategy};
+use crate::data::{
+ CommandOrPlugin, Direction, FloatingPaneCoordinates, InputMode, KeyWithModifier,
+ NewPanePlacement, PaneId, PluginTag, ResizeStrategy, UnblockCondition,
+};
use crate::errors::prelude::*;
use crate::input::actions::Action;
use crate::input::actions::{SearchDirection, SearchOption};
use crate::input::command::{OpenFilePayload, RunCommandAction};
+use crate::input::layout::SplitSize;
use crate::input::layout::{
- PluginUserConfiguration, RunPlugin, RunPluginLocation, RunPluginOrAlias,
+ FloatingPaneLayout, LayoutConstraint, PercentOrFixed, PluginAlias, PluginUserConfiguration,
+ Run, RunPlugin, RunPluginLocation, RunPluginOrAlias, SplitDirection, SwapFloatingLayout,
+ SwapTiledLayout, TiledPaneLayout,
};
use crate::input::mouse::{MouseEvent, MouseEventType};
use crate::position::Position;
@@ -38,11 +100,16 @@ impl TryFrom<ProtobufAction> for Action {
None => Ok(Action::Quit),
},
Some(ProtobufActionName::Write) => match protobuf_action.optional_payload {
- Some(OptionalPayload::WritePayload(write_payload)) => Ok(Action::Write {
- key_with_modifier: None,
- bytes: write_payload.bytes_to_write,
- is_kitty_keyboard_protocol: false,
- }),
+ Some(OptionalPayload::WritePayload(write_payload)) => {
+ let key_with_modifier = write_payload
+ .key_with_modifier
+ .and_then(|k| k.try_into().ok());
+ Ok(Action::Write {
+ key_with_modifier,
+ bytes: write_payload.bytes_to_write,
+ is_kitty_keyboard_protocol: write_payload.is_kitty_keyboard_protocol,
+ })
+ },
_ => Err("Wrong payload for Action::Write"),
},
Some(ProtobufActionName::WriteChars) => match protobuf_action.optional_payload {
@@ -246,6 +313,7 @@ impl TryFrom<ProtobufAction> for Action {
.direction
.and_then(|d| ProtobufResizeDirection::from_i32(d))
.and_then(|d| d.try_into().ok());
+ let near_current_pane = payload.near_current_pane;
let should_float = payload.should_float;
let should_be_in_place = false;
Ok(Action::EditFile {
@@ -255,12 +323,14 @@ impl TryFrom<ProtobufAction> for Action {
in_place: should_be_in_place,
start_suppressed: false,
coordinates: None,
+ near_current_pane,
})
},
_ => Err("Wrong payload for Action::NewPane"),
},
Some(ProtobufActionName::NewFloatingPane) => match protobuf_action.optional_payload {
Some(OptionalPayload::NewFloatingPanePayload(payload)) => {
+ let near_current_pane = payload.near_current_pane;
if let Some(payload) = payload.command {
let pane_name = payload.pane_name.clone();
let run_command_action: RunCommandAction = payload.try_into()?;
@@ -268,12 +338,14 @@ impl TryFrom<ProtobufAction> for Action {
command: Some(run_command_action),
pane_name,
coordinates: None,
+ near_current_pane,
})
} else {
Ok(Action::NewFloatingPane {
command: None,
pane_name: None,
coordinates: None,
+ near_current_pane,
})
}
},
@@ -285,6 +357,7 @@ impl TryFrom<ProtobufAction> for Action {
.direction
.and_then(|d| ProtobufResizeDirection::from_i32(d))
.and_then(|d| d.try_into().ok());
+ let near_current_pane = payload.near_current_pane;
if let Some(payload) = payload.command {
let pane_name = payload.pane_name.clone();
let run_command_action: RunCommandAction = payload.try_into()?;
@@ -292,12 +365,14 @@ impl TryFrom<ProtobufAction> for Action {
direction,
command: Some(run_command_action),
pane_name,
+ near_current_pane,
})
} else {
Ok(Action::NewTiledPane {
direction,
command: None,
pane_name: None,
+ near_current_pane,
})
}
},
@@ -331,9 +406,77 @@ impl TryFrom<ProtobufAction> for Action {
},
Some(ProtobufActionName::NewTab) => {
match protobuf_action.optional_payload {
- Some(_) => Err("NewTab should not have a payload"),
+ Some(OptionalPayload::NewTabPayload(payload)) => {
+ // New behavior: extract all fields from 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;
+ let should_change_focus_to_new_tab = payload.should_change_focus_to_new_tab;
+ let cwd = payload.cwd.map(PathBuf::from);
+
+ let initial_panes = if payload.initial_panes.is_empty() {
+ None
+ } else {
+ Some(
+ payload
+ .initial_panes
+ .into_iter()
+ .map(|p| p.try_into())
+ .collect::<Result<Vec<_>, _>>()?,
+ )
+ };
+
+ let first_pane_unblock_condition = payload
+ .first_pane_unblock_condition
+ .and_then(|uc| ProtobufUnblockCondition::from_i32(uc))
+ .and_then(|uc| uc.try_into().ok());
+
+ Ok(Action::NewTab {
+ tiled_layout,
+ floating_layouts,
+ swap_tiled_layouts,
+ swap_floating_layouts,
+ tab_name,
+ should_change_focus_to_new_tab,
+ cwd,
+ initial_panes,
+ first_pane_unblock_condition,
+ })
+ },
None => {
- // we do not serialize the layouts of this action
+ // Backwards compatibility: accept None payload for existing plugins
+ // Return the same defaults as before
Ok(Action::NewTab {
tiled_layout: None,
floating_layouts: vec![],
@@ -342,8 +485,11 @@ impl TryFrom<ProtobufAction> for Action {
tab_name: None,
should_change_focus_to_new_tab: true,
cwd: None,
+ initial_panes: None,
+ first_pane_unblock_condition: None,
})
},
+ _ => Err("Wrong payload for Action::NewTab"),
}
},
Some(ProtobufActionName::NoOp) => match protobuf_action.optional_payload {
@@ -405,6 +551,7 @@ impl TryFrom<ProtobufAction> for Action {
let run_command_action = run_command_action.try_into()?;
Ok(Action::Run {
command: run_command_action,
+ near_current_pane: false,
})
},
_ => Err("Wrong payload for Action::Run"),
@@ -657,9 +804,11 @@ impl TryFrom<ProtobufAction> for Action {
Some(OptionalPayload::FocusTerminalPaneWithIdPayload(payload)) => {
let terminal_pane_id = payload.pane_id;
let should_float_if_hidden = payload.should_float;
+ let should_be_in_place_if_hidden = payload.should_be_in_place;
Ok(Action::FocusTerminalPaneWithId {
pane_id: terminal_pane_id,
should_float_if_hidden,
+ should_be_in_place_if_hidden,
})
},
_ => Err("Wrong payload for Action::FocusTerminalPaneWithId"),
@@ -670,9 +819,11 @@ impl TryFrom<ProtobufAction> for Action {
Some(OptionalPayload::FocusPluginPaneWithIdPayload(payload)) => {
let plugin_pane_id = payload.pane_id;
let should_float_if_hidden = payload.should_float;
+ let should_be_in_place_if_hidden = payload.should_be_in_place;
Ok(Action::FocusPluginPaneWithId {
pane_id: plugin_pane_id,
should_float_if_hidden,
+ should_be_in_place_if_hidden,
})
},
_ => Err("Wrong payload for Action::FocusPluginPaneWithId"),
@@ -768,8 +919,60 @@ impl TryFrom<ProtobufAction> for Action {
None => Ok(Action::NewStackedPane {
command: None,
pane_name: None,
+ near_current_pane: false,
}),
},
+ Some(ProtobufActionName::NewBlockingPane) => match protobuf_action.optional_payload {
+ Some(OptionalPayload::NewBlockingPanePayload(payload)) => {
+ let placement: NewPanePlacement = payload
+ .placement
+ .ok_or("NewBlockingPanePayload must have a placement")?
+ .try_into()?;
+ let pane_name = payload.pane_name;
+ let command = payload.command.and_then(|c| c.try_into().ok());
+ let unblock_condition = payload
+ .unblock_condition
+ .and_then(|uc| ProtobufUnblockCondition::from_i32(uc))
+ .and_then(|uc| uc.try_into().ok());
+ let near_current_pane = payload.near_current_pane;
+ Ok(Action::NewBlockingPane {
+ placement,
+ pane_name,
+ command,
+ unblock_condition,
+ near_current_pane,
+ })
+ },
+ _ => Err("Wrong payload for Action::NewBlockingPane"),
+ },
+ Some(ProtobufActionName::NewInPlacePane) => match protobuf_action.optional_payload {
+ Some(OptionalPayload::NewInPlacePanePayload(payload)) => {
+ let near_current_pane = payload.near_current_pane;
+ let pane_id_to_replace =
+ payload.pane_id_to_replace.and_then(|p| p.try_into().ok());
+ let close_replace_pane = payload.close_replace_pane;
+ if let Some(command) = payload.command {
+ let pane_name = command.pane_name.clone();
+ let run_command_action: RunCommandAction = command.try_into()?;
+ Ok(Action::NewInPlacePane {
+ command: Some(run_command_action),
+ pane_name,
+ near_current_pane,
+ pane_id_to_replace,
+ close_replace_pane,
+ })
+ } else {
+ Ok(Action::NewInPlacePane {
+ command: None,
+ pane_name: payload.pane_name,
+ near_current_pane,
+ pane_id_to_replace,
+ close_replace_pane,
+ })
+ }
+ },
+ _ => Err("Wrong payload for Action::NewInPlacePane"),
+ },
_ => Err("Unknown Action"),
}
}
@@ -784,15 +987,20 @@ impl TryFrom<Action> for ProtobufAction {
optional_payload: None,
}),
Action::Write {
- key_with_modifier: _,
+ key_with_modifier,
bytes,
- is_kitty_keyboard_protocol: _,
- } => Ok(ProtobufAction {
- name: ProtobufActionName::Write as i32,
- optional_payload: Some(OptionalPayload::WritePayload(WritePayload {
- bytes_to_write: bytes,
- })),
- }),
+ is_kitty_keyboard_protocol,
+ } => {
+ let protobuf_key_with_modifier = key_with_modifier.and_then(|k| k.try_into().ok());
+ Ok(ProtobufAction {
+ name: ProtobufActionName::Write as i32,
+ optional_payload: Some(OptionalPayload::WritePayload(WritePayload {
+ key_with_modifier: protobuf_key_with_modifier,
+ bytes_to_write: bytes,
+ is_kitty_keyboard_protocol,
+ })),
+ })
+ },
Action::WriteChars {
chars: chars_to_write,
} => Ok(ProtobufAction {
@@ -982,6 +1190,7 @@ impl TryFrom<Action> for ProtobufAction {
in_place: _should_be_in_place,
start_suppressed: _start_suppressed,
coordinates: _floating_pane_coordinates,
+ near_current_pane,
} => {
let file_to_edit = open_file_payload.path.display().to_string();
let cwd = open_file_payload.cwd.map(|cwd| cwd.display().to_string());
@@ -997,6 +1206,7 @@ impl TryFrom<Action> for ProtobufAction {
should_float,
direction,
cwd,
+ near_current_pane,
})),
})
},
@@ -1004,6 +1214,7 @@ impl TryFrom<Action> for ProtobufAction {
command: run_command_action,
pane_name,
coordinates: _coordinates,
+ near_current_pane,
} => {
let command = run_command_action.and_then(|r| {
let mut protobuf_run_command_action: ProtobufRunCommandAction =
@@ -1014,7 +1225,10 @@ impl TryFrom<Action> for ProtobufAction {
Ok(ProtobufAction {
name: ProtobufActionName::NewFloatingPane as i32,
optional_payload: Some(OptionalPayload::NewFloatingPanePayload(
- NewFloatingPanePayload { command },
+ NewFloatingPanePayload {
+ command,
+ near_current_pane,
+ },
)),
})
},
@@ -1022,6 +1236,7 @@ impl TryFrom<Action> for ProtobufAction {
direction,
command: run_command_action,
pane_name,
+ near_current_pane,
} => {
let direction = direction.and_then(|direction| {
let protobuf_direction: ProtobufResizeDirection = direction.try_into().ok()?;
@@ -1037,7 +1252,11 @@ impl TryFrom<Action> for ProtobufAction {
Ok(ProtobufAction {
name: ProtobufActionName::NewTiledPane as i32,
optional_payload: Some(OptionalPayload::NewTiledPanePayload(
- NewTiledPanePayload { direction, command },
+ NewTiledPanePayload {
+ direction,
+ command,
+ near_current_pane,
+ },
)),
})
},
@@ -1062,18 +1281,82 @@ impl TryFrom<Action> for ProtobufAction {
optional_payload: None,
}),
Action::NewTab {
- tiled_layout: _,
- floating_layouts: _,
- swap_tiled_layouts: _,
- swap_floating_layouts: _,
- tab_name: _,
- should_change_focus_to_new_tab: _,
- cwd: _,
+ tiled_layout,
+ floating_layouts,
+ swap_tiled_layouts,
+ swap_floating_layouts,
+ tab_name,
+ should_change_focus_to_new_tab,
+ cwd,
+ initial_panes,
+ first_pane_unblock_condition,
} => {
- // we do not serialize the various newtab payloads
+ // Always send payload (even if all fields are default)
+ let protobuf_tiled_layout = tiled_layout
+ .as_ref()
+ .map(|l| l.clone().try_into())
+ .transpose()?;
+
+ let protobuf_floating_layouts = floating_layouts
+ .iter()
+ .map(|l| l.clone().try_into())
+ .collect::<Result<Vec<_>, _>>()?;
+
+ let protobuf_swap_tiled_layouts = swap_tiled_layouts
+ .as_ref()
+ .map(|layouts| {
+ layouts
+ .iter()
+ .map(|l| l.clone().try_into())
+ .collect::<Result<Vec<_>, _>>()
+ })
+ .transpose()?
+ .unwrap_or_default();
+
+ let protobuf_swap_floating_layouts = swap_floating_layouts
+ .as_ref()
+ .map(|layouts| {
+ layouts
+ .iter()
+ .map(|l| l.clone().try_into())
+ .collect::<Result<Vec<_>, _>>()
+ })
+ .transpose()?
+ .unwrap_or_default();
+
+ let cwd_string = cwd.as_ref().map(|p| p.display().to_string());
+
+ let protobuf_initial_panes = initial_panes
+ .as_ref()
+ .map(|panes| {
+ panes
+ .iter()
+ .map(|p| p.clone().try_into())
+ .collect::<Result<Vec<_>, _>>()
+ })
+ .transpose()?
+ .unwrap_or_default();
+
+ let protobuf_first_pane_unblock_condition = first_pane_unblock_condition
+ .map(|uc| {
+ let protobuf_uc: ProtobufUnblockCondition = uc.try_into().ok()?;
+ Some(protobuf_uc as i32)
+ })
+ .flatten();
+
Ok(ProtobufAction {
name: ProtobufActionName::NewTab as i32,
- optional_payload: None,
+ optional_payload: Some(OptionalPayload::NewTabPayload(NewTabPayload {
+ 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(),
+ should_change_focus_to_new_tab,
+ cwd: cwd_string,
+ initial_panes: protobuf_initial_panes,
+ first_pane_unblock_condition: protobuf_first_pane_unblock_condition,
+ })),
})
},
Action::GoToNextTab => Ok(ProtobufAction {
@@ -1123,6 +1406,7 @@ impl TryFrom<Action> for ProtobufAction {
},
Action::Run {
command: run_command_action,
+ near_current_pane: _,
} => {
let run_command_action: ProtobufRunCommandAction = run_command_action.try_into()?;
Ok(ProtobufAction {
@@ -1277,24 +1561,28 @@ impl TryFrom<Action> for ProtobufAction {
Action::FocusTerminalPaneWithId {
pane_id: terminal_pane_id,
should_float_if_hidden,
+ should_be_in_place_if_hidden,
} => Ok(ProtobufAction {
name: ProtobufActionName::FocusTerminalPaneWithId as i32,
optional_payload: Some(OptionalPayload::FocusTerminalPaneWithIdPayload(
PaneIdAndShouldFloat {
pane_id: terminal_pane_id,
should_float: should_float_if_hidden,
+ should_be_in_place: should_be_in_place_if_hidden,
},
)),
}),
Action::FocusPluginPaneWithId {
pane_id: plugin_pane_id,
should_float_if_hidden,
+ should_be_in_place_if_hidden,
} => Ok(ProtobufAction {
name: ProtobufActionName::FocusPluginPaneWithId as i32,
optional_payload: Some(OptionalPayload::FocusPluginPaneWithIdPayload(
PaneIdAndShouldFloat {
pane_id: plugin_pane_id,
should_float: should_float_if_hidden,
+ should_be_in_place: should_be_in_place_if_hidden,
},
)),
}),
@@ -1363,16 +1651,72 @@ impl TryFrom<Action> for ProtobufAction {
Action::NewStackedPane {
command: _,
pane_name: _,
+ near_current_pane: _,
} => Ok(ProtobufAction {
name: ProtobufActionName::NewStackedPane as i32,
optional_payload: None,
}),
+ Action::NewBlockingPane {
+ placement,
+ pane_name,
+ command,
+ unblock_condition,
+ near_current_pane,
+ } => {
+ let placement: ProtobufNewPanePlacement = placement.try_into()?;
+ let command = command.and_then(|c| {
+ let protobuf_command: ProtobufRunCommandAction = c.try_into().ok()?;
+ Some(protobuf_command)
+ });
+ let unblock_condition = unblock_condition
+ .map(|uc| {
+ let protobuf_uc: ProtobufUnblockCondition = uc.try_into().ok()?;
+ Some(protobuf_uc as i32)
+ })
+ .flatten();
+ Ok(ProtobufAction {
+ name: ProtobufActionName::NewBlockingPane as i32,
+ optional_payload: Some(OptionalPayload::NewBlockingPanePayload(
+ NewBlockingPanePayload {
+ placement: Some(placement),
+ pane_name,
+ command,
+ unblock_condition,
+ near_current_pane,
+ },
+ )),
+ })
+ },
+ Action::NewInPlacePane {
+ command: run_command_action,
+ pane_name,
+ near_current_pane,
+ pane_id_to_replace,
+ close_replace_pane,
+ } => {
+ let command = run_command_action.and_then(|r| {
+ let mut protobuf_run_command_action: ProtobufRunCommandAction =
+ r.try_into().ok()?;
+ let pane_name = pane_name.and_then(|n| n.try_into().ok());
+ protobuf_run_command_action.pane_name = pane_name;
+ Some(protobuf_run_command_action)
+ });
+ let pane_id_to_replace = pane_id_to_replace.and_then(|p| p.try_into().ok());
+ Ok(ProtobufAction {
+ name: ProtobufActionName::NewInPlacePane as i32,
+ optional_payload: Some(OptionalPayload::NewInPlacePanePayload(
+ NewInPlacePanePayload {
+ command,
+ pane_name: None, // pane_name is already embedded in command
+ near_current_pane,
+ pane_id_to_replace,
+ close_replace_pane,
+ },
+ )),
+ })
+ },
Action::NoOp
| Action::Confirm
- | Action::NewInPlacePane {
- command: _,
- pane_name: _,
- }
| Action::NewInPlacePluginPane {
plugin: _,
pane_name: _,
@@ -1389,7 +1733,6 @@ impl TryFrom<Action> for ProtobufAction {
coordinates: _,
}
| Action::SkipConfirm { action: _ }
- | Action::NewBlockingPane { .. }
| Action::SwitchSession { .. } => Err("Unsupported action"),
}
}
@@ -1616,3 +1959,914 @@ impl TryFrom<&ProtobufPluginConfiguration> for BTreeMap<String, String> {
Ok(converted)
}
}
+
+impl TryFrom<ProtobufKeyWithModifier> for KeyWithModifier {
+ type Error = &'static str;
+ fn try_from(protobuf_key: ProtobufKeyWithModifier) -> Result<Self, &'static str> {
+ let bare_key = match ProtobufBareKey::from_i32(protobuf_key.bare_key) {
+ Some(ProtobufBareKey::PageDown) => crate::data::BareKey::PageDown,
+ Some(ProtobufBareKey::PageUp) => crate::data::BareKey::PageUp,
+ Some(ProtobufBareKey::Left) => crate::data::BareKey::Left,
+ Some(ProtobufBareKey::Down) => crate::data::BareKey::Down,
+ Some(ProtobufBareKey::Up) => crate::data::BareKey::Up,
+ Some(ProtobufBareKey::Right) => crate::data::BareKey::Right,
+ Some(ProtobufBareKey::Home) => crate::data::BareKey::Home,
+ Some(ProtobufBareKey::End) => crate::data::BareKey::End,
+ Some(ProtobufBareKey::Backspace) => crate::data::BareKey::Backspace,
+ Some(ProtobufBareKey::Delete) => crate::data::BareKey::Delete,
+ Some(ProtobufBareKey::Insert) => crate::data::BareKey::Insert,
+ Some(ProtobufBareKey::F1) => crate::data::BareKey::F(1),
+ Some(ProtobufBareKey::F2) => crate::data::BareKey::F(2),
+ Some(ProtobufBareKey::F3) => crate::data::BareKey::F(3),
+ Some(ProtobufBareKey::F4) => crate::data::BareKey::F(4),
+ Some(ProtobufBareKey::F5) => crate::data::BareKey::F(5),
+ Some(ProtobufBareKey::F6) => crate::data::BareKey::F(6),
+ Some(ProtobufBareKey::F7) => crate::data::BareKey::F(7),
+ Some(ProtobufBareKey::F8) => crate::data::BareKey::F(8),
+ Some(ProtobufBareKey::F9) => crate::data::BareKey::F(9),
+ Some(ProtobufBareKey::F10) => crate::data::BareKey::F(10),
+ Some(ProtobufBareKey::F11) => crate::data::BareKey::F(11),
+ Some(ProtobufBareKey::F12) => crate::data::BareKey::F(12),
+ Some(ProtobufBareKey::Char) => {
+ if let Some(character) = protobuf_key.character {
+ let ch = character
+ .chars()
+ .next()
+ .ok_or("BareKey::Char requires a character")?;
+ crate::data::BareKey::Char(ch)
+ } else {
+ return Err("BareKey::Char requires a character");
+ }
+ },
+ Some(ProtobufBareKey::Tab) => crate::data::BareKey::Tab,
+ Some(ProtobufBareKey::Esc) => crate::data::BareKey::Esc,
+ Some(ProtobufBareKey::Enter) => crate::data::BareKey::Enter,
+ Some(ProtobufBareKey::CapsLock) => crate::data::BareKey::CapsLock,
+ Some(ProtobufBareKey::ScrollLock) => crate::data::BareKey::ScrollLock,
+ Some(ProtobufBareKey::NumLock) => crate::data::BareKey::NumLock,
+ Some(ProtobufBareKey::PrintScreen) => crate::data::BareKey::PrintScreen,
+ Some(ProtobufBareKey::Pause) => crate::data::BareKey::Pause,
+ Some(ProtobufBareKey::Menu) => crate::data::BareKey::Menu,
+ _ => return Err("Unknown BareKey"),
+ };
+
+ let mut key_modifiers = std::collections::BTreeSet::new();
+ for modifier in protobuf_key.key_modifiers {
+ let key_modifier = match ProtobufKeyModifier::from_i32(modifier) {
+ Some(ProtobufKeyModifier::Ctrl) => crate::data::KeyModifier::Ctrl,
+ Some(ProtobufKeyModifier::Alt) => crate::data::KeyModifier::Alt,
+ Some(ProtobufKeyModifier::Shift) => crate::data::KeyModifier::Shift,
+ Some(ProtobufKeyModifier::Super) => crate::data::KeyModifier::Super,
+ _ => continue,
+ };
+ key_modifiers.insert(key_modifier);
+ }
+
+ Ok(KeyWithModifier {
+ bare_key,
+ key_modifiers,
+ })
+ }
+}
+
+impl TryFrom<KeyWithModifier> for ProtobufKeyWithModifier {
+ type Error = &'static str;
+ fn try_from(key: KeyWithModifier) -> Result<Self, &'static str> {
+ let (bare_key, character) = match key.bare_key {
+ crate::data::BareKey::PageDown => (ProtobufBareKey::PageDown as i32, None),
+ crate::data::BareKey::PageUp => (ProtobufBareKey::PageUp as i32, None),
+ crate::data::BareKey::Left => (ProtobufBareKey::Left as i32, None),
+ crate::data::BareKey::Down => (ProtobufBareKey::Down as i32, None),
+ crate::data::BareKey::Up => (ProtobufBareKey::Up as i32, None),
+ crate::data::BareKey::Right => (ProtobufBareKey::Right as i32, None),
+ crate::data::BareKey::Home => (ProtobufBareKey::Home as i32, None),
+ crate::data::BareKey::End => (ProtobufBareKey::End as i32, None),
+ crate::data::BareKey::Backspace => (ProtobufBareKey::Backspace as i32, None),
+ crate::data::BareKey::Delete => (ProtobufBareKey::Delete as i32, None),
+ crate::data::BareKey::Insert => (ProtobufBareKey::Insert as i32, None),
+ crate::data::BareKey::F(1) => (ProtobufBareKey::F1 as i32, None),
+ crate::data::BareKey::F(2) => (ProtobufBareKey::F2 as i32, None),
+ crate::data::BareKey::F(3) => (ProtobufBareKey::F3 as i32, None),
+ crate::data::BareKey::F(4) => (ProtobufBareKey::F4 as i32, None),
+ crate::data::BareKey::F(5) => (ProtobufBareKey::F5 as i32, None),
+ crate::data::BareKey::F(6) => (ProtobufBareKey::F6 as i32, None),
+ crate::data::BareKey::F(7) => (ProtobufBareKey::F7 as i32, None),
+ crate::data::BareKey::F(8) => (ProtobufBareKey::F8 as i32, None),
+ crate::data::BareKey::F(9) => (ProtobufBareKey::F9 as i32, None),
+ crate::data::BareKey::F(10) => (ProtobufBareKey::F10 as i32, None),
+ crate::data::BareKey::F(11) => (ProtobufBareKey::F11 as i32, None),
+ crate::data::BareKey::F(12) => (ProtobufBareKey::F12 as i32, None),
+ crate::data::BareKey::Char(c) => (ProtobufBareKey::Char as i32, Some(c.to_string())),
+ crate::data::BareKey::Tab => (ProtobufBareKey::Tab as i32, None),
+ crate::data::BareKey::Esc => (ProtobufBareKey::Esc as i32, None),
+ crate::data::BareKey::Enter => (ProtobufBareKey::Enter as i32, None),
+ crate::data::BareKey::CapsLock => (ProtobufBareKey::CapsLock as i32, None),
+ crate::data::BareKey::ScrollLock => (ProtobufBareKey::ScrollLock as i32, None),
+ crate::data::BareKey::NumLock => (ProtobufBareKey::NumLock as i32, None),
+ crate::data::BareKey::PrintScreen => (ProtobufBareKey::PrintScreen as i32, None),
+ crate::data::BareKey::Pause => (ProtobufBareKey::Pause as i32, None),
+ crate::data::BareKey::Menu => (ProtobufBareKey::Menu as i32, None),
+ _ => return Err("Unsupported BareKey"),
+ };
+
+ let key_modifiers: Vec<i32> = key
+ .key_modifiers
+ .iter()
+ .map(|m| match m {
+ crate::data::KeyModifier::Ctrl => ProtobufKeyModifier::Ctrl as i32,
+ crate::data::KeyModifier::Alt => ProtobufKeyModifier::Alt as i32,
+ crate::data::KeyModifier::Shift => ProtobufKeyModifier::Shift as i32,
+ crate::data::KeyModifier::Super => ProtobufKeyModifier::Super as i32,
+ })
+ .collect();
+
+ Ok(ProtobufKeyWithModifier {
+ bare_key,
+ key_modifiers,
+ character,
+ })
+ }
+}
+
+// UnblockCondition conversions
+impl TryFrom<ProtobufUnblockCondition> for UnblockCondition {
+ type Error = &'static str;
+ fn try_from(protobuf_uc: ProtobufUnblockCondition) -> Result<Self, &'static str> {
+ match protobuf_uc {
+ ProtobufUnblockCondition::UnblockOnExitSuccess => Ok(UnblockCondition::OnExitSuccess),
+ ProtobufUnblockCondition::UnblockOnExitFailure => Ok(UnblockCondition::OnExitFailure),
+ ProtobufUnblockCondition::UnblockOnAnyExit => Ok(UnblockCondition::OnAnyExit),
+ }
+ }
+}
+
+impl TryFrom<UnblockCondition> for ProtobufUnblockCondition {
+ type Error = &'static str;
+ fn try_from(uc: UnblockCondition) -> Result<Self, &'static str> {
+ match uc {
+ UnblockCondition::OnExitSuccess => Ok(ProtobufUnblockCondition::UnblockOnExitSuccess),
+ UnblockCondition::OnExitFailure => Ok(ProtobufUnblockCondition::UnblockOnExitFailure),
+ UnblockCondition::OnAnyExit => Ok(ProtobufUnblockCondition::UnblockOnAnyExit),
+ }
+ }
+}
+
+impl TryFrom<i32> for UnblockCondition {
+ type Error = &'static str;
+ fn try_from(value: i32) -> Result<Self, &'static str> {
+ match ProtobufUnblockCondition::from_i32(value) {
+ Some(uc) => uc.try_into(),
+ None => Err("Invalid UnblockCondition value"),
+ }
+ }
+}
+
+// PaneId conversions
+impl TryFrom<ProtobufPaneId> for PaneId {
+ type Error = &'static str;
+ fn try_from(protobuf_pane_id: ProtobufPaneId) -> Result<Self, &'static str> {
+ use super::generated_api::api::action::pane_id::PaneIdVariant;
+
+ match protobuf_pane_id.pane_id_variant {
+ Some(PaneIdVariant::Terminal(id)) => Ok(PaneId::Terminal(id)),
+ Some(PaneIdVariant::Plugin(id)) => Ok(PaneId::Plugin(id)),
+ None => Err("PaneId must have either terminal or plugin id"),
+ }
+ }
+}
+
+impl TryFrom<PaneId> for ProtobufPaneId {
+ type Error = &'static str;
+ fn try_from(pane_id: PaneId) -> Result<Self, &'static str> {
+ use super::generated_api::api::action::pane_id::PaneIdVariant;
+
+ let pane_id_variant = match pane_id {
+ PaneId::Terminal(id) => Some(PaneIdVariant::Terminal(id)),
+ PaneId::Plugin(id) => Some(PaneIdVariant::Plugin(id)),
+ };
+ Ok(ProtobufPaneId { pane_id_variant })
+ }
+}
+
+// SplitSize conversions
+impl TryFrom<ProtobufSplitSize> for SplitSize {
+ type Error = &'static str;
+ fn try_from(protobuf_split_size: ProtobufSplitSize) -> Result<Self, &'static str> {
+ use super::generated_api::api::action::split_size::SplitSizeVariant;
+
+ match protobuf_split_size.split_size_variant {
+ Some(SplitSizeVariant::Percent(p)) => Ok(SplitSize::Percent(p as usize)),
+ Some(SplitSizeVariant::Fixed(f)) => Ok(SplitSize::Fixed(f as usize)),
+ None => Err("SplitSize must have either percent or fixed value"),
+ }
+ }
+}
+
+impl TryFrom<SplitSize> for ProtobufSplitSize {
+ type Error = &'static str;
+ fn try_from(split_size: SplitSize) -> Result<Self, &'static str> {
+ use super::generated_api::api::action::split_size::SplitSizeVariant;
+
+ let split_size_variant = match split_size {
+ SplitSize::Percent(p) => Some(SplitSizeVariant::Percent(p as u32)),
+ SplitSize::Fixed(f) => Some(SplitSizeVariant::Fixed(f as u32)),
+ };
+ Ok(ProtobufSplitSize { split_size_variant })
+ }
+}
+
+// FloatingPaneCoordinates conversions
+impl TryFrom<ProtobufFloatingPaneCoordinates> for FloatingPaneCoordinates {
+ type Error = &'static str;
+ fn try_from(protobuf_coords: ProtobufFloatingPaneCoordinates) -> Result<Self, &'static str> {
+ Ok(FloatingPaneCoordinates {
+ x: protobuf_coords.x.and_then(|x| x.try_into().ok()),
+ y: protobuf_coords.y.and_then(|y| y.try_into().ok()),
+ width: protobuf_coords.width.and_then(|w| w.try_into().ok()),
+ height: protobuf_coords.height.and_then(|h| h.try_into().ok()),
+ pinned: protobuf_coords.pinned,
+ })
+ }
+}
+
+impl TryFrom<FloatingPaneCoordinates> for ProtobufFloatingPaneCoordinates {
+ type Error = &'static str;
+ fn try_from(coords: FloatingPaneCoordinates) -> Result<Self, &'static str> {
+ Ok(ProtobufFloatingPaneCoordinates {
+ x: coords.x.and_then(|x| x.try_into().ok()),
+ y: coords.y.and_then(|y| y.try_into().ok()),
+ width: coords.width.and_then(|w| w.try_into().ok()),
+ height: coords.height.and_then(|h| h.try_into().ok()),
+ pinned: coords.pinned,
+ })
+ }
+}
+
+// NewPanePlacement conversions
+impl TryFrom<ProtobufNewPanePlacement> for NewPanePlacement {
+ type Error = &'static str;
+ fn try_from(protobuf_placement: ProtobufNewPanePlacement) -> Result<Self, &'static str> {
+ use super::generated_api::api::action::new_pane_placement::PlacementVariant;
+
+ match protobuf_placement.placement_variant {
+ Some(PlacementVariant::NoPreference(_)) => Ok(NewPanePlacement::NoPreference),
+ Some(PlacementVariant::Tiled(tiled)) => {
+ let direction = tiled
+ .direction
+ .and_then(|d| ProtobufResizeDirection::from_i32(d))
+ .and_then(|d| d.try_into().ok());
+ Ok(NewPanePlacement::Tiled(direction))
+ },
+ Some(PlacementVariant::Floating(floating)) => {
+ let coords = floating.coordinates.and_then(|c| c.try_into().ok());
+ Ok(NewPanePlacement::Floating(coords))
+ },
+ Some(PlacementVariant::InPlace(config)) => {
+ let pane_id_to_replace =
+ config.pane_id_to_replace.and_then(|id| id.try_into().ok());
+ Ok(NewPanePlacement::InPlace {
+ pane_id_to_replace,
+ close_replaced_pane: config.close_replaced_pane,
+ })
+ },
+ Some(PlacementVariant::Stacked(stacked)) => {
+ let pane_id = stacked.pane_id.and_then(|id| id.try_into().ok());
+ Ok(NewPanePlacement::Stacked(pane_id))
+ },
+ None => Err("NewPanePlacement must have a placement variant"),
+ }
+ }
+}
+
+impl TryFrom<NewPanePlacement> for ProtobufNewPanePlacement {
+ type Error = &'static str;
+ fn try_from(placement: NewPanePlacement) -> Result<Self, &'static str> {
+ use super::generated_api::api::action::new_pane_placement::PlacementVariant;
+
+ let placement_variant = match placement {
+ NewPanePlacement::NoPreference => Some(PlacementVariant::NoPreference(true)),
+ NewPanePlacement::Tiled(direction) => {
+ let direction = direction.and_then(|d| {
+ let protobuf_direction: ProtobufResizeDirection = d.try_into().ok()?;
+ Some(protobuf_direction as i32)
+ });
+ Some(PlacementVariant::Tiled(ProtobufTiledPlacement {
+ direction,
+ }))
+ },
+ NewPanePlacement::Floating(coords) => {
+ let coordinates = coords.and_then(|c| c.try_into().ok());
+ Some(PlacementVariant::Floating(ProtobufFloatingPlacement {
+ coordinates,
+ }))
+ },
+ NewPanePlacement::InPlace {
+ pane_id_to_replace,
+ close_replaced_pane,
+ } => {
+ let pane_id_to_replace = pane_id_to_replace.and_then(|id| id.try_into().ok());
+ Some(PlacementVariant::InPlace(ProtobufInPlaceConfig {
+ pane_id_to_replace,
+ close_replaced_pane,
+ }))
+ },
+ NewPanePlacement::Stacked(pane_id) => {
+ let pane_id = pane_id.and_then(|id| id.try_into().ok());
+ Some(PlacementVariant::Stacked(ProtobufStackedPlacement {
+ pane_id,
+ }))
+ },
+ };
+
+ Ok(ProtobufNewPanePlacement { placement_variant })
+ }
+}
+
+// Layout type conversions
+
+impl TryFrom<ProtobufPercentOrFixed> for PercentOrFixed {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufPercentOrFixed) -> Result<Self, Self::Error> {
+ use super::generated_api::api::action::percent_or_fixed::SizeType;
+ match protobuf.size_type {
+ Some(SizeType::Percent(p)) => Ok(PercentOrFixed::Percent(p as usize)),
+ Some(SizeType::Fixed(f)) => Ok(PercentOrFixed::Fixed(f as usize)),
+ None => Err("PercentOrFixed must have a size_type"),
+ }
+ }
+}
+
+impl TryFrom<PercentOrFixed> for ProtobufPercentOrFixed {
+ type Error = &'static str;
+ fn try_from(internal: PercentOrFixed) -> Result<Self, Self::Error> {
+ use super::generated_api::api::action::percent_or_fixed::SizeType;
+ let size_type = match internal {
+ PercentOrFixed::Percent(p) => Some(SizeType::Percent(p as u32)),
+ PercentOrFixed::Fixed(f) => Some(SizeType::Fixed(f as u32)),
+ };
+ Ok(ProtobufPercentOrFixed { size_type })
+ }
+}
+
+impl TryFrom<ProtobufSplitDirection> for SplitDirection {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufSplitDirection) -> Result<Self, Self::Error> {
+ match protobuf {
+ ProtobufSplitDirection::Horizontal => Ok(SplitDirection::Horizontal),
+ ProtobufSplitDirection::Vertical => Ok(SplitDirection::Vertical),
+ ProtobufSplitDirection::Unspecified => Err("SplitDirection cannot be unspecified"),
+ }
+ }
+}
+
+impl TryFrom<SplitDirection> for ProtobufSplitDirection {
+ type Error = &'static str;
+ fn try_from(internal: SplitDirection) -> Result<Self, Self::Error> {
+ Ok(match internal {
+ SplitDirection::Horizontal => ProtobufSplitDirection::Horizontal,
+ SplitDirection::Vertical => ProtobufSplitDirection::Vertical,
+ })
+ }
+}
+
+impl TryFrom<ProtobufLayoutConstraint> for LayoutConstraint {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufLayoutConstraint) -> Result<Self, Self::Error> {
+ match protobuf {
+ ProtobufLayoutConstraint::MaxPanes => Ok(LayoutConstraint::MaxPanes(0)),
+ ProtobufLayoutConstraint::MinPanes => Ok(LayoutConstraint::MinPanes(0)),
+ ProtobufLayoutConstraint::ExactPanes => Ok(LayoutConstraint::ExactPanes(0)),
+ ProtobufLayoutConstraint::NoConstraint => Ok(LayoutConstraint::NoConstraint),
+ ProtobufLayoutConstraint::Unspecified => Err("LayoutConstraint cannot be unspecified"),
+ }
+ }
+}
+
+impl TryFrom<LayoutConstraint> for ProtobufLayoutConstraint {
+ type Error = &'static str;
+ fn try_from(internal: LayoutConstraint) -> Result<Self, Self::Error> {
+ Ok(match internal {
+ LayoutConstraint::MaxPanes(_) => ProtobufLayoutConstraint::MaxPanes,
+ LayoutConstraint::MinPanes(_) => ProtobufLayoutConstraint::MinPanes,
+ LayoutConstraint::ExactPanes(_) => ProtobufLayoutConstraint::ExactPanes,
+ LayoutConstraint::NoConstraint => ProtobufLayoutConstraint::NoConstraint,
+ })
+ }
+}
+
+impl TryFrom<ProtobufLayoutConstraintWithValue> for LayoutConstraint {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufLayoutConstraintWithValue) -> Result<Self, Self::Error> {
+ let constraint_type = ProtobufLayoutConstraint::from_i32(protobuf.constraint_type)
+ .ok_or("Invalid constraint type")?;
+ match constraint_type {
+ ProtobufLayoutConstraint::MaxPanes => Ok(LayoutConstraint::MaxPanes(
+ protobuf.value.unwrap_or(0) as usize,
+ )),
+ ProtobufLayoutConstraint::MinPanes => Ok(LayoutConstraint::MinPanes(
+ protobuf.value.unwrap_or(0) as usize,
+ )),
+ ProtobufLayoutConstraint::ExactPanes => Ok(LayoutConstraint::ExactPanes(
+ protobuf.value.unwrap_or(0) as usize,
+ )),
+ ProtobufLayoutConstraint::NoConstraint => Ok(LayoutConstraint::NoConstraint),
+ ProtobufLayoutConstraint::Unspecified => Err("LayoutConstraint cannot be unspecified"),
+ }
+ }
+}
+
+impl TryFrom<LayoutConstraint> for ProtobufLayoutConstraintWithValue {
+ type Error = &'static str;
+ fn try_from(internal: LayoutConstraint) -> Result<Self, Self::Error> {
+ let (constraint_type, value) = match internal {
+ LayoutConstraint::MaxPanes(v) => {
+ (ProtobufLayoutConstraint::MaxPanes as i32, Some(v as u32))
+ },
+ LayoutConstraint::MinPanes(v) => {
+ (ProtobufLayoutConstraint::MinPanes as i32, Some(v as u32))
+ },
+ LayoutConstraint::ExactPanes(v) => {
+ (ProtobufLayoutConstraint::ExactPanes as i32, Some(v as u32))
+ },
+ LayoutConstraint::NoConstraint => (ProtobufLayoutConstraint::NoConstraint as i32, None),
+ };
+ Ok(ProtobufLayoutConstraintWithValue {
+ constraint_type,
+ value,
+ })
+ }
+}
+
+impl TryFrom<ProtobufPluginTag> for PluginTag {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufPluginTag) -> Result<Self, Self::Error> {
+ Ok(PluginTag::new(protobuf.tag))
+ }
+}
+
+impl TryFrom<PluginTag> for ProtobufPluginTag {
+ type Error = &'static str;
+ fn try_from(internal: PluginTag) -> Result<Self, Self::Error> {
+ Ok(ProtobufPluginTag {
+ tag: internal.into(),
+ })
+ }
+}
+
+impl TryFrom<ProtobufPluginUserConfiguration> for PluginUserConfiguration {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufPluginUserConfiguration) -> Result<Self, Self::Error> {
+ let btree_map: BTreeMap<String, String> = protobuf.configuration.into_iter().collect();
+ Ok(PluginUserConfiguration::new(btree_map))
+ }
+}
+
+impl TryFrom<PluginUserConfiguration> for ProtobufPluginUserConfiguration {
+ type Error = &'static str;
+ fn try_from(internal: PluginUserConfiguration) -> Result<Self, Self::Error> {
+ let configuration = internal.inner().clone().into_iter().collect();
+ Ok(ProtobufPluginUserConfiguration { configuration })
+ }
+}
+
+impl TryFrom<ProtobufRunPluginLocationData> for RunPluginLocation {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufRunPluginLocationData) -> Result<Self, Self::Error> {
+ use super::generated_api::api::action::run_plugin_location_data::LocationData;
+ match protobuf.location_data {
+ Some(LocationData::FilePath(path)) => Ok(RunPluginLocation::File(PathBuf::from(path))),
+ Some(LocationData::ZellijTag(tag)) => Ok(RunPluginLocation::Zellij(tag.try_into()?)),
+ Some(LocationData::RemoteUrl(url)) => Ok(RunPluginLocation::Remote(url)),
+ None => Err("RunPluginLocationData must have location_data"),
+ }
+ }
+}
+
+impl TryFrom<RunPluginLocation> for ProtobufRunPluginLocationData {
+ type Error = &'static str;
+ fn try_from(internal: RunPluginLocation) -> Result<Self, Self::Error> {
+ use super::generated_api::api::action::{
+ run_plugin_location_data::LocationData,
+ RunPluginLocation as ProtobufRunPluginLocationType,
+ };
+ let (location_type, location_data) = match internal {
+ RunPluginLocation::File(path) => (
+ ProtobufRunPluginLocationType::File as i32,
+ Some(LocationData::FilePath(path.display().to_string())),
+ ),
+ RunPluginLocation::Zellij(tag) => (
+ ProtobufRunPluginLocationType::Zellij as i32,
+ Some(LocationData::ZellijTag(tag.try_into()?)),
+ ),
+ RunPluginLocation::Remote(url) => (
+ ProtobufRunPluginLocationType::Remote as i32,
+ Some(LocationData::RemoteUrl(url)),
+ ),
+ };
+ Ok(ProtobufRunPluginLocationData {
+ location_type,
+ location_data,
+ })
+ }
+}
+
+impl TryFrom<ProtobufRunPlugin> for RunPlugin {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufRunPlugin) -> Result<Self, Self::Error> {
+ let location = protobuf
+ .location
+ .ok_or("RunPlugin must have location")?
+ .try_into()?;
+ let configuration = protobuf
+ .configuration
+ .ok_or("RunPlugin must have configuration")?
+ .try_into()?;
+ let initial_cwd = protobuf.initial_cwd.map(PathBuf::from);
+ Ok(RunPlugin {
+ _allow_exec_host_cmd: protobuf.allow_exec_host_cmd,
+ location,
+ configuration,
+ initial_cwd,
+ })
+ }
+}
+
+impl TryFrom<RunPlugin> for ProtobufRunPlugin {
+ type Error = &'static str;
+ fn try_from(internal: RunPlugin) -> Result<Self, Self::Error> {
+ Ok(ProtobufRunPlugin {
+ allow_exec_host_cmd: internal._allow_exec_host_cmd,
+ location: Some(internal.location.try_into()?),
+ configuration: Some(internal.configuration.try_into()?),
+ initial_cwd: internal.initial_cwd.map(|p| p.display().to_string()),
+ })
+ }
+}
+
+impl TryFrom<ProtobufPluginAlias> for PluginAlias {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufPluginAlias) -> Result<Self, Self::Error> {
+ let configuration = protobuf.configuration.map(|c| c.try_into()).transpose()?;
+ let initial_cwd = protobuf.initial_cwd.map(PathBuf::from);
+ let run_plugin = protobuf.run_plugin.map(|r| r.try_into()).transpose()?;
+ Ok(PluginAlias {
+ name: protobuf.name,
+ configuration,
+ initial_cwd,
+ run_plugin,
+ })
+ }
+}
+
+impl TryFrom<PluginAlias> for ProtobufPluginAlias {
+ type Error = &'static str;
+ fn try_from(internal: PluginAlias) -> Result<Self, Self::Error> {
+ Ok(ProtobufPluginAlias {
+ name: internal.name,
+ configuration: internal.configuration.map(|c| c.try_into()).transpose()?,
+ initial_cwd: internal.initial_cwd.map(|p| p.display().to_string()),
+ run_plugin: internal.run_plugin.map(|r| r.try_into()).transpose()?,
+ })
+ }
+}
+
+impl TryFrom<ProtobufRunPluginOrAlias> for RunPluginOrAlias {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufRunPluginOrAlias) -> Result<Self, Self::Error> {
+ use super::generated_api::api::action::run_plugin_or_alias::PluginType;
+ match protobuf.plugin_type {
+ Some(PluginType::Plugin(plugin)) => Ok(RunPluginOrAlias::RunPlugin(plugin.try_into()?)),
+ Some(PluginType::Alias(alias)) => Ok(RunPluginOrAlias::Alias(alias.try_into()?)),
+ None => Err("RunPluginOrAlias must have plugin_type"),
+ }
+ }
+}
+
+impl TryFrom<RunPluginOrAlias> for ProtobufRunPluginOrAlias {
+ type Error = &'static str;
+ fn try_from(internal: RunPluginOrAlias) -> Result<Self, Self::Error> {
+ use super::generated_api::api::action::run_plugin_or_alias::PluginType;
+ let plugin_type = match internal {
+ RunPluginOrAlias::RunPlugin(plugin) => Some(PluginType::Plugin(plugin.try_into()?)),
+ RunPluginOrAlias::Alias(alias) => Some(PluginType::Alias(alias.try_into()?)),
+ };
+ Ok(ProtobufRunPluginOrAlias { plugin_type })
+ }
+}
+
+impl TryFrom<ProtobufRunEditFileAction> for Run {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufRunEditFileAction) -> Result<Self, Self::Error> {
+ let file_path = PathBuf::from(protobuf.file_path);
+ let line_number = protobuf.line_number.map(|n| n as usize);
+ let cwd = protobuf.cwd.map(PathBuf::from);
+ Ok(Run::EditFile(file_path, line_number, cwd))
+ }
+}
+
+impl TryFrom<ProtobufPaneRun> for Run {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufPaneRun) -> Result<Self, Self::Error> {
+ use super::generated_api::api::action::pane_run::RunType;
+ use crate::input::command::RunCommand;
+ match protobuf.run_type {
+ Some(RunType::Command(cmd)) => {
+ let run_command_action: RunCommandAction = cmd.try_into()?;
+ let run_command: RunCommand = run_command_action.into();
+ Ok(Run::Command(run_command))
+ },
+ Some(RunType::Plugin(plugin)) => Ok(Run::Plugin(plugin.try_into()?)),
+ Some(RunType::EditFile(edit)) => edit.try_into(),
+ Some(RunType::Cwd(cwd)) => Ok(Run::Cwd(PathBuf::from(cwd))),
+ None => Err("PaneRun must have run_type"),
+ }
+ }
+}
+
+impl TryFrom<Run> for ProtobufPaneRun {
+ type Error = &'static str;
+ fn try_from(internal: Run) -> Result<Self, Self::Error> {
+ use super::generated_api::api::action::pane_run::RunType;
+ let run_type = match internal {
+ Run::Command(cmd) => {
+ let run_command_action: RunCommandAction = cmd.into();
+ Some(RunType::Command(run_command_action.try_into()?))
+ },
+ Run::Plugin(plugin) => Some(RunType::Plugin(plugin.try_into()?)),
+ Run::EditFile(file_path, line_number, cwd) => {
+ Some(RunType::EditFile(ProtobufRunEditFileAction {
+ file_path: file_path.display().to_string(),
+ line_number: line_number.map(|n| n as u32),
+ cwd: cwd.map(|p| p.display().to_string()),
+ }))
+ },
+ Run::Cwd(cwd) => Some(RunType::Cwd(cwd.display().to_string())),
+ };
+ Ok(ProtobufPaneRun { run_type })
+ }
+}
+
+impl TryFrom<ProtobufCommandOrPlugin> for CommandOrPlugin {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufCommandOrPlugin) -> Result<Self, Self::Error> {
+ use super::generated_api::api::action::command_or_plugin::CommandOrPluginType;
+ match protobuf.command_or_plugin_type {
+ Some(CommandOrPluginType::Command(cmd)) => {
+ Ok(CommandOrPlugin::Command(cmd.try_into()?))
+ },
+ Some(CommandOrPluginType::Plugin(plugin)) => {
+ Ok(CommandOrPlugin::Plugin(plugin.try_into()?))
+ },
+ None => Err("CommandOrPlugin must have command_or_plugin_type"),
+ }
+ }
+}
+
+impl TryFrom<CommandOrPlugin> for ProtobufCommandOrPlugin {
+ type Error = &'static str;
+ fn try_from(internal: CommandOrPlugin) -> Result<Self, Self::Error> {
+ use super::generated_api::api::action::command_or_plugin::CommandOrPluginType;
+ let command_or_plugin_type = match internal {
+ CommandOrPlugin::Command(cmd) => Some(CommandOrPluginType::Command(cmd.try_into()?)),
+ CommandOrPlugin::Plugin(plugin) => {
+ Some(CommandOrPluginType::Plugin(plugin.try_into()?))
+ },
+ };
+ Ok(ProtobufCommandOrPlugin {
+ command_or_plugin_type,
+ })
+ }
+}
+
+impl TryFrom<ProtobufTiledPaneLayout> for TiledPaneLayout {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufTiledPaneLayout) -> Result<Self, Self::Error> {
+ let children_split_direction =
+ ProtobufSplitDirection::from_i32(protobuf.children_split_direction)
+ .ok_or("Invalid split direction")?
+ .try_into()?;
+ let children = protobuf
+ .children
+ .into_iter()
+ .map(|c| c.try_into())
+ .collect::<Result<Vec<_>, _>>()?;
+ let split_size = protobuf.split_size.map(|s| s.try_into()).transpose()?;
+ let run = protobuf.run.map(|r| r.try_into()).transpose()?;
+ let focus = protobuf.focus.and_then(|f| {
+ if f == "true" {
+ Some(true)
+ } else if f == "false" {
+ Some(false)
+ } else {
+ None
+ }
+ });
+ let run_instructions_to_ignore = vec![]; // Not serialized in protobuf
+ Ok(TiledPaneLayout {
+ children_split_direction,
+ name: protobuf.name,
+ children,
+ split_size,
+ run,
+ borderless: protobuf.borderless,
+ focus,
+ external_children_index: protobuf.external_children_index.map(|i| i as usize),
+ children_are_stacked: protobuf.children_are_stacked,
+ is_expanded_in_stack: protobuf.is_expanded_in_stack,
+ exclude_from_sync: protobuf.exclude_from_sync,
+ run_instructions_to_ignore,
+ hide_floating_panes: protobuf.hide_floating_panes,
+ pane_initial_contents: protobuf.pane_initial_contents,
+ })
+ }
+}
+
+impl TryFrom<TiledPaneLayout> for ProtobufTiledPaneLayout {
+ type Error = &'static str;
+ fn try_from(internal: TiledPaneLayout) -> Result<Self, Self::Error> {
+ let children_split_direction: ProtobufSplitDirection =
+ internal.children_split_direction.try_into()?;
+ let children = internal
+ .children
+ .into_iter()
+ .map(|c| c.try_into())
+ .collect::<Result<Vec<_>, _>>()?;
+ let split_size = internal.split_size.map(|s| s.try_into()).transpose()?;
+ let run = internal.run.map(|r| r.try_into()).transpose()?;
+ let focus = internal.focus.map(|f| f.to_string());
+ Ok(ProtobufTiledPaneLayout {
+ children_split_direction: children_split_direction as i32,
+ name: internal.name,
+ children,
+ split_size,
+ run,
+ borderless: internal.borderless,
+ focus,
+ external_children_index: internal.external_children_index.map(|i| i as u32),
+ children_are_stacked: internal.children_are_stacked,
+ is_expanded_in_stack: internal.is_expanded_in_stack,
+ exclude_from_sync: internal.exclude_from_sync,
+ hide_floating_panes: internal.hide_floating_panes,
+ pane_initial_contents: internal.pane_initial_contents,
+ })
+ }
+}
+
+impl TryFrom<ProtobufFloatingPaneLayout> for FloatingPaneLayout {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufFloatingPaneLayout) -> Result<Self, Self::Error> {
+ let height = protobuf.height.map(|h| h.try_into()).transpose()?;
+ let width = protobuf.width.map(|w| w.try_into()).transpose()?;
+ let x = protobuf.x.map(|x| x.try_into()).transpose()?;
+ let y = protobuf.y.map(|y| y.try_into()).transpose()?;
+ let run = protobuf.run.map(|r| r.try_into()).transpose()?;
+ Ok(FloatingPaneLayout {
+ name: protobuf.name,
+ height,
+ width,
+ x,
+ y,
+ pinned: protobuf.pinned,
+ run,
+ focus: protobuf.focus,
+ already_running: protobuf.already_running,
+ pane_initial_contents: protobuf.pane_initial_contents,
+ logical_position: protobuf.logical_position.map(|p| p as usize),
+ })
+ }
+}
+
+impl TryFrom<FloatingPaneLayout> for ProtobufFloatingPaneLayout {
+ type Error = &'static str;
+ fn try_from(internal: FloatingPaneLayout) -> Result<Self, Self::Error> {
+ let height = internal.height.map(|h| h.try_into()).transpose()?;
+ let width = internal.width.map(|w| w.try_into()).transpose()?;
+ let x = internal.x.map(|x| x.try_into()).transpose()?;
+ let y = internal.y.map(|y| y.try_into()).transpose()?;
+ let run = internal.run.map(|r| r.try_into()).transpose()?;
+ Ok(ProtobufFloatingPaneLayout {
+ name: internal.name,
+ height,
+ width,
+ x,
+ y,
+ pinned: internal.pinned,
+ run,
+ focus: internal.focus,
+ already_running: internal.already_running,
+ pane_initial_contents: internal.pane_initial_contents,
+ logical_position: internal.logical_position.map(|p| p as u32),
+ })
+ }
+}
+
+impl TryFrom<ProtobufLayoutConstraintTiledPair> for (LayoutConstraint, TiledPaneLayout) {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufLayoutConstraintTiledPair) -> Result<Self, Self::Error> {
+ let constraint = protobuf
+ .constraint
+ .ok_or("LayoutConstraintTiledPair must have constraint")?
+ .try_into()?;
+ let layout = protobuf
+ .layout
+ .ok_or("LayoutConstraintTiledPair must have layout")?
+ .try_into()?;
+ Ok((constraint, layout))
+ }
+}
+
+impl TryFrom<(LayoutConstraint, TiledPaneLayout)> for ProtobufLayoutConstraintTiledPair {
+ type Error = &'static str;
+ fn try_from(internal: (LayoutConstraint, TiledPaneLayout)) -> Result<Self, Self::Error> {
+ Ok(ProtobufLayoutConstraintTiledPair {
+ constraint: Some(internal.0.try_into()?),
+ layout: Some(internal.1.try_into()?),
+ })
+ }
+}
+
+impl TryFrom<ProtobufLayoutConstraintFloatingPair> for (LayoutConstraint, Vec<FloatingPaneLayout>) {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufLayoutConstraintFloatingPair) -> Result<Self, Self::Error> {
+ let constraint = protobuf
+ .constraint
+ .ok_or("LayoutConstraintFloatingPair must have constraint")?
+ .try_into()?;
+ let layouts = protobuf
+ .layouts
+ .into_iter()
+ .map(|l| l.try_into())
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok((constraint, layouts))
+ }
+}
+
+impl TryFrom<(LayoutConstraint, Vec<FloatingPaneLayout>)> for ProtobufLayoutConstraintFloatingPair {
+ type Error = &'static str;
+ fn try_from(
+ internal: (LayoutConstraint, Vec<FloatingPaneLayout>),
+ ) -> Result<Self, Self::Error> {
+ Ok(ProtobufLayoutConstraintFloatingPair {
+ constraint: Some(internal.0.try_into()?),
+ layouts: internal
+ .1
+ .into_iter()
+ .map(|l| l.try_into())
+ .collect::<Result<Vec<_>, _>>()?,
+ })
+ }
+}
+
+impl TryFrom<ProtobufSwapTiledLayout> for SwapTiledLayout {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufSwapTiledLayout) -> Result<Self, Self::Error> {
+ let constraint_map: BTreeMap<LayoutConstraint, TiledPaneLayout> = protobuf
+ .constraint_map
+ .into_iter()
+ .map(|pair| pair.try_into())
+ .collect::<Result<BTreeMap<_, _>, _>>()?;
+ Ok((constraint_map, protobuf.name))
+ }
+}
+
+impl TryFrom<SwapTiledLayout> for ProtobufSwapTiledLayout {
+ type Error = &'static str;
+ fn try_from(internal: SwapTiledLayout) -> Result<Self, Self::Error> {
+ let constraint_map = internal
+ .0
+ .into_iter()
+ .map(|(constraint, layout)| (constraint, layout).try_into())
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(ProtobufSwapTiledLayout {
+ constraint_map,
+ name: internal.1,
+ })
+ }
+}
+
+impl TryFrom<ProtobufSwapFloatingLayout> for SwapFloatingLayout {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufSwapFloatingLayout) -> Result<Self, Self::Error> {
+ let constraint_map: BTreeMap<LayoutConstraint, Vec<FloatingPaneLayout>> = protobuf
+ .constraint_map
+ .into_iter()
+ .map(|pair| pair.try_into())
+ .collect::<Result<BTreeMap<_, _>, _>>()?;
+ Ok((constraint_map, protobuf.name))
+ }
+}
+
+impl TryFrom<SwapFloatingLayout> for ProtobufSwapFloatingLayout {
+ type Error = &'static str;
+ fn try_from(internal: SwapFloatingLayout) -> Result<Self, Self::Error> {
+ let constraint_map = internal
+ .0
+ .into_iter()
+ .map(|(constraint, layouts)| (constraint, layouts).try_into())
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(ProtobufSwapFloatingLayout {
+ constraint_map,
+ name: internal.1,
+ })
+ }
+}