fix(plugins): multiple-select + compact-bar tooltip multiplayer issues (#4312)

* fix: allow stacking panes if root pane is floating

* fix: handle multiple client gracefully in multiple select

* style(fmt): rustfmt

* fix compact-bar tooltip multiuser duplication

* style(fmt): rustfmt
This commit is contained in:
Aram Drevekenin 2025-07-22 09:13:41 +02:00 committed by GitHub
parent ba680fc2eb
commit 6af82a9e99
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 224 additions and 83 deletions

View file

@ -59,6 +59,7 @@ struct State {
persist: bool, persist: bool,
is_first_run: bool, is_first_run: bool,
own_tab_index: Option<usize>, own_tab_index: Option<usize>,
own_client_id: u16,
} }
struct TabRenderData { struct TabRenderData {
@ -72,10 +73,12 @@ register_plugin!(State);
impl ZellijPlugin for State { impl ZellijPlugin for State {
fn load(&mut self, configuration: BTreeMap<String, String>) { fn load(&mut self, configuration: BTreeMap<String, String>) {
let plugin_ids = get_plugin_ids();
self.own_plugin_id = Some(plugin_ids.plugin_id);
self.own_client_id = plugin_ids.client_id;
self.initialize_configuration(configuration); self.initialize_configuration(configuration);
self.setup_subscriptions(); self.setup_subscriptions();
self.configure_keybinds(); self.configure_keybinds();
self.own_plugin_id = Some(get_plugin_ids().plugin_id);
} }
fn update(&mut self, event: Event) -> bool { fn update(&mut self, event: Event) -> bool {
@ -104,14 +107,10 @@ impl ZellijPlugin for State {
} else if message.name == MSG_TOGGLE_TOOLTIP } else if message.name == MSG_TOGGLE_TOOLTIP
&& message.is_private && message.is_private
&& self.toggle_tooltip_key.is_some() && self.toggle_tooltip_key.is_some()
// only launch once per plugin instance
&& self.own_tab_index == Some(self.active_tab_idx.saturating_sub(1)) && self.own_tab_index == Some(self.active_tab_idx.saturating_sub(1))
// only launch // only launch once per client of plugin instance
// tooltip once && Some(format!("{}", self.own_client_id)) == message.payload
// even if there
// are a few
// instances of
// compact-bar
// running
{ {
self.toggle_persisted_tooltip(self.mode_info.mode); self.toggle_persisted_tooltip(self.mode_info.mode);
} }
@ -166,7 +165,10 @@ impl State {
fn configure_keybinds(&self) { fn configure_keybinds(&self) {
if !self.is_tooltip && self.toggle_tooltip_key.is_some() { if !self.is_tooltip && self.toggle_tooltip_key.is_some() {
if let Some(toggle_key) = &self.toggle_tooltip_key { if let Some(toggle_key) = &self.toggle_tooltip_key {
reconfigure(bind_toggle_key_config(toggle_key), false); reconfigure(
bind_toggle_key_config(toggle_key, self.own_client_id),
false,
);
} }
} }
} }
@ -551,7 +553,7 @@ impl State {
} }
} }
fn bind_toggle_key_config(toggle_key: &str) -> String { fn bind_toggle_key_config(toggle_key: &str, client_id: u16) -> String {
format!( format!(
r#" r#"
keybinds {{ keybinds {{
@ -560,11 +562,12 @@ fn bind_toggle_key_config(toggle_key: &str) -> String {
MessagePlugin "compact-bar" {{ MessagePlugin "compact-bar" {{
name "toggle_tooltip" name "toggle_tooltip"
tooltip "{}" tooltip "{}"
payload "{}"
}} }}
}} }}
}} }}
}} }}
"#, "#,
toggle_key, toggle_key toggle_key, toggle_key, client_id
) )
} }

View file

