feat(plugin-api): allow replacing pane with existing pane (#4246)
* work * make the api work * some cleanups * close pane * style(fmt): rustfmt
This commit is contained in:
parent
16dd0a91cd
commit
11015c8fe4
9 changed files with 164 additions and 10 deletions
|
|
@ -495,6 +495,14 @@ fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) {
|
|||
PluginCommand::ClearKeyPressesIntercepts => {
|
||||
clear_key_presses_intercepts(&mut env)
|
||||
},
|
||||
PluginCommand::ReplacePaneWithExistingPane(
|
||||
pane_id_to_replace,
|
||||
existing_pane_id,
|
||||
) => replace_pane_with_existing_pane(
|
||||
&mut env,
|
||||
pane_id_to_replace.into(),
|
||||
existing_pane_id.into(),
|
||||
),
|
||||
},
|
||||
(PermissionStatus::Denied, permission) => {
|
||||
log::error!(
|
||||
|
|
@ -2455,6 +2463,19 @@ fn clear_key_presses_intercepts(env: &mut PluginEnv) {
|
|||
.send_to_screen(ScreenInstruction::ClearKeyPressesIntercepts(env.client_id));
|
||||
}
|
||||
|
||||
fn replace_pane_with_existing_pane(
|
||||
env: &mut PluginEnv,
|
||||
pane_to_replace: PaneId,
|
||||
existing_pane: PaneId,
|
||||
) {
|
||||
let _ = env
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ReplacePaneWithExistingPane(
|
||||
pane_to_replace,
|
||||
existing_pane,
|
||||
));
|
||||
}
|
||||
|
||||
// Custom panic handler for plugins.
|
||||
//
|
||||
// This is called when a panic occurs in a plugin. Since most panics will likely originate in the
|
||||
|
|
@ -2621,6 +2642,7 @@ fn check_command_permission(
|
|||
| PluginCommand::CloseMultiplePanes(..)
|
||||
| PluginCommand::FloatMultiplePanes(..)
|
||||
| PluginCommand::EmbedMultiplePanes(..)
|
||||
| PluginCommand::ReplacePaneWithExistingPane(..)
|
||||
| PluginCommand::KillSessions(..) => PermissionType::ChangeApplicationState,
|
||||
PluginCommand::UnblockCliPipeInput(..)
|
||||
| PluginCommand::BlockCliPipeInput(..)
|
||||
|
|
|
|||
|
|
@ -422,6 +422,7 @@ pub enum ScreenInstruction {
|
|||
SetMouseSelectionSupport(PaneId, bool),
|
||||
InterceptKeyPresses(PluginId, ClientId),
|
||||
ClearKeyPressesIntercepts(ClientId),
|
||||
ReplacePaneWithExistingPane(PaneId, PaneId),
|
||||
}
|
||||
|
||||
impl From<&ScreenInstruction> for ScreenContext {
|
||||
|
|
@ -654,6 +655,9 @@ impl From<&ScreenInstruction> for ScreenContext {
|
|||
ScreenInstruction::ClearKeyPressesIntercepts(..) => {
|
||||
ScreenContext::ClearKeyPressesIntercepts
|
||||
},
|
||||
ScreenInstruction::ReplacePaneWithExistingPane(..) => {
|
||||
ScreenContext::ReplacePaneWithExistingPane
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2608,6 +2612,50 @@ impl Screen {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn replace_pane_with_existing_pane(
|
||||
&mut self,
|
||||
pane_id_to_replace: PaneId,
|
||||
pane_id_of_existing_pane: PaneId,
|
||||
) {
|
||||
let Some(tab_index_of_pane_id_to_replace) = self
|
||||
.tabs
|
||||
.iter()
|
||||
.find(|(_tab_index, tab)| tab.has_pane_with_pid(&pane_id_to_replace))
|
||||
.map(|(_tab_index, tab)| tab.position)
|
||||
else {
|
||||
log::error!("Could not find tab");
|
||||
return;
|
||||
};
|
||||
let Some(tab_index_of_existing_pane) = self
|
||||
.tabs
|
||||
.iter()
|
||||
.find(|(_tab_index, tab)| tab.has_pane_with_pid(&pane_id_of_existing_pane))
|
||||
.map(|(_tab_index, tab)| tab.position)
|
||||
else {
|
||||
log::error!("Could not find tab");
|
||||
return;
|
||||
};
|
||||
let Some(extracted_pane_from_other_tab) = self
|
||||
.tabs
|
||||
.iter_mut()
|
||||
.find(|(_, t)| t.position == tab_index_of_existing_pane)
|
||||
.and_then(|(_, t)| t.extract_pane(pane_id_of_existing_pane, false))
|
||||
else {
|
||||
log::error!("Failed to find pane");
|
||||
return;
|
||||
};
|
||||
if let Some(tab) = self
|
||||
.tabs
|
||||
.iter_mut()
|
||||
.find(|(_, t)| t.position == tab_index_of_pane_id_to_replace)
|
||||
{
|
||||
tab.1.close_pane_and_replace_with_other_pane(
|
||||
pane_id_to_replace,
|
||||
extracted_pane_from_other_tab,
|
||||
);
|
||||
}
|
||||
let _ = self.log_and_report_session_state();
|
||||
}
|
||||
pub fn reconfigure(
|
||||
&mut self,
|
||||
new_keybinds: Keybinds,
|
||||
|
|
@ -5488,6 +5536,9 @@ pub(crate) fn screen_thread_main(
|
|||
ScreenInstruction::ClearKeyPressesIntercepts(client_id) => {
|
||||
keybind_intercepts.remove(&client_id);
|
||||
},
|
||||
ScreenInstruction::ReplacePaneWithExistingPane(old_pane_id, new_pane_id) => {
|
||||
screen.replace_pane_with_existing_pane(old_pane_id, new_pane_id)
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1617,6 +1617,30 @@ impl Tab {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn close_pane_and_replace_with_other_pane(
|
||||
&mut self,
|
||||
pane_id_to_replace: PaneId,
|
||||
pane_to_replace_with: Box<dyn Pane>,
|
||||
) {
|
||||
let mut replaced_pane = if self.floating_panes.panes_contain(&pane_id_to_replace) {
|
||||
self.floating_panes
|
||||
.replace_pane(pane_id_to_replace, pane_to_replace_with)
|
||||
.ok()
|
||||
} else {
|
||||
self.tiled_panes
|
||||
.replace_pane(pane_id_to_replace, pane_to_replace_with)
|
||||
};
|
||||
if let Some(replaced_pane) = replaced_pane.take() {
|
||||
let pane_id = replaced_pane.pid();
|
||||
let _ = self.senders.send_to_pty(PtyInstruction::ClosePane(pane_id));
|
||||
let _ = self.senders.send_to_plugin(PluginInstruction::Update(vec![(
|
||||
None,
|
||||
None,
|
||||
Event::PaneClosed(pane_id.into()),
|
||||
)]));
|
||||
drop(replaced_pane);
|
||||
}
|
||||
}
|
||||
pub fn horizontal_split(
|
||||
&mut self,
|
||||
pid: PaneId,
|
||||
|
|
|
|||
|
|
@ -1451,6 +1451,14 @@ pub fn clear_key_presses_intercepts() {
|
|||
unsafe { host_run_plugin_command() };
|
||||
}
|
||||
|
||||
pub fn replace_pane_with_existing_pane(pane_id_to_replace: PaneId, existing_pane_id: PaneId) {
|
||||
let plugin_command =
|
||||
PluginCommand::ReplacePaneWithExistingPane(pane_id_to_replace, existing_pane_id);
|
||||
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
|
||||
object_to_stdout(&protobuf_plugin_command.encode_to_vec());
|
||||
unsafe { host_run_plugin_command() };
|
||||
}
|
||||
|
||||
// Utility Functions
|
||||
|
||||
#[allow(unused)]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
pub struct PluginCommand {
|
||||
#[prost(enumeration="CommandName", tag="1")]
|
||||
pub name: i32,
|
||||
#[prost(oneof="plugin_command::Payload", tags="2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110")]
|
||||
#[prost(oneof="plugin_command::Payload", tags="2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111")]
|
||||
pub payload: ::core::option::Option<plugin_command::Payload>,
|
||||
}
|
||||
/// Nested message and enum types in `PluginCommand`.
|
||||
|
|
@ -211,10 +211,20 @@ pub mod plugin_command {
|
|||
RevokeWebLoginTokenPayload(super::RevokeWebLoginTokenPayload),
|
||||
#[prost(message, tag="110")]
|
||||
RenameWebLoginTokenPayload(super::RenameWebLoginTokenPayload),
|
||||
#[prost(message, tag="111")]
|
||||
ReplacePaneWithExistingPanePayload(super::ReplacePaneWithExistingPanePayload),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ReplacePaneWithExistingPanePayload {
|
||||
#[prost(message, optional, tag="1")]
|
||||
pub pane_id_to_replace: ::core::option::Option<PaneId>,
|
||||
#[prost(message, optional, tag="2")]
|
||||
pub existing_pane_id: ::core::option::Option<PaneId>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct RenameWebLoginTokenPayload {
|
||||
#[prost(string, tag="1")]
|
||||
pub old_name: ::prost::alloc::string::String,
|
||||
|
|
@ -1013,6 +1023,7 @@ pub enum CommandName {
|
|||
RenameWebLoginToken = 142,
|
||||
InterceptKeyPresses = 143,
|
||||
ClearKeyPressesIntercepts = 144,
|
||||
ReplacePaneWithExistingPane = 155,
|
||||
}
|
||||
impl CommandName {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
|
|
@ -1166,6 +1177,7 @@ impl CommandName {
|
|||
CommandName::RenameWebLoginToken => "RenameWebLoginToken",
|
||||
CommandName::InterceptKeyPresses => "InterceptKeyPresses",
|
||||
CommandName::ClearKeyPressesIntercepts => "ClearKeyPressesIntercepts",
|
||||
CommandName::ReplacePaneWithExistingPane => "ReplacePaneWithExistingPane",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
|
|
@ -1316,6 +1328,7 @@ impl CommandName {
|
|||
"RenameWebLoginToken" => Some(Self::RenameWebLoginToken),
|
||||
"InterceptKeyPresses" => Some(Self::InterceptKeyPresses),
|
||||
"ClearKeyPressesIntercepts" => Some(Self::ClearKeyPressesIntercepts),
|
||||
"ReplacePaneWithExistingPane" => Some(Self::ReplacePaneWithExistingPane),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2496,4 +2496,5 @@ pub enum PluginCommand {
|
|||
RenameWebLoginToken(String, String), // (original_name, new_name)
|
||||
InterceptKeyPresses,
|
||||
ClearKeyPressesIntercepts,
|
||||
ReplacePaneWithExistingPane(PaneId, PaneId), // (pane id to replace, pane id of existing)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -387,6 +387,7 @@ pub enum ScreenContext {
|
|||
SetMouseSelectionSupport,
|
||||
InterceptKeyPresses,
|
||||
ClearKeyPressesIntercepts,
|
||||
ReplacePaneWithExistingPane,
|
||||
}
|
||||
|
||||
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ enum CommandName {
|
|||
RenameWebLoginToken = 142;
|
||||
InterceptKeyPresses = 143;
|
||||
ClearKeyPressesIntercepts = 144;
|
||||
ReplacePaneWithExistingPane = 155;
|
||||
}
|
||||
|
||||
message PluginCommand {
|
||||
|
|
@ -263,9 +264,15 @@ message PluginCommand {
|
|||
GenerateWebLoginTokenPayload generate_web_login_token_payload = 108;
|
||||
RevokeWebLoginTokenPayload revoke_web_login_token_payload = 109;
|
||||
RenameWebLoginTokenPayload rename_web_login_token_payload = 110;
|
||||
ReplacePaneWithExistingPanePayload replace_pane_with_existing_pane_payload = 111;
|
||||
}
|
||||
}
|
||||
|
||||
message ReplacePaneWithExistingPanePayload {
|
||||
PaneId pane_id_to_replace = 1;
|
||||
PaneId existing_pane_id = 2;
|
||||
}
|
||||
|
||||
message RenameWebLoginTokenPayload {
|
||||
string old_name = 1;
|
||||
string new_name = 2;
|
||||
|
|
|
|||
|
|
@ -26,15 +26,15 @@ pub use super::generated_api::api::{
|
|||
PaneIdAndFloatingPaneCoordinates, PaneType as ProtobufPaneType,
|
||||
PluginCommand as ProtobufPluginCommand, PluginMessagePayload, RebindKeysPayload,
|
||||
ReconfigurePayload, ReloadPluginPayload, RenameWebLoginTokenPayload,
|
||||
RenameWebTokenResponse, RequestPluginPermissionPayload, RerunCommandPanePayload,
|
||||
ResizePaneIdWithDirectionPayload, ResizePayload, RevokeAllWebTokensResponse,
|
||||
RevokeTokenResponse, RevokeWebLoginTokenPayload, RunCommandPayload,
|
||||
ScrollDownInPaneIdPayload, ScrollToBottomInPaneIdPayload, ScrollToTopInPaneIdPayload,
|
||||
ScrollUpInPaneIdPayload, SetFloatingPanePinnedPayload, SetSelfMouseSelectionSupportPayload,
|
||||
SetTimeoutPayload, ShowPaneWithIdPayload, StackPanesPayload, SubscribePayload,
|
||||
SwitchSessionPayload, SwitchTabToPayload, TogglePaneEmbedOrEjectForPaneIdPayload,
|
||||
TogglePaneIdFullscreenPayload, UnsubscribePayload, WebRequestPayload,
|
||||
WriteCharsToPaneIdPayload, WriteToPaneIdPayload,
|
||||
RenameWebTokenResponse, ReplacePaneWithExistingPanePayload, RequestPluginPermissionPayload,
|
||||
RerunCommandPanePayload, ResizePaneIdWithDirectionPayload, ResizePayload,
|
||||
RevokeAllWebTokensResponse, RevokeTokenResponse, RevokeWebLoginTokenPayload,
|
||||
RunCommandPayload, ScrollDownInPaneIdPayload, ScrollToBottomInPaneIdPayload,
|
||||
ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload, SetFloatingPanePinnedPayload,
|
||||
SetSelfMouseSelectionSupportPayload, SetTimeoutPayload, ShowPaneWithIdPayload,
|
||||
StackPanesPayload, SubscribePayload, SwitchSessionPayload, SwitchTabToPayload,
|
||||
TogglePaneEmbedOrEjectForPaneIdPayload, TogglePaneIdFullscreenPayload, UnsubscribePayload,
|
||||
WebRequestPayload, WriteCharsToPaneIdPayload, WriteToPaneIdPayload,
|
||||
},
|
||||
plugin_permission::PermissionType as ProtobufPermissionType,
|
||||
resize::ResizeAction as ProtobufResizeAction,
|
||||
|
|
@ -1711,6 +1711,22 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
|
|||
Some(_) => Err("ClearKeyPressesIntercepts should have no payload, found a payload"),
|
||||
None => Ok(PluginCommand::ClearKeyPressesIntercepts),
|
||||
},
|
||||
Some(CommandName::ReplacePaneWithExistingPane) => match protobuf_plugin_command.payload
|
||||
{
|
||||
Some(Payload::ReplacePaneWithExistingPanePayload(
|
||||
replace_pane_with_other_pane_payload,
|
||||
)) => Ok(PluginCommand::ReplacePaneWithExistingPane(
|
||||
replace_pane_with_other_pane_payload
|
||||
.pane_id_to_replace
|
||||
.and_then(|p_id| PaneId::try_from(p_id).ok())
|
||||
.ok_or("Failed to parse ReplacePaneWithExistingPanePayload")?,
|
||||
replace_pane_with_other_pane_payload
|
||||
.existing_pane_id
|
||||
.and_then(|p_id| PaneId::try_from(p_id).ok())
|
||||
.ok_or("Failed to parse ReplacePaneWithExistingPanePayload")?,
|
||||
)),
|
||||
_ => Err("Mismatched payload for ReplacePaneWithExistingPane"),
|
||||
},
|
||||
None => Err("Unrecognized plugin command"),
|
||||
}
|
||||
}
|
||||
|
|
@ -2848,6 +2864,17 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
|
|||
name: CommandName::ClearKeyPressesIntercepts as i32,
|
||||
payload: None,
|
||||
}),
|
||||
PluginCommand::ReplacePaneWithExistingPane(pane_id_to_replace, existing_pane_id) => {
|
||||
Ok(ProtobufPluginCommand {
|
||||
name: CommandName::ReplacePaneWithExistingPane as i32,
|
||||
payload: Some(Payload::ReplacePaneWithExistingPanePayload(
|
||||
ReplacePaneWithExistingPanePayload {
|
||||
pane_id_to_replace: ProtobufPaneId::try_from(pane_id_to_replace).ok(),
|
||||
existing_pane_id: ProtobufPaneId::try_from(existing_pane_id).ok(),
|
||||
},
|
||||
)),
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue