feat(plugins): optionally move plugin to focused tab (#2725)

* feat(plugins): move_to_focused_tab attribute for LaunchOrFocusPlugin

* style(fmt): rustfmt
This commit is contained in:
Aram Drevekenin 2023-08-25 12:24:43 +02:00 committed by GitHub
parent 877c467f9f
commit b44ba85895
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 129 additions and 12 deletions

View file

@ -240,7 +240,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<Key>)> {
action_key(&km, &[A::SearchToggleOption(SOpt::WholeWord)])), action_key(&km, &[A::SearchToggleOption(SOpt::WholeWord)])),
]} else if mi.mode == IM::Session { vec![ ]} else if mi.mode == IM::Session { vec![
(s("Detach"), s("Detach"), action_key(&km, &[Action::Detach])), (s("Detach"), s("Detach"), action_key(&km, &[Action::Detach])),
(s("Session Manager"), s("Manager"), action_key(&km, &[A::LaunchOrFocusPlugin(Default::default(), true), TO_NORMAL])), // not entirely accurate (s("Session Manager"), s("Manager"), action_key(&km, &[A::LaunchOrFocusPlugin(Default::default(), true, true), TO_NORMAL])), // not entirely accurate
(s("Select pane"), s("Select"), to_normal_key), (s("Select pane"), s("Select"), to_normal_key),
]} else if mi.mode == IM::Tmux { vec![ ]} else if mi.mode == IM::Tmux { vec![
(s("Move focus"), s("Move"), action_key_group(&km, &[ (s("Move focus"), s("Move"), action_key_group(&km, &[

View file

@ -621,11 +621,12 @@ pub(crate) fn route_action(
.send_to_screen(ScreenInstruction::StartOrReloadPluginPane(run_plugin, None)) .send_to_screen(ScreenInstruction::StartOrReloadPluginPane(run_plugin, None))
.with_context(err_context)?; .with_context(err_context)?;
}, },
Action::LaunchOrFocusPlugin(run_plugin, should_float) => { Action::LaunchOrFocusPlugin(run_plugin, should_float, move_to_focused_tab) => {
senders senders
.send_to_screen(ScreenInstruction::LaunchOrFocusPlugin( .send_to_screen(ScreenInstruction::LaunchOrFocusPlugin(
run_plugin, run_plugin,
should_float, should_float,
move_to_focused_tab,
client_id, client_id,
)) ))
.with_context(err_context)?; .with_context(err_context)?;

View file

@ -281,9 +281,9 @@ pub enum ScreenInstruction {
StartPluginLoadingIndication(u32, LoadingIndication), // u32 - plugin_id StartPluginLoadingIndication(u32, LoadingIndication), // u32 - plugin_id
ProgressPluginLoadingOffset(u32), // u32 - plugin id ProgressPluginLoadingOffset(u32), // u32 - plugin id
RequestStateUpdateForPlugins, RequestStateUpdateForPlugins,
LaunchOrFocusPlugin(RunPlugin, bool, ClientId), // bool is should_float LaunchOrFocusPlugin(RunPlugin, bool, bool, ClientId), // bools are: should_float, move_to_focused_tab
SuppressPane(PaneId, ClientId), // bool is should_float SuppressPane(PaneId, ClientId), // bool is should_float
FocusPaneWithId(PaneId, bool, ClientId), // bool is should_float FocusPaneWithId(PaneId, bool, ClientId), // bool is should_float
RenamePane(PaneId, Vec<u8>), RenamePane(PaneId, Vec<u8>),
RenameTab(usize, Vec<u8>), RenameTab(usize, Vec<u8>),
RequestPluginPermissions( RequestPluginPermissions(
@ -1618,18 +1618,47 @@ impl Screen {
&mut self, &mut self,
run_plugin: &RunPlugin, run_plugin: &RunPlugin,
should_float: bool, should_float: bool,
move_to_focused_tab: bool,
client_id: ClientId, client_id: ClientId,
) -> Result<bool> { ) -> Result<bool> {
// true => found and focused, false => not // true => found and focused, false => not
let err_context = || format!("failed to focus_plugin_pane"); let err_context = || format!("failed to focus_plugin_pane");
let mut tab_index_and_plugin_pane_id = None; let mut tab_index_and_plugin_pane_id = None;
let mut plugin_pane_to_move_to_active_tab = None;
let focused_tab_index = *self.active_tab_indices.get(&client_id).unwrap_or(&0);
let all_tabs = self.get_tabs_mut(); let all_tabs = self.get_tabs_mut();
for (tab_index, tab) in all_tabs.iter_mut() { for (tab_index, tab) in all_tabs.iter_mut() {
if let Some(plugin_pane_id) = tab.find_plugin(&run_plugin) { if let Some(plugin_pane_id) = tab.find_plugin(&run_plugin) {
tab_index_and_plugin_pane_id = Some((*tab_index, plugin_pane_id)); tab_index_and_plugin_pane_id = Some((*tab_index, plugin_pane_id));
if move_to_focused_tab && focused_tab_index != *tab_index {
plugin_pane_to_move_to_active_tab =
tab.extract_pane(plugin_pane_id, Some(client_id));
}
break; break;
} }
} }
if let Some(plugin_pane_to_move_to_active_tab) = plugin_pane_to_move_to_active_tab.take() {
let pane_id = plugin_pane_to_move_to_active_tab.pid();
let new_active_tab = self.get_active_tab_mut(client_id)?;
if should_float {
new_active_tab.show_floating_panes();
new_active_tab.add_floating_pane(
plugin_pane_to_move_to_active_tab,
pane_id,
Some(client_id),
)?;
} else {
new_active_tab.hide_floating_panes();
new_active_tab.add_tiled_pane(
plugin_pane_to_move_to_active_tab,
pane_id,
Some(client_id),
)?;
}
return Ok(true);
}
match tab_index_and_plugin_pane_id { match tab_index_and_plugin_pane_id {
Some((tab_index, plugin_pane_id)) => { Some((tab_index, plugin_pane_id)) => {
self.go_to_tab(tab_index + 1, client_id)?; self.go_to_tab(tab_index + 1, client_id)?;
@ -2969,7 +2998,12 @@ pub(crate) fn screen_thread_main(
screen.log_and_report_session_state()?; screen.log_and_report_session_state()?;
screen.render()?; screen.render()?;
}, },
ScreenInstruction::LaunchOrFocusPlugin(run_plugin, should_float, client_id) => { ScreenInstruction::LaunchOrFocusPlugin(
run_plugin,
should_float,
move_to_focused_tab,
client_id,
) => {
let client_id = if screen.active_tab_indices.contains_key(&client_id) { let client_id = if screen.active_tab_indices.contains_key(&client_id) {
Some(client_id) Some(client_id)
} else { } else {
@ -2983,7 +3017,12 @@ pub(crate) fn screen_thread_main(
}); });
match client_id_and_focused_tab { match client_id_and_focused_tab {
Some((tab_index, client_id)) => { Some((tab_index, client_id)) => {
if screen.focus_plugin_pane(&run_plugin, should_float, client_id)? { if screen.focus_plugin_pane(
&run_plugin,
should_float,
move_to_focused_tab,
client_id,
)? {
screen.render()?; screen.render()?;
screen.log_and_report_session_state()?; screen.log_and_report_session_state()?;
} else { } else {

View file

@ -2245,6 +2245,49 @@ impl Tab {
closed_pane closed_pane
} }
} }
pub fn extract_pane(
&mut self,
id: PaneId,
client_id: Option<ClientId>,
) -> Option<Box<dyn Pane>> {
if self.floating_panes.panes_contain(&id) {
let closed_pane = self.floating_panes.remove_pane(id);
self.floating_panes.move_clients_out_of_pane(id);
if !self.floating_panes.has_panes() {
self.hide_floating_panes();
}
self.set_force_render();
self.floating_panes.set_force_render();
if self.auto_layout
&& !self.swap_layouts.is_floating_damaged()
&& self.floating_panes.visible_panes_count() > 0
{
self.swap_layouts.set_is_floating_damaged();
// only relayout if the user is already "in" a layout, otherwise this might be
// confusing
let _ = self.next_swap_layout(client_id, false);
}
closed_pane
} else if self.tiled_panes.panes_contain(&id) {
if self.tiled_panes.fullscreen_is_active() {
self.tiled_panes.unset_fullscreen();
}
let closed_pane = self.tiled_panes.remove_pane(id);
self.set_force_render();
self.tiled_panes.set_force_render();
if self.auto_layout && !self.swap_layouts.is_tiled_damaged() {
self.swap_layouts.set_is_tiled_damaged();
// only relayout if the user is already "in" a layout, otherwise this might be
// confusing
let _ = self.next_swap_layout(client_id, false);
}
closed_pane
} else if self.suppressed_panes.contains_key(&id) {
self.suppressed_panes.remove(&id)
} else {
None
}
}
pub fn hold_pane( pub fn hold_pane(
&mut self, &mut self,
id: PaneId, id: PaneId,

View file

@ -2564,6 +2564,7 @@ pub fn send_cli_launch_or_focus_plugin_action() {
); );
let cli_action = CliAction::LaunchOrFocusPlugin { let cli_action = CliAction::LaunchOrFocusPlugin {
floating: true, floating: true,
move_to_focused_tab: true,
url: url::Url::parse("file:/path/to/fake/plugin").unwrap(), url: url::Url::parse("file:/path/to/fake/plugin").unwrap(),
configuration: Default::default(), configuration: Default::default(),
}; };
@ -2621,6 +2622,7 @@ pub fn send_cli_launch_or_focus_plugin_action_when_plugin_is_already_loaded() {
); );
let cli_action = CliAction::LaunchOrFocusPlugin { let cli_action = CliAction::LaunchOrFocusPlugin {
floating: true, floating: true,
move_to_focused_tab: true,
url: url::Url::parse("file:/path/to/fake/plugin").unwrap(), url: url::Url::parse("file:/path/to/fake/plugin").unwrap(),
configuration: Default::default(), configuration: Default::default(),
}; };

View file

@ -116,6 +116,7 @@ keybinds {
bind "w" { bind "w" {
LaunchOrFocusPlugin "zellij:session-manager" { LaunchOrFocusPlugin "zellij:session-manager" {
floating true floating true
move_to_focused_tab true
}; };
SwitchToMode "Normal" SwitchToMode "Normal"
} }

View file

@ -384,6 +384,8 @@ pub enum CliAction {
LaunchOrFocusPlugin { LaunchOrFocusPlugin {
#[clap(short, long, value_parser)] #[clap(short, long, value_parser)]
floating: bool, floating: bool,
#[clap(short, long, value_parser)]
move_to_focused_tab: bool,
url: Url, url: Url,
#[clap(short, long, value_parser)] #[clap(short, long, value_parser)]
configuration: Option<PluginUserConfiguration>, configuration: Option<PluginUserConfiguration>,

View file

@ -204,7 +204,7 @@ pub enum Action {
LeftClick(Position), LeftClick(Position),
RightClick(Position), RightClick(Position),
MiddleClick(Position), MiddleClick(Position),
LaunchOrFocusPlugin(RunPlugin, bool), // bool => should float LaunchOrFocusPlugin(RunPlugin, bool, bool), // bools => should float, move_to_focused_tab
LeftMouseRelease(Position), LeftMouseRelease(Position),
RightMouseRelease(Position), RightMouseRelease(Position),
MiddleMouseRelease(Position), MiddleMouseRelease(Position),
@ -501,6 +501,7 @@ impl Action {
CliAction::LaunchOrFocusPlugin { CliAction::LaunchOrFocusPlugin {
url, url,
floating, floating,
move_to_focused_tab,
configuration, configuration,
} => { } => {
let current_dir = get_current_dir(); let current_dir = get_current_dir();
@ -511,7 +512,11 @@ impl Action {
_allow_exec_host_cmd: false, _allow_exec_host_cmd: false,
configuration: configuration.unwrap_or_default(), configuration: configuration.unwrap_or_default(),
}; };
Ok(vec![Action::LaunchOrFocusPlugin(run_plugin, floating)]) Ok(vec![Action::LaunchOrFocusPlugin(
run_plugin,
floating,
move_to_focused_tab,
)])
}, },
} }
} }

View file

@ -906,6 +906,9 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
let should_float = command_metadata let should_float = command_metadata
.and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "floating")) .and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "floating"))
.unwrap_or(false); .unwrap_or(false);
let move_to_focused_tab = command_metadata
.and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "move_to_focused_tab"))
.unwrap_or(false);
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
let location = RunPluginLocation::parse(&plugin_path, Some(current_dir))?; let location = RunPluginLocation::parse(&plugin_path, Some(current_dir))?;
let configuration = KdlLayoutParser::parse_plugin_user_configuration(&kdl_action)?; let configuration = KdlLayoutParser::parse_plugin_user_configuration(&kdl_action)?;
@ -914,7 +917,11 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
_allow_exec_host_cmd: false, _allow_exec_host_cmd: false,
configuration, configuration,
}; };
Ok(Action::LaunchOrFocusPlugin(run_plugin, should_float)) Ok(Action::LaunchOrFocusPlugin(
run_plugin,
should_float,
move_to_focused_tab,
))
}, },
"PreviousSwapLayout" => Ok(Action::PreviousSwapLayout), "PreviousSwapLayout" => Ok(Action::PreviousSwapLayout),
"NextSwapLayout" => Ok(Action::NextSwapLayout), "NextSwapLayout" => Ok(Action::NextSwapLayout),

View file

@ -84,6 +84,7 @@ message LaunchOrFocusPluginPayload {
string plugin_url = 1; string plugin_url = 1;
bool should_float = 2; bool should_float = 2;
optional PluginConfiguration plugin_configuration = 3; optional PluginConfiguration plugin_configuration = 3;
bool move_to_focused_tab = 4;
} }
message GoToTabNamePayload { message GoToTabNamePayload {

View file

@ -399,7 +399,12 @@ impl TryFrom<ProtobufAction> for Action {
configuration, configuration,
}; };
let should_float = payload.should_float; let should_float = payload.should_float;
Ok(Action::LaunchOrFocusPlugin(run_plugin, should_float)) let move_to_focused_tab = payload.move_to_focused_tab;
Ok(Action::LaunchOrFocusPlugin(
run_plugin,
should_float,
move_to_focused_tab,
))
}, },
_ => Err("Wrong payload for Action::LaunchOrFocusPlugin"), _ => Err("Wrong payload for Action::LaunchOrFocusPlugin"),
} }
@ -954,7 +959,7 @@ impl TryFrom<Action> for ProtobufAction {
optional_payload: Some(OptionalPayload::MiddleClickPayload(position)), optional_payload: Some(OptionalPayload::MiddleClickPayload(position)),
}) })
}, },
Action::LaunchOrFocusPlugin(run_plugin, should_float) => { Action::LaunchOrFocusPlugin(run_plugin, should_float, move_to_focused_tab) => {
let url: Url = Url::from(&run_plugin.location); let url: Url = Url::from(&run_plugin.location);
Ok(ProtobufAction { Ok(ProtobufAction {
name: ProtobufActionName::LaunchOrFocusPlugin as i32, name: ProtobufActionName::LaunchOrFocusPlugin as i32,
@ -962,6 +967,7 @@ impl TryFrom<Action> for ProtobufAction {
LaunchOrFocusPluginPayload { LaunchOrFocusPluginPayload {
plugin_url: url.into(), plugin_url: url.into(),
should_float, should_float,
move_to_focused_tab,
plugin_configuration: Some(run_plugin.configuration.try_into()?), plugin_configuration: Some(run_plugin.configuration.try_into()?),
}, },
)), )),

View file

@ -2503,10 +2503,12 @@ Config {
configuration: PluginUserConfiguration( configuration: PluginUserConfiguration(
{ {
"floating": "true", "floating": "true",
"move_to_focused_tab": "true",
}, },
), ),
}, },
true, true,
true,
), ),
SwitchToMode( SwitchToMode(
Normal, Normal,

View file

@ -2503,10 +2503,12 @@ Config {
configuration: PluginUserConfiguration( configuration: PluginUserConfiguration(
{ {
"floating": "true", "floating": "true",
"move_to_focused_tab": "true",
}, },
), ),
}, },
true, true,
true,
), ),
SwitchToMode( SwitchToMode(
Normal, Normal,

View file

@ -2503,10 +2503,12 @@ Config {
configuration: PluginUserConfiguration( configuration: PluginUserConfiguration(
{ {
"floating": "true", "floating": "true",
"move_to_focused_tab": "true",
}, },
), ),
}, },
true, true,
true,
), ),
SwitchToMode( SwitchToMode(
Normal, Normal,

View file

@ -2503,10 +2503,12 @@ Config {
configuration: PluginUserConfiguration( configuration: PluginUserConfiguration(
{ {
"floating": "true", "floating": "true",
"move_to_focused_tab": "true",
}, },
), ),
}, },
true, true,
true,
), ),
SwitchToMode( SwitchToMode(
Normal, Normal,

View file

@ -2503,10 +2503,12 @@ Config {
configuration: PluginUserConfiguration( configuration: PluginUserConfiguration(
{ {
"floating": "true", "floating": "true",
"move_to_focused_tab": "true",
}, },
), ),
}, },
true, true,
true,
), ),
SwitchToMode( SwitchToMode(
Normal, Normal,