@ -11,6 +11,7 @@ pub struct App {
total_tabs_in_session: Option<usize>, total_tabs_in_session: Option<usize>,
grouped_panes: Vec<PaneId>, grouped_panes: Vec<PaneId>,
grouped_panes_count: usize, grouped_panes_count: usize,
all_client_grouped_panes: BTreeMap<ClientId, Vec<PaneId>>,
mode_info: ModeInfo, mode_info: ModeInfo,
closing: bool, closing: bool,
highlighted_at: Option<Instant>, highlighted_at: Option<Instant>,
@ -47,6 +48,8 @@ impl ZellijPlugin for App {
if self.closing { if self.closing {
return false; return false;
} }
intercept_key_presses(); // we do this here so that all clients (even those connected after
// load) will have their keys intercepted
match event { match event {
Event::ModeUpdate(mode_info) => self.handle_mode_update(mode_info), Event::ModeUpdate(mode_info) => self.handle_mode_update(mode_info),
Event::PaneUpdate(pane_manifest) => self.handle_pane_update(pane_manifest), Event::PaneUpdate(pane_manifest) => self.handle_pane_update(pane_manifest),
@ -59,6 +62,10 @@ impl ZellijPlugin for App {
fn render(&mut self, rows: usize, cols: usize) { fn render(&mut self, rows: usize, cols: usize) {
self.update_current_size(rows, cols); self.update_current_size(rows, cols);
if self.grouped_panes_count == 0 {
self.render_no_panes_message(rows, cols);
} else {
let ui_width = self.calculate_ui_width(); let ui_width = self.calculate_ui_width();
self.update_baseline_ui_width(ui_width); self.update_baseline_ui_width(ui_width);
let base_x = cols.saturating_sub(self.baseline_ui_width) / 2; let base_x = cols.saturating_sub(self.baseline_ui_width) / 2;
@ -67,6 +74,7 @@ impl ZellijPlugin for App {
self.render_shortcuts(base_x, base_y + 2); self.render_shortcuts(base_x, base_y + 2);
self.render_controls(base_x, base_y + 7); self.render_controls(base_x, base_y + 7);
} }
}
} }
impl App { impl App {
@ -88,7 +96,7 @@ impl App {
let controls_width = group_controls_length(&self.mode_info); let controls_width = group_controls_length(&self.mode_info);
let header_width = Self::header_text().0.len(); let header_width = Self::header_text().0.len();
let shortcuts_max_width = Self::shortcuts_max_width(); let shortcuts_max_width = self.shortcuts_max_width();
std::cmp::max( std::cmp::max(
controls_width, controls_width,
@ -96,6 +104,20 @@ impl App {
) )
} }
fn render_no_panes_message(&self, rows: usize, cols: usize) {
let message = "PANES SELECTED FOR OTHER CLIENT";
let message_component = Text::new(message).color_all(2);
let base_x = cols.saturating_sub(message.len()) / 2;
let base_y = rows / 2;
print_text_with_coordinates(message_component, base_x, base_y, None, None);
let esc_message = "<ESC> - close";
let esc_message_component = Text::new(esc_message).color_substring(3, "<ESC>");
let esc_base_x = cols.saturating_sub(esc_message.len()) / 2;
let esc_base_y = base_y + 2;
print_text_with_coordinates(esc_message_component, esc_base_x, esc_base_y, None, None);
}
fn header_text() -> (&'static str, Text) { fn header_text() -> (&'static str, Text) {
let header_text = "<ESC> - cancel, <TAB> - move"; let header_text = "<ESC> - cancel, <TAB> - move";
let header_text_component = Text::new(header_text) let header_text_component = Text::new(header_text)
@ -104,10 +126,10 @@ impl App {
(header_text, header_text_component) (header_text, header_text_component)
} }
fn shortcuts_max_width() -> usize { fn shortcuts_max_width(&self) -> usize {
std::cmp::max( std::cmp::max(
std::cmp::max( std::cmp::max(
Self::group_actions_text().0.len(), self.group_actions_text().0.len(),
Self::shortcuts_line1_text().0.len(), Self::shortcuts_line1_text().0.len(),
), ),
std::cmp::max( std::cmp::max(
@ -117,10 +139,18 @@ impl App {
) )
} }
fn group_actions_text() -> (&'static str, Text) { fn group_actions_text(&self) -> (&'static str, Text) {
let text = "GROUP ACTIONS"; let count_text = if self.grouped_panes_count == 1 {
let component = Text::new(text).color_all(2); format!("GROUP ACTIONS ({} SELECTED PANE)", self.grouped_panes_count)
(text, component) } else {
format!(
"GROUP ACTIONS ({} SELECTED PANES)",
self.grouped_panes_count
)
};
let component = Text::new(&count_text).color_all(2);
(Box::leak(count_text.into_boxed_str()), component)
} }
fn shortcuts_line1_text() -> (&'static str, Text) { fn shortcuts_line1_text() -> (&'static str, Text) {
@ -164,7 +194,8 @@ impl App {
return false; return false;
}; };
self.update_grouped_panes(&pane_manifest, own_client_id); self.update_all_client_grouped_panes(&pane_manifest);
self.update_own_grouped_panes(&pane_manifest, own_client_id);
self.update_tab_info(&pane_manifest); self.update_tab_info(&pane_manifest);
self.total_tabs_in_session = Some(pane_manifest.panes.keys().count()); self.total_tabs_in_session = Some(pane_manifest.panes.keys().count());
@ -183,7 +214,28 @@ impl App {
false false
} }
fn update_grouped_panes(&mut self, pane_manifest: &PaneManifest, own_client_id: ClientId) { fn update_all_client_grouped_panes(&mut self, pane_manifest: &PaneManifest) {
self.all_client_grouped_panes.clear();
for (_tab_index, pane_infos) in &pane_manifest.panes {
for pane_info in pane_infos {
for (client_id, _index_in_pane_group) in &pane_info.index_in_pane_group {
let pane_id = if pane_info.is_plugin {
PaneId::Plugin(pane_info.id)
} else {
PaneId::Terminal(pane_info.id)
};
self.all_client_grouped_panes
.entry(*client_id)
.or_insert_with(Vec::new)
.push(pane_id);
}
}
}
}
fn update_own_grouped_panes(&mut self, pane_manifest: &PaneManifest, own_client_id: ClientId) {
self.grouped_panes.clear(); self.grouped_panes.clear();
let mut count = 0; let mut count = 0;
let mut panes_with_index = Vec::new(); let mut panes_with_index = Vec::new();
@ -209,20 +261,15 @@ impl App {
self.grouped_panes.push(pane_id); self.grouped_panes.push(pane_id);
} }
if count == 0 { if self.all_clients_have_empty_groups() {
self.close_self(); self.close_self();
} }
let previous_count = self.grouped_panes_count; let previous_count = self.grouped_panes_count;
self.grouped_panes_count = count; self.grouped_panes_count = count;
if let Some(own_plugin_id) = self.own_plugin_id { if let Some(own_plugin_id) = self.own_plugin_id {
let title = if count == 1 {
"SELECTED PANE"
} else {
"SELECTED PANES"
};
if previous_count != count { if previous_count != count {
rename_plugin_pane(own_plugin_id, format!("{} {}", count, title)); rename_plugin_pane(own_plugin_id, "Multiple Pane Select".to_string());
} }
if previous_count != 0 && count != 0 && previous_count != count { if previous_count != 0 && count != 0 && previous_count != count {
if self.doherty_threshold_elapsed_since_highlight() { if self.doherty_threshold_elapsed_since_highlight() {
@ -234,6 +281,12 @@ impl App {
} }
} }
fn all_clients_have_empty_groups(&self) -> bool {
self.all_client_grouped_panes
.values()
.all(|panes| panes.is_empty())
}
fn doherty_threshold_elapsed_since_highlight(&self) -> bool { fn doherty_threshold_elapsed_since_highlight(&self) -> bool {
self.highlighted_at self.highlighted_at
.map(|h| h.elapsed() >= std::time::Duration::from_millis(400)) .map(|h| h.elapsed() >= std::time::Duration::from_millis(400))
@ -266,7 +319,7 @@ impl App {
BareKey::Char('c') => self.close_grouped_panes(), BareKey::Char('c') => self.close_grouped_panes(),
BareKey::Tab => self.next_coordinates(), BareKey::Tab => self.next_coordinates(),
BareKey::Esc => { BareKey::Esc => {
self.ungroup_panes_in_zellij(&self.grouped_panes.clone()); self.ungroup_panes_in_zellij();
self.close_self(); self.close_self();
}, },
_ => return false, _ => return false,
@ -290,7 +343,7 @@ impl App {
fn render_shortcuts(&self, base_x: usize, base_y: usize) { fn render_shortcuts(&self, base_x: usize, base_y: usize) {
let mut running_y = base_y; let mut running_y = base_y;
print_text_with_coordinates(Self::group_actions_text().1, base_x, running_y, None, None); print_text_with_coordinates(self.group_actions_text().1, base_x, running_y, None, None);
running_y += 1; running_y += 1;
print_text_with_coordinates( print_text_with_coordinates(
@ -337,28 +390,28 @@ impl App {
self.execute_action_and_close(|pane_ids| { self.execute_action_and_close(|pane_ids| {
break_panes_to_new_tab(pane_ids, None, true); break_panes_to_new_tab(pane_ids, None, true);
}); });
self.ungroup_panes_in_zellij(&self.grouped_panes.clone()); self.ungroup_panes_in_zellij();
} }
pub fn stack_grouped_panes(&mut self) { pub fn stack_grouped_panes(&mut self) {
self.execute_action_and_close(|pane_ids| { self.execute_action_and_close(|pane_ids| {
stack_panes(pane_ids.to_vec()); stack_panes(pane_ids.to_vec());
}); });
self.ungroup_panes_in_zellij(&self.grouped_panes.clone()); self.ungroup_panes_in_zellij();
} }
pub fn float_grouped_panes(&mut self) { pub fn float_grouped_panes(&mut self) {
self.execute_action_and_close(|pane_ids| { self.execute_action_and_close(|pane_ids| {
float_multiple_panes(pane_ids.to_vec()); float_multiple_panes(pane_ids.to_vec());
}); });
self.ungroup_panes_in_zellij(&self.grouped_panes.clone()); self.ungroup_panes_in_zellij();
} }
pub fn embed_grouped_panes(&mut self) { pub fn embed_grouped_panes(&mut self) {
self.execute_action_and_close(|pane_ids| { self.execute_action_and_close(|pane_ids| {
embed_multiple_panes(pane_ids.to_vec()); embed_multiple_panes(pane_ids.to_vec());
}); });
self.ungroup_panes_in_zellij(&self.grouped_panes.clone()); self.ungroup_panes_in_zellij();
} }
pub fn break_grouped_panes_right(&mut self) { pub fn break_grouped_panes_right(&mut self) {
@ -385,7 +438,7 @@ impl App {
let pane_ids = self.grouped_panes.clone(); let pane_ids = self.grouped_panes.clone();
if own_tab_index > 0 { if own_tab_index > 0 {
break_panes_to_tab_with_index(&pane_ids, own_tab_index - 1, true); break_panes_to_tab_with_index(&pane_ids, own_tab_index.saturating_sub(1), true);
} else { } else {
break_panes_to_new_tab(&pane_ids, None, true); break_panes_to_new_tab(&pane_ids, None, true);
} }
@ -399,9 +452,16 @@ impl App {
}); });
} }
pub fn ungroup_panes_in_zellij(&mut self, pane_ids: &[PaneId]) { pub fn ungroup_panes_in_zellij(&mut self) {
group_and_ungroup_panes(vec![], pane_ids.to_vec()); let all_grouped_panes: Vec<PaneId> = self
.all_client_grouped_panes
.values()
.flat_map(|panes| panes.iter().cloned())
.collect();
let for_all_clients = true;
group_and_ungroup_panes(vec![], all_grouped_panes, for_all_clients);
} }
pub fn close_self(&mut self) { pub fn close_self(&mut self) {
self.closing = true; self.closing = true;
close_self(); close_self();

View file

@ -94,6 +94,35 @@ impl PaneGroups {
self.launch_plugin(screen_size, client_id); self.launch_plugin(screen_size, client_id);
} }
} }
pub fn group_and_ungroup_panes_for_all_clients(
&mut self,
pane_ids_to_group: Vec<PaneId>,
pane_ids_to_ungroup: Vec<PaneId>,
screen_size: Size,
) {
let previous_groups = self.clone_inner();
let mut should_launch = false;
let all_connected_clients: Vec<ClientId> = self.panes_in_group.keys().copied().collect();
for client_id in &all_connected_clients {
let client_pane_group = self
.panes_in_group
.entry(*client_id)
.or_insert_with(|| vec![]);
client_pane_group.append(&mut pane_ids_to_group.clone());
client_pane_group.retain(|p| !pane_ids_to_ungroup.contains(p));
if self.should_launch_plugin(&previous_groups, &client_id) {
should_launch = true;
}
}
if should_launch {
if let Some(first_client) = all_connected_clients.first() {
self.launch_plugin(screen_size, first_client);
}
}
}
pub fn override_groups_with(&mut self, new_pane_groups: HashMap<ClientId, Vec<PaneId>>) { pub fn override_groups_with(&mut self, new_pane_groups: HashMap<ClientId, Vec<PaneId>>) {
self.panes_in_group = new_pane_groups; self.panes_in_group = new_pane_groups;
} }

View file

@ -1315,7 +1315,7 @@ impl Grid {
// the state is corrupted // the state is corrupted
return; return;
} }
if scroll_region_bottom == self.height - 1 && scroll_region_top == 0 { if scroll_region_bottom == self.height.saturating_sub(1) && scroll_region_top == 0 {
if self.alternate_screen_state.is_none() { if self.alternate_screen_state.is_none() {
self.transfer_rows_to_lines_above(1); self.transfer_rows_to_lines_above(1);
} else { } else {
@ -1547,7 +1547,7 @@ impl Grid {
if y >= scroll_region_top && y <= scroll_region_bottom { if y >= scroll_region_top && y <= scroll_region_bottom {
self.cursor.y = std::cmp::min(scroll_region_bottom, y + y_offset); self.cursor.y = std::cmp::min(scroll_region_bottom, y + y_offset);
} else { } else {
self.cursor.y = std::cmp::min(self.height - 1, y + y_offset); self.cursor.y = std::cmp::min(self.height.saturating_sub(1), y + y_offset);
} }
self.pad_lines_until(self.cursor.y, pad_character.clone()); self.pad_lines_until(self.cursor.y, pad_character.clone());
self.pad_current_line_until(self.cursor.x, pad_character); self.pad_current_line_until(self.cursor.x, pad_character);

View file

@ -444,13 +444,16 @@ fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) {
close_plugin_after_replace, close_plugin_after_replace,
context, context,
), ),
PluginCommand::GroupAndUngroupPanes(panes_to_group, panes_to_ungroup) => { PluginCommand::GroupAndUngroupPanes(
group_and_ungroup_panes( panes_to_group,
panes_to_ungroup,
for_all_clients,
) => group_and_ungroup_panes(
env, env,
panes_to_group.into_iter().map(|p| p.into()).collect(), panes_to_group.into_iter().map(|p| p.into()).collect(),
panes_to_ungroup.into_iter().map(|p| p.into()).collect(), panes_to_ungroup.into_iter().map(|p| p.into()).collect(),
) for_all_clients,
}, ),
PluginCommand::HighlightAndUnhighlightPanes( PluginCommand::HighlightAndUnhighlightPanes(
panes_to_highlight, panes_to_highlight,
panes_to_unhighlight, panes_to_unhighlight,
@ -2270,12 +2273,14 @@ fn group_and_ungroup_panes(
env: &PluginEnv, env: &PluginEnv,
panes_to_group: Vec<PaneId>, panes_to_group: Vec<PaneId>,
panes_to_ungroup: Vec<PaneId>, panes_to_ungroup: Vec<PaneId>,
for_all_clients: bool,
) { ) {
let _ = env let _ = env
.senders .senders
.send_to_screen(ScreenInstruction::GroupAndUngroupPanes( .send_to_screen(ScreenInstruction::GroupAndUngroupPanes(
panes_to_group, panes_to_group,
panes_to_ungroup, panes_to_ungroup,
for_all_clients,
env.client_id, env.client_id,
)); ));
} }

View file

@ -412,7 +412,7 @@ pub enum ScreenInstruction {
ChangeFloatingPanesCoordinates(Vec<(PaneId, FloatingPaneCoordinates)>), ChangeFloatingPanesCoordinates(Vec<(PaneId, FloatingPaneCoordinates)>),
AddHighlightPaneFrameColorOverride(Vec<PaneId>, Option<String>), // Option<String> => optional AddHighlightPaneFrameColorOverride(Vec<PaneId>, Option<String>), // Option<String> => optional
// message // message
GroupAndUngroupPanes(Vec<PaneId>, Vec<PaneId>, ClientId), // panes_to_group, panes_to_ungroup GroupAndUngroupPanes(Vec<PaneId>, Vec<PaneId>, bool, ClientId), // panes_to_group, panes_to_ungroup, bool -> for all clients
HighlightAndUnhighlightPanes(Vec<PaneId>, Vec<PaneId>, ClientId), // panes_to_highlight, panes_to_unhighlight HighlightAndUnhighlightPanes(Vec<PaneId>, Vec<PaneId>, ClientId), // panes_to_highlight, panes_to_unhighlight
FloatMultiplePanes(Vec<PaneId>, ClientId), FloatMultiplePanes(Vec<PaneId>, ClientId),
EmbedMultiplePanes(Vec<PaneId>, ClientId), EmbedMultiplePanes(Vec<PaneId>, ClientId),
@ -2791,6 +2791,17 @@ impl Screen {
log::error!("Failed to find tab for root_pane_id: {:?}", root_pane_id); log::error!("Failed to find tab for root_pane_id: {:?}", root_pane_id);
return None; return None;
}; };
let root_pane_id_is_floating = self
.tabs
.get(&root_tab_id)
.map(|t| t.pane_id_is_floating(&root_pane_id))
.unwrap_or(false);
if root_pane_id_is_floating {
self.tabs.get_mut(&root_tab_id).map(|tab| {
let _ = tab.toggle_pane_embed_or_floating_for_pane_id(root_pane_id, None);
});
}
let mut panes_to_stack = vec![]; let mut panes_to_stack = vec![];
let target_tab_has_room_for_stack = self let target_tab_has_room_for_stack = self
@ -3118,8 +3129,19 @@ impl Screen {
&mut self, &mut self,
pane_ids_to_group: Vec<PaneId>, pane_ids_to_group: Vec<PaneId>,
pane_ids_to_ungroup: Vec<PaneId>, pane_ids_to_ungroup: Vec<PaneId>,
for_all_clients: bool,
client_id: ClientId, client_id: ClientId,
) { ) {
if for_all_clients {
{
let mut current_pane_group = self.current_pane_group.borrow_mut();
current_pane_group.group_and_ungroup_panes_for_all_clients(
pane_ids_to_group,
pane_ids_to_ungroup,
self.size,
);
}
} else {
{ {
let mut current_pane_group = self.current_pane_group.borrow_mut(); let mut current_pane_group = self.current_pane_group.borrow_mut();
current_pane_group.group_and_ungroup_panes( current_pane_group.group_and_ungroup_panes(
@ -3129,6 +3151,7 @@ impl Screen {
&client_id, &client_id,
); );
} }
}
self.retain_only_existing_panes_in_pane_groups(); self.retain_only_existing_panes_in_pane_groups();
let _ = self.log_and_report_session_state(); let _ = self.log_and_report_session_state();
} }
@ -5495,9 +5518,15 @@ pub(crate) fn screen_thread_main(
ScreenInstruction::GroupAndUngroupPanes( ScreenInstruction::GroupAndUngroupPanes(
pane_ids_to_group, pane_ids_to_group,
pane_ids_to_ungroup, pane_ids_to_ungroup,
for_all_clients,
client_id, client_id,
) => { ) => {
screen.group_and_ungroup_panes(pane_ids_to_group, pane_ids_to_ungroup, client_id); screen.group_and_ungroup_panes(
pane_ids_to_group,
pane_ids_to_ungroup,
for_all_clients,
client_id,
);
let _ = screen.log_and_report_session_state(); let _ = screen.log_and_report_session_state();
}, },
ScreenInstruction::TogglePaneInGroup(client_id) => { ScreenInstruction::TogglePaneInGroup(client_id) => {

View file

@ -1316,9 +1316,16 @@ pub fn stop_sharing_current_session() {
unsafe { host_run_plugin_command() }; unsafe { host_run_plugin_command() };
} }
pub fn group_and_ungroup_panes(pane_ids_to_group: Vec<PaneId>, pane_ids_to_ungroup: Vec<PaneId>) { pub fn group_and_ungroup_panes(
let plugin_command = pane_ids_to_group: Vec<PaneId>,
PluginCommand::GroupAndUngroupPanes(pane_ids_to_group, pane_ids_to_ungroup); pane_ids_to_ungroup: Vec<PaneId>,
for_all_clients: bool,
) {
let plugin_command = PluginCommand::GroupAndUngroupPanes(
pane_ids_to_group,
pane_ids_to_ungroup,
for_all_clients,
);
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
object_to_stdout(&protobuf_plugin_command.encode_to_vec()); object_to_stdout(&protobuf_plugin_command.encode_to_vec());
unsafe { host_run_plugin_command() }; unsafe { host_run_plugin_command() };

View file

@ -292,6 +292,8 @@ pub struct GroupAndUngroupPanesPayload {
pub pane_ids_to_group: ::prost::alloc::vec::Vec<PaneId>, pub pane_ids_to_group: ::prost::alloc::vec::Vec<PaneId>,
#[prost(message, repeated, tag="2")] #[prost(message, repeated, tag="2")]
pub pane_ids_to_ungroup: ::prost::alloc::vec::Vec<PaneId>, pub pane_ids_to_ungroup: ::prost::alloc::vec::Vec<PaneId>,
#[prost(bool, tag="3")]
pub for_all_clients: bool,
} }
#[allow(clippy::derive_partial_eq_without_eq)] #[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)] #[derive(Clone, PartialEq, ::prost::Message)]

View file

@ -2519,7 +2519,8 @@ pub enum PluginCommand {
ShareCurrentSession, ShareCurrentSession,
StopSharingCurrentSession, StopSharingCurrentSession,
OpenFileInPlaceOfPlugin(FileToOpen, bool, Context), // bool -> close_plugin_after_replace OpenFileInPlaceOfPlugin(FileToOpen, bool, Context), // bool -> close_plugin_after_replace
GroupAndUngroupPanes(Vec<PaneId>, Vec<PaneId>), // panes to group, panes to ungroup GroupAndUngroupPanes(Vec<PaneId>, Vec<PaneId>, bool), // panes to group, panes to ungroup,
// bool -> for all clients
HighlightAndUnhighlightPanes(Vec<PaneId>, Vec<PaneId>), // panes to highlight, panes to HighlightAndUnhighlightPanes(Vec<PaneId>, Vec<PaneId>), // panes to highlight, panes to
// unhighlight // unhighlight
CloseMultiplePanes(Vec<PaneId>), CloseMultiplePanes(Vec<PaneId>),

View file

@ -316,6 +316,7 @@ message HighlightAndUnhighlightPanesPayload {
message GroupAndUngroupPanesPayload { message GroupAndUngroupPanesPayload {
repeated PaneId pane_ids_to_group = 1; repeated PaneId pane_ids_to_group = 1;
repeated PaneId pane_ids_to_ungroup = 2; repeated PaneId pane_ids_to_ungroup = 2;
bool for_all_clients = 3;
} }
message OpenFileInPlaceOfPluginPayload { message OpenFileInPlaceOfPluginPayload {

View file

@ -1591,6 +1591,7 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
.into_iter() .into_iter()
.filter_map(|p| p.try_into().ok()) .filter_map(|p| p.try_into().ok())
.collect(), .collect(),
group_and_ungroup_panes_payload.for_all_clients,
)) ))
}, },
_ => Err("Mismatched payload for GroupAndUngroupPanes"), _ => Err("Mismatched payload for GroupAndUngroupPanes"),
@ -2750,8 +2751,11 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
}, },
)), )),
}), }),
PluginCommand::GroupAndUngroupPanes(panes_to_group, panes_to_ungroup) => { PluginCommand::GroupAndUngroupPanes(
Ok(ProtobufPluginCommand { panes_to_group,
panes_to_ungroup,
for_all_clients,
) => Ok(ProtobufPluginCommand {
name: CommandName::GroupAndUngroupPanes as i32, name: CommandName::GroupAndUngroupPanes as i32,
payload: Some(Payload::GroupAndUngroupPanesPayload( payload: Some(Payload::GroupAndUngroupPanesPayload(
GroupAndUngroupPanesPayload { GroupAndUngroupPanesPayload {
@ -2763,10 +2767,10 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
.iter() .iter()
.filter_map(|&p| p.try_into().ok()) .filter_map(|&p| p.try_into().ok())
.collect(), .collect(),
for_all_clients,
}, },
)), )),
}) }),
},
PluginCommand::StartWebServer => Ok(ProtobufPluginCommand { PluginCommand::StartWebServer => Ok(ProtobufPluginCommand {
name: CommandName::StartWebServer as i32, name: CommandName::StartWebServer as i32,
payload: None, payload: None,