diff --git a/Cargo.lock b/Cargo.lock index 83298ba1..47f3b7e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2231,6 +2231,14 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "multiple-select" +version = "0.1.0" +dependencies = [ + "fuzzy-matcher", + "zellij-tile", +] + [[package]] name = "names" version = "0.14.0" diff --git a/Cargo.toml b/Cargo.toml index d3ffdaf9..7b25ca0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ members = [ "default-plugins/configuration", "default-plugins/plugin-manager", "default-plugins/about", + "default-plugins/multiple-select", "zellij-client", "zellij-server", "zellij-utils", diff --git a/default-plugins/compact-bar/src/line.rs b/default-plugins/compact-bar/src/line.rs index 5d50d527..43431446 100644 --- a/default-plugins/compact-bar/src/line.rs +++ b/default-plugins/compact-bar/src/line.rs @@ -2,6 +2,7 @@ use ansi_term::ANSIStrings; use unicode_width::UnicodeWidthStr; use crate::{LinePart, ARROW_SEPARATOR}; +use zellij_tile::prelude::actions::Action; use zellij_tile::prelude::*; use zellij_tile_utils::style; @@ -251,6 +252,8 @@ pub fn tab_line( mode: InputMode, active_swap_layout_name: &Option, is_swap_layout_dirty: bool, + mode_info: &ModeInfo, + grouped_pane_count: Option, ) -> Vec { let mut tabs_after_active = all_tabs.split_off(active_tab_index); let mut tabs_before_active = all_tabs; @@ -286,15 +289,21 @@ pub fn tab_line( if current_title_len < cols { let mut remaining_space = cols - current_title_len; let remaining_bg = palette.text_unselected.background; - if let Some(swap_layout_status) = swap_layout_status( - remaining_space, - active_swap_layout_name, - is_swap_layout_dirty, - mode, - &palette, - tab_separator(capabilities), - ) { - remaining_space -= swap_layout_status.len; + let right_side_component = match grouped_pane_count { + Some(grouped_pane_count) => { + render_group_controls(mode_info, grouped_pane_count, remaining_space) + }, + None => swap_layout_status( + remaining_space, + active_swap_layout_name, + is_swap_layout_dirty, + mode, + &palette, + tab_separator(capabilities), + ), + }; + if let Some(right_side_component) = right_side_component { + remaining_space -= right_side_component.len; let mut buffer = String::new(); for _ in 0..remaining_space { buffer.push_str(&style!(remaining_bg, remaining_bg).paint(" ").to_string()); @@ -304,7 +313,7 @@ pub fn tab_line( len: remaining_space, tab_index: None, }); - prefix.push(swap_layout_status); + prefix.push(right_side_component); } } @@ -373,3 +382,274 @@ fn swap_layout_status( None => None, } } + +fn render_group_controls( + help: &ModeInfo, + grouped_pane_count: usize, + max_len: usize, +) -> Option { + let currently_marking_group = help.currently_marking_pane_group.unwrap_or(false); + let keymap = help.get_mode_keybinds(); + let (common_modifiers, multiple_select_key, pane_group_toggle_key, group_mark_toggle_key) = { + let multiple_select_key = multiple_select_key(&keymap); + let pane_group_toggle_key = single_action_key(&keymap, &[Action::TogglePaneInGroup]); + let group_mark_toggle_key = single_action_key(&keymap, &[Action::ToggleGroupMarking]); + let common_modifiers = get_common_modifiers( + vec![ + multiple_select_key.iter().next(), + pane_group_toggle_key.iter().next(), + group_mark_toggle_key.iter().next(), + ] + .into_iter() + .filter_map(|k| k) + .collect(), + ); + let multiple_select_key: Vec = multiple_select_key + .iter() + .map(|k| k.strip_common_modifiers(&common_modifiers)) + .collect(); + let pane_group_toggle_key: Vec = pane_group_toggle_key + .iter() + .map(|k| k.strip_common_modifiers(&common_modifiers)) + .collect(); + let group_mark_toggle_key: Vec = group_mark_toggle_key + .iter() + .map(|k| k.strip_common_modifiers(&common_modifiers)) + .collect(); + ( + common_modifiers, + multiple_select_key, + pane_group_toggle_key, + group_mark_toggle_key, + ) + }; + let multiple_select_key = multiple_select_key + .iter() + .next() + .map(|key| format!("{}", key)) + .unwrap_or("UNBOUND".to_owned()); + let pane_group_toggle_key = pane_group_toggle_key + .iter() + .next() + .map(|key| format!("{}", key)) + .unwrap_or("UNBOUND".to_owned()); + let group_mark_toggle_key = group_mark_toggle_key + .iter() + .next() + .map(|key| format!("{}", key)) + .unwrap_or("UNBOUND".to_owned()); + let background = help.style.colors.text_unselected.background; + let foreground = help.style.colors.text_unselected.base; + let superkey_prefix_style = style!(foreground, background).bold(); + let common_modifier_text = if common_modifiers.is_empty() { + "".to_owned() + } else { + format!( + "{} + ", + common_modifiers + .iter() + .map(|c| c.to_string()) + .collect::>() + .join("-") + ) + }; + + // full + let full_selected_panes_text = if common_modifier_text.is_empty() { + format!("{} SELECTED PANES", grouped_pane_count) + } else { + format!("{} SELECTED PANES |", grouped_pane_count) + }; + let full_group_actions_text = format!("<{}> Group Actions", &multiple_select_key); + let full_toggle_group_text = format!("<{}> Toggle Group", &pane_group_toggle_key); + let full_group_mark_toggle_text = format!("<{}> Follow Focus", &group_mark_toggle_key); + let ribbon_paddings_len = 12; + let full_controls_line_len = full_selected_panes_text.chars().count() + + 1 + + common_modifier_text.chars().count() + + full_group_actions_text.chars().count() + + full_toggle_group_text.chars().count() + + full_group_mark_toggle_text.chars().count() + + ribbon_paddings_len + + 1; // 1 for the end padding + + // medium + let medium_selected_panes_text = if common_modifier_text.is_empty() { + format!("{} SELECTED", grouped_pane_count) + } else { + format!("{} SELECTED |", grouped_pane_count) + }; + let medium_group_actions_text = format!("<{}> Actions", &multiple_select_key); + let medium_toggle_group_text = format!("<{}> Toggle", &pane_group_toggle_key); + let medium_group_mark_toggle_text = format!("<{}> Follow", &group_mark_toggle_key); + let ribbon_paddings_len = 12; + let medium_controls_line_len = medium_selected_panes_text.chars().count() + + 1 + + common_modifier_text.chars().count() + + medium_group_actions_text.chars().count() + + medium_toggle_group_text.chars().count() + + medium_group_mark_toggle_text.chars().count() + + ribbon_paddings_len + + 1; // 1 for the end padding + + // short + let short_selected_panes_text = if common_modifier_text.is_empty() { + format!("{} SELECTED", grouped_pane_count) + } else { + format!("{} SELECTED |", grouped_pane_count) + }; + let short_group_actions_text = format!("<{}>", &multiple_select_key); + let short_toggle_group_text = format!("<{}>", &pane_group_toggle_key); + let short_group_mark_toggle_text = format!("<{}>", &group_mark_toggle_key); + let color_emphasis_range_end = if common_modifier_text.is_empty() { + 0 + } else { + 2 + }; + let ribbon_paddings_len = 12; + let short_controls_line_len = short_selected_panes_text.chars().count() + + 1 + + common_modifier_text.chars().count() + + short_group_actions_text.chars().count() + + short_toggle_group_text.chars().count() + + short_group_mark_toggle_text.chars().count() + + ribbon_paddings_len + + 1; // 1 for the end padding + + let ( + selected_panes_text, + group_actions_text, + toggle_group_text, + group_mark_toggle_text, + controls_line_len, + ) = if max_len >= full_controls_line_len { + ( + full_selected_panes_text, + full_group_actions_text, + full_toggle_group_text, + full_group_mark_toggle_text, + full_controls_line_len, + ) + } else if max_len >= medium_controls_line_len { + ( + medium_selected_panes_text, + medium_group_actions_text, + medium_toggle_group_text, + medium_group_mark_toggle_text, + medium_controls_line_len, + ) + } else if max_len >= short_controls_line_len { + ( + short_selected_panes_text, + short_group_actions_text, + short_toggle_group_text, + short_group_mark_toggle_text, + short_controls_line_len, + ) + } else { + return None; + }; + let selected_panes = serialize_text( + &Text::new(&selected_panes_text) + .color_range( + 3, + ..selected_panes_text + .chars() + .count() + .saturating_sub(color_emphasis_range_end), + ) + .opaque(), + ); + let group_actions_ribbon = serialize_ribbon( + &Text::new(&group_actions_text).color_range(0, 1..=multiple_select_key.chars().count()), + ); + let toggle_group_ribbon = serialize_ribbon( + &Text::new(&toggle_group_text).color_range(0, 1..=pane_group_toggle_key.chars().count()), + ); + let mut group_mark_toggle_ribbon = Text::new(&group_mark_toggle_text) + .color_range(0, 1..=group_mark_toggle_key.chars().count()); + if currently_marking_group { + group_mark_toggle_ribbon = group_mark_toggle_ribbon.selected(); + } + let group_mark_toggle_ribbon = serialize_ribbon(&group_mark_toggle_ribbon); + let controls_line = if common_modifiers.is_empty() { + format!( + "{} {}{}{}", + selected_panes, group_actions_ribbon, toggle_group_ribbon, group_mark_toggle_ribbon + ) + } else { + let common_modifier = + serialize_text(&Text::new(&common_modifier_text).color_range(0, ..).opaque()); + format!( + "{} {}{}{}{}", + selected_panes, + common_modifier, + group_actions_ribbon, + toggle_group_ribbon, + group_mark_toggle_ribbon + ) + }; + let remaining_space = max_len.saturating_sub(controls_line_len); + let mut padding = String::new(); + let mut padding_len = 0; + for _ in 0..remaining_space { + padding.push_str(&ANSIStrings(&[superkey_prefix_style.paint(" ")]).to_string()); + padding_len += 1; + } + Some(LinePart { + part: format!("{}{}", padding, controls_line), + len: controls_line_len + padding_len, + tab_index: None, + }) +} + +fn multiple_select_key(keymap: &[(KeyWithModifier, Vec)]) -> Vec { + let mut matching = keymap.iter().find_map(|(key, acvec)| { + let has_match = acvec + .iter() + .find(|a| a.launches_plugin("zellij:multiple-select")) // TODO: make this an alias + .is_some(); + if has_match { + Some(key.clone()) + } else { + None + } + }); + if let Some(matching) = matching.take() { + vec![matching] + } else { + vec![] + } +} + +fn single_action_key( + keymap: &[(KeyWithModifier, Vec)], + action: &[Action], +) -> Vec { + let mut matching = keymap.iter().find_map(|(key, acvec)| { + if acvec.iter().next() == action.iter().next() { + Some(key.clone()) + } else { + None + } + }); + if let Some(matching) = matching.take() { + vec![matching] + } else { + vec![] + } +} + +fn get_common_modifiers(mut keyvec: Vec<&KeyWithModifier>) -> Vec { + if keyvec.is_empty() { + return vec![]; + } + let mut common_modifiers = keyvec.pop().unwrap().key_modifiers.clone(); + for key in keyvec { + common_modifiers = common_modifiers + .intersection(&key.key_modifiers) + .cloned() + .collect(); + } + common_modifiers.into_iter().collect() +} diff --git a/default-plugins/compact-bar/src/main.rs b/default-plugins/compact-bar/src/main.rs index 3775b12f..d4a43ab8 100644 --- a/default-plugins/compact-bar/src/main.rs +++ b/default-plugins/compact-bar/src/main.rs @@ -26,6 +26,8 @@ struct State { tab_line: Vec, text_copy_destination: Option, display_system_clipboard_failure: bool, + own_client_id: Option, + grouped_panes_count: Option, } static ARROW_SEPARATOR: &str = ""; @@ -42,7 +44,9 @@ impl ZellijPlugin for State { EventType::CopyToClipboard, EventType::InputReceived, EventType::SystemClipboardFailure, + EventType::PaneUpdate, ]); + self.own_client_id = Some(get_plugin_ids().client_id); } fn update(&mut self, event: Event) -> bool { @@ -108,6 +112,28 @@ impl ZellijPlugin for State { self.text_copy_destination = None; self.display_system_clipboard_failure = false; }, + Event::PaneUpdate(pane_manifest) => { + if let Some(own_client_id) = self.own_client_id { + let mut grouped_panes_count = 0; + for (_tab_index, pane_infos) in pane_manifest.panes { + for pane_info in pane_infos { + let is_in_pane_group = + pane_info.index_in_pane_group.get(&own_client_id).is_some(); + if is_in_pane_group { + grouped_panes_count += 1; + } + } + } + if Some(grouped_panes_count) != self.grouped_panes_count { + if grouped_panes_count == 0 { + self.grouped_panes_count = None; + } else { + self.grouped_panes_count = Some(grouped_panes_count); + } + should_render = true; + } + } + }, _ => { eprintln!("Got unrecognized event: {:?}", event); }, @@ -181,6 +207,8 @@ impl ZellijPlugin for State { self.mode_info.mode, &active_swap_layout_name, is_swap_layout_dirty, + &self.mode_info, + self.grouped_panes_count, ); let output = self .tab_line diff --git a/default-plugins/configuration/src/presets.rs b/default-plugins/configuration/src/presets.rs index 41d7a79e..54713635 100644 --- a/default-plugins/configuration/src/presets.rs +++ b/default-plugins/configuration/src/presets.rs @@ -168,6 +168,14 @@ keybinds clear-defaults=true {{ bind "{secondary_modifier} -" {{ Resize "Decrease"; }} bind "{secondary_modifier} [" {{ PreviousSwapLayout; }} bind "{secondary_modifier} ]" {{ NextSwapLayout; }} + bind "{secondary_modifier} m" {{ + LaunchOrFocusPlugin "zellij:multiple-select" {{ + floating true + move_to_focused_tab true + }} + }} + bind "{secondary_modifier} p" {{ TogglePaneInGroup; }} + bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }} }} shared_except "locked" "renametab" "renamepane" {{ bind "Enter" {{ SwitchToMode "Locked"; }} @@ -392,6 +400,14 @@ keybinds clear-defaults=true {{ bind "{secondary_modifier} -" {{ Resize "Decrease"; }} bind "{secondary_modifier} [" {{ PreviousSwapLayout; }} bind "{secondary_modifier} ]" {{ NextSwapLayout; }} + bind "{secondary_modifier} m" {{ + LaunchOrFocusPlugin "zellij:multiple-select" {{ + floating true + move_to_focused_tab true + }} + }} + bind "{secondary_modifier} p" {{ TogglePaneInGroup; }} + bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }} }} shared_except "normal" "locked" {{ bind "Enter" "Esc" {{ SwitchToMode "Normal"; }} @@ -595,6 +611,14 @@ keybinds clear-defaults=true {{ bind "{secondary_modifier} -" {{ Resize "Decrease"; }} bind "{secondary_modifier} [" {{ PreviousSwapLayout; }} bind "{secondary_modifier} ]" {{ NextSwapLayout; }} + bind "{secondary_modifier} m" {{ + LaunchOrFocusPlugin "zellij:multiple-select" {{ + floating true + move_to_focused_tab true + }} + }} + bind "{secondary_modifier} p" {{ TogglePaneInGroup; }} + bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }} }} shared_except "normal" "locked" {{ bind "Enter" "Esc" {{ SwitchToMode "Normal"; }} @@ -1158,6 +1182,14 @@ keybinds clear-defaults=true {{ bind "{secondary_modifier} -" {{ Resize "Decrease"; }} bind "{secondary_modifier} [" {{ PreviousSwapLayout; }} bind "{secondary_modifier} ]" {{ NextSwapLayout; }} + bind "{secondary_modifier} m" {{ + LaunchOrFocusPlugin "zellij:multiple-select" {{ + floating true + move_to_focused_tab true + }} + }} + bind "{secondary_modifier} p" {{ TogglePaneInGroup; }} + bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }} }} shared_except "normal" "locked" {{ bind "Enter" "Esc" {{ SwitchToMode "Normal"; }} diff --git a/default-plugins/fixture-plugin-for-tests/src/main.rs b/default-plugins/fixture-plugin-for-tests/src/main.rs index 24f0f649..7f66fa4e 100644 --- a/default-plugins/fixture-plugin-for-tests/src/main.rs +++ b/default-plugins/fixture-plugin-for-tests/src/main.rs @@ -67,6 +67,7 @@ impl ZellijPlugin for State { EventType::FileSystemCreate, EventType::FileSystemUpdate, EventType::FileSystemDelete, + EventType::BeforeClose, ]); watch_filesystem(); } @@ -535,6 +536,10 @@ impl ZellijPlugin for State { self.received_payload = Some(payload.clone()); } }, + Event::BeforeClose => { + // this is just to assert something to make sure this event was triggered + highlight_and_unhighlight_panes(vec![PaneId::Terminal(1)], vec![PaneId::Plugin(1)]); + }, Event::SystemClipboardFailure => { // this is just to trigger the worker message post_message_to(PluginMessage { diff --git a/default-plugins/multiple-select/.cargo/config.toml b/default-plugins/multiple-select/.cargo/config.toml new file mode 100644 index 00000000..6b509f5b --- /dev/null +++ b/default-plugins/multiple-select/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-wasip1" diff --git a/default-plugins/multiple-select/.gitignore b/default-plugins/multiple-select/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/default-plugins/multiple-select/.gitignore @@ -0,0 +1 @@ +/target diff --git a/default-plugins/multiple-select/Cargo.toml b/default-plugins/multiple-select/Cargo.toml new file mode 100644 index 00000000..c5580ad4 --- /dev/null +++ b/default-plugins/multiple-select/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "multiple-select" +version = "0.1.0" +authors = ["Aram Drevekenin "] +edition = "2021" +license = "MIT" + +[dependencies] +zellij-tile = { path = "../../zellij-tile" } +fuzzy-matcher = "0.3.7" diff --git a/default-plugins/multiple-select/LICENSE.md b/default-plugins/multiple-select/LICENSE.md new file mode 120000 index 00000000..f0608a63 --- /dev/null +++ b/default-plugins/multiple-select/LICENSE.md @@ -0,0 +1 @@ +../../LICENSE.md \ No newline at end of file diff --git a/default-plugins/multiple-select/src/main.rs b/default-plugins/multiple-select/src/main.rs new file mode 100644 index 00000000..773fd6fb --- /dev/null +++ b/default-plugins/multiple-select/src/main.rs @@ -0,0 +1,212 @@ +pub mod state; +pub mod ui; + +use state::{MarkedIndex, VisibilityAndFocus}; +use std::collections::BTreeMap; +use ui::PaneItem; +use zellij_tile::prelude::*; + +#[derive(Debug, Default)] +pub struct App { + own_plugin_id: Option, + own_client_id: Option, + own_tab_index: Option, + total_tabs_in_session: Option, + search_string: String, + previous_search_string: String, // used eg. for the new tab title when breaking panes + left_side_panes: Vec, + right_side_panes: Vec, + search_results: Option>, + visibility_and_focus: VisibilityAndFocus, + marked_index: Option, +} + +register_plugin!(App); + +impl ZellijPlugin for App { + fn load(&mut self, _configuration: BTreeMap) { + subscribe(&[ + EventType::Key, + EventType::Mouse, + EventType::ModeUpdate, + EventType::RunCommandResult, + EventType::TabUpdate, + EventType::PaneUpdate, + EventType::FailedToWriteConfigToDisk, + EventType::ConfigWasWrittenToDisk, + EventType::BeforeClose, + ]); + let plugin_ids = get_plugin_ids(); + self.own_plugin_id = Some(plugin_ids.plugin_id); + self.own_client_id = Some(plugin_ids.client_id); + rename_plugin_pane(plugin_ids.plugin_id, "Multiple Select"); + } + fn update(&mut self, event: Event) -> bool { + let mut should_render = false; + match event { + Event::PaneUpdate(pane_manifest) => { + self.react_to_zellij_state_update(pane_manifest); + should_render = true; + }, + Event::Key(key) => { + match key.bare_key { + BareKey::Tab if key.has_no_modifiers() => { + self.visibility_and_focus.toggle_focus(); + self.marked_index = None; + self.update_highlighted_panes(); + should_render = true; + }, + BareKey::Char(character) + if key.has_no_modifiers() + && self.visibility_and_focus.left_side_is_focused() + && self.marked_index.is_none() => + { + self.search_string.push(character); + self.update_search_results(); + should_render = true; + }, + BareKey::Backspace + if key.has_no_modifiers() + && self.visibility_and_focus.left_side_is_focused() + && self.marked_index.is_none() => + { + self.search_string.pop(); + self.update_search_results(); + should_render = true; + }, + BareKey::Enter if key.has_no_modifiers() => { + if self.visibility_and_focus.left_side_is_focused() { + if let Some(marked_index) = self.marked_index.take() { + let keep_left_side_focused = false; + self.group_panes(marked_index, keep_left_side_focused); + } else { + match self.search_results.take() { + Some(search_results) => { + self.group_search_results(search_results) + }, + None => self.group_all_panes(), + } + self.handle_left_side_emptied(); + } + } + should_render = true; + }, + BareKey::Right + if key.has_no_modifiers() + && self.visibility_and_focus.left_side_is_focused() => + { + if let Some(marked_index) = self.marked_index.take() { + let keep_left_side_focused = true; + self.group_panes(marked_index, keep_left_side_focused); + should_render = true; + } + }, + BareKey::Left + if key.has_no_modifiers() + && self.visibility_and_focus.right_side_is_focused() => + { + if self.visibility_and_focus.right_side_is_focused() { + if let Some(marked_index) = self.marked_index.take() { + self.ungroup_panes(marked_index); + should_render = true; + } + } + }, + BareKey::Char('c') if key.has_modifiers(&[KeyModifier::Ctrl]) => { + if self.visibility_and_focus.right_side_is_focused() { + // this means we're in the selection panes part and we want to clear + // them + self.ungroup_all_panes(); + } else if self.visibility_and_focus.left_side_is_focused() { + if self.marked_index.is_some() { + self.marked_index = None; + self.update_highlighted_panes(); + } else { + self.ungroup_all_panes_and_close_self(); + } + } + should_render = true; + }, + BareKey::Down if key.has_no_modifiers() => { + self.move_marked_index_down(); + should_render = true; + }, + BareKey::Up if key.has_no_modifiers() => { + self.move_marked_index_up(); + should_render = true; + }, + BareKey::Char(' ') if key.has_no_modifiers() && self.marked_index.is_some() => { + self.mark_entry(); + should_render = true; + }, + BareKey::Char('b') + if key.has_no_modifiers() + && self.visibility_and_focus.right_side_is_focused() => + { + self.break_grouped_panes_to_new_tab(); + }, + BareKey::Char('s') + if key.has_no_modifiers() + && self.visibility_and_focus.right_side_is_focused() => + { + self.stack_grouped_panes(); + }, + BareKey::Char('f') + if key.has_no_modifiers() + && self.visibility_and_focus.right_side_is_focused() => + { + self.float_grouped_panes(); + }, + BareKey::Char('e') + if key.has_no_modifiers() + && self.visibility_and_focus.right_side_is_focused() => + { + self.embed_grouped_panes(); + }, + BareKey::Char('r') + if key.has_no_modifiers() + && self.visibility_and_focus.right_side_is_focused() => + { + self.break_grouped_panes_right(); + }, + BareKey::Char('l') + if key.has_no_modifiers() + && self.visibility_and_focus.right_side_is_focused() => + { + self.break_grouped_panes_left(); + }, + BareKey::Char('c') + if key.has_no_modifiers() + && self.visibility_and_focus.right_side_is_focused() => + { + self.close_grouped_panes(); + }, + _ => {}, + } + }, + Event::BeforeClose => { + self.unhighlight_all_panes(); + }, + _ => {}, + } + should_render + } + fn render(&mut self, rows: usize, cols: usize) { + self.render_close_shortcut(cols); + self.render_tab_shortcut(cols, rows); + match self.visibility_and_focus { + VisibilityAndFocus::OnlyLeftSideVisible => self.render_left_side(rows, cols, true), + VisibilityAndFocus::OnlyRightSideVisible => self.render_right_side(rows, cols, true), + VisibilityAndFocus::BothSidesVisibleLeftSideFocused => { + self.render_left_side(rows, cols, true); + self.render_right_side(rows, cols, false); + }, + VisibilityAndFocus::BothSidesVisibleRightSideFocused => { + self.render_left_side(rows, cols, false); + self.render_right_side(rows, cols, true); + }, + } + self.render_focus_boundary(rows, cols); + self.render_help_line(rows, cols); + } +} diff --git a/default-plugins/multiple-select/src/state.rs b/default-plugins/multiple-select/src/state.rs new file mode 100644 index 00000000..3bf863f8 --- /dev/null +++ b/default-plugins/multiple-select/src/state.rs @@ -0,0 +1,634 @@ +use crate::{ui::PaneItem, App}; +use std::collections::{BTreeMap, BTreeSet, HashSet}; + +use fuzzy_matcher::skim::SkimMatcherV2; +use fuzzy_matcher::FuzzyMatcher; + +use zellij_tile::prelude::*; + +#[derive(Debug, Default)] +pub struct MarkedIndex { + pub main_index: usize, + pub additional_indices: HashSet, +} + +impl MarkedIndex { + pub fn new(main_index: usize) -> Self { + MarkedIndex { + main_index, + additional_indices: HashSet::new(), + } + } +} + +impl MarkedIndex { + pub fn toggle_additional_mark(&mut self) { + if self.additional_indices.contains(&self.main_index) { + self.additional_indices.retain(|a| a != &self.main_index); + } else { + self.additional_indices.insert(self.main_index); + } + } +} + +#[derive(Debug)] +pub enum VisibilityAndFocus { + OnlyLeftSideVisible, + OnlyRightSideVisible, + BothSidesVisibleLeftSideFocused, + BothSidesVisibleRightSideFocused, +} + +impl Default for VisibilityAndFocus { + fn default() -> Self { + VisibilityAndFocus::OnlyLeftSideVisible + } +} + +impl VisibilityAndFocus { + pub fn only_left_side_is_focused(&self) -> bool { + match self { + VisibilityAndFocus::OnlyLeftSideVisible => true, + _ => false, + } + } + pub fn left_side_is_focused(&self) -> bool { + match self { + VisibilityAndFocus::OnlyLeftSideVisible + | VisibilityAndFocus::BothSidesVisibleLeftSideFocused => true, + _ => false, + } + } + pub fn right_side_is_focused(&self) -> bool { + match self { + VisibilityAndFocus::OnlyRightSideVisible + | VisibilityAndFocus::BothSidesVisibleRightSideFocused => true, + _ => false, + } + } + pub fn hide_left_side(&mut self) { + *self = VisibilityAndFocus::OnlyRightSideVisible + } + pub fn hide_right_side(&mut self) { + *self = VisibilityAndFocus::OnlyLeftSideVisible + } + pub fn focus_right_side(&mut self) { + *self = VisibilityAndFocus::BothSidesVisibleRightSideFocused + } + pub fn toggle_focus(&mut self) { + match self { + VisibilityAndFocus::BothSidesVisibleLeftSideFocused => { + *self = VisibilityAndFocus::BothSidesVisibleRightSideFocused + }, + VisibilityAndFocus::BothSidesVisibleRightSideFocused => { + *self = VisibilityAndFocus::BothSidesVisibleLeftSideFocused + }, + VisibilityAndFocus::OnlyLeftSideVisible => { + *self = VisibilityAndFocus::BothSidesVisibleRightSideFocused + }, + VisibilityAndFocus::OnlyRightSideVisible => { + *self = VisibilityAndFocus::BothSidesVisibleLeftSideFocused + }, + } + } + pub fn show_both_sides(&mut self) { + match self { + VisibilityAndFocus::OnlyLeftSideVisible => { + *self = VisibilityAndFocus::BothSidesVisibleLeftSideFocused + }, + VisibilityAndFocus::OnlyRightSideVisible => { + *self = VisibilityAndFocus::BothSidesVisibleRightSideFocused + }, + VisibilityAndFocus::BothSidesVisibleLeftSideFocused + | VisibilityAndFocus::BothSidesVisibleRightSideFocused => { + // no-op + }, + } + } +} + +impl App { + pub fn react_to_zellij_state_update(&mut self, pane_manifest: PaneManifest) { + let is_first_update = self.right_side_panes.is_empty() && self.left_side_panes.is_empty(); + let panes_on_the_left_before = self.left_side_panes.len(); + let panes_on_the_right_before = self.right_side_panes.len(); + self.update_tab_info(&pane_manifest); + self.update_panes(pane_manifest); + if is_first_update && !self.right_side_panes.is_empty() { + // in this case, the plugin was started with an existing group + // most likely, the user wants to perform operations just on this group, so we + // only show the group, giving the option to add more panes + self.visibility_and_focus.hide_left_side(); + } + let pane_count_changed = (panes_on_the_left_before != self.left_side_panes.len()) + || (panes_on_the_right_before != self.right_side_panes.len()); + if !is_first_update && pane_count_changed { + let has_panes_on_the_right = !self.right_side_panes.is_empty(); + let has_panes_on_the_left = !self.left_side_panes.is_empty(); + if has_panes_on_the_right && has_panes_on_the_left { + self.visibility_and_focus.show_both_sides(); + } else if has_panes_on_the_right { + self.visibility_and_focus.hide_left_side(); + } else if has_panes_on_the_left { + self.visibility_and_focus.hide_right_side(); + } + } + } + pub fn update_panes(&mut self, pane_manifest: PaneManifest) { + let mut all_panes = BTreeMap::new(); + for (_tab_index, pane_infos) in pane_manifest.panes { + for pane_info in pane_infos { + if pane_info.is_selectable { + if pane_info.is_plugin { + all_panes.insert(PaneId::Plugin(pane_info.id), pane_info); + } else { + all_panes.insert(PaneId::Terminal(pane_info.id), pane_info); + } + } + } + } + self.left_side_panes + .retain(|p| all_panes.contains_key(&p.id)); + self.right_side_panes + .retain(|p| all_panes.contains_key(&p.id)); + let mut new_selected_panes: BTreeMap = BTreeMap::new(); // usize -> index_in_pane_group + for (pane_id, pane) in all_panes.into_iter() { + let is_known = self + .left_side_panes + .iter() + .find(|p| p.id == pane_id) + .is_some() + || self + .right_side_panes + .iter() + .find(|p| p.id == pane_id) + .is_some(); + let index_in_pane_group = self + .own_client_id + .and_then(|own_client_id| pane.index_in_pane_group.get(&own_client_id)); + let is_grouped_for_own_client_id = index_in_pane_group.is_some(); + if !is_known { + if is_grouped_for_own_client_id { + if let Some(index_in_pane_group) = index_in_pane_group { + // we do this rather than adding them directly to right_side_panes so that + // we can make sure they're in the same order as the group is so that + // things like stacking order will do the right thing + new_selected_panes.insert( + *index_in_pane_group, + PaneItem { + text: pane.title, + id: pane_id, + color_indices: vec![], + }, + ); + } + } else { + self.left_side_panes.push(PaneItem { + text: pane.title, + id: pane_id, + color_indices: vec![], + }); + } + } else { + if is_grouped_for_own_client_id { + if let Some(position) = + self.left_side_panes.iter().position(|p| p.id == pane_id) + { + // pane was added to a pane group outside the plugin (eg. with mouse selection) + let mut pane = self.left_side_panes.remove(position); + pane.clear(); + self.right_side_panes.push(pane); + } + } else { + if let Some(position) = + self.right_side_panes.iter().position(|p| p.id == pane_id) + { + // pane was removed from a pane group outside the plugin (eg. with mouse selection) + let mut pane = self.right_side_panes.remove(position); + pane.clear(); + self.left_side_panes.push(pane); + } + } + } + } + for (_index_in_pane_group, pane_item) in new_selected_panes.into_iter() { + self.right_side_panes.push(pane_item); + } + } + pub fn update_tab_info(&mut self, pane_manifest: &PaneManifest) { + for (tab_index, pane_infos) in &pane_manifest.panes { + for pane_info in pane_infos { + if pane_info.is_plugin && Some(pane_info.id) == self.own_plugin_id { + self.own_tab_index = Some(*tab_index); + } + } + } + self.total_tabs_in_session = Some(pane_manifest.panes.keys().count()); + } + pub fn update_search_results(&mut self) { + let mut matches = vec![]; + let matcher = SkimMatcherV2::default().use_cache(true); + for pane_item in &self.left_side_panes { + if let Some((score, indices)) = + matcher.fuzzy_indices(&pane_item.text, &self.search_string) + { + let mut pane_item = pane_item.clone(); + pane_item.color_indices = indices; + matches.push((score, pane_item)); + } + } + matches.sort_by(|(a_score, _a), (b_score, _b)| b_score.cmp(&a_score)); + if self.search_string.is_empty() { + self.search_results = None; + } else { + self.search_results = Some( + matches + .into_iter() + .map(|(_s, pane_item)| pane_item) + .collect(), + ); + } + } + pub fn group_panes_in_zellij(&mut self, pane_ids: Vec) { + group_and_ungroup_panes(pane_ids, vec![]); + } + pub fn ungroup_panes_in_zellij(&mut self, pane_ids: Vec) { + group_and_ungroup_panes(vec![], pane_ids); + } + pub fn update_highlighted_panes(&self) { + let mut pane_ids_to_highlight = vec![]; + let mut pane_ids_to_unhighlight = vec![]; + if let Some(marked_index) = &self.marked_index { + if self.visibility_and_focus.left_side_is_focused() { + if let Some(main_index_pane_id) = self + .search_results + .as_ref() + .and_then(|s| s.get(marked_index.main_index)) + .or_else(|| self.left_side_panes.get(marked_index.main_index)) + .map(|p| p.id) + { + pane_ids_to_highlight.push(main_index_pane_id); + } + for index in &marked_index.additional_indices { + if let Some(pane_id) = self + .search_results + .as_ref() + .and_then(|s| s.get(*index)) + .or_else(|| self.left_side_panes.get(*index)) + .map(|p| p.id) + { + pane_ids_to_highlight.push(pane_id); + } + } + } else { + if let Some(main_index_pane_id) = self + .right_side_panes + .get(marked_index.main_index) + .map(|p| p.id) + { + pane_ids_to_highlight.push(main_index_pane_id); + } + for index in &marked_index.additional_indices { + if let Some(pane_id) = self.right_side_panes.get(*index).map(|p| p.id) { + pane_ids_to_highlight.push(pane_id); + } + } + } + } + for pane in &self.left_side_panes { + if !pane_ids_to_highlight.contains(&pane.id) { + pane_ids_to_unhighlight.push(pane.id); + } + } + for pane in &self.right_side_panes { + if !pane_ids_to_highlight.contains(&pane.id) { + pane_ids_to_unhighlight.push(pane.id); + } + } + highlight_and_unhighlight_panes(pane_ids_to_highlight, pane_ids_to_unhighlight); + } + pub fn unhighlight_all_panes(&mut self) { + let mut pane_ids_to_unhighlight = HashSet::new(); + for pane_item in &self.left_side_panes { + pane_ids_to_unhighlight.insert(pane_item.id); + } + for pane_item in &self.right_side_panes { + pane_ids_to_unhighlight.insert(pane_item.id); + } + highlight_and_unhighlight_panes(vec![], pane_ids_to_unhighlight.into_iter().collect()); + } + pub fn ungroup_all_panes(&mut self) { + let mut unselected_panes = vec![]; + for pane_item in self.right_side_panes.iter_mut() { + pane_item.clear(); + unselected_panes.push(pane_item.id); + } + self.left_side_panes.append(&mut self.right_side_panes); + self.ungroup_panes_in_zellij(unselected_panes); + self.visibility_and_focus.hide_right_side(); + self.marked_index = None; + } + pub fn ungroup_all_panes_and_close_self(&mut self) { + let mut pane_ids_to_ungroup = HashSet::new(); + for pane_item in &self.left_side_panes { + pane_ids_to_ungroup.insert(pane_item.id); + } + for pane_item in &self.right_side_panes { + pane_ids_to_ungroup.insert(pane_item.id); + } + group_and_ungroup_panes(vec![], pane_ids_to_ungroup.into_iter().collect()); + close_self(); + } + pub fn group_panes(&mut self, mut marked_index: MarkedIndex, keep_left_side_focused: bool) { + let mut all_selected_indices: BTreeSet = + marked_index.additional_indices.drain().collect(); + all_selected_indices.insert(marked_index.main_index); + + // reverse so that the indices will remain consistent while + // removing + let mut selected_panes = vec![]; + for index in all_selected_indices.iter().rev() { + let index = self + .search_results + .as_mut() + .and_then(|search_results| { + if search_results.len() > *index { + Some(search_results.remove(*index)) + } else { + None + } + }) + .and_then(|selected_search_result| { + self.left_side_panes + .iter() + .position(|p| p.id == selected_search_result.id) + }) + .unwrap_or(*index); + if self.left_side_panes.len() > index { + let selected_pane = self.left_side_panes.remove(index); + selected_panes.push(selected_pane); + } + } + let pane_ids_to_make_selected: Vec = selected_panes.iter().map(|p| p.id).collect(); + self.right_side_panes + .append(&mut selected_panes.into_iter().rev().collect()); + + let displayed_list_len = match self.search_results.as_ref() { + Some(search_results) => search_results.len(), + None => self.left_side_panes.len(), + }; + + if displayed_list_len == 0 { + self.handle_left_side_emptied(); + } else if keep_left_side_focused { + if marked_index.main_index > displayed_list_len.saturating_sub(1) { + self.marked_index = Some(MarkedIndex::new(displayed_list_len.saturating_sub(1))); + } else { + self.marked_index = Some(marked_index); + } + self.visibility_and_focus.show_both_sides(); + } else { + self.visibility_and_focus.focus_right_side(); + } + + self.group_panes_in_zellij(pane_ids_to_make_selected); + self.update_highlighted_panes(); + } + pub fn ungroup_panes(&mut self, mut marked_index: MarkedIndex) { + let mut all_selected_indices: BTreeSet = + marked_index.additional_indices.drain().collect(); + all_selected_indices.insert(marked_index.main_index); + + // reverse so that the indices will remain consistent while + // removing + let mut selected_panes = vec![]; + for index in all_selected_indices.iter().rev() { + if self.right_side_panes.len() > *index { + let mut selected_pane = self.right_side_panes.remove(*index); + selected_pane.clear(); + selected_panes.push(selected_pane); + } + } + self.ungroup_panes_in_zellij(selected_panes.iter().map(|p| p.id).collect()); + self.left_side_panes + .append(&mut selected_panes.into_iter().rev().collect()); + + if self.right_side_panes.is_empty() { + self.marked_index = None; + self.visibility_and_focus.hide_right_side(); + } else if marked_index.main_index > self.right_side_panes.len().saturating_sub(1) { + self.marked_index = Some(MarkedIndex::new( + self.right_side_panes.len().saturating_sub(1), + )); + self.visibility_and_focus.show_both_sides(); + } else { + self.marked_index = Some(marked_index); + self.visibility_and_focus.show_both_sides(); + } + self.update_highlighted_panes(); + } + pub fn group_search_results(&mut self, search_results: Vec) { + let mut pane_ids_to_make_selected = vec![]; + for search_result in search_results { + let pane_id = search_result.id; + pane_ids_to_make_selected.push(pane_id); + self.left_side_panes.retain(|p| p.id != pane_id); + self.right_side_panes.push(search_result); + } + self.group_panes_in_zellij(pane_ids_to_make_selected); + } + pub fn group_all_panes(&mut self) { + let pane_ids_to_make_selected: Vec = + self.left_side_panes.iter().map(|p| p.id).collect(); + self.right_side_panes.append(&mut self.left_side_panes); + self.group_panes_in_zellij(pane_ids_to_make_selected); + } + pub fn handle_left_side_emptied(&mut self) { + self.visibility_and_focus.hide_left_side(); + self.previous_search_string = self.search_string.drain(..).collect(); + self.marked_index = None; + self.search_results = None; + self.update_highlighted_panes(); + } + pub fn move_marked_index_down(&mut self) { + match self.marked_index.as_mut() { + Some(marked_index) => { + let is_searching = self.search_results.is_some(); + let search_result_count = + self.search_results.as_ref().map(|s| s.len()).unwrap_or(0); + if self.visibility_and_focus.left_side_is_focused() + && is_searching + && marked_index.main_index == search_result_count.saturating_sub(1) + { + marked_index.main_index = 0; + } else if self.visibility_and_focus.left_side_is_focused() + && !is_searching + && marked_index.main_index == self.left_side_panes.len().saturating_sub(1) + { + marked_index.main_index = 0; + } else if self.visibility_and_focus.right_side_is_focused() + && marked_index.main_index == self.right_side_panes.len().saturating_sub(1) + { + marked_index.main_index = 0; + } else { + marked_index.main_index += 1 + } + }, + None => { + if self.visibility_and_focus.left_side_is_focused() { + let is_searching = self.search_results.is_some(); + let has_search_results = self + .search_results + .as_ref() + .map(|s| !s.is_empty()) + .unwrap_or(false); + if is_searching && has_search_results { + self.marked_index = Some(MarkedIndex::new(0)); + } else if !is_searching && !self.left_side_panes.is_empty() { + self.marked_index = Some(MarkedIndex::new(0)); + } + } else if self.visibility_and_focus.right_side_is_focused() + && !self.right_side_panes.is_empty() + { + self.marked_index = Some(MarkedIndex::new(0)); + } + }, + } + self.update_highlighted_panes(); + } + pub fn move_marked_index_up(&mut self) { + match self.marked_index.as_mut() { + Some(marked_index) => { + if self.visibility_and_focus.left_side_is_focused() && marked_index.main_index == 0 + { + if let Some(search_result_count) = self.search_results.as_ref().map(|s| s.len()) + { + marked_index.main_index = search_result_count.saturating_sub(1); + } else { + marked_index.main_index = self.left_side_panes.len().saturating_sub(1); + } + } else if self.visibility_and_focus.right_side_is_focused() + && marked_index.main_index == 0 + { + marked_index.main_index = self.right_side_panes.len().saturating_sub(1); + } else { + marked_index.main_index = marked_index.main_index.saturating_sub(1); + } + }, + None => { + if self.visibility_and_focus.left_side_is_focused() { + let is_searching = self.search_results.is_some(); + let has_search_results = self + .search_results + .as_ref() + .map(|s| !s.is_empty()) + .unwrap_or(false); + if is_searching && has_search_results { + let search_results_count = + self.search_results.as_ref().map(|s| s.len()).unwrap_or(0); + self.marked_index = + Some(MarkedIndex::new(search_results_count.saturating_sub(1))); + } else if !is_searching && !self.left_side_panes.is_empty() { + self.marked_index = Some(MarkedIndex::new( + self.left_side_panes.len().saturating_sub(1), + )); + } + } else if self.visibility_and_focus.right_side_is_focused() + && !self.right_side_panes.is_empty() + { + self.marked_index = Some(MarkedIndex::new( + self.right_side_panes.len().saturating_sub(1), + )); + } + }, + } + self.update_highlighted_panes(); + } + pub fn mark_entry(&mut self) { + if let Some(marked_index) = self.marked_index.as_mut() { + marked_index.toggle_additional_mark(); + self.update_highlighted_panes(); + } + } + pub fn break_grouped_panes_to_new_tab(&mut self) { + let pane_ids_to_break_to_new_tab: Vec = + self.right_side_panes.drain(..).map(|p| p.id).collect(); + let title_for_new_tab = if !self.previous_search_string.is_empty() { + Some(self.previous_search_string.clone()) + } else { + None + }; + break_panes_to_new_tab(&pane_ids_to_break_to_new_tab, title_for_new_tab, true); + self.ungroup_panes_in_zellij(pane_ids_to_break_to_new_tab); + close_self(); + } + pub fn stack_grouped_panes(&mut self) { + let pane_ids_to_stack: Vec = + self.right_side_panes.drain(..).map(|p| p.id).collect(); + stack_panes(pane_ids_to_stack.clone()); + self.ungroup_panes_in_zellij(pane_ids_to_stack); + close_self(); + } + pub fn float_grouped_panes(&mut self) { + let pane_ids_to_float: Vec = + self.right_side_panes.drain(..).map(|p| p.id).collect(); + float_multiple_panes(pane_ids_to_float.clone()); + self.ungroup_panes_in_zellij(pane_ids_to_float); + close_self(); + } + pub fn embed_grouped_panes(&mut self) { + let pane_ids_to_embed: Vec = + self.right_side_panes.drain(..).map(|p| p.id).collect(); + embed_multiple_panes(pane_ids_to_embed.clone()); + self.ungroup_panes_in_zellij(pane_ids_to_embed); + close_self(); + } + pub fn break_grouped_panes_right(&mut self) { + if let Some(own_tab_index) = self.own_tab_index { + if Some(own_tab_index + 1) < self.total_tabs_in_session { + let pane_ids_to_break_right: Vec = + self.right_side_panes.drain(..).map(|p| p.id).collect(); + break_panes_to_tab_with_index(&pane_ids_to_break_right, own_tab_index + 1, true); + } else { + let pane_ids_to_break_to_new_tab: Vec = + self.right_side_panes.drain(..).map(|p| p.id).collect(); + let title_for_new_tab = if !self.previous_search_string.is_empty() { + Some(self.previous_search_string.clone()) + } else { + None + }; + break_panes_to_new_tab(&pane_ids_to_break_to_new_tab, title_for_new_tab, true); + } + close_self(); + } + } + pub fn break_grouped_panes_left(&mut self) { + if let Some(own_tab_index) = self.own_tab_index { + if own_tab_index > 0 { + let pane_ids_to_break_left: Vec = + self.right_side_panes.drain(..).map(|p| p.id).collect(); + break_panes_to_tab_with_index( + &pane_ids_to_break_left, + own_tab_index.saturating_sub(1), + true, + ); + } else { + let pane_ids_to_break_to_new_tab: Vec = + self.right_side_panes.drain(..).map(|p| p.id).collect(); + let title_for_new_tab = if !self.previous_search_string.is_empty() { + Some(self.previous_search_string.clone()) + } else { + None + }; + break_panes_to_new_tab(&pane_ids_to_break_to_new_tab, title_for_new_tab, true); + } + close_self(); + } + } + pub fn close_grouped_panes(&mut self) { + let pane_ids_to_close: Vec = + self.right_side_panes.drain(..).map(|p| p.id).collect(); + close_multiple_panes(pane_ids_to_close); + close_self(); + } +} diff --git a/default-plugins/multiple-select/src/ui.rs b/default-plugins/multiple-select/src/ui.rs new file mode 100644 index 00000000..0c74b0fe --- /dev/null +++ b/default-plugins/multiple-select/src/ui.rs @@ -0,0 +1,873 @@ +use crate::{App, VisibilityAndFocus}; +use zellij_tile::prelude::*; + +const TOP_LEFT_CORNER_CHARACTER: &'static str = "┌"; +const TOP_RIGHT_CORNER_CHARACTER: &'static str = "┐"; +const BOTTOM_LEFT_CORNER_CHARACTER: &'static str = "└"; +const BOTTOM_RIGHT_CORNER_CHARACTER: &'static str = "┘"; +const BOUNDARY_CHARACTER: &'static str = "│"; +const HORIZONTAL_BOUNDARY_CHARACTER: &'static str = "─"; + +#[derive(Debug, Clone)] +pub struct PaneItem { + pub text: String, + pub id: PaneId, + pub color_indices: Vec, +} + +impl PaneItem { + pub fn clear(&mut self) { + self.color_indices.clear(); + } + pub fn render(&self, max_width_for_item: usize) -> NestedListItem { + let pane_item_text_len = self.text.chars().count(); + if pane_item_text_len <= max_width_for_item { + NestedListItem::new(&self.text) + .color_range(0, ..) + .color_indices(3, self.color_indices.iter().copied().collect()) + } else { + let length_of_each_half = max_width_for_item.saturating_sub(3) / 2; + let first_half: String = self.text.chars().take(length_of_each_half).collect(); + let second_half: String = self + .text + .chars() + .rev() + .take(length_of_each_half) + .collect::>() + .iter() + .rev() + .collect(); + let second_half_start_index = pane_item_text_len.saturating_sub(length_of_each_half); + let adjusted_indices: Vec = self + .color_indices + .iter() + .filter_map(|i| { + if i < &length_of_each_half { + Some(*i) + } else if i >= &second_half_start_index { + Some(i.saturating_sub(second_half_start_index) + length_of_each_half + 3) + //3 for the bulletin + } else { + None + } + }) + .collect(); + NestedListItem::new(format!("{}...{}", first_half, second_half)) + .color_range(0, ..) + .color_indices(3, adjusted_indices) + } + } +} + +// rendering code +impl App { + pub fn render_close_shortcut(&self, cols: usize) { + let should_render_close_shortcut = + self.visibility_and_focus.left_side_is_focused() && self.marked_index.is_none(); + if should_render_close_shortcut { + let x_coordinates_right_padding = + if self.visibility_and_focus.only_left_side_is_focused() { + 5 + } else { + 1 + }; + let ctrl_c_shortcut_text = " - Close"; + let ctrl_c_shortcut = Text::new(ctrl_c_shortcut_text).color_range(3, ..=7); + print_text_with_coordinates( + ctrl_c_shortcut, + cols.saturating_sub(ctrl_c_shortcut_text.chars().count()) + .saturating_sub(x_coordinates_right_padding), + 0, + None, + None, + ); + } + } + pub fn render_tab_shortcut(&self, cols: usize, rows: usize) { + match self.visibility_and_focus { + VisibilityAndFocus::BothSidesVisibleRightSideFocused => { + let side_width = self.calculate_side_width(cols); + let tab_shortcut = Text::new(" - select more panes").color_range(3, ..=4); + print_text_with_coordinates( + tab_shortcut, + side_width + 6, + rows.saturating_sub(2), + None, + None, + ); + }, + VisibilityAndFocus::BothSidesVisibleLeftSideFocused => { + let side_width = self.calculate_side_width(cols); + let tab_shortcut_text = " - browse selected panes"; + let tab_shortcut = Text::new(tab_shortcut_text).color_range(3, ..=4); + print_text_with_coordinates( + tab_shortcut, + side_width.saturating_sub(tab_shortcut_text.chars().count() + 1), + rows.saturating_sub(2), + None, + None, + ); + }, + VisibilityAndFocus::OnlyRightSideVisible => { + let tab_shortcut = Text::new(" - select more panes").color_range(3, ..=4); + print_text_with_coordinates(tab_shortcut, 4, rows.saturating_sub(2), None, None); + }, + VisibilityAndFocus::OnlyLeftSideVisible => { + // not visible + }, + }; + } + pub fn render_left_side(&self, rows: usize, cols: usize, is_focused: bool) { + let title_y = 0; + let left_side_base_x = 1; + let list_y = 2; + let side_width = self.calculate_side_width(cols); + let max_left_list_height = rows.saturating_sub(8); + let ( + extra_pane_count_on_top_left, + extra_pane_count_on_bottom_left, + extra_selected_item_count_on_top_left, + extra_selected_item_count_on_bottom_left, + left_side_panes, + ) = self.left_side_panes_list(side_width, max_left_list_height); + let (filter_prompt_text, filter_prompt) = self.filter_panes_prompt(); + let filter = self.filter(side_width.saturating_sub(filter_prompt_text.chars().count() + 1)); + let ( + _enter_select_panes_text, + enter_select_panes, + space_shortcut_text, + space_shortcut, + _escape_shortcut_text, + escape_shortcut, + ) = self.left_side_controls(side_width); + print_text_with_coordinates(filter_prompt, left_side_base_x, title_y, None, None); + if is_focused { + print_text_with_coordinates( + filter, + left_side_base_x + filter_prompt_text.chars().count(), + title_y, + None, + None, + ); + } + print_nested_list_with_coordinates( + left_side_panes.clone(), + left_side_base_x, + list_y, + Some(side_width), + None, + ); + if is_focused { + if let Some(marked_index) = self.marked_index.as_ref().map(|i| i.main_index) { + print_text_with_coordinates( + Text::new(">").color_range(3, ..).selected(), + left_side_base_x + 1, + (list_y + marked_index).saturating_sub(extra_pane_count_on_top_left), + None, + None, + ); + } + } + if extra_pane_count_on_top_left > 0 { + self.print_extra_pane_count( + extra_pane_count_on_top_left, + extra_selected_item_count_on_top_left, + list_y.saturating_sub(1), + left_side_base_x, + side_width, + ); + } + if extra_pane_count_on_bottom_left > 0 { + self.print_extra_pane_count( + extra_pane_count_on_bottom_left, + extra_selected_item_count_on_bottom_left, + list_y + left_side_panes.len(), + left_side_base_x, + side_width, + ); + } + if is_focused && !left_side_panes.is_empty() { + let controls_x = 1; + print_text_with_coordinates( + enter_select_panes, + controls_x, + list_y + left_side_panes.len() + 1, + None, + None, + ); + if self.marked_index.is_some() { + print_text_with_coordinates( + space_shortcut.clone(), + controls_x, + list_y + left_side_panes.len() + 2, + None, + None, + ); + print_text_with_coordinates( + escape_shortcut.clone(), + controls_x + space_shortcut_text.chars().count() + 1, + list_y + left_side_panes.len() + 2, + None, + None, + ); + } + } + } + pub fn render_right_side(&self, rows: usize, cols: usize, is_focused: bool) { + let side_width = self.calculate_side_width(cols); + let right_side_base_x = match self.visibility_and_focus { + VisibilityAndFocus::OnlyLeftSideVisible | VisibilityAndFocus::OnlyRightSideVisible => 1, + VisibilityAndFocus::BothSidesVisibleLeftSideFocused + | VisibilityAndFocus::BothSidesVisibleRightSideFocused => side_width + 4, + }; + let title_y = 0; + let list_y: usize = 2; + let max_right_list_height = rows.saturating_sub(11); + let selected_prompt = self.selected_panes_title(); + let ( + extra_pane_count_on_top_right, + extra_pane_count_on_bottom_right, + extra_selected_item_count_on_top_right, + extra_selected_item_count_on_bottom_right, + right_side_panes, + ) = self.right_side_panes_list(side_width, max_right_list_height); + let right_side_pane_count = right_side_panes.len(); + let ( + right_side_controls_1, + right_side_controls_2, + right_side_controls_3, + right_side_controls_4, + ) = self.right_side_controls(side_width); + if extra_pane_count_on_top_right > 0 { + self.print_extra_pane_count( + extra_pane_count_on_top_right, + extra_selected_item_count_on_top_right, + list_y.saturating_sub(1), + right_side_base_x, + side_width, + ); + } + if extra_pane_count_on_bottom_right > 0 { + self.print_extra_pane_count( + extra_pane_count_on_bottom_right, + extra_selected_item_count_on_bottom_right, + list_y + right_side_panes.len(), + right_side_base_x, + side_width, + ); + } + print_text_with_coordinates(selected_prompt, right_side_base_x + 3, title_y, None, None); + print_nested_list_with_coordinates( + right_side_panes, + right_side_base_x, + list_y, + Some(side_width), + None, + ); + if is_focused { + if let Some(marked_index) = self.marked_index.as_ref().map(|i| i.main_index) { + print_text_with_coordinates( + Text::new(">").color_range(3, ..).selected(), + right_side_base_x + 1, + (list_y + marked_index).saturating_sub(extra_pane_count_on_top_right), + None, + None, + ); + } + } + if is_focused && !self.right_side_panes.is_empty() { + print_text_with_coordinates( + right_side_controls_1, + right_side_base_x + 1, + list_y + right_side_pane_count + 1, + None, + None, + ); + print_text_with_coordinates( + right_side_controls_2, + right_side_base_x + 1, + list_y + right_side_pane_count + 3, + None, + None, + ); + print_text_with_coordinates( + right_side_controls_3, + right_side_base_x + 1, + list_y + right_side_pane_count + 4, + None, + None, + ); + print_text_with_coordinates( + right_side_controls_4, + right_side_base_x + 1, + list_y + right_side_pane_count + 5, + None, + None, + ); + } + } + pub fn render_help_line(&self, rows: usize, cols: usize) { + let help_line_text = match self.visibility_and_focus { + VisibilityAndFocus::OnlyLeftSideVisible => { + let full_help_line = "Help: Select one or more panes to group for bulk operations"; + let short_help_line = "Help: Select panes to group"; + if cols >= full_help_line.chars().count() { + full_help_line + } else { + short_help_line + } + }, + VisibilityAndFocus::OnlyRightSideVisible => { + let full_help_line = "Help: Perform bulk operations on all selected panes"; + let short_help_line = "Help: Perform bulk operations"; + if cols >= full_help_line.chars().count() { + full_help_line + } else { + short_help_line + } + }, + _ => { + let full_help_line = + "Help: Select panes on the left, then perform operations on the right."; + let short_help_line = "Help: Group panes for bulk operations"; + if cols >= full_help_line.chars().count() { + full_help_line + } else { + short_help_line + } + }, + }; + let help_line = Text::new(help_line_text); + print_text_with_coordinates(help_line, 0, rows, None, None); + } + pub fn render_focus_boundary(&self, rows: usize, cols: usize) { + let side_width = self.calculate_side_width(cols); + let x = match self.visibility_and_focus { + VisibilityAndFocus::OnlyRightSideVisible => 0, + VisibilityAndFocus::BothSidesVisibleLeftSideFocused + | VisibilityAndFocus::BothSidesVisibleRightSideFocused + | VisibilityAndFocus::OnlyLeftSideVisible => side_width + 2, + }; + let y = 0; + let height = rows.saturating_sub(2); + for i in y..=height { + if i == y && self.visibility_and_focus.left_side_is_focused() { + print_text_with_coordinates( + Text::new(TOP_RIGHT_CORNER_CHARACTER), + x, + i, + None, + None, + ); + print_text_with_coordinates( + Text::new(HORIZONTAL_BOUNDARY_CHARACTER), + x.saturating_sub(1), + i, + None, + None, + ); + print_text_with_coordinates( + Text::new(HORIZONTAL_BOUNDARY_CHARACTER), + x.saturating_sub(2), + i, + None, + None, + ); + } else if i == y && !self.visibility_and_focus.left_side_is_focused() { + print_text_with_coordinates(Text::new(TOP_LEFT_CORNER_CHARACTER), x, i, None, None); + print_text_with_coordinates( + Text::new(HORIZONTAL_BOUNDARY_CHARACTER), + x + 1, + i, + None, + None, + ); + print_text_with_coordinates( + Text::new(HORIZONTAL_BOUNDARY_CHARACTER), + x + 2, + i, + None, + None, + ); + } else if i == height && self.visibility_and_focus.left_side_is_focused() { + print_text_with_coordinates( + Text::new(BOTTOM_RIGHT_CORNER_CHARACTER), + x, + i, + None, + None, + ); + print_text_with_coordinates( + Text::new(HORIZONTAL_BOUNDARY_CHARACTER), + x.saturating_sub(1), + i, + None, + None, + ); + print_text_with_coordinates( + Text::new(HORIZONTAL_BOUNDARY_CHARACTER), + x.saturating_sub(2), + i, + None, + None, + ); + } else if i == height && !self.visibility_and_focus.left_side_is_focused() { + print_text_with_coordinates( + Text::new(BOTTOM_LEFT_CORNER_CHARACTER), + x, + i, + None, + None, + ); + print_text_with_coordinates( + Text::new(HORIZONTAL_BOUNDARY_CHARACTER), + x + 1, + i, + None, + None, + ); + print_text_with_coordinates( + Text::new(HORIZONTAL_BOUNDARY_CHARACTER), + x + 2, + i, + None, + None, + ); + } else { + print_text_with_coordinates(Text::new(BOUNDARY_CHARACTER), x, i, None, None); + } + } + } + pub fn calculate_side_width(&self, cols: usize) -> usize { + match self.visibility_and_focus { + VisibilityAndFocus::OnlyLeftSideVisible | VisibilityAndFocus::OnlyRightSideVisible => { + cols.saturating_sub(4) + }, + VisibilityAndFocus::BothSidesVisibleLeftSideFocused + | VisibilityAndFocus::BothSidesVisibleRightSideFocused => (cols / 2).saturating_sub(3), + } + } +} + +// ui components +impl App { + fn filter_panes_prompt(&self) -> (&'static str, Text) { + let search_prompt_text = if self.search_string.is_empty() { + "ALL PANES " + } else { + "FILTER: " + }; + let search_prompt = if self.visibility_and_focus.left_side_is_focused() { + Text::new(&search_prompt_text).color_range(2, ..) + } else { + Text::new(&search_prompt_text) + }; + (search_prompt_text, search_prompt) + } + fn filter(&self, max_width: usize) -> Text { + let search_string_text = if self.marked_index.is_none() && self.search_string.is_empty() { + let full = "[Type filter term...]"; + let short = "[...]"; + if max_width >= full.chars().count() { + full.to_owned() + } else { + short.to_owned() + } + } else if self.marked_index.is_none() && !self.search_string.is_empty() { + if max_width >= self.search_string.chars().count() + 1 { + format!("{}_", self.search_string) + } else { + let truncated: String = self + .search_string + .chars() + .rev() + .take(max_width.saturating_sub(4)) + .collect::>() + .iter() + .rev() + .collect(); + format!("...{}_", truncated) + } + } else if self.marked_index.is_some() && !self.search_string.is_empty() { + if max_width >= self.search_string.chars().count() { + format!("{}", self.search_string) + } else { + let truncated: String = self + .search_string + .chars() + .rev() + .take(max_width.saturating_sub(4)) + .collect::>() + .iter() + .rev() + .collect(); + format!("...{}", truncated) + } + } else { + format!("") + }; + Text::new(&search_string_text).color_range(3, ..) + } + fn left_side_panes_list( + &self, + max_width: usize, + max_list_height: usize, + ) -> (usize, usize, usize, usize, Vec) { + // returns: extra_pane_count_on_top, extra_pane_count_on_bottom, + // extra_selected_item_count_on_top, extra_selected_item_count_on_bottom, list + let mut left_side_panes = vec![]; + let pane_items_on_the_left = self + .search_results + .as_ref() + .unwrap_or_else(|| &self.left_side_panes); + let max_width_for_item = max_width.saturating_sub(3); // 3 for the list bulletin + let item_count = pane_items_on_the_left.iter().count(); + let first_item_index = if self.visibility_and_focus.left_side_is_focused() { + self.marked_index + .as_ref() + .map(|s| s.main_index.saturating_sub(max_list_height / 2)) + .unwrap_or(0) + } else { + 0 + }; + let last_item_index = std::cmp::min( + (max_list_height + first_item_index).saturating_sub(1), + item_count.saturating_sub(1), + ); + for (i, pane_item) in pane_items_on_the_left + .iter() + .enumerate() + .skip(first_item_index) + { + if i > last_item_index { + break; + } + let mut item = pane_item.render(max_width_for_item); + if Some(i) == self.marked_index.as_ref().map(|s| s.main_index) + && self.visibility_and_focus.left_side_is_focused() + { + item = item.selected(); + if self + .marked_index + .as_ref() + .map(|s| s.additional_indices.contains(&i)) + .unwrap_or(false) + { + item = item.selected().color_range(1, ..); + } + } else if self + .marked_index + .as_ref() + .map(|s| s.additional_indices.contains(&i)) + .unwrap_or(false) + && self.visibility_and_focus.left_side_is_focused() + { + item = item.selected(); + } + left_side_panes.push(item); + } + let extra_panes_on_top = first_item_index; + let extra_panes_on_bottom = item_count.saturating_sub(last_item_index + 1); + let extra_selected_item_count_on_top = if self.visibility_and_focus.left_side_is_focused() { + self.marked_index + .as_ref() + .map(|s| { + s.additional_indices + .iter() + .filter(|a| a < &&first_item_index) + .count() + }) + .unwrap_or(0) + } else { + 0 + }; + let extra_selected_item_count_on_bottom = + if self.visibility_and_focus.left_side_is_focused() { + self.marked_index + .as_ref() + .map(|s| { + s.additional_indices + .iter() + .filter(|a| a > &&last_item_index) + .count() + }) + .unwrap_or(0) + } else { + 0 + }; + ( + extra_panes_on_top, + extra_panes_on_bottom, + extra_selected_item_count_on_top, + extra_selected_item_count_on_bottom, + left_side_panes, + ) + } + fn left_side_controls( + &self, + max_width: usize, + ) -> (&'static str, Text, &'static str, Text, &'static str, Text) { + // returns three components and their text + let (enter_select_panes_text, enter_select_panes) = if self.marked_index.is_some() { + let enter_select_panes_text_full = " - select, <↓↑→> - navigate"; + let enter_select_panes_text_short = " / <↓↑→>..."; + if max_width >= enter_select_panes_text_full.chars().count() { + let enter_select_panes_full = Text::new(enter_select_panes_text_full) + .color_range(3, ..=6) + .color_range(3, 18..=22); + (enter_select_panes_text_full, enter_select_panes_full) + } else { + let enter_select_panes_short = Text::new(enter_select_panes_text_short) + .color_range(3, ..=6) + .color_range(3, 10..=14); + (enter_select_panes_text_short, enter_select_panes_short) + } + } else { + let enter_select_panes_text_full = " - select all, <↓↑> - navigate"; + let enter_select_panes_text_short = " / <↓↑>..."; + if max_width >= enter_select_panes_text_full.chars().count() { + let enter_select_panes_full = Text::new(enter_select_panes_text_full) + .color_range(3, ..=6) + .color_range(3, 21..=25); + (enter_select_panes_text_full, enter_select_panes_full) + } else { + let enter_select_panes_short = Text::new(enter_select_panes_text_short) + .color_range(3, ..=6) + .color_range(3, 10..=13); + (enter_select_panes_text_short, enter_select_panes_short) + } + }; + let space_shortcut_text_full = " - mark many,"; + let space_shortcut_text_short = " /"; + if self.marked_index.is_some() { + let escape_shortcut_text_full = " - remove marks"; + let escape_shortcut_text_short = "..."; + let (escape_shortcut, space_shortcut, escape_shortcut_text, space_shortcut_text) = + if max_width + >= space_shortcut_text_full.chars().count() + + escape_shortcut_text_full.chars().count() + { + ( + Text::new(escape_shortcut_text_full).color_range(3, ..=7), + Text::new(space_shortcut_text_full).color_range(3, ..=6), + escape_shortcut_text_full, + space_shortcut_text_full, + ) + } else { + ( + Text::new(escape_shortcut_text_short).color_range(3, ..=7), + Text::new(space_shortcut_text_short).color_range(3, ..=6), + escape_shortcut_text_short, + space_shortcut_text_short, + ) + }; + ( + enter_select_panes_text, + enter_select_panes, + space_shortcut_text, + space_shortcut, + escape_shortcut_text, + escape_shortcut, + ) + } else { + let escape_shortcut_text = if self.right_side_panes.is_empty() { + " - Close" + } else { + "" + }; + let escape_shortcut = Text::new(escape_shortcut_text).color_range(3, ..=7); + let space_shortcut = Text::new(space_shortcut_text_full).color_range(3, ..=6); + ( + enter_select_panes_text, + enter_select_panes, + space_shortcut_text_full, + space_shortcut, + escape_shortcut_text, + escape_shortcut, + ) + } + } + fn selected_panes_title(&self) -> Text { + let selected_prompt_text = "SELECTED PANES: "; + let selected_prompt = if self.visibility_and_focus.left_side_is_focused() { + Text::new(selected_prompt_text) + } else { + Text::new(selected_prompt_text).color_range(2, ..) + }; + selected_prompt + } + fn right_side_panes_list( + &self, + max_width: usize, + max_list_height: usize, + ) -> (usize, usize, usize, usize, Vec) { + // returns: extra_pane_count_on_top, extra_pane_count_on_bottom, + // extra_selected_item_count_on_top, extra_selected_item_count_on_bottom, list + let mut right_side_panes = vec![]; + let item_count = self.right_side_panes.iter().count(); + let first_item_index = if self.visibility_and_focus.left_side_is_focused() { + 0 + } else { + self.marked_index + .as_ref() + .map(|s| s.main_index.saturating_sub(max_list_height / 2)) + .unwrap_or(0) + }; + let last_item_index = std::cmp::min( + (max_list_height + first_item_index).saturating_sub(1), + item_count.saturating_sub(1), + ); + + let max_width_for_item = max_width.saturating_sub(3); // 3 for the list bulletin + for (i, pane_item) in self + .right_side_panes + .iter() + .enumerate() + .skip(first_item_index) + { + if i > last_item_index { + break; + } + let mut item = pane_item.render(max_width_for_item); + if &Some(i) == &self.marked_index.as_ref().map(|s| s.main_index) + && self.visibility_and_focus.right_side_is_focused() + { + item = item.selected(); + if self + .marked_index + .as_ref() + .map(|s| s.additional_indices.contains(&i)) + .unwrap_or(false) + { + item = item.selected().color_range(1, ..); + } + } else if self + .marked_index + .as_ref() + .map(|s| s.additional_indices.contains(&i)) + .unwrap_or(false) + && self.visibility_and_focus.right_side_is_focused() + { + item = item.selected(); + } + right_side_panes.push(item); + } + + let extra_panes_on_top = first_item_index; + let extra_panes_on_bottom = self + .right_side_panes + .iter() + .len() + .saturating_sub(last_item_index + 1); + let extra_selected_item_count_on_top = if self.visibility_and_focus.left_side_is_focused() { + 0 + } else { + self.marked_index + .as_ref() + .map(|s| { + s.additional_indices + .iter() + .filter(|a| a < &&first_item_index) + .count() + }) + .unwrap_or(0) + }; + let extra_selected_item_count_on_bottom = + if self.visibility_and_focus.left_side_is_focused() { + 0 + } else { + self.marked_index + .as_ref() + .map(|s| { + s.additional_indices + .iter() + .filter(|a| a > &&last_item_index) + .count() + }) + .unwrap_or(0) + }; + ( + extra_panes_on_top, + extra_panes_on_bottom, + extra_selected_item_count_on_top, + extra_selected_item_count_on_bottom, + right_side_panes, + ) + } + fn right_side_controls(&self, cols: usize) -> (Text, Text, Text, Text) { + let right_side_controls_text_1_full = "<←↓↑> - navigate, - clear"; + let right_side_controls_text_1_short = "<←↓↑>/..."; + let right_side_controls_1 = if cols >= right_side_controls_text_1_full.chars().count() { + Text::new(right_side_controls_text_1_full) + .color_range(3, ..=4) + .color_range(3, 18..=25) + } else { + Text::new(right_side_controls_text_1_short) + .color_range(3, ..=4) + .color_range(3, 6..=13) + }; + let right_side_controls_text_2_full = " - break out, - stack, - close"; + let right_side_controls_text_2_short = "//..."; + let right_side_controls_2 = if cols >= right_side_controls_text_2_full.chars().count() { + Text::new(right_side_controls_text_2_full) + .color_range(3, ..=2) + .color_range(3, 17..=19) + .color_range(3, 30..=32) + } else { + Text::new(right_side_controls_text_2_short) + .color_range(3, ..=2) + .color_range(3, 4..=6) + .color_range(3, 8..=10) + }; + let right_side_controls_text_3_full = " - break right, - break left"; + let right_side_controls_text_3_short = "/..."; + let right_side_controls_3 = if cols >= right_side_controls_text_3_full.chars().count() { + Text::new(right_side_controls_text_3_full) + .color_range(3, ..=2) + .color_range(3, 19..=21) + } else { + Text::new(right_side_controls_text_3_short) + .color_range(3, ..=2) + .color_range(3, 4..=6) + }; + let right_side_controls_text_4_full = " - embed, - float"; + let right_side_controls_text_4_short = "/..."; + let right_side_controls_4 = if cols >= right_side_controls_text_4_full.chars().count() { + Text::new(right_side_controls_text_4_full) + .color_range(3, ..=2) + .color_range(3, 13..=15) + } else { + Text::new(right_side_controls_text_4_short) + .color_range(3, ..=2) + .color_range(3, 4..=6) + }; + ( + right_side_controls_1, + right_side_controls_2, + right_side_controls_3, + right_side_controls_4, + ) + } + fn print_extra_pane_count( + &self, + count: usize, + selected_count: usize, + y: usize, + list_x: usize, + list_width: usize, + ) { + let extra_count_text = if selected_count > 0 { + format!("[+{} ({} selected)]", count, selected_count) + } else { + format!("[+{}]", count) + }; + let extra_count = Text::new(&extra_count_text).color_range(1, ..); + print_text_with_coordinates( + extra_count, + (list_x + list_width).saturating_sub(extra_count_text.chars().count()), + y, + None, + None, + ); + } +} diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs index 65b73819..dd5d54c5 100644 --- a/default-plugins/status-bar/src/main.rs +++ b/default-plugins/status-bar/src/main.rs @@ -39,6 +39,8 @@ struct State { display_system_clipboard_failure: bool, classic_ui: bool, base_mode_is_locked: bool, + own_client_id: Option, + grouped_panes_count: Option, } register_plugin!(State); @@ -197,10 +199,12 @@ impl ZellijPlugin for State { .get("classic") .map(|c| c == "true") .unwrap_or(false); + self.own_client_id = Some(get_plugin_ids().client_id); set_selectable(false); subscribe(&[ EventType::ModeUpdate, EventType::TabUpdate, + EventType::PaneUpdate, EventType::CopyToClipboard, EventType::InputReceived, EventType::SystemClipboardFailure, @@ -223,6 +227,28 @@ impl ZellijPlugin for State { } self.tabs = tabs; }, + Event::PaneUpdate(pane_manifest) => { + if let Some(own_client_id) = self.own_client_id { + let mut grouped_panes_count = 0; + for (_tab_index, pane_infos) in pane_manifest.panes { + for pane_info in pane_infos { + let is_in_pane_group = + pane_info.index_in_pane_group.get(&own_client_id).is_some(); + if is_in_pane_group { + grouped_panes_count += 1; + } + } + } + if Some(grouped_panes_count) != self.grouped_panes_count { + if grouped_panes_count == 0 { + self.grouped_panes_count = None; + } else { + self.grouped_panes_count = Some(grouped_panes_count); + } + should_render = true; + } + } + }, Event::CopyToClipboard(copy_destination) => { match self.text_copy_destination { Some(text_copy_destination) => { @@ -280,6 +306,7 @@ impl ZellijPlugin for State { self.base_mode_is_locked, self.text_copy_destination, self.display_system_clipboard_failure, + self.grouped_panes_count, ), fill_bg, ); diff --git a/default-plugins/status-bar/src/one_line_ui.rs b/default-plugins/status-bar/src/one_line_ui.rs index 83217425..f6b88491 100644 --- a/default-plugins/status-bar/src/one_line_ui.rs +++ b/default-plugins/status-bar/src/one_line_ui.rs @@ -6,7 +6,7 @@ use ansi_term::{ use std::collections::HashMap; use zellij_tile::prelude::actions::Action; use zellij_tile::prelude::*; -use zellij_tile_utils::palette_match; +use zellij_tile_utils::{palette_match, style}; use crate::first_line::{to_char, KeyAction, KeyMode, KeyShortcut}; use crate::second_line::{system_clipboard_error, text_copied_hint}; @@ -22,6 +22,7 @@ pub fn one_line_ui( base_mode_is_locked: bool, text_copied_to_clipboard_destination: Option, clipboard_failure: bool, + grouped_pane_count: Option, ) -> LinePart { if let Some(text_copied_to_clipboard_destination) = text_copied_to_clipboard_destination { return text_copied_hint(text_copied_to_clipboard_destination); @@ -35,11 +36,18 @@ pub fn one_line_ui( *max_len = max_len.saturating_sub(line_part.len); }; + let currently_marking_pane_group = help.currently_marking_pane_group.unwrap_or(false); render_mode_key_indicators(help, max_len, separator, base_mode_is_locked) .map(|mode_key_indicators| append(&mode_key_indicators, &mut max_len)) .and_then(|_| match help.mode { - InputMode::Normal | InputMode::Locked => render_secondary_info(help, tab_info, max_len) - .map(|secondary_info| append(&secondary_info, &mut max_len)), + InputMode::Normal | InputMode::Locked => match grouped_pane_count { + Some(grouped_pane_count) => { + render_group_controls(help, grouped_pane_count, max_len) + }, + None if currently_marking_pane_group => render_group_controls(help, 0, max_len), + None => render_secondary_info(help, tab_info, max_len), + } + .map(|secondary_info| append(&secondary_info, &mut max_len)), _ => add_keygroup_separator(help, max_len) .map(|key_group_separator| append(&key_group_separator, &mut max_len)) .and_then(|_| keybinds(help, max_len)) @@ -656,6 +664,225 @@ fn render_common_modifiers( line_part_to_render.len += prefix_text.chars().count() + separator.chars().count(); } +fn render_group_controls( + help: &ModeInfo, + grouped_pane_count: usize, + max_len: usize, +) -> Option { + let currently_marking_group = help.currently_marking_pane_group.unwrap_or(false); + let keymap = help.get_mode_keybinds(); + let (common_modifiers, multiple_select_key, pane_group_toggle_key, group_mark_toggle_key) = { + let multiple_select_key = multiple_select_key(&keymap); + let pane_group_toggle_key = single_action_key(&keymap, &[Action::TogglePaneInGroup]); + let group_mark_toggle_key = single_action_key(&keymap, &[Action::ToggleGroupMarking]); + let common_modifiers = get_common_modifiers( + vec![ + multiple_select_key.iter().next(), + pane_group_toggle_key.iter().next(), + group_mark_toggle_key.iter().next(), + ] + .into_iter() + .filter_map(|k| k) + .collect(), + ); + let multiple_select_key: Vec = multiple_select_key + .iter() + .map(|k| k.strip_common_modifiers(&common_modifiers)) + .collect(); + let pane_group_toggle_key: Vec = pane_group_toggle_key + .iter() + .map(|k| k.strip_common_modifiers(&common_modifiers)) + .collect(); + let group_mark_toggle_key: Vec = group_mark_toggle_key + .iter() + .map(|k| k.strip_common_modifiers(&common_modifiers)) + .collect(); + ( + common_modifiers, + multiple_select_key, + pane_group_toggle_key, + group_mark_toggle_key, + ) + }; + let multiple_select_key = multiple_select_key + .iter() + .next() + .map(|key| format!("{}", key)) + .unwrap_or("UNBOUND".to_owned()); + let pane_group_toggle_key = pane_group_toggle_key + .iter() + .next() + .map(|key| format!("{}", key)) + .unwrap_or("UNBOUND".to_owned()); + let group_mark_toggle_key = group_mark_toggle_key + .iter() + .next() + .map(|key| format!("{}", key)) + .unwrap_or("UNBOUND".to_owned()); + let background = help.style.colors.text_unselected.background; + let foreground = help.style.colors.text_unselected.base; + let superkey_prefix_style = style!(foreground, background).bold(); + let common_modifier_text = if common_modifiers.is_empty() { + "".to_owned() + } else { + format!( + "{} + ", + common_modifiers + .iter() + .map(|c| c.to_string()) + .collect::>() + .join("-") + ) + }; + + // full + let full_selected_panes_text = if common_modifier_text.is_empty() { + format!("{} SELECTED PANES", grouped_pane_count) + } else { + format!("{} SELECTED PANES |", grouped_pane_count) + }; + let full_group_actions_text = format!("<{}> Group Actions", &multiple_select_key); + let full_toggle_group_text = format!("<{}> Toggle Group", &pane_group_toggle_key); + let full_group_mark_toggle_text = format!("<{}> Follow Focus", &group_mark_toggle_key); + let ribbon_paddings_len = 12; + let full_controls_line_len = full_selected_panes_text.chars().count() + + 1 + + common_modifier_text.chars().count() + + full_group_actions_text.chars().count() + + full_toggle_group_text.chars().count() + + full_group_mark_toggle_text.chars().count() + + ribbon_paddings_len + + 1; // 1 for the end padding + + // medium + let medium_selected_panes_text = if common_modifier_text.is_empty() { + format!("{} SELECTED", grouped_pane_count) + } else { + format!("{} SELECTED |", grouped_pane_count) + }; + let medium_group_actions_text = format!("<{}> Actions", &multiple_select_key); + let medium_toggle_group_text = format!("<{}> Toggle", &pane_group_toggle_key); + let medium_group_mark_toggle_text = format!("<{}> Follow", &group_mark_toggle_key); + let ribbon_paddings_len = 12; + let medium_controls_line_len = medium_selected_panes_text.chars().count() + + 1 + + common_modifier_text.chars().count() + + medium_group_actions_text.chars().count() + + medium_toggle_group_text.chars().count() + + medium_group_mark_toggle_text.chars().count() + + ribbon_paddings_len + + 1; // 1 for the end padding + + // short + let short_selected_panes_text = if common_modifier_text.is_empty() { + format!("{} SELECTED", grouped_pane_count) + } else { + format!("{} SELECTED |", grouped_pane_count) + }; + let short_group_actions_text = format!("<{}>", &multiple_select_key); + let short_toggle_group_text = format!("<{}>", &pane_group_toggle_key); + let short_group_mark_toggle_text = format!("<{}>", &group_mark_toggle_key); + let color_emphasis_range_end = if common_modifier_text.is_empty() { + 0 + } else { + 2 + }; + let ribbon_paddings_len = 12; + let short_controls_line_len = short_selected_panes_text.chars().count() + + 1 + + common_modifier_text.chars().count() + + short_group_actions_text.chars().count() + + short_toggle_group_text.chars().count() + + short_group_mark_toggle_text.chars().count() + + ribbon_paddings_len + + 1; // 1 for the end padding + + let ( + selected_panes_text, + group_actions_text, + toggle_group_text, + group_mark_toggle_text, + controls_line_len, + ) = if max_len >= full_controls_line_len { + ( + full_selected_panes_text, + full_group_actions_text, + full_toggle_group_text, + full_group_mark_toggle_text, + full_controls_line_len, + ) + } else if max_len >= medium_controls_line_len { + ( + medium_selected_panes_text, + medium_group_actions_text, + medium_toggle_group_text, + medium_group_mark_toggle_text, + medium_controls_line_len, + ) + } else if max_len >= short_controls_line_len { + ( + short_selected_panes_text, + short_group_actions_text, + short_toggle_group_text, + short_group_mark_toggle_text, + short_controls_line_len, + ) + } else { + return None; + }; + let selected_panes = serialize_text( + &Text::new(&selected_panes_text) + .color_range( + 3, + ..selected_panes_text + .chars() + .count() + .saturating_sub(color_emphasis_range_end), + ) + .opaque(), + ); + let group_actions_ribbon = serialize_ribbon( + &Text::new(&group_actions_text).color_range(0, 1..=multiple_select_key.chars().count()), + ); + let toggle_group_ribbon = serialize_ribbon( + &Text::new(&toggle_group_text).color_range(0, 1..=pane_group_toggle_key.chars().count()), + ); + let mut group_mark_toggle_ribbon = Text::new(&group_mark_toggle_text) + .color_range(0, 1..=group_mark_toggle_key.chars().count()); + if currently_marking_group { + group_mark_toggle_ribbon = group_mark_toggle_ribbon.selected(); + } + let group_mark_toggle_ribbon = serialize_ribbon(&group_mark_toggle_ribbon); + let controls_line = if common_modifiers.is_empty() { + format!( + "{} {}{}{}", + selected_panes, group_actions_ribbon, toggle_group_ribbon, group_mark_toggle_ribbon + ) + } else { + let common_modifier = + serialize_text(&Text::new(&common_modifier_text).color_range(0, ..).opaque()); + format!( + "{} {}{}{}{}", + selected_panes, + common_modifier, + group_actions_ribbon, + toggle_group_ribbon, + group_mark_toggle_ribbon + ) + }; + let remaining_space = max_len.saturating_sub(controls_line_len); + let mut padding = String::new(); + let mut padding_len = 0; + for _ in 0..remaining_space { + padding.push_str(&ANSIStrings(&[superkey_prefix_style.paint(" ")]).to_string()); + padding_len += 1; + } + Some(LinePart { + part: format!("{}{}", padding, controls_line), + len: controls_line_len + padding_len, + }) +} + fn render_secondary_info( help: &ModeInfo, tab_info: Option<&TabInfo>, @@ -1168,6 +1395,7 @@ fn add_keygroup_separator(help: &ModeInfo, max_len: usize) -> Option { bits.push( Style::new() .fg(separator_color) + .on(bg_color) .bold() .paint(format!(" {} ", mode_help_text)), ); @@ -1330,6 +1558,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec)]) -> Vec)]) -> Vec { + let mut matching = keymap.iter().find_map(|(key, acvec)| { + let has_match = acvec + .iter() + .find(|a| a.launches_plugin("zellij:multiple-select")) // TODO: make this an alias + .is_some(); + if has_match { + Some(key.clone()) + } else { + None + } + }); + if let Some(matching) = matching.take() { + vec![matching] + } else { + vec![] + } +} + fn style_key_with_modifier(keyvec: &[KeyWithModifier], color_index: Option) -> LinePart { if keyvec.is_empty() { return LinePart::default(); diff --git a/example/config.kdl b/example/config.kdl index b54fe172..92fbda49 100644 --- a/example/config.kdl +++ b/example/config.kdl @@ -138,6 +138,14 @@ keybinds { bind "Alt k" "Alt Up" { MoveFocus "Up"; } bind "Alt =" "Alt +" { Resize "Increase"; } bind "Alt -" { Resize "Decrease"; } + bind "Alt m" { + LaunchOrFocusPlugin "zellij:multiple-select" { + floating true + move_to_focused_tab true + } + } + bind "Alt p" { TogglePaneInGroup; } + bind "Alt Shift p" { ToggleGroupMarking; } } shared_except "normal" "locked" { bind "Enter" "Space" "Esc" { SwitchToMode "Normal"; } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 1bb3eec9..f1ffaa42 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -69,6 +69,10 @@ fn workspace_members() -> &'static Vec { crate_name: "default-plugins/about", build: true, }, + WorkspaceMember { + crate_name: "default-plugins/multiple-select", + build: true, + }, WorkspaceMember { crate_name: "zellij-utils", build: false, diff --git a/zellij-server/src/background_jobs.rs b/zellij-server/src/background_jobs.rs index 6a660f09..02f8ece5 100644 --- a/zellij-server/src/background_jobs.rs +++ b/zellij-server/src/background_jobs.rs @@ -55,6 +55,7 @@ pub enum BackgroundJob { Vec, // body BTreeMap, // context ), + HighlightPanesWithMessage(Vec, String), RenderToClients, Exit, } @@ -76,12 +77,16 @@ impl From<&BackgroundJob> for BackgroundJobContext { BackgroundJob::WebRequest(..) => BackgroundJobContext::WebRequest, BackgroundJob::ReportPluginList(..) => BackgroundJobContext::ReportPluginList, BackgroundJob::RenderToClients => BackgroundJobContext::ReportPluginList, + BackgroundJob::HighlightPanesWithMessage(..) => { + BackgroundJobContext::HighlightPanesWithMessage + }, BackgroundJob::Exit => BackgroundJobContext::Exit, } } } -static FLASH_DURATION_MS: u64 = 1000; +static LONG_FLASH_DURATION_MS: u64 = 1000; +static FLASH_DURATION_MS: u64 = 400; // Doherty threshold static PLUGIN_ANIMATION_OFFSET_DURATION_MD: u64 = 500; static SESSION_READ_DURATION: u64 = 1000; static DEFAULT_SERIALIZATION_INTERVAL: u64 = 60000; @@ -129,7 +134,7 @@ pub(crate) fn background_jobs_main( Some(text), ), ); - task::sleep(std::time::Duration::from_millis(FLASH_DURATION_MS)).await; + task::sleep(std::time::Duration::from_millis(LONG_FLASH_DURATION_MS)).await; let _ = senders.send_to_screen( ScreenInstruction::ClearPaneFrameColorOverride(pane_ids), ); @@ -411,6 +416,26 @@ pub(crate) fn background_jobs_main( }); } }, + BackgroundJob::HighlightPanesWithMessage(pane_ids, text) => { + if job_already_running(job, &mut running_jobs) { + continue; + } + task::spawn({ + let senders = bus.senders.clone(); + async move { + let _ = senders.send_to_screen( + ScreenInstruction::AddHighlightPaneFrameColorOverride( + pane_ids.clone(), + Some(text), + ), + ); + task::sleep(std::time::Duration::from_millis(FLASH_DURATION_MS)).await; + let _ = senders.send_to_screen( + ScreenInstruction::ClearPaneFrameColorOverride(pane_ids), + ); + } + }); + }, BackgroundJob::Exit => { for loading_plugin in loading_plugins.values() { loading_plugin.store(false, Ordering::SeqCst); @@ -431,7 +456,9 @@ fn job_already_running( ) -> bool { match running_jobs.get_mut(&job) { Some(current_running_job_start_time) => { - if current_running_job_start_time.elapsed() > Duration::from_millis(FLASH_DURATION_MS) { + if current_running_job_start_time.elapsed() + > Duration::from_millis(LONG_FLASH_DURATION_MS) + { *current_running_job_start_time = Instant::now(); false } else { diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 7c0de0b0..cc551273 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -369,6 +369,10 @@ impl SessionMetaData { hide_session_name: new_config.ui.pane_frames.hide_session_name, stacked_resize: new_config.options.stacked_resize.unwrap_or(true), default_editor: new_config.options.scrollback_editor.clone(), + advanced_mouse_actions: new_config + .options + .advanced_mouse_actions + .unwrap_or(true), }) .unwrap(); self.senders diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs index e862f86a..4913ee51 100644 --- a/zellij-server/src/panes/floating_panes/mod.rs +++ b/zellij-server/src/panes/floating_panes/mod.rs @@ -343,7 +343,12 @@ impl FloatingPanes { } Ok(()) } - pub fn render(&mut self, output: &mut Output) -> Result<()> { + pub fn render( + &mut self, + output: &mut Output, + mouse_hover_pane_id: &HashMap, + current_pane_group: HashMap>, + ) -> Result<()> { let err_context = || "failed to render output"; let connected_clients: Vec = { self.connected_clients.borrow().iter().copied().collect() }; @@ -393,6 +398,8 @@ impl FloatingPanes { false, false, true, + mouse_hover_pane_id, + current_pane_group.clone(), ); for client_id in &connected_clients { let client_mode = self @@ -1095,10 +1102,10 @@ impl FloatingPanes { Err(anyhow!("Pane not found")) } } - pub fn pane_info(&self) -> Vec { + pub fn pane_info(&self, current_pane_group: &HashMap>) -> Vec { let mut pane_infos = vec![]; for (pane_id, pane) in self.panes.iter() { - let mut pane_info_for_pane = pane_info_for_pane(pane_id, pane); + let mut pane_info_for_pane = pane_info_for_pane(pane_id, pane, current_pane_group); let is_focused = self.active_panes.pane_id_is_focused(pane_id); pane_info_for_pane.is_floating = true; pane_info_for_pane.is_suppressed = false; @@ -1139,4 +1146,48 @@ impl FloatingPanes { pane.update_rounded_corners(rounded_corners); } } + pub fn next_selectable_pane_id_above(&mut self, pane_id: &PaneId) -> Option { + let display_area = *self.display_area.borrow(); + let viewport = *self.viewport.borrow(); + let floating_pane_grid = FloatingPaneGrid::new( + &mut self.panes, + &mut self.desired_pane_positions, + display_area, + viewport, + ); + floating_pane_grid.next_selectable_pane_id_above(&pane_id) + } + pub fn next_selectable_pane_id_below(&mut self, pane_id: &PaneId) -> Option { + let display_area = *self.display_area.borrow(); + let viewport = *self.viewport.borrow(); + let floating_pane_grid = FloatingPaneGrid::new( + &mut self.panes, + &mut self.desired_pane_positions, + display_area, + viewport, + ); + floating_pane_grid.next_selectable_pane_id_below(&pane_id) + } + pub fn next_selectable_pane_id_to_the_left(&mut self, pane_id: &PaneId) -> Option { + let display_area = *self.display_area.borrow(); + let viewport = *self.viewport.borrow(); + let floating_pane_grid = FloatingPaneGrid::new( + &mut self.panes, + &mut self.desired_pane_positions, + display_area, + viewport, + ); + floating_pane_grid.next_selectable_pane_id_to_the_left(&pane_id) + } + pub fn next_selectable_pane_id_to_the_right(&mut self, pane_id: &PaneId) -> Option { + let display_area = *self.display_area.borrow(); + let viewport = *self.viewport.borrow(); + let floating_pane_grid = FloatingPaneGrid::new( + &mut self.panes, + &mut self.desired_pane_positions, + display_area, + viewport, + ); + floating_pane_grid.next_selectable_pane_id_to_the_right(&pane_id) + } } diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs index 9986585c..52ac3788 100644 --- a/zellij-server/src/panes/plugin_pane.rs +++ b/zellij-server/src/panes/plugin_pane.rs @@ -646,7 +646,16 @@ impl Pane for PluginPane { fn add_red_pane_frame_color_override(&mut self, error_text: Option) { self.pane_frame_color_override = Some((self.style.colors.exit_code_error.base, error_text)); } - fn clear_pane_frame_color_override(&mut self) { + fn add_highlight_pane_frame_color_override( + &mut self, + text: Option, + _client_id: Option, + ) { + // TODO: if we have a client_id, we should only highlight the frame for this client + self.pane_frame_color_override = Some((self.style.colors.frame_highlight.base, text)); + } + fn clear_pane_frame_color_override(&mut self, _client_id: Option) { + // TODO: if we have a client_id, we should only clear the highlight for this client self.pane_frame_color_override = None; } fn frame_color_override(&self) -> Option { diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index eb2f862d..445d332a 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -742,7 +742,16 @@ impl Pane for TerminalPane { fn add_red_pane_frame_color_override(&mut self, error_text: Option) { self.pane_frame_color_override = Some((self.style.colors.exit_code_error.base, error_text)); } - fn clear_pane_frame_color_override(&mut self) { + fn add_highlight_pane_frame_color_override( + &mut self, + text: Option, + _client_id: Option, + ) { + // TODO: if we have a client_id, we should only highlight the frame for this client + self.pane_frame_color_override = Some((self.style.colors.frame_highlight.base, text)); + } + fn clear_pane_frame_color_override(&mut self, _client_id: Option) { + // TODO: if we have a client_id, we should only clear the highlight for this client self.pane_frame_color_override = None; } fn frame_color_override(&self) -> Option { diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index 49794c71..9ec50fb0 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -200,6 +200,15 @@ impl TiledPanes { .is_some(); has_room_for_new_pane || pane_grid.has_room_for_new_stacked_pane() || self.panes.is_empty() } + pub fn room_left_in_stack_of_pane_id(&mut self, pane_id: &PaneId) -> Option { + let mut pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + pane_grid.room_left_in_stack_of_pane_id(pane_id) + } pub fn assign_geom_for_pane_with_run(&mut self, run: Option) { // here we're removing the first pane we find with this run instruction and re-adding it so @@ -226,6 +235,26 @@ impl TiledPanes { } } } + pub fn add_pane_to_stack(&mut self, pane_id_in_stack: &PaneId, mut pane: Box) { + let mut pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + match pane_grid.make_room_in_stack_of_pane_id_for_pane(pane_id_in_stack) { + Ok(new_pane_geom) => { + pane.set_geom(new_pane_geom); + self.panes.insert(pane.pid(), pane); + self.set_force_render(); // TODO: why do we need this? + return; + }, + Err(_e) => { + let should_relayout = false; + return self.add_pane_without_stacked_resize(pane.pid(), pane, should_relayout); + }, + } + } fn add_pane( &mut self, pane_id: PaneId, @@ -658,6 +687,18 @@ impl TiledPanes { self.set_force_render(); self.reapply_pane_frames(); } + pub fn pane_ids_in_stack_of_pane_id(&mut self, pane_id: &PaneId) -> Vec { + if let Some(stack_id) = self + .panes + .get(pane_id) + .and_then(|p| p.position_and_size().stacked) + { + StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) + .pane_ids_in_stack(stack_id) + } else { + vec![] + } + } pub fn focus_pane_for_all_clients_in_stack(&mut self, pane_id: PaneId, stack_id: usize) { let connected_clients: Vec = self.connected_clients.borrow().iter().copied().collect(); @@ -880,7 +921,13 @@ impl TiledPanes { pub fn has_panes(&self) -> bool { !self.panes.is_empty() } - pub fn render(&mut self, output: &mut Output, floating_panes_are_visible: bool) -> Result<()> { + pub fn render( + &mut self, + output: &mut Output, + floating_panes_are_visible: bool, + mouse_hover_pane_id: &HashMap, + current_pane_group: HashMap>, + ) -> Result<()> { let err_context = || "failed to render tiled panes"; let connected_clients: Vec = @@ -930,6 +977,8 @@ impl TiledPanes { pane_is_stacked_under, pane_is_stacked_over, should_draw_pane_frames, + &mouse_hover_pane_id, + current_pane_group.clone(), ); for client_id in &connected_clients { let client_mode = self @@ -1716,7 +1765,7 @@ impl TiledPanes { *self.viewport.borrow(), ); let next_index = pane_grid - .next_selectable_pane_id_below(&active_pane_id) + .next_selectable_pane_id_below(&active_pane_id, false) .or_else(|| pane_grid.progress_stack_down_if_in_stack(&active_pane_id)); match next_index { Some(p) => { @@ -1767,7 +1816,7 @@ impl TiledPanes { *self.viewport.borrow(), ); let next_index = pane_grid - .next_selectable_pane_id_above(&active_pane_id) + .next_selectable_pane_id_above(&active_pane_id, false) .or_else(|| pane_grid.progress_stack_up_if_in_stack(&active_pane_id)); match next_index { Some(p) => { @@ -1972,7 +2021,7 @@ impl TiledPanes { *self.viewport.borrow(), ); let next_index = pane_grid - .next_selectable_pane_id_below(&pane_id) + .next_selectable_pane_id_below(&pane_id, false) .or_else(|| pane_grid.progress_stack_down_if_in_stack(&pane_id)); if let Some(p) = next_index { let current_position = self.panes.get(&pane_id).unwrap(); @@ -2127,7 +2176,7 @@ impl TiledPanes { *self.viewport.borrow(), ); let next_index = pane_grid - .next_selectable_pane_id_above(&pane_id) + .next_selectable_pane_id_above(&pane_id, false) .or_else(|| pane_grid.progress_stack_up_if_in_stack(&pane_id)); if let Some(p) = next_index { let current_position = self.panes.get(&pane_id).unwrap(); @@ -2443,10 +2492,10 @@ impl TiledPanes { .find(|(_id, pane)| run_plugin_or_alias.is_equivalent_to_run(pane.invoked_with())) .map(|(id, _)| *id) } - pub fn pane_info(&self) -> Vec { + pub fn pane_info(&self, current_pane_group: &HashMap>) -> Vec { let mut pane_infos = vec![]; for (pane_id, pane) in self.panes.iter() { - let mut pane_info_for_pane = pane_info_for_pane(pane_id, pane); + let mut pane_info_for_pane = pane_info_for_pane(pane_id, pane, ¤t_pane_group); let is_focused = self.active_panes.pane_id_is_focused(pane_id); pane_info_for_pane.is_floating = false; pane_info_for_pane.is_suppressed = false; @@ -2495,6 +2544,44 @@ impl TiledPanes { StackedPanes::new_from_btreemap(&mut self.panes, &self.panes_to_hide) .stacked_pane_ids_under_and_over_flexible_panes() } + pub fn next_selectable_pane_id_above(&mut self, pane_id: &PaneId) -> Option { + let pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + let include_panes_in_stack = true; + pane_grid.next_selectable_pane_id_above(&pane_id, include_panes_in_stack) + } + pub fn next_selectable_pane_id_below(&mut self, pane_id: &PaneId) -> Option { + let pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + let include_panes_in_stack = true; + pane_grid.next_selectable_pane_id_below(&pane_id, include_panes_in_stack) + } + pub fn next_selectable_pane_id_to_the_left(&mut self, pane_id: &PaneId) -> Option { + let pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + pane_grid.next_selectable_pane_id_to_the_left(&pane_id) + } + pub fn next_selectable_pane_id_to_the_right(&mut self, pane_id: &PaneId) -> Option { + let pane_grid = TiledPaneGrid::new( + &mut self.panes, + &self.panes_to_hide, + *self.display_area.borrow(), + *self.viewport.borrow(), + ); + pane_grid.next_selectable_pane_id_to_the_right(&pane_id) + } } #[allow(clippy::borrowed_box)] diff --git a/zellij-server/src/panes/tiled_panes/stacked_panes.rs b/zellij-server/src/panes/tiled_panes/stacked_panes.rs index 8044b0b0..4df175e8 100644 --- a/zellij-server/src/panes/tiled_panes/stacked_panes.rs +++ b/zellij-server/src/panes/tiled_panes/stacked_panes.rs @@ -500,6 +500,20 @@ impl<'a> StackedPanes<'a> { } Err(anyhow!("Not enough room for another pane!")) } + pub fn room_left_in_stack_of_pane_id(&self, pane_id: &PaneId) -> Option { + // if the pane is stacked, returns the number of panes possible to add to this stack + let Ok(stack) = self.positions_in_stack(pane_id) else { + return None; + }; + stack.iter().find_map(|(_p_id, p)| { + if !p.rows.is_fixed() { + // this is the flexible pane + Some(p.rows.as_usize().saturating_sub(MIN_TERMINAL_HEIGHT)) + } else { + None + } + }) + } pub fn new_stack(&self, root_pane_id: PaneId, pane_count_in_stack: usize) -> Vec { let mut stacked_geoms = vec![]; let panes = self.panes.borrow(); diff --git a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs index facb66c7..ddb225a4 100644 --- a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs +++ b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs @@ -1009,7 +1009,11 @@ impl<'a> TiledPaneGrid<'a> { None => None, } } - pub fn next_selectable_pane_id_below(&self, current_pane_id: &PaneId) -> Option { + pub fn next_selectable_pane_id_below( + &self, + current_pane_id: &PaneId, + include_panes_in_stack: bool, + ) -> Option { let panes = self.panes.borrow(); let current_pane = panes.get(current_pane_id)?; let panes: Vec<(PaneId, &&mut Box)> = panes @@ -1021,9 +1025,14 @@ impl<'a> TiledPaneGrid<'a> { .iter() .enumerate() .filter(|(_, (_, c))| { - c.is_directly_below(Box::as_ref(current_pane)) - && c.vertically_overlaps_with(Box::as_ref(current_pane)) - && !c.current_geom().is_stacked() + if include_panes_in_stack { + c.is_directly_below(Box::as_ref(current_pane)) + && c.vertically_overlaps_with(Box::as_ref(current_pane)) + } else { + c.is_directly_below(Box::as_ref(current_pane)) + && c.vertically_overlaps_with(Box::as_ref(current_pane)) + && !c.current_geom().is_stacked() + } }) .max_by_key(|(_, (_, c))| c.active_at()) .map(|(_, (pid, _))| pid) @@ -1074,7 +1083,11 @@ impl<'a> TiledPaneGrid<'a> { .copied(); next_index } - pub fn next_selectable_pane_id_above(&self, current_pane_id: &PaneId) -> Option { + pub fn next_selectable_pane_id_above( + &self, + current_pane_id: &PaneId, + include_panes_in_stack: bool, + ) -> Option { let panes = self.panes.borrow(); let current_pane = panes.get(current_pane_id)?; let panes: Vec<(PaneId, &&mut Box)> = panes @@ -1086,9 +1099,14 @@ impl<'a> TiledPaneGrid<'a> { .iter() .enumerate() .filter(|(_, (_, c))| { - c.is_directly_above(Box::as_ref(current_pane)) - && c.vertically_overlaps_with(Box::as_ref(current_pane)) - && !c.current_geom().is_stacked() + if include_panes_in_stack { + c.is_directly_above(Box::as_ref(current_pane)) + && c.vertically_overlaps_with(Box::as_ref(current_pane)) + } else { + c.is_directly_above(Box::as_ref(current_pane)) + && c.vertically_overlaps_with(Box::as_ref(current_pane)) + && !c.current_geom().is_stacked() + } }) .max_by_key(|(_, (_, c))| c.active_at()) .map(|(_, (pid, _))| pid) @@ -1413,6 +1431,9 @@ impl<'a> TiledPaneGrid<'a> { .iter() .any(|(_p_id, p)| p.current_geom().rows.as_usize() > MIN_TERMINAL_HEIGHT) } + pub fn room_left_in_stack_of_pane_id(&mut self, pane_id: &PaneId) -> Option { + StackedPanes::new(self.panes.clone()).room_left_in_stack_of_pane_id(pane_id) + } pub fn make_room_in_stack_for_pane(&mut self) -> Result { StackedPanes::new(self.panes.clone()).make_room_for_new_pane() } diff --git a/zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs b/zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs index 0782cf51..7cc67608 100644 --- a/zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs +++ b/zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs @@ -1218,7 +1218,7 @@ impl Pane for MockPane { fn add_red_pane_frame_color_override(&mut self, _error_text: Option) { unimplemented!() } - fn clear_pane_frame_color_override(&mut self) { + fn clear_pane_frame_color_override(&mut self, _client_id: Option) { unimplemented!() } fn frame_color_override(&self) -> Option { diff --git a/zellij-server/src/plugins/plugin_map.rs b/zellij-server/src/plugins/plugin_map.rs index 6716dffd..62020f4c 100644 --- a/zellij-server/src/plugins/plugin_map.rs +++ b/zellij-server/src/plugins/plugin_map.rs @@ -50,18 +50,21 @@ impl PluginMap { pub fn remove_plugins( &mut self, pid: PluginId, - ) -> Vec<( - Arc>, - Arc>, - HashMap>, - )> { - let mut removed = vec![]; + ) -> HashMap< + (PluginId, ClientId), + ( + Arc>, + Arc>, + HashMap>, + ), + > { + let mut removed = HashMap::new(); let ids_in_plugin_map: Vec<(PluginId, ClientId)> = self.plugin_assets.keys().copied().collect(); for (plugin_id, client_id) in ids_in_plugin_map { if pid == plugin_id { if let Some(plugin_asset) = self.plugin_assets.remove(&(plugin_id, client_id)) { - removed.push(plugin_asset); + removed.insert((plugin_id, client_id), plugin_asset); } } } diff --git a/zellij-server/src/plugins/unit/plugin_tests.rs b/zellij-server/src/plugins/unit/plugin_tests.rs index 327252a1..60f850cc 100644 --- a/zellij-server/src/plugins/unit/plugin_tests.rs +++ b/zellij-server/src/plugins/unit/plugin_tests.rs @@ -8622,3 +8622,74 @@ pub fn list_clients_plugin_command() { .unwrap(); assert_snapshot!(format!("{:#?}", list_clients_instruction)); } + +#[test] +#[ignore] +pub fn before_close_plugin_event() { + let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its + // destructor removes the directory + let plugin_host_folder = PathBuf::from(temp_folder.path()); + let cache_path = plugin_host_folder.join("permissions_test.kdl"); + let (plugin_thread_sender, screen_receiver, teardown) = + create_plugin_thread(Some(plugin_host_folder)); + let plugin_should_float = Some(false); + let plugin_title = Some("test_plugin".to_owned()); + let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { + _allow_exec_host_cmd: false, + location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), + configuration: Default::default(), + ..Default::default() + }); + let tab_index = 1; + let client_id = 1; + let size = Size { + cols: 121, + rows: 20, + }; + let received_screen_instructions = Arc::new(Mutex::new(vec![])); + let screen_thread = grant_permissions_and_log_actions_in_thread!( + received_screen_instructions, + ScreenInstruction::HighlightAndUnhighlightPanes, + screen_receiver, + 1, + &PermissionType::ChangeApplicationState, + cache_path, + plugin_thread_sender, + client_id + ); + + let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); + let _ = plugin_thread_sender.send(PluginInstruction::Load( + plugin_should_float, + false, + plugin_title, + run_plugin, + Some(tab_index), + None, + client_id, + size, + None, + false, + )); + std::thread::sleep(std::time::Duration::from_millis(5000)); + // here we send an unload to plugin id 0 (the first plugin id, presumably this plugin) + // so that its BeforeClose Event will be triggered and it will send a + // HighlightAndUnhighlightPanes + // instruction which we can assert below + let _ = plugin_thread_sender.send(PluginInstruction::Unload(0)); + screen_thread.join().unwrap(); // this might take a while if the cache is cold + teardown(); + let sent_instruction = received_screen_instructions + .lock() + .unwrap() + .iter() + .find_map(|i| { + if let ScreenInstruction::HighlightAndUnhighlightPanes(..) = i { + Some(i.clone()) + } else { + None + } + }) + .unwrap(); + assert_snapshot!(format!("{:#?}", sent_instruction)); +} diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__before_close_plugin_event.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__before_close_plugin_event.snap new file mode 100644 index 00000000..c2f442c1 --- /dev/null +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__before_close_plugin_event.snap @@ -0,0 +1,17 @@ +--- +source: zellij-server/src/plugins/./unit/plugin_tests.rs +expression: "format!(\"{:#?}\", sent_instruction)" +--- +HighlightAndUnhighlightPanes( + [ + Terminal( + 1, + ), + ], + [ + Plugin( + 1, + ), + ], + 1, +) diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap index cefe14b2..44226784 100644 --- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap @@ -1,6 +1,5 @@ --- source: zellij-server/src/plugins/./unit/plugin_tests.rs -assertion_line: 1143 expression: "format!(\"{:#?}\", switch_to_mode_event)" --- Some( @@ -222,10 +221,10 @@ Some( 154, ), emphasis_0: EightBit( - 154, + 201, ), emphasis_1: EightBit( - 154, + 99, ), emphasis_2: EightBit( 154, @@ -318,6 +317,7 @@ Some( ), editor: None, shell: None, + currently_marking_pane_group: None, }, 1, ), diff --git a/zellij-server/src/plugins/wasm_bridge.rs b/zellij-server/src/plugins/wasm_bridge.rs index 5efebf38..d01a24e7 100644 --- a/zellij-server/src/plugins/wasm_bridge.rs +++ b/zellij-server/src/plugins/wasm_bridge.rs @@ -318,14 +318,43 @@ impl WasmBridge { pub fn unload_plugin(&mut self, pid: PluginId) -> Result<()> { info!("Bye from plugin {}", &pid); let mut plugin_map = self.plugin_map.lock().unwrap(); - for (running_plugin, _, workers) in plugin_map.remove_plugins(pid) { + for ((plugin_id, client_id), (running_plugin, subscriptions, workers)) in + plugin_map.remove_plugins(pid) + { for (_worker_name, worker_sender) in workers { drop(worker_sender.send(MessageToWorker::Exit)); } - let running_plugin = running_plugin.lock().unwrap(); - let cache_dir = running_plugin.store.data().plugin_own_data_dir.clone(); - if let Err(e) = std::fs::remove_dir_all(cache_dir) { - log::error!("Failed to remove cache dir for plugin: {:?}", e); + let subscriptions = subscriptions.lock().unwrap(); + if subscriptions.contains(&EventType::BeforeClose) { + let mut running_plugin = running_plugin.lock().unwrap(); + match apply_before_close_event_to_plugin( + pid, + client_id, + &mut running_plugin, + self.senders.clone(), + ) { + Ok(()) => {}, + Err(e) => { + log::error!("{:?}", e); + + // https://stackoverflow.com/questions/66450942/in-rust-is-there-a-way-to-make-literal-newlines-in-r-using-windows-c + let stringified_error = format!("{:?}", e).replace("\n", "\n\r"); + + handle_plugin_crash(plugin_id, stringified_error, self.senders.clone()); + }, + } + let cache_dir = running_plugin.store.data().plugin_own_data_dir.clone(); + if let Err(e) = std::fs::remove_dir_all(cache_dir) { + log::error!("Failed to remove cache dir for plugin: {:?}", e); + } + } else { + // this is duplicated because of locking/unlocking order between running_plugin and + // subscriptions + let running_plugin = running_plugin.lock().unwrap(); + let cache_dir = running_plugin.store.data().plugin_own_data_dir.clone(); + if let Err(e) = std::fs::remove_dir_all(cache_dir) { + log::error!("Failed to remove cache dir for plugin: {:?}", e); + } } } self.cached_plugin_map.clear(); @@ -1651,3 +1680,36 @@ pub fn handle_plugin_crash(plugin_id: PluginId, message: String, senders: Thread loading_indication, )); } + +pub fn apply_before_close_event_to_plugin( + plugin_id: PluginId, + client_id: ClientId, + running_plugin: &mut RunningPlugin, + senders: ThreadSenders, +) -> Result<()> { + let instance = &running_plugin.instance; + + let err_context = || format!("Failed to apply event to plugin {plugin_id}"); + let event = Event::BeforeClose; + let protobuf_event: ProtobufEvent = event + .clone() + .try_into() + .map_err(|e| anyhow!("Failed to convert to protobuf: {:?}", e))?; + let update = instance + .get_typed_func::<(), i32>(&mut running_plugin.store, "update") + .with_context(err_context)?; + wasi_write_object(running_plugin.store.data(), &protobuf_event.encode_to_vec()) + .with_context(err_context)?; + let _should_render = update + .call(&mut running_plugin.store, ()) + .with_context(err_context)?; + let pipes_to_block_or_unblock = pipes_to_block_or_unblock(running_plugin, None); + let plugin_render_asset = + PluginRenderAsset::new(plugin_id, client_id, vec![]).with_pipes(pipes_to_block_or_unblock); + let _ = senders + .send_to_plugin(PluginInstruction::UnblockCliPipes(vec![ + plugin_render_asset, + ])) + .context("failed to unblock input pipe"); + Ok(()) +} diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs index 25964938..f8dad922 100644 --- a/zellij-server/src/plugins/zellij_exports.rs +++ b/zellij-server/src/plugins/zellij_exports.rs @@ -431,6 +431,30 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) { close_plugin_after_replace, context, ), + PluginCommand::GroupAndUngroupPanes(panes_to_group, panes_to_ungroup) => { + group_and_ungroup_panes( + env, + panes_to_group.into_iter().map(|p| p.into()).collect(), + panes_to_ungroup.into_iter().map(|p| p.into()).collect(), + ) + }, + PluginCommand::HighlightAndUnhighlightPanes( + panes_to_highlight, + panes_to_unhighlight, + ) => highlight_and_unhighlight_panes( + env, + panes_to_highlight.into_iter().map(|p| p.into()).collect(), + panes_to_unhighlight.into_iter().map(|p| p.into()).collect(), + ), + PluginCommand::CloseMultiplePanes(pane_ids) => { + close_multiple_panes(env, pane_ids.into_iter().map(|p| p.into()).collect()) + }, + PluginCommand::FloatMultiplePanes(pane_ids) => { + float_multiple_panes(env, pane_ids.into_iter().map(|p| p.into()).collect()) + }, + PluginCommand::EmbedMultiplePanes(pane_ids) => { + embed_multiple_panes(env, pane_ids.into_iter().map(|p| p.into()).collect()) + }, }, (PermissionStatus::Denied, permission) => { log::error!( @@ -547,6 +571,7 @@ fn get_plugin_ids(env: &PluginEnv) { plugin_id: env.plugin_id, zellij_pid: process::id(), initial_cwd: env.plugin_cwd.clone(), + client_id: env.client_id, }; ProtobufPluginIds::try_from(ids) .map_err(|e| anyhow!("Failed to serialized plugin ids: {}", e)) @@ -1864,7 +1889,7 @@ fn set_floating_pane_pinned(env: &PluginEnv, pane_id: PaneId, should_be_pinned: fn stack_panes(env: &PluginEnv, pane_ids: Vec) { let _ = env .senders - .send_to_screen(ScreenInstruction::StackPanes(pane_ids)); + .send_to_screen(ScreenInstruction::StackPanes(pane_ids, env.client_id)); } fn change_floating_panes_coordinates( @@ -2147,6 +2172,65 @@ fn load_new_plugin( } } +fn group_and_ungroup_panes( + env: &PluginEnv, + panes_to_group: Vec, + panes_to_ungroup: Vec, +) { + let _ = env + .senders + .send_to_screen(ScreenInstruction::GroupAndUngroupPanes( + panes_to_group, + panes_to_ungroup, + env.client_id, + )); +} + +fn highlight_and_unhighlight_panes( + env: &PluginEnv, + panes_to_highlight: Vec, + panes_to_unhighlight: Vec, +) { + let _ = env + .senders + .send_to_screen(ScreenInstruction::HighlightAndUnhighlightPanes( + panes_to_highlight, + panes_to_unhighlight, + env.client_id, + )); +} + +fn close_multiple_panes(env: &PluginEnv, pane_ids: Vec) { + for pane_id in pane_ids { + match pane_id { + PaneId::Terminal(terminal_pane_id) => { + close_terminal_pane(env, terminal_pane_id); + }, + PaneId::Plugin(plugin_pane_id) => { + close_plugin_pane(env, plugin_pane_id); + }, + } + } +} + +fn float_multiple_panes(env: &PluginEnv, pane_ids: Vec) { + let _ = env + .senders + .send_to_screen(ScreenInstruction::FloatMultiplePanes( + pane_ids, + env.client_id, + )); +} + +fn embed_multiple_panes(env: &PluginEnv, pane_ids: Vec) { + let _ = env + .senders + .send_to_screen(ScreenInstruction::EmbedMultiplePanes( + pane_ids, + env.client_id, + )); +} + // Custom panic handler for plugins. // // This is called when a panic occurs in a plugin. Since most panics will likely originate in the @@ -2308,6 +2392,11 @@ fn check_command_permission( | PluginCommand::SetFloatingPanePinned(..) | PluginCommand::StackPanes(..) | PluginCommand::ChangeFloatingPanesCoordinates(..) + | PluginCommand::GroupAndUngroupPanes(..) + | PluginCommand::HighlightAndUnhighlightPanes(..) + | PluginCommand::CloseMultiplePanes(..) + | PluginCommand::FloatMultiplePanes(..) + | PluginCommand::EmbedMultiplePanes(..) | PluginCommand::KillSessions(..) => PermissionType::ChangeApplicationState, PluginCommand::UnblockCliPipeInput(..) | PluginCommand::BlockCliPipeInput(..) diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index fbfe04d8..d566ecad 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -938,6 +938,7 @@ pub(crate) fn route_action( senders .send_to_screen(ScreenInstruction::StackPanes( pane_ids_to_stack.iter().map(|p| PaneId::from(*p)).collect(), + client_id, )) .with_context(err_context)?; }, @@ -949,6 +950,16 @@ pub(crate) fn route_action( )])) .with_context(err_context)?; }, + Action::TogglePaneInGroup => { + senders + .send_to_screen(ScreenInstruction::TogglePaneInGroup(client_id)) + .with_context(err_context)?; + }, + Action::ToggleGroupMarking => { + senders + .send_to_screen(ScreenInstruction::ToggleGroupMarking(client_id)) + .with_context(err_context)?; + }, } Ok(should_break) } diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 0932151d..be7e7d7d 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -371,6 +371,7 @@ pub enum ScreenInstruction { hide_session_name: bool, stacked_resize: bool, default_editor: Option, + advanced_mouse_actions: bool, }, RerunCommandPane(u32), // u32 - terminal pane id ResizePaneWithId(ResizeStrategy, PaneId), @@ -404,8 +405,16 @@ pub enum ScreenInstruction { ListClientsToPlugin(PluginId, ClientId), TogglePanePinned(ClientId), SetFloatingPanePinned(PaneId, bool), - StackPanes(Vec), + StackPanes(Vec, ClientId), ChangeFloatingPanesCoordinates(Vec<(PaneId, FloatingPaneCoordinates)>), + AddHighlightPaneFrameColorOverride(Vec, Option), // Option => optional + // message + GroupAndUngroupPanes(Vec, Vec, ClientId), // panes_to_group, panes_to_ungroup + HighlightAndUnhighlightPanes(Vec, Vec, ClientId), // panes_to_highlight, panes_to_unhighlight + FloatMultiplePanes(Vec, ClientId), + EmbedMultiplePanes(Vec, ClientId), + TogglePaneInGroup(ClientId), + ToggleGroupMarking(ClientId), } impl From<&ScreenInstruction> for ScreenContext { @@ -616,6 +625,17 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenInstruction::ChangeFloatingPanesCoordinates(..) => { ScreenContext::ChangeFloatingPanesCoordinates }, + ScreenInstruction::AddHighlightPaneFrameColorOverride(..) => { + ScreenContext::AddHighlightPaneFrameColorOverride + }, + ScreenInstruction::GroupAndUngroupPanes(..) => ScreenContext::GroupAndUngroupPanes, + ScreenInstruction::HighlightAndUnhighlightPanes(..) => { + ScreenContext::HighlightAndUnhighlightPanes + }, + ScreenInstruction::FloatMultiplePanes(..) => ScreenContext::FloatMultiplePanes, + ScreenInstruction::EmbedMultiplePanes(..) => ScreenContext::EmbedMultiplePanes, + ScreenInstruction::TogglePaneInGroup(..) => ScreenContext::TogglePaneInGroup, + ScreenInstruction::ToggleGroupMarking(..) => ScreenContext::ToggleGroupMarking, } } } @@ -697,6 +717,9 @@ pub(crate) struct Screen { default_layout_name: Option, explicitly_disable_kitty_keyboard_protocol: bool, default_editor: Option, + current_pane_group: Rc>>>, + advanced_mouse_actions: bool, + currently_marking_pane_group: Rc>>, } impl Screen { @@ -723,6 +746,7 @@ impl Screen { explicitly_disable_kitty_keyboard_protocol: bool, stacked_resize: bool, default_editor: Option, + advanced_mouse_actions: bool, ) -> Self { let session_name = mode_info.session_name.clone().unwrap_or_default(); let session_info = SessionInfo::new(session_name.clone()); @@ -766,6 +790,9 @@ impl Screen { layout_dir, explicitly_disable_kitty_keyboard_protocol, default_editor, + current_pane_group: Rc::new(RefCell::new(HashMap::new())), + currently_marking_pane_group: Rc::new(RefCell::new(HashMap::new())), + advanced_mouse_actions, } } @@ -1257,6 +1284,10 @@ impl Screen { &mut self.tabs } + pub fn get_tabs(&self) -> &BTreeMap { + &self.tabs + } + /// Returns an immutable reference to this [`Screen`]'s active [`Tab`]. pub fn get_active_tab(&self, client_id: ClientId) -> Result<&Tab> { match self.active_tab_indices.get(&client_id) { @@ -1366,6 +1397,9 @@ impl Screen { self.styled_underlines, self.explicitly_disable_kitty_keyboard_protocol, self.default_editor.clone(), + self.current_pane_group.clone(), + self.currently_marking_pane_group.clone(), + self.advanced_mouse_actions, ); for (client_id, mode_info) in &self.mode_info { tab.change_mode_info(mode_info.clone(), *client_id); @@ -2474,6 +2508,7 @@ impl Screen { hide_session_name: bool, stacked_resize: bool, default_editor: Option, + advanced_mouse_actions: bool, client_id: ClientId, ) -> Result<()> { let should_support_arrow_fonts = !simplified_ui; @@ -2488,6 +2523,7 @@ impl Screen { self.copy_options.command = copy_command.clone(); self.copy_options.copy_on_select = copy_on_select; self.draw_pane_frames = pane_frames; + self.advanced_mouse_actions = advanced_mouse_actions; self.default_mode_info .update_arrow_fonts(should_support_arrow_fonts); self.default_mode_info @@ -2507,6 +2543,7 @@ impl Screen { tab.update_copy_options(&self.copy_options); tab.set_pane_frames(pane_frames); tab.update_arrow_fonts(should_support_arrow_fonts); + tab.update_advanced_mouse_actions(advanced_mouse_actions); } // client specific configuration @@ -2559,10 +2596,11 @@ impl Screen { ); } } - pub fn stack_panes(&mut self, mut pane_ids_to_stack: Vec) { + pub fn stack_panes(&mut self, mut pane_ids_to_stack: Vec) -> Option { + // if successful, returns the pane id of the root pane if pane_ids_to_stack.is_empty() { log::error!("Got an empty list of pane_ids to stack"); - return; + return None; } let stack_size = pane_ids_to_stack.len(); let root_pane_id = pane_ids_to_stack.remove(0); @@ -2579,19 +2617,20 @@ impl Screen { .copied() else { log::error!("Failed to find tab for root_pane_id: {:?}", root_pane_id); - return; + return None; }; + + let mut panes_to_stack = vec![]; let target_tab_has_room_for_stack = self .tabs - .get(&root_tab_id) + .get_mut(&root_tab_id) .map(|t| t.has_room_for_stack(root_pane_id, stack_size)) .unwrap_or(false); if !target_tab_has_room_for_stack { log::error!("No room for stack with root pane id: {:?}", root_pane_id); - return; + return None; } - let mut panes_to_stack = vec![]; for (tab_id, tab) in self.tabs.iter_mut() { if tab_id == &root_tab_id { // we do this before we extract panes so that the extraction won't trigger a @@ -2614,6 +2653,7 @@ impl Screen { self.tabs .get_mut(&root_tab_id) .map(|t| t.stack_panes(root_pane_id, panes_to_stack)); + return Some(root_pane_id); } pub fn change_floating_panes_coordinates( &mut self, @@ -2629,6 +2669,87 @@ impl Screen { } } } + pub fn handle_mouse_event(&mut self, event: MouseEvent, client_id: ClientId) { + match self + .get_active_tab_mut(client_id) + .and_then(|tab| tab.handle_mouse_event(&event, client_id)) + { + Ok(mouse_effect) => { + if let Some(pane_id) = mouse_effect.group_toggle { + if self.advanced_mouse_actions { + self.toggle_pane_id_in_group(pane_id, &client_id); + } + } + if let Some(pane_id) = mouse_effect.group_add { + if self.advanced_mouse_actions { + self.add_pane_id_to_group(pane_id, &client_id); + } + } + if mouse_effect.ungroup { + if self.advanced_mouse_actions { + self.clear_pane_group(&client_id); + } + } + if mouse_effect.state_changed { + let _ = self.log_and_report_session_state(); + } + if !mouse_effect.leave_clipboard_message { + let _ = self + .bus + .senders + .send_to_plugin(PluginInstruction::Update(vec![( + None, + Some(client_id), + Event::InputReceived, + )])); + } + self.render(None).non_fatal(); + }, + Err(e) => { + eprintln!("mouse event error: {:?}", e); + log::error!("Failed to process MouseEvent: {}", e); + }, + } + } + pub fn toggle_pane_in_group(&mut self, client_id: ClientId) -> Result<()> { + let err_context = "Can't add pane to group"; + let active_tab = self + .get_active_tab(client_id) + .with_context(|| err_context)?; + let active_pane_id = active_tab + .get_active_pane_id(client_id) + .with_context(|| err_context)?; + self.toggle_pane_id_in_group(active_pane_id, &client_id); + let _ = self.log_and_report_session_state(); + Ok(()) + } + pub fn toggle_group_marking(&mut self, client_id: ClientId) -> Result<()> { + let (was_marking_before, marking_pane_group_now) = { + let mut currently_marking_pane_group = self.currently_marking_pane_group.borrow_mut(); + let previous_value = currently_marking_pane_group + .remove(&client_id) + .unwrap_or(false); + let new_value = !previous_value; + if new_value { + currently_marking_pane_group.insert(client_id, true); + } + (previous_value, new_value) + }; + if marking_pane_group_now { + let active_pane_id = self.get_active_pane_id(&client_id); + if let Some(active_pane_id) = active_pane_id { + self.add_pane_id_to_group(active_pane_id, &client_id); + } + } + let value_changed = was_marking_before != marking_pane_group_now; + if value_changed { + for tab in self.tabs.values_mut() { + tab.update_input_modes()?; + } + let _ = self.log_and_report_session_state(); + } + Ok(()) + } fn unblock_input(&self) -> Result<()> { self.bus .senders @@ -2773,6 +2894,117 @@ impl Screen { fn connected_clients_contains(&self, client_id: &ClientId) -> bool { self.connected_clients.borrow().contains(client_id) } + fn get_client_pane_group(&self, client_id: &ClientId) -> HashSet { + self.current_pane_group + .borrow() + .get(client_id) + .map(|p| p.iter().copied().collect()) + .unwrap_or_else(|| HashSet::new()) + } + fn clear_pane_group(&mut self, client_id: &ClientId) { + self.current_pane_group + .borrow_mut() + .get_mut(client_id) + .map(|p| p.clear()); + self.currently_marking_pane_group + .borrow_mut() + .remove(client_id); + } + fn toggle_pane_id_in_group(&mut self, pane_id: PaneId, client_id: &ClientId) { + { + let mut pane_groups = self.current_pane_group.borrow_mut(); + let client_pane_group = pane_groups.entry(*client_id).or_insert_with(|| vec![]); + if client_pane_group.contains(&pane_id) { + client_pane_group.retain(|p| p != &pane_id); + } else { + client_pane_group.push(pane_id); + }; + } + self.retain_only_existing_panes_in_pane_groups(); + } + fn add_pane_id_to_group(&mut self, pane_id: PaneId, client_id: &ClientId) { + { + let mut pane_groups = self.current_pane_group.borrow_mut(); + let client_pane_group = pane_groups.entry(*client_id).or_insert_with(|| vec![]); + if !client_pane_group.contains(&pane_id) { + client_pane_group.push(pane_id); + } + } + self.retain_only_existing_panes_in_pane_groups(); + } + fn add_active_pane_to_group_if_marking(&mut self, client_id: &ClientId) { + { + if self + .currently_marking_pane_group + .borrow() + .get(client_id) + .copied() + .unwrap_or(false) + { + let active_pane_id = self.get_active_pane_id(&client_id); + if let Some(active_pane_id) = active_pane_id { + self.add_pane_id_to_group(active_pane_id, &client_id); + } + } + } + self.retain_only_existing_panes_in_pane_groups(); + } + fn get_active_pane_id(&self, client_id: &ClientId) -> Option { + let active_tab = self.get_active_tab(*client_id).ok()?; + active_tab.get_active_pane_id(*client_id) + } + + fn group_and_ungroup_panes( + &mut self, + mut pane_ids_to_group: Vec, + pane_ids_to_ungroup: Vec, + client_id: ClientId, + ) { + { + let mut current_pane_group = self.current_pane_group.borrow_mut(); + let client_pane_group = current_pane_group + .entry(client_id) + .or_insert_with(|| vec![]); + client_pane_group.append(&mut pane_ids_to_group); + client_pane_group.retain(|p| !pane_ids_to_ungroup.contains(p)); + } + self.retain_only_existing_panes_in_pane_groups(); + let _ = self.log_and_report_session_state(); + } + fn retain_only_existing_panes_in_pane_groups(&mut self) { + let clients_with_empty_group = { + let mut clients_with_empty_group = vec![]; + let mut current_pane_group = self.current_pane_group.borrow_mut(); + for (client_id, panes_in_group) in current_pane_group.iter_mut() { + let all_tabs = self.get_tabs(); + panes_in_group.retain(|p_id| { + let mut found = false; + for tab in all_tabs.values() { + if tab.has_pane_with_pid(&p_id) { + found = true; + break; + } + } + found + }); + if panes_in_group.is_empty() { + clients_with_empty_group.push(*client_id) + } + } + clients_with_empty_group + }; + for client_id in &clients_with_empty_group { + self.currently_marking_pane_group + .borrow_mut() + .remove(client_id); + } + if !clients_with_empty_group.is_empty() { + let all_tabs = self.get_tabs_mut(); + for tab in all_tabs.values_mut() { + let _ = tab.update_input_modes(); + } + } + } } #[cfg(not(test))] @@ -2839,6 +3071,7 @@ pub(crate) fn screen_thread_main( .unwrap_or(false); // by default, we try to support this if the terminal supports it and // the program running inside a pane requests it let stacked_resize = config_options.stacked_resize.unwrap_or(true); + let advanced_mouse_actions = config_options.advanced_mouse_actions.unwrap_or(true); let thread_senders = bus.senders.clone(); let mut screen = Screen::new( @@ -2872,6 +3105,7 @@ pub(crate) fn screen_thread_main( explicitly_disable_kitty_keyboard_protocol, stacked_resize, default_editor, + advanced_mouse_actions, ); let mut pending_tab_ids: HashSet = HashSet::new(); @@ -3053,7 +3287,6 @@ pub(crate) fn screen_thread_main( .toggle_pane_embed_or_floating(client_id), ?); screen.unblock_input()?; screen.log_and_report_session_state()?; - screen.render(None)?; }, ScreenInstruction::ToggleFloatingPanes(client_id, default_shell) => { @@ -3195,12 +3428,14 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_focus_left(client_id), ? ); + screen.add_active_pane_to_group_if_marking(&client_id); screen.render(None)?; screen.unblock_input()?; screen.log_and_report_session_state()?; }, ScreenInstruction::MoveFocusLeftOrPreviousTab(client_id) => { screen.move_focus_left_or_previous_tab(client_id)?; + screen.add_active_pane_to_group_if_marking(&client_id); screen.unblock_input()?; screen.render(None)?; screen.log_and_report_session_state()?; @@ -3212,6 +3447,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_focus_down(client_id), ? ); + screen.add_active_pane_to_group_if_marking(&client_id); screen.render(None)?; screen.unblock_input()?; screen.log_and_report_session_state()?; @@ -3223,12 +3459,14 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_focus_right(client_id), ? ); + screen.add_active_pane_to_group_if_marking(&client_id); screen.render(None)?; screen.unblock_input()?; screen.log_and_report_session_state()?; }, ScreenInstruction::MoveFocusRightOrNextTab(client_id) => { screen.move_focus_right_or_next_tab(client_id)?; + screen.add_active_pane_to_group_if_marking(&client_id); screen.unblock_input()?; screen.render(None)?; screen.log_and_report_session_state()?; @@ -3240,6 +3478,7 @@ pub(crate) fn screen_thread_main( |tab: &mut Tab, client_id: ClientId| tab.move_focus_up(client_id), ? ); + screen.add_active_pane_to_group_if_marking(&client_id); screen.render(None)?; screen.unblock_input()?; screen.log_and_report_session_state()?; @@ -3546,6 +3785,7 @@ pub(crate) fn screen_thread_main( screen.unblock_input()?; screen.log_and_report_session_state()?; + screen.retain_only_existing_panes_in_pane_groups(); }, ScreenInstruction::HoldPane(id, exit_status, run_command) => { let is_first_run = false; @@ -3863,31 +4103,7 @@ pub(crate) fn screen_thread_main( screen.unblock_input()?; }, ScreenInstruction::MouseEvent(event, client_id) => { - match screen - .get_active_tab_mut(client_id) - .and_then(|tab| tab.handle_mouse_event(&event, client_id)) - { - Ok(mouse_effect) => { - if mouse_effect.state_changed { - screen.log_and_report_session_state()?; - } - if !mouse_effect.leave_clipboard_message { - let _ = - screen - .bus - .senders - .send_to_plugin(PluginInstruction::Update(vec![( - None, - Some(client_id), - Event::InputReceived, - )])); - } - screen.render(None).non_fatal(); - }, - Err(e) => { - log::error!("Failed to process MouseEvent: {}", e); - }, - } + screen.handle_mouse_event(event, client_id); }, ScreenInstruction::Copy(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab @@ -4019,12 +4235,28 @@ pub(crate) fn screen_thread_main( } screen.render(None)?; }, + ScreenInstruction::AddHighlightPaneFrameColorOverride(pane_ids, error_text) => { + let all_tabs = screen.get_tabs_mut(); + for pane_id in pane_ids { + for tab in all_tabs.values_mut() { + if tab.has_pane_with_pid(&pane_id) { + tab.add_highlight_pane_frame_color_override( + pane_id, + error_text.clone(), + None, + ); + break; + } + } + } + screen.render(None)?; + }, ScreenInstruction::ClearPaneFrameColorOverride(pane_ids) => { let all_tabs = screen.get_tabs_mut(); for pane_id in pane_ids { for tab in all_tabs.values_mut() { if tab.has_pane_with_pid(&pane_id) { - tab.clear_pane_frame_color_override(pane_id); + tab.clear_pane_frame_color_override(pane_id, None); break; } } @@ -4642,6 +4874,7 @@ pub(crate) fn screen_thread_main( hide_session_name, stacked_resize, default_editor, + advanced_mouse_actions, } => { screen .reconfigure( @@ -4659,6 +4892,7 @@ pub(crate) fn screen_thread_main( hide_session_name, stacked_resize, default_editor, + advanced_mouse_actions, client_id, ) .non_fatal(); @@ -4845,7 +5079,7 @@ pub(crate) fn screen_thread_main( let all_tabs = screen.get_tabs_mut(); for tab in all_tabs.values_mut() { if tab.has_pane_with_pid(&pane_id) { - tab.toggle_pane_embed_or_floating_for_pane_id(pane_id) + tab.toggle_pane_embed_or_floating_for_pane_id(pane_id, None) .non_fatal(); break; } @@ -4869,6 +5103,17 @@ pub(crate) fn screen_thread_main( new_tab_name, client_id, )?; + // TODO: is this a race? + let pane_group = screen.get_client_pane_group(&client_id); + if !pane_group.is_empty() { + let _ = screen.bus.senders.send_to_background_jobs( + BackgroundJob::HighlightPanesWithMessage( + pane_group.iter().copied().collect(), + "BROKEN OUT".to_owned(), + ), + ); + } + screen.clear_pane_group(&client_id); }, ScreenInstruction::BreakPanesToTabWithIndex { pane_ids, @@ -4882,6 +5127,16 @@ pub(crate) fn screen_thread_main( should_change_focus_to_new_tab, client_id, )?; + let pane_group = screen.get_client_pane_group(&client_id); + if !pane_group.is_empty() { + let _ = screen.bus.senders.send_to_background_jobs( + BackgroundJob::HighlightPanesWithMessage( + pane_group.iter().copied().collect(), + "BROKEN OUT".to_owned(), + ), + ); + } + screen.clear_pane_group(&client_id); }, ScreenInstruction::TogglePanePinned(client_id) => { screen.toggle_pane_pinned(client_id); @@ -4889,16 +5144,133 @@ pub(crate) fn screen_thread_main( ScreenInstruction::SetFloatingPanePinned(pane_id, should_be_pinned) => { screen.set_floating_pane_pinned(pane_id, should_be_pinned); }, - ScreenInstruction::StackPanes(pane_ids_to_stack) => { - screen.stack_panes(pane_ids_to_stack); - let _ = screen.unblock_input(); - let _ = screen.render(None); + ScreenInstruction::StackPanes(pane_ids_to_stack, client_id) => { + if let Some(root_pane_id) = screen.stack_panes(pane_ids_to_stack) { + let _ = screen.focus_pane_with_id(root_pane_id, false, client_id); + let _ = screen.unblock_input(); + let _ = screen.render(None); + let pane_group = screen.get_client_pane_group(&client_id); + if !pane_group.is_empty() { + let _ = screen.bus.senders.send_to_background_jobs( + BackgroundJob::HighlightPanesWithMessage( + pane_group.iter().copied().collect(), + "STACKED".to_owned(), + ), + ); + } + screen.clear_pane_group(&client_id); + } }, ScreenInstruction::ChangeFloatingPanesCoordinates(pane_ids_and_coordinates) => { screen.change_floating_panes_coordinates(pane_ids_and_coordinates); let _ = screen.unblock_input(); let _ = screen.render(None); }, + ScreenInstruction::GroupAndUngroupPanes( + pane_ids_to_group, + pane_ids_to_ungroup, + client_id, + ) => { + screen.group_and_ungroup_panes(pane_ids_to_group, pane_ids_to_ungroup, client_id); + let _ = screen.log_and_report_session_state(); + }, + ScreenInstruction::TogglePaneInGroup(client_id) => { + screen.toggle_pane_in_group(client_id).non_fatal(); + }, + ScreenInstruction::ToggleGroupMarking(client_id) => { + screen.toggle_group_marking(client_id).non_fatal(); + }, + ScreenInstruction::HighlightAndUnhighlightPanes( + pane_ids_to_highlight, + pane_ids_to_unhighlight, + client_id, + ) => { + { + let all_tabs = screen.get_tabs_mut(); + for pane_id in pane_ids_to_highlight { + for tab in all_tabs.values_mut() { + if tab.has_pane_with_pid(&pane_id) { + tab.add_highlight_pane_frame_color_override( + pane_id, + None, + Some(client_id), + ); + } + } + } + for pane_id in pane_ids_to_unhighlight { + for tab in all_tabs.values_mut() { + if tab.has_pane_with_pid(&pane_id) { + tab.clear_pane_frame_color_override(pane_id, Some(client_id)); + } + } + } + screen.render(None)?; + } + let _ = screen.log_and_report_session_state(); + }, + ScreenInstruction::FloatMultiplePanes(pane_ids_to_float, client_id) => { + { + let all_tabs = screen.get_tabs_mut(); + let mut ejected_panes_in_group = vec![]; + for pane_id in pane_ids_to_float { + for tab in all_tabs.values_mut() { + if tab.has_pane_with_pid(&pane_id) { + if !tab.pane_id_is_floating(&pane_id) { + ejected_panes_in_group.push(pane_id); + tab.toggle_pane_embed_or_floating_for_pane_id( + pane_id, + Some(client_id), + ) + .non_fatal(); + } + tab.show_floating_panes(); + } + } + } + screen.render(None)?; + if !ejected_panes_in_group.is_empty() { + let _ = screen.bus.senders.send_to_background_jobs( + BackgroundJob::HighlightPanesWithMessage( + ejected_panes_in_group, + "EJECTED".to_owned(), + ), + ); + } + } + let _ = screen.log_and_report_session_state(); + }, + ScreenInstruction::EmbedMultiplePanes(pane_ids_to_float, client_id) => { + { + let all_tabs = screen.get_tabs_mut(); + let mut embedded_panes_in_group = vec![]; + for pane_id in pane_ids_to_float { + for tab in all_tabs.values_mut() { + if tab.has_pane_with_pid(&pane_id) { + if tab.pane_id_is_floating(&pane_id) { + embedded_panes_in_group.push(pane_id); + tab.toggle_pane_embed_or_floating_for_pane_id( + pane_id, + Some(client_id), + ) + .non_fatal(); + } + tab.hide_floating_panes(); + } + } + } + screen.render(None)?; + if !embedded_panes_in_group.is_empty() { + let _ = screen.bus.senders.send_to_background_jobs( + BackgroundJob::HighlightPanesWithMessage( + embedded_panes_in_group, + "EMBEDDED".to_owned(), + ), + ); + } + } + let _ = screen.log_and_report_session_state(); + }, } } Ok(()) diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index 507cfb3c..e3a36ae5 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -46,7 +46,7 @@ use std::cell::RefCell; use std::rc::Rc; use std::time::Instant; use std::{ - collections::{HashMap, HashSet}, + collections::{BTreeMap, HashMap, HashSet}, str, }; use zellij_utils::{ @@ -152,6 +152,9 @@ enum BufferedTabInstruction { pub struct MouseEffect { pub state_changed: bool, pub leave_clipboard_message: bool, + pub group_toggle: Option, + pub group_add: Option, + pub ungroup: bool, } impl MouseEffect { @@ -159,18 +162,54 @@ impl MouseEffect { MouseEffect { state_changed: true, leave_clipboard_message: false, + group_toggle: None, + group_add: None, + ungroup: false, } } pub fn leave_clipboard_message() -> Self { MouseEffect { state_changed: false, leave_clipboard_message: true, + group_toggle: None, + group_add: None, + ungroup: false, } } pub fn state_changed_and_leave_clipboard_message() -> Self { MouseEffect { state_changed: true, leave_clipboard_message: true, + group_toggle: None, + group_add: None, + ungroup: false, + } + } + pub fn group_toggle(pane_id: PaneId) -> Self { + MouseEffect { + state_changed: true, + leave_clipboard_message: false, + group_toggle: Some(pane_id), + group_add: None, + ungroup: false, + } + } + pub fn group_add(pane_id: PaneId) -> Self { + MouseEffect { + state_changed: true, + leave_clipboard_message: false, + group_toggle: None, + group_add: Some(pane_id), + ungroup: false, + } + } + pub fn ungroup() -> Self { + MouseEffect { + state_changed: true, + leave_clipboard_message: false, + group_toggle: None, + group_add: None, + ungroup: true, } } } @@ -222,6 +261,10 @@ pub(crate) struct Tab { arrow_fonts: bool, styled_underlines: bool, explicitly_disable_kitty_keyboard_protocol: bool, + mouse_hover_pane_id: HashMap, + current_pane_group: Rc>>>, + advanced_mouse_actions: bool, + currently_marking_pane_group: Rc>>, } #[derive(Clone, Debug, Default, Serialize, Deserialize)] @@ -525,7 +568,13 @@ pub trait Pane { // No-op by default, only terminal panes support holding } fn add_red_pane_frame_color_override(&mut self, _error_text: Option); - fn clear_pane_frame_color_override(&mut self); + fn add_highlight_pane_frame_color_override( + &mut self, + _text: Option, + _client_id: Option, + ) { + } + fn clear_pane_frame_color_override(&mut self, _client_id: Option); fn frame_color_override(&self) -> Option; fn invoked_with(&self) -> &Option; fn set_title(&mut self, title: String); @@ -626,6 +675,9 @@ impl Tab { styled_underlines: bool, explicitly_disable_kitty_keyboard_protocol: bool, default_editor: Option, + current_pane_group: Rc>>>, + currently_marking_pane_group: Rc>>, + advanced_mouse_actions: bool, ) -> Self { let name = if name.is_empty() { format!("Tab #{}", index + 1) @@ -720,6 +772,10 @@ impl Tab { styled_underlines, explicitly_disable_kitty_keyboard_protocol, default_editor, + mouse_hover_pane_id: HashMap::new(), + current_pane_group, + currently_marking_pane_group, + advanced_mouse_actions, } } @@ -829,6 +885,9 @@ impl Tab { .non_fatal(); } self.set_force_render(); + self.senders + .send_to_pty_writer(PtyWriteInstruction::ApplyCachedResizes) + .with_context(|| format!("failed to apply cached resizes"))?; Ok(()) } fn relayout_tiled_panes(&mut self, search_backwards: bool) -> Result<()> { @@ -873,6 +932,9 @@ impl Tab { // we do this so that the new swap layout has a chance to pass through the constraint system self.tiled_panes.resize(display_area); self.set_should_clear_display_before_rendering(); + self.senders + .send_to_pty_writer(PtyWriteInstruction::ApplyCachedResizes) + .with_context(|| format!("failed to apply cached resizes"))?; Ok(()) } pub fn previous_swap_layout(&mut self) -> Result<()> { @@ -882,9 +944,6 @@ impl Tab { } else { self.relayout_tiled_panes(search_backwards)?; } - self.senders - .send_to_pty_writer(PtyWriteInstruction::ApplyCachedResizes) - .with_context(|| format!("failed to update plugins with mode info"))?; Ok(()) } pub fn next_swap_layout(&mut self) -> Result<()> { @@ -894,9 +953,6 @@ impl Tab { } else { self.relayout_tiled_panes(search_backwards)?; } - self.senders - .send_to_pty_writer(PtyWriteInstruction::ApplyCachedResizes) - .with_context(|| format!("failed to update plugins with mode info"))?; Ok(()) } pub fn apply_buffered_instructions(&mut self) -> Result<()> { @@ -936,6 +992,7 @@ impl Tab { // this updates all plugins with the client's input mode let mode_infos = self.mode_info.borrow(); let mut plugin_updates = vec![]; + let currently_marking_pane_group = self.currently_marking_pane_group.borrow(); for client_id in self.connected_clients.borrow().iter() { let mut mode_info = mode_infos .get(client_id) @@ -943,6 +1000,8 @@ impl Tab { .clone(); mode_info.shell = Some(self.default_shell.clone()); mode_info.editor = self.default_editor.clone(); + mode_info.currently_marking_pane_group = + currently_marking_pane_group.get(client_id).copied(); plugin_updates.push((None, Some(*client_id), Event::ModeUpdate(mode_info))); } self.senders @@ -1044,6 +1103,9 @@ impl Tab { pub fn has_no_connected_clients(&self) -> bool { self.connected_clients.borrow().is_empty() } + pub fn pane_id_is_floating(&self, pane_id: &PaneId) -> bool { + self.floating_panes.panes_contain(pane_id) + } pub fn toggle_pane_embed_or_floating(&mut self, client_id: ClientId) -> Result<()> { let err_context = || format!("failed to toggle embedded/floating pane for client {client_id}"); @@ -1079,7 +1141,11 @@ impl Tab { } Ok(()) } - pub fn toggle_pane_embed_or_floating_for_pane_id(&mut self, pane_id: PaneId) -> Result<()> { + pub fn toggle_pane_embed_or_floating_for_pane_id( + &mut self, + pane_id: PaneId, + client_id: Option, + ) -> Result<()> { let err_context = || { format!( "failed to toggle embedded/floating pane for pane_id {:?}", @@ -1097,7 +1163,7 @@ impl Tab { format!("failed to find floating pane (ID: {pane_id:?}) to embed",) }) .with_context(err_context)?; - self.add_tiled_pane(floating_pane_to_embed, pane_id, None)?; + self.add_tiled_pane(floating_pane_to_embed, pane_id, client_id)?; } } else if self.tiled_panes.panes_contain(&pane_id) { if self.get_selectable_tiled_panes().count() <= 1 { @@ -2210,14 +2276,21 @@ impl Tab { floating_panes_stack, ); + let current_pane_group: HashMap> = + { self.current_pane_group.borrow().clone() }; self.tiled_panes - .render(output, self.floating_panes.panes_are_visible()) + .render( + output, + self.floating_panes.panes_are_visible(), + &self.mouse_hover_pane_id, + current_pane_group.clone(), + ) .with_context(err_context)?; if (self.floating_panes.panes_are_visible() && self.floating_panes.has_active_panes()) || self.floating_panes.has_pinned_panes() { self.floating_panes - .render(output) + .render(output, &self.mouse_hover_pane_id, current_pane_group) .with_context(err_context)?; } @@ -2817,7 +2890,7 @@ impl Tab { 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(); + let _ = self.relayout_floating_panes(false); } } else { if self.tiled_panes.fullscreen_is_active() { @@ -2830,7 +2903,7 @@ impl Tab { 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(); + let _ = self.relayout_tiled_panes(false); } }; let _ = self.senders.send_to_plugin(PluginInstruction::Update(vec![( @@ -2878,7 +2951,7 @@ impl Tab { 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(); + let _ = self.relayout_floating_panes(false); } // we do this so that the logical index will not affect ordering in the target tab if let Some(closed_pane) = closed_pane.as_mut() { @@ -2896,7 +2969,7 @@ impl Tab { 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(); + let _ = self.relayout_tiled_panes(false); } // we do this so that the logical index will not affect ordering in the target tab if let Some(closed_pane) = closed_pane.as_mut() { @@ -3460,6 +3533,13 @@ impl Tab { .ok_or_else(|| anyhow!("Failed to find pane at position"))? .pid(); match event.event_type { + MouseEventType::Press if event.alt => { + self.mouse_hover_pane_id.remove(&client_id); + Ok(MouseEffect::group_toggle(pane_id_at_position)) + }, + MouseEventType::Motion if event.alt => { + Ok(MouseEffect::group_add(pane_id_at_position)) + }, MouseEventType::Press => { if pane_id_at_position == active_pane_id { self.handle_active_pane_left_mouse_press(event, client_id) @@ -3474,6 +3554,9 @@ impl Tab { self.handle_scrollwheel_up(&event.position, 3, client_id) } else if event.wheel_down { self.handle_scrollwheel_down(&event.position, 3, client_id) + } else if event.right && event.alt { + self.mouse_hover_pane_id.remove(&client_id); + Ok(MouseEffect::ungroup()) } else if event.right { self.handle_right_click(&event, client_id) } else if event.middle { @@ -3838,6 +3921,12 @@ impl Tab { .with_context(err_context)?; } } + self.mouse_hover_pane_id.remove(&client_id); + } else { + let pane_id = pane.pid(); + if self.advanced_mouse_actions { + self.mouse_hover_pane_id.insert(client_id, pane_id); + } } }; Ok(MouseEffect::leave_clipboard_message()) @@ -4024,7 +4113,7 @@ impl Tab { Ok(()) } - pub fn visible(&self, visible: bool) -> Result<()> { + pub fn visible(&mut self, visible: bool) -> Result<()> { let pids_in_this_tab = self.tiled_panes.pane_ids().filter_map(|p| match p { PaneId::Plugin(pid) => Some(pid), _ => None, @@ -4036,6 +4125,9 @@ impl Tab { self.senders .send_to_plugin(PluginInstruction::Update(plugin_updates)) .with_context(|| format!("failed to set visibility of tab to {visible}"))?; + if !visible { + self.mouse_hover_pane_id.clear(); + } Ok(()) } @@ -4200,7 +4292,12 @@ impl Tab { pane.add_red_pane_frame_color_override(error_text); } } - pub fn clear_pane_frame_color_override(&mut self, pane_id: PaneId) { + pub fn add_highlight_pane_frame_color_override( + &mut self, + pane_id: PaneId, + error_text: Option, + client_id: Option, + ) { if let Some(pane) = self .tiled_panes .get_pane_mut(pane_id) @@ -4212,7 +4309,26 @@ impl Tab { .map(|s_p| &mut s_p.1) }) { - pane.clear_pane_frame_color_override(); + pane.add_highlight_pane_frame_color_override(error_text, client_id); + } + } + pub fn clear_pane_frame_color_override( + &mut self, + pane_id: PaneId, + client_id: Option, + ) { + if let Some(pane) = self + .tiled_panes + .get_pane_mut(pane_id) + .or_else(|| self.floating_panes.get_pane_mut(pane_id)) + .or_else(|| { + self.suppressed_panes + .values_mut() + .find(|s_p| s_p.1.pid() == pane_id) + .map(|s_p| &mut s_p.1) + }) + { + pane.clear_pane_frame_color_override(client_id); } } pub fn update_plugin_loading_stage(&mut self, pid: u32, loading_indication: LoadingIndication) { @@ -4351,12 +4467,14 @@ impl Tab { } pub fn pane_infos(&self) -> Vec { let mut pane_info = vec![]; - let mut tiled_pane_info = self.tiled_panes.pane_info(); - let mut floating_pane_info = self.floating_panes.pane_info(); + let current_pane_group = { self.current_pane_group.borrow().clone() }; + let mut tiled_pane_info = self.tiled_panes.pane_info(¤t_pane_group); + let mut floating_pane_info = self.floating_panes.pane_info(¤t_pane_group); pane_info.append(&mut tiled_pane_info); pane_info.append(&mut floating_pane_info); for (pane_id, (_is_scrollback_editor, pane)) in self.suppressed_panes.iter() { - let mut pane_info_for_suppressed_pane = pane_info_for_pane(pane_id, pane); + let mut pane_info_for_suppressed_pane = + pane_info_for_pane(pane_id, pane, ¤t_pane_group); pane_info_for_suppressed_pane.is_floating = false; pane_info_for_suppressed_pane.is_suppressed = true; pane_info_for_suppressed_pane.is_focused = false; @@ -4397,7 +4515,7 @@ impl Tab { // confusing and not what the user intends self.swap_layouts.set_is_floating_damaged(); // we do this so that we won't skip to the // next layout - self.next_swap_layout()?; + self.relayout_floating_panes(false)?; } Ok(()) } @@ -4431,7 +4549,7 @@ impl Tab { // confusing and not what the user intends self.swap_layouts.set_is_tiled_damaged(); // we do this so that we won't skip to the // next layout - self.next_swap_layout()?; + self.relayout_tiled_panes(false)?; } Ok(()) } @@ -4589,6 +4707,9 @@ impl Tab { pub fn update_auto_layout(&mut self, auto_layout: bool) { self.auto_layout = auto_layout; } + pub fn update_advanced_mouse_actions(&mut self, advanced_mouse_actions: bool) { + self.advanced_mouse_actions = advanced_mouse_actions; + } pub fn extract_suppressed_panes(&mut self) -> SuppressedPanes { self.suppressed_panes.drain().collect() } @@ -4609,16 +4730,24 @@ impl Tab { self.set_force_render(); } } - pub fn has_room_for_stack(&self, root_pane_id: PaneId, stack_size: usize) -> bool { + pub fn has_room_for_stack(&mut self, root_pane_id: PaneId, stack_size: usize) -> bool { if self.floating_panes.panes_contain(&root_pane_id) || self.suppressed_panes.contains_key(&root_pane_id) { log::error!("Root pane of stack cannot be floating or suppressed"); return false; } - self.get_pane_with_id(root_pane_id) - .map(|p| p.position_and_size().rows.as_usize() >= stack_size + MIN_TERMINAL_HEIGHT) - .unwrap_or(false) + if self.pane_is_stacked(root_pane_id) { + let room_left_in_stack = self + .tiled_panes + .room_left_in_stack_of_pane_id(&root_pane_id) + .unwrap_or(0); + stack_size <= room_left_in_stack + } else { + self.get_pane_with_id(root_pane_id) + .map(|p| p.position_and_size().rows.as_usize() >= stack_size + MIN_TERMINAL_HEIGHT) + .unwrap_or(false) + } } pub fn set_tiled_panes_damaged(&mut self) { self.swap_layouts.set_is_tiled_damaged(); @@ -4629,33 +4758,53 @@ impl Tab { return; } self.swap_layouts.set_is_tiled_damaged(); // TODO: verify we can do all the below first - - // + 1 for the root pane - let mut stack_geoms = self - .tiled_panes - .stack_panes(root_pane_id, panes_to_stack.len() + 1); - if stack_geoms.is_empty() { - log::error!("Failed to find room for stacked panes"); - return; - } - self.tiled_panes - .set_geom_for_pane_with_id(&root_pane_id, stack_geoms.remove(0)); - let mut focused_pane_id_in_stack = None; - for mut pane in panes_to_stack.drain(..) { - let pane_id = pane.pid(); - let stack_geom = stack_geoms.remove(0); - pane.set_geom(stack_geom); - self.tiled_panes.add_pane_with_existing_geom(pane_id, pane); - if self.tiled_panes.pane_id_is_focused(&pane_id) { - focused_pane_id_in_stack = Some(pane_id); + if self.pane_is_stacked(root_pane_id) { + if let Some(lowest_pane_id_in_stack) = self + .tiled_panes + .pane_ids_in_stack_of_pane_id(&root_pane_id) + .last() + { + // we get lowest_pane_id_in_stack so that we can extract the pane below and re-add + // it to its own stack - this has the effect of making it the last pane in the + // stack so that the rest of the panes will later be added below it - which makes + // sense from the perspective of the user + if let Some(pane) = self.extract_pane(root_pane_id, true) { + self.tiled_panes + .add_pane_to_stack(&lowest_pane_id_in_stack, pane); + } + } + for pane in panes_to_stack.drain(..) { + self.tiled_panes.add_pane_to_stack(&root_pane_id, pane); } - } - // if we had a focused pane in the stack, we expand it - if let Some(focused_pane_id_in_stack) = focused_pane_id_in_stack { - self.tiled_panes - .expand_pane_in_stack(focused_pane_id_in_stack); - } else if self.tiled_panes.pane_id_is_focused(&root_pane_id) { self.tiled_panes.expand_pane_in_stack(root_pane_id); + } else { + // + 1 for the root pane + let mut stack_geoms = self + .tiled_panes + .stack_panes(root_pane_id, panes_to_stack.len() + 1); + if stack_geoms.is_empty() { + log::error!("Failed to find room for stacked panes"); + return; + } + self.tiled_panes + .set_geom_for_pane_with_id(&root_pane_id, stack_geoms.remove(0)); + let mut focused_pane_id_in_stack = None; + for mut pane in panes_to_stack.drain(..) { + let pane_id = pane.pid(); + let stack_geom = stack_geoms.remove(0); + pane.set_geom(stack_geom); + self.tiled_panes.add_pane_with_existing_geom(pane_id, pane); + if self.tiled_panes.pane_id_is_focused(&pane_id) { + focused_pane_id_in_stack = Some(pane_id); + } + } + // if we had a focused pane in the stack, we expand it + if let Some(focused_pane_id_in_stack) = focused_pane_id_in_stack { + self.tiled_panes + .expand_pane_in_stack(focused_pane_id_in_stack); + } else if self.tiled_panes.pane_id_is_focused(&root_pane_id) { + self.tiled_panes.expand_pane_in_stack(root_pane_id); + } } } pub fn change_floating_pane_coordinates( @@ -4723,9 +4872,18 @@ impl Tab { (is_scrollback_editor, replaced_pane), ); } + fn pane_is_stacked(&self, pane_id: PaneId) -> bool { + self.get_pane_with_id(pane_id) + .map(|p| p.position_and_size().stacked.is_some()) + .unwrap_or(false) + } } -pub fn pane_info_for_pane(pane_id: &PaneId, pane: &Box) -> PaneInfo { +pub fn pane_info_for_pane( + pane_id: &PaneId, + pane: &Box, + current_pane_group: &HashMap>, +) -> PaneInfo { let mut pane_info = PaneInfo::default(); pane_info.pane_x = pane.x(); pane_info.pane_content_x = pane.get_content_x(); @@ -4741,6 +4899,17 @@ pub fn pane_info_for_pane(pane_id: &PaneId, pane: &Box) -> PaneInfo { pane_info.exited = pane.exited(); pane_info.exit_status = pane.exit_status(); pane_info.is_held = pane.is_held(); + let index_in_pane_group: BTreeMap = current_pane_group + .iter() + .filter_map(|(client_id, pane_ids)| { + if let Some(position) = pane_ids.iter().position(|p_id| p_id == &pane.pid()) { + Some((*client_id, position)) + } else { + None + } + }) + .collect(); + pane_info.index_in_pane_group = index_in_pane_group; match pane_id { PaneId::Terminal(terminal_id) => { diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 08c60cf6..acbe04c1 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -224,10 +224,13 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let current_group = Rc::new(RefCell::new(HashMap::new())); + let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let debug = false; let arrow_fonts = true; let styled_underlines = true; let explicitly_disable_kitty_keyboard_protocol = false; + let advanced_mouse_actions = true; let mut tab = Tab::new( index, position, @@ -256,6 +259,9 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { styled_underlines, explicitly_disable_kitty_keyboard_protocol, None, + current_group, + currently_marking_pane_group, + advanced_mouse_actions, ); tab.apply_layout( TiledPaneLayout::default(), @@ -292,10 +298,13 @@ fn create_new_tab_without_pane_frames(size: Size, default_mode: ModeInfo) -> Tab let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let current_group = Rc::new(RefCell::new(HashMap::new())); + let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let debug = false; let arrow_fonts = true; let styled_underlines = true; let explicitly_disable_kitty_keyboard_protocol = false; + let advanced_mouse_actions = true; let mut tab = Tab::new( index, position, @@ -324,6 +333,9 @@ fn create_new_tab_without_pane_frames(size: Size, default_mode: ModeInfo) -> Tab styled_underlines, explicitly_disable_kitty_keyboard_protocol, None, + current_group, + currently_marking_pane_group, + advanced_mouse_actions, ); tab.apply_layout( TiledPaneLayout::default(), @@ -375,10 +387,13 @@ fn create_new_tab_with_swap_layouts( let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let current_group = Rc::new(RefCell::new(HashMap::new())); + let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let debug = false; let arrow_fonts = true; let styled_underlines = true; let explicitly_disable_kitty_keyboard_protocol = false; + let advanced_mouse_actions = true; let mut tab = Tab::new( index, position, @@ -407,6 +422,9 @@ fn create_new_tab_with_swap_layouts( styled_underlines, explicitly_disable_kitty_keyboard_protocol, None, + current_group, + currently_marking_pane_group, + advanced_mouse_actions, ); let ( base_layout, @@ -459,10 +477,13 @@ fn create_new_tab_with_os_api( let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let current_group = Rc::new(RefCell::new(HashMap::new())); + let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let debug = false; let arrow_fonts = true; let styled_underlines = true; let explicitly_disable_kitty_keyboard_protocol = false; + let advanced_mouse_actions = true; let mut tab = Tab::new( index, position, @@ -491,6 +512,9 @@ fn create_new_tab_with_os_api( styled_underlines, explicitly_disable_kitty_keyboard_protocol, None, + current_group, + currently_marking_pane_group, + advanced_mouse_actions, ); tab.apply_layout( TiledPaneLayout::default(), @@ -529,10 +553,13 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str) let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); let layout = Layout::from_str(layout, "layout_file_name".into(), None, None).unwrap(); let (tab_layout, floating_panes_layout) = layout.new_tab(); + let current_group = Rc::new(RefCell::new(HashMap::new())); + let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let debug = false; let arrow_fonts = true; let styled_underlines = true; let explicitly_disable_kitty_keyboard_protocol = false; + let advanced_mouse_actions = true; let mut tab = Tab::new( index, position, @@ -561,6 +588,9 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str) styled_underlines, explicitly_disable_kitty_keyboard_protocol, None, + current_group, + currently_marking_pane_group, + advanced_mouse_actions, ); let pane_ids = tab_layout .extract_run_instructions() @@ -613,10 +643,13 @@ fn create_new_tab_with_mock_pty_writer( let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let current_group = Rc::new(RefCell::new(HashMap::new())); + let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let debug = false; let arrow_fonts = true; let styled_underlines = true; let explicitly_disable_kitty_keyboard_protocol = false; + let advanced_mouse_actions = true; let mut tab = Tab::new( index, position, @@ -645,6 +678,9 @@ fn create_new_tab_with_mock_pty_writer( styled_underlines, explicitly_disable_kitty_keyboard_protocol, None, + current_group, + currently_marking_pane_group, + advanced_mouse_actions, ); tab.apply_layout( TiledPaneLayout::default(), @@ -688,10 +724,13 @@ fn create_new_tab_with_sixel_support( let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); let copy_options = CopyOptions::default(); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let current_group = Rc::new(RefCell::new(HashMap::new())); + let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let debug = false; let arrow_fonts = true; let styled_underlines = true; let explicitly_disable_kitty_keyboard_protocol = false; + let advanced_mouse_actions = true; let mut tab = Tab::new( index, position, @@ -720,6 +759,9 @@ fn create_new_tab_with_sixel_support( styled_underlines, explicitly_disable_kitty_keyboard_protocol, None, + current_group, + currently_marking_pane_group, + advanced_mouse_actions, ); tab.apply_layout( TiledPaneLayout::default(), diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs index 05a9c84b..f80b870b 100644 --- a/zellij-server/src/tab/unit/tab_tests.rs +++ b/zellij-server/src/tab/unit/tab_tests.rs @@ -165,10 +165,13 @@ fn create_new_tab(size: Size, stacked_resize: bool) -> Tab { let copy_options = CopyOptions::default(); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let current_pane_group = Rc::new(RefCell::new(HashMap::new())); + let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let debug = false; let arrow_fonts = true; let styled_underlines = true; let explicitly_disable_kitty_keyboard_protocol = false; + let advanced_mouse_actions = true; let mut tab = Tab::new( index, position, @@ -197,6 +200,9 @@ fn create_new_tab(size: Size, stacked_resize: bool) -> Tab { styled_underlines, explicitly_disable_kitty_keyboard_protocol, None, + current_pane_group, + currently_marking_pane_group, + advanced_mouse_actions, ); tab.apply_layout( TiledPaneLayout::default(), @@ -232,10 +238,13 @@ fn create_new_tab_with_layout(size: Size, layout: TiledPaneLayout) -> Tab { let copy_options = CopyOptions::default(); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let current_pane_group = Rc::new(RefCell::new(HashMap::new())); + let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let debug = false; let arrow_fonts = true; let styled_underlines = true; let explicitly_disable_kitty_keyboard_protocol = false; + let advanced_mouse_actions = true; let mut tab = Tab::new( index, position, @@ -264,6 +273,9 @@ fn create_new_tab_with_layout(size: Size, layout: TiledPaneLayout) -> Tab { styled_underlines, explicitly_disable_kitty_keyboard_protocol, None, + current_pane_group, + currently_marking_pane_group, + advanced_mouse_actions, ); let mut new_terminal_ids = vec![]; for i in 0..layout.extract_run_instructions().len() { @@ -304,11 +316,14 @@ fn create_new_tab_with_cell_size( let copy_options = CopyOptions::default(); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let stacked_resize = Rc::new(RefCell::new(true)); + let current_pane_group = Rc::new(RefCell::new(HashMap::new())); + let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let debug = false; let arrow_fonts = true; let styled_underlines = true; let explicitly_disable_kitty_keyboard_protocol = false; - let stacked_resize = Rc::new(RefCell::new(true)); + let advanced_mouse_actions = true; let mut tab = Tab::new( index, position, @@ -337,6 +352,9 @@ fn create_new_tab_with_cell_size( styled_underlines, explicitly_disable_kitty_keyboard_protocol, None, + current_pane_group, + currently_marking_pane_group, + advanced_mouse_actions, ); tab.apply_layout( TiledPaneLayout::default(), diff --git a/zellij-server/src/ui/boundaries.rs b/zellij-server/src/ui/boundaries.rs index f0fdebd6..91dc76d4 100644 --- a/zellij-server/src/ui/boundaries.rs +++ b/zellij-server/src/ui/boundaries.rs @@ -33,7 +33,7 @@ pub type BoundaryType = &'static str; // easy way to refer to boundary_type abov pub struct BoundarySymbol { boundary_type: BoundaryType, invisible: bool, - color: Option, + color: Option<(PaletteColor, usize)>, // (color, color_precedence) } impl BoundarySymbol { @@ -41,10 +41,10 @@ impl BoundarySymbol { BoundarySymbol { boundary_type, invisible: false, - color: Some(PaletteColor::EightBit(colors::GRAY)), + color: Some((PaletteColor::EightBit(colors::GRAY), 0)), } } - pub fn color(&mut self, color: Option) -> Self { + pub fn color(&mut self, color: Option<(PaletteColor, usize)>) -> Self { self.color = color; *self } @@ -66,7 +66,7 @@ impl BoundarySymbol { TerminalCharacter::new_singlewidth_styled( character, RESET_STYLES - .foreground(self.color.map(|palette_color| palette_color.into())) + .foreground(self.color.map(|palette_color| palette_color.0.into())) .into(), ) }; @@ -79,7 +79,7 @@ impl Display for BoundarySymbol { match self.invisible { true => write!(f, " "), false => match self.color { - Some(color) => match color { + Some(color) => match color.0 { PaletteColor::Rgb((r, g, b)) => { write!(f, "{}", RGB(r, g, b).paint(self.boundary_type)) }, @@ -99,7 +99,17 @@ fn combine_symbols( ) -> Option { use boundary_type::*; let invisible = current_symbol.invisible || next_symbol.invisible; - let color = current_symbol.color.or(next_symbol.color); + let color = match (current_symbol.color, next_symbol.color) { + (Some(current_symbol_color), Some(next_symbol_color)) => { + let ret = if current_symbol_color.1 >= next_symbol_color.1 { + Some(current_symbol_color) + } else { + Some(next_symbol_color) + }; + ret + }, + _ => current_symbol.color.or(next_symbol.color), + }; match (current_symbol.boundary_type, next_symbol.boundary_type) { (CROSS, _) | (_, CROSS) => { // (┼, *) or (*, ┼) => Some(┼) @@ -447,7 +457,7 @@ impl Boundaries { pub fn add_rect( &mut self, rect: &dyn Pane, - color: Option, + color: Option<(PaletteColor, usize)>, // (color, color_precedence) pane_is_on_top_of_stack: bool, pane_is_on_bottom_of_stack: bool, pane_is_stacked_under: bool, diff --git a/zellij-server/src/ui/components/text.rs b/zellij-server/src/ui/components/text.rs index 56b1f428..c0088e60 100644 --- a/zellij-server/src/ui/components/text.rs +++ b/zellij-server/src/ui/components/text.rs @@ -59,13 +59,13 @@ pub fn stringify_text( } text_width += character_width; - if text.selected { + if text.selected || text.opaque { // we do this so that selected text will appear selected // even if it does not have color indices stringified.push_str(&format!("{}", base_text_style)); } - if !text.indices.is_empty() || text.selected { + if !text.indices.is_empty() || text.selected || text.opaque { let character_with_styling = color_index_character(character, i, &text, style, base_text_style); stringified.push_str(&character_with_styling); diff --git a/zellij-server/src/ui/pane_boundaries_frame.rs b/zellij-server/src/ui/pane_boundaries_frame.rs index 295e3bce..35979bd4 100644 --- a/zellij-server/src/ui/pane_boundaries_frame.rs +++ b/zellij-server/src/ui/pane_boundaries_frame.rs @@ -53,7 +53,7 @@ enum ExitStatus { pub struct FrameParams { pub focused_client: Option, - pub is_main_client: bool, + pub is_main_client: bool, // more accurately: is_focused_for_main_client pub other_focused_clients: Vec, pub style: Style, pub color: Option, @@ -63,6 +63,7 @@ pub struct FrameParams { pub should_draw_pane_frames: bool, pub pane_is_floating: bool, pub content_offset: Offset, + pub mouse_is_hovering_over_pane: bool, } #[derive(Default, PartialEq)] @@ -84,6 +85,7 @@ pub struct PaneFrame { is_pinned: bool, is_floating: bool, content_offset: Offset, + mouse_is_hovering_over_pane: bool, } impl PaneFrame { @@ -111,6 +113,7 @@ impl PaneFrame { is_pinned: false, is_floating: frame_params.pane_is_floating, content_offset: frame_params.content_offset, + mouse_is_hovering_over_pane: frame_params.mouse_is_hovering_over_pane, } } pub fn is_pinned(mut self, is_pinned: bool) -> Self { @@ -755,6 +758,34 @@ impl PaneFrame { }; Ok(res) } + fn render_mouse_shortcuts_undertitle(&self) -> Result> { + let max_undertitle_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners + let mut left_boundary = + foreground_color(self.get_corner(boundary_type::BOTTOM_LEFT), self.color); + let mut right_boundary = + foreground_color(self.get_corner(boundary_type::BOTTOM_RIGHT), self.color); + let res = if self.is_main_client { + self.empty_undertitle(max_undertitle_length) + } else { + let (mut hover_shortcuts, hover_shortcuts_len) = self.hover_shortcuts_part_full(); + if hover_shortcuts_len <= max_undertitle_length { + // render exit status and tips + let mut padding = String::new(); + for _ in hover_shortcuts_len..max_undertitle_length { + padding.push_str(boundary_type::HORIZONTAL); + } + let mut ret = vec![]; + ret.append(&mut left_boundary); + ret.append(&mut hover_shortcuts); + ret.append(&mut foreground_color(&padding, self.color)); + ret.append(&mut right_boundary); + ret + } else { + self.empty_undertitle(max_undertitle_length) + } + }; + Ok(res) + } pub fn clicked_on_pinned(&mut self, position: Position) -> bool { if self.is_floating { // TODO: this is not entirely accurate because our relative position calculation in @@ -810,7 +841,16 @@ impl PaneFrame { character_chunks.push(CharacterChunk::new(title, x, y)); } else if row == self.geom.rows - 1 { // bottom row - if self.exit_status.is_some() || self.is_first_run { + if self.mouse_is_hovering_over_pane && !self.is_main_client { + let x = self.geom.x; + let y = self.geom.y + row; + character_chunks.push(CharacterChunk::new( + self.render_mouse_shortcuts_undertitle() + .with_context(err_context)?, + x, + y, + )); + } else if self.exit_status.is_some() || self.is_first_run { let x = self.geom.x; let y = self.geom.y + row; character_chunks.push(CharacterChunk::new( @@ -964,6 +1004,26 @@ impl PaneFrame { + break_tip.len(), ) } + fn hover_shortcuts_part_full(&self) -> (Vec, usize) { + // (title part, length) + let mut hover_shortcuts = vec![]; + let alt_click_text = " Alt "; + let alt_click_tip = " - group,"; + let alt_right_click_text = " Alt "; + let alt_right_click_tip = " - ungroup all "; + + hover_shortcuts.append(&mut foreground_color(alt_click_text, self.color)); + hover_shortcuts.append(&mut foreground_color(alt_click_tip, self.color)); + hover_shortcuts.append(&mut foreground_color(alt_right_click_text, self.color)); + hover_shortcuts.append(&mut foreground_color(alt_right_click_tip, self.color)); + ( + hover_shortcuts, + alt_click_text.chars().count() + + alt_click_tip.chars().count() + + alt_right_click_text.chars().count() + + alt_right_click_tip.chars().count(), + ) + } fn empty_undertitle(&self, max_undertitle_length: usize) -> Vec { let mut left_boundary = foreground_color(self.get_corner(boundary_type::BOTTOM_LEFT), self.color); diff --git a/zellij-server/src/ui/pane_contents_and_ui.rs b/zellij-server/src/ui/pane_contents_and_ui.rs index c47e68d8..d3d99f0e 100644 --- a/zellij-server/src/ui/pane_contents_and_ui.rs +++ b/zellij-server/src/ui/pane_contents_and_ui.rs @@ -4,7 +4,7 @@ use crate::tab::Pane; use crate::ui::boundaries::Boundaries; use crate::ui::pane_boundaries_frame::FrameParams; use crate::ClientId; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use zellij_utils::data::{client_id_to_colors, InputMode, PaletteColor, Style}; use zellij_utils::errors::prelude::*; pub struct PaneContentsAndUi<'a> { @@ -17,6 +17,8 @@ pub struct PaneContentsAndUi<'a> { pane_is_stacked_under: bool, pane_is_stacked_over: bool, should_draw_pane_frames: bool, + mouse_is_hovering_over_pane_for_clients: HashSet, + current_pane_group: HashMap>, } impl<'a> PaneContentsAndUi<'a> { @@ -30,6 +32,8 @@ impl<'a> PaneContentsAndUi<'a> { pane_is_stacked_under: bool, pane_is_stacked_over: bool, should_draw_pane_frames: bool, + mouse_hover_pane_id: &HashMap, + current_pane_group: HashMap>, ) -> Self { let mut focused_clients: Vec = active_panes .iter() @@ -37,6 +41,16 @@ impl<'a> PaneContentsAndUi<'a> { .map(|(c_id, _p_id)| *c_id) .collect(); focused_clients.sort_unstable(); + let mouse_is_hovering_over_pane_for_clients = mouse_hover_pane_id + .iter() + .filter_map(|(client_id, pane_id)| { + if pane_id == &pane.pid() { + Some(*client_id) + } else { + None + } + }) + .collect(); PaneContentsAndUi { pane, output, @@ -47,6 +61,8 @@ impl<'a> PaneContentsAndUi<'a> { pane_is_stacked_under, pane_is_stacked_over, should_draw_pane_frames, + mouse_is_hovering_over_pane_for_clients, + current_pane_group, } } pub fn render_pane_contents_to_multiple_clients( @@ -217,13 +233,16 @@ impl<'a> PaneContentsAndUi<'a> { is_main_client: pane_focused_for_client_id, other_focused_clients: vec![], style: self.style, - color: frame_color, + color: frame_color.map(|c| c.0), other_cursors_exist_in_session: false, pane_is_stacked_over: self.pane_is_stacked_over, pane_is_stacked_under: self.pane_is_stacked_under, should_draw_pane_frames: self.should_draw_pane_frames, pane_is_floating, content_offset: self.pane.get_content_offset(), + mouse_is_hovering_over_pane: self + .mouse_is_hovering_over_pane_for_clients + .contains(&client_id), } } else { FrameParams { @@ -231,13 +250,16 @@ impl<'a> PaneContentsAndUi<'a> { is_main_client: pane_focused_for_client_id, other_focused_clients, style: self.style, - color: frame_color, + color: frame_color.map(|c| c.0), other_cursors_exist_in_session: self.multiple_users_exist_in_session, pane_is_stacked_over: self.pane_is_stacked_over, pane_is_stacked_under: self.pane_is_stacked_under, should_draw_pane_frames: self.should_draw_pane_frames, pane_is_floating, content_offset: self.pane.get_content_offset(), + mouse_is_hovering_over_pane: self + .mouse_is_hovering_over_pane_for_clients + .contains(&client_id), } }; @@ -279,27 +301,48 @@ impl<'a> PaneContentsAndUi<'a> { client_id: ClientId, mode: InputMode, session_is_mirrored: bool, - ) -> Option { + ) -> Option<(PaletteColor, usize)> { + // (color, color_precedence) (the color_precedence is used + // for the no-pane-frames mode) let pane_focused_for_client_id = self.focused_clients.contains(&client_id); - if let Some(override_color) = self.pane.frame_color_override() { - Some(override_color) + let pane_is_in_group = self + .current_pane_group + .get(&client_id) + .map(|p| p.contains(&self.pane.pid())) + .unwrap_or(false); + if self.pane.frame_color_override().is_some() && !pane_is_in_group { + self.pane + .frame_color_override() + .map(|override_color| (override_color, 4)) + } else if pane_is_in_group && !pane_focused_for_client_id { + Some((self.style.colors.frame_highlight.emphasis_0, 2)) + } else if pane_is_in_group && pane_focused_for_client_id { + Some((self.style.colors.frame_highlight.emphasis_1, 3)) } else if pane_focused_for_client_id { match mode { InputMode::Normal | InputMode::Locked => { if session_is_mirrored || !self.multiple_users_exist_in_session { - Some(self.style.colors.frame_selected.base) + Some((self.style.colors.frame_selected.base, 3)) } else { let colors = client_id_to_colors( client_id, self.style.colors.multiplayer_user_colors, ); - colors.map(|colors| colors.0) + colors.map(|colors| (colors.0, 3)) } }, - _ => Some(self.style.colors.frame_highlight.base), + _ => Some((self.style.colors.frame_highlight.base, 3)), } + } else if self + .mouse_is_hovering_over_pane_for_clients + .contains(&client_id) + { + Some((self.style.colors.frame_highlight.base, 1)) } else { - self.style.colors.frame_unselected.map(|frame| frame.base) + self.style + .colors + .frame_unselected + .map(|frame| (frame.base, 0)) } } } diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 02ff99fc..9e744097 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -19,9 +19,11 @@ use zellij_utils::input::layout::{ FloatingPaneLayout, Layout, PluginAlias, PluginUserConfiguration, Run, RunPlugin, RunPluginLocation, RunPluginOrAlias, SplitDirection, SplitSize, TiledPaneLayout, }; +use zellij_utils::input::mouse::MouseEvent; use zellij_utils::input::options::Options; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; +use zellij_utils::position::Position; use crate::background_jobs::BackgroundJob; use crate::pty_writer::PtyWriteInstruction; @@ -243,7 +245,7 @@ impl ServerOsApi for FakeInputOutput { } } -fn create_new_screen(size: Size) -> Screen { +fn create_new_screen(size: Size, advanced_mouse_actions: bool) -> Screen { let mut bus: Bus = Bus::empty(); let fake_os_input = FakeInputOutput::default(); bus.os_input = Some(Box::new(fake_os_input)); @@ -293,6 +295,7 @@ fn create_new_screen(size: Size) -> Screen { explicitly_disable_kitty_keyboard_protocol, stacked_resize, None, + advanced_mouse_actions, ); screen } @@ -316,6 +319,7 @@ struct MockScreen { pub config_options: Options, pub session_metadata: SessionMetaData, pub config: Config, + advanced_mouse_actions: bool, last_opened_tab_index: Option, } @@ -325,7 +329,8 @@ impl MockScreen { initial_layout: Option, initial_floating_panes_layout: Vec, ) -> std::thread::JoinHandle<()> { - let config = self.config.clone(); + let mut config = self.config.clone(); + config.options.advanced_mouse_actions = Some(self.advanced_mouse_actions); let client_attributes = self.client_attributes.clone(); let screen_bus = Bus::new( vec![self.screen_receiver.take().unwrap()], @@ -626,8 +631,12 @@ impl MockScreen { session_metadata, last_opened_tab_index: None, config: Config::default(), + advanced_mouse_actions: true, } } + pub fn set_advanced_hover_effects(&mut self, advanced_mouse_actions: bool) { + self.advanced_mouse_actions = advanced_mouse_actions; + } } macro_rules! log_actions_in_thread { @@ -682,7 +691,7 @@ fn open_new_tab() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 0); new_tab(&mut screen, 2, 1); @@ -701,7 +710,7 @@ pub fn switch_to_prev_tab() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 1); new_tab(&mut screen, 2, 2); @@ -720,7 +729,7 @@ pub fn switch_to_next_tab() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 1); new_tab(&mut screen, 2, 2); @@ -740,7 +749,7 @@ pub fn switch_to_tab_name() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 1); new_tab(&mut screen, 2, 2); @@ -774,7 +783,7 @@ pub fn close_tab() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 1); new_tab(&mut screen, 2, 2); @@ -794,7 +803,7 @@ pub fn close_the_middle_tab() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 1); new_tab(&mut screen, 2, 2); @@ -816,7 +825,7 @@ fn move_focus_left_at_left_screen_edge_changes_tab() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 1); new_tab(&mut screen, 2, 2); @@ -848,10 +857,13 @@ fn basic_move_of_active_tab_to_left() { } fn create_fixed_size_screen() -> Screen { - create_new_screen(Size { - cols: 121, - rows: 20, - }) + create_new_screen( + Size { + cols: 121, + rows: 20, + }, + true, + ) } #[test] @@ -981,7 +993,7 @@ fn move_focus_right_at_right_screen_edge_changes_tab() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 1); new_tab(&mut screen, 2, 2); @@ -1002,7 +1014,7 @@ pub fn toggle_to_previous_tab_simple() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(position_and_size); + let mut screen = create_new_screen(position_and_size, true); new_tab(&mut screen, 1, 1); new_tab(&mut screen, 2, 2); @@ -1030,7 +1042,7 @@ pub fn toggle_to_previous_tab_create_tabs_only() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(position_and_size); + let mut screen = create_new_screen(position_and_size, true); new_tab(&mut screen, 1, 0); new_tab(&mut screen, 2, 1); @@ -1080,7 +1092,7 @@ pub fn toggle_to_previous_tab_delete() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(position_and_size); + let mut screen = create_new_screen(position_and_size, true); new_tab(&mut screen, 1, 0); new_tab(&mut screen, 2, 1); @@ -1176,7 +1188,7 @@ fn switch_to_tab_with_fullscreen() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 1); { @@ -1212,7 +1224,7 @@ fn update_screen_pixel_dimensions() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); let initial_pixel_dimensions = screen.pixel_dimensions; screen.update_pixel_dimensions(PixelDimensions { character_cell_size: Some(SizeInPixels { @@ -1291,7 +1303,7 @@ fn attach_after_first_tab_closed() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 0); { @@ -1314,7 +1326,7 @@ fn open_new_floating_pane_with_custom_coordinates() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 0); let active_tab = screen.get_active_tab_mut(1).unwrap(); @@ -1349,7 +1361,7 @@ fn open_new_floating_pane_with_custom_coordinates_exceeding_viewport() { cols: 121, rows: 20, }; - let mut screen = create_new_screen(size); + let mut screen = create_new_screen(size, true); new_tab(&mut screen, 1, 0); let active_tab = screen.get_active_tab_mut(1).unwrap(); @@ -1378,6 +1390,257 @@ fn open_new_floating_pane_with_custom_coordinates_exceeding_viewport() { assert_eq!(active_pane.cols(), 10, "columns set properly"); } +#[test] +pub fn mouse_hover_effect() { + let size = Size { + cols: 130, + rows: 20, + }; + let client_id = 1; + let mut initial_layout = TiledPaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + let screen_thread = mock_screen.run(Some(initial_layout), vec![]); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_thread = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let hover_mouse_event_1 = MouseEvent::new_buttonless_motion(Position::new(5, 70)); + let _ = mock_screen.to_screen.send(ScreenInstruction::MouseEvent( + hover_mouse_event_1, + client_id, + )); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_thread, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } +} + +#[test] +pub fn disabled_mouse_hover_effect() { + let size = Size { + cols: 130, + rows: 20, + }; + let client_id = 1; + let mut initial_layout = TiledPaneLayout::default(); + initial_layout.children_split_direction = SplitDirection::Vertical; + initial_layout.children = vec![TiledPaneLayout::default(), TiledPaneLayout::default()]; + let mut mock_screen = MockScreen::new(size); + mock_screen.set_advanced_hover_effects(false); + let screen_thread = mock_screen.run(Some(initial_layout), vec![]); + let received_server_instructions = Arc::new(Mutex::new(vec![])); + let server_receiver = mock_screen.server_receiver.take().unwrap(); + let server_thread = log_actions_in_thread!( + received_server_instructions, + ServerInstruction::KillSession, + server_receiver + ); + let hover_mouse_event_1 = MouseEvent::new_buttonless_motion(Position::new(5, 70)); + let _ = mock_screen.to_screen.send(ScreenInstruction::MouseEvent( + hover_mouse_event_1, + client_id, + )); + std::thread::sleep(std::time::Duration::from_millis(100)); + mock_screen.teardown(vec![server_thread, screen_thread]); + let snapshots = take_snapshots_and_cursor_coordinates_from_render_events( + received_server_instructions.lock().unwrap().iter(), + size, + ); + for (_cursor_coordinates, snapshot) in snapshots { + assert_snapshot!(format!("{}", snapshot)); + } +} + +#[test] +fn group_panes_with_mouse() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut screen = create_new_screen(size, true); + + new_tab(&mut screen, 1, 0); + new_tab(&mut screen, 2, 1); + screen.handle_mouse_event( + MouseEvent::new_left_press_with_alt_event(Position::new(2, 80)), + client_id, + ); + + assert_eq!( + screen.current_pane_group.borrow().get(&client_id), + Some(&vec![PaneId::Terminal(2)]), + "Pane Id added to client's pane group" + ); + + screen.handle_mouse_event( + MouseEvent::new_left_press_with_alt_event(Position::new(2, 80)), + client_id, + ); + + assert_eq!( + screen.current_pane_group.borrow().get(&client_id), + Some(&vec![]), + "Pane Id removed from client's pane group" + ); +} + +#[test] +fn group_panes_with_keyboard() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut screen = create_new_screen(size, true); + + new_tab(&mut screen, 1, 0); + new_tab(&mut screen, 2, 1); + let _ = screen.toggle_pane_in_group(client_id); + + assert_eq!( + screen.current_pane_group.borrow().get(&client_id), + Some(&vec![PaneId::Terminal(2)]), + "Pane Id added to client's pane group" + ); + + let _ = screen.toggle_pane_in_group(client_id); + + assert_eq!( + screen.current_pane_group.borrow().get(&client_id), + Some(&vec![]), + "Pane Id removed from client's pane group" + ); +} + +#[test] +fn group_panes_following_focus() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut screen = create_new_screen(size, true); + + new_tab(&mut screen, 1, 0); + + { + let active_tab = screen.get_active_tab_mut(client_id).unwrap(); + let should_float = Some(false); + for i in 2..5 { + active_tab + .new_pane( + PaneId::Terminal(i), + None, + should_float, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + } + } + { + screen.toggle_group_marking(client_id).unwrap(); + screen + .get_active_tab_mut(client_id) + .unwrap() + .move_focus_up(client_id) + .unwrap(); + screen.add_active_pane_to_group_if_marking(&client_id); + assert_eq!( + screen.current_pane_group.borrow().get(&client_id), + Some(&vec![PaneId::Terminal(4), PaneId::Terminal(3)]), + "Pane Id of focused pane and newly focused pane above added to pane group" + ); + } + { + let _ = screen.toggle_group_marking(client_id); + screen + .get_active_tab_mut(client_id) + .unwrap() + .move_focus_up(client_id) + .unwrap(); + let _ = screen.add_active_pane_to_group_if_marking(&client_id); + assert_eq!(screen.current_pane_group.borrow().get(&client_id), Some(&vec![PaneId::Terminal(4), PaneId::Terminal(3)]), "Pane Id of newly focused pane not added to group after the group marking was toggled off"); + } +} + +#[test] +fn break_group_with_mouse() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + let mut screen = create_new_screen(size, true); + + new_tab(&mut screen, 1, 0); + + { + let active_tab = screen.get_active_tab_mut(client_id).unwrap(); + let should_float = Some(false); + for i in 2..5 { + active_tab + .new_pane( + PaneId::Terminal(i), + None, + should_float, + None, + None, + false, + Some(client_id), + ) + .unwrap(); + } + } + { + screen.toggle_group_marking(client_id).unwrap(); + screen + .get_active_tab_mut(client_id) + .unwrap() + .move_focus_up(client_id) + .unwrap(); + screen.add_active_pane_to_group_if_marking(&client_id); + screen + .get_active_tab_mut(client_id) + .unwrap() + .move_focus_up(client_id) + .unwrap(); + screen.add_active_pane_to_group_if_marking(&client_id); + assert_eq!( + screen.current_pane_group.borrow().get(&client_id), + Some(&vec![ + PaneId::Terminal(4), + PaneId::Terminal(3), + PaneId::Terminal(2) + ]), + "Group contains 3 panes" + ); + } + + screen.handle_mouse_event( + MouseEvent::new_right_press_with_alt_event(Position::new(2, 80)), + client_id, + ); + assert_eq!( + screen.current_pane_group.borrow().get(&client_id), + Some(&vec![]), + "Group cleared by mouse event" + ); +} + // Following are tests for sending CLI actions // these tests are only partially relevant to Screen // and are included here for two reasons: diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__disabled_mouse_hover_effect.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__disabled_mouse_hover_effect.snap new file mode 100644 index 00000000..9be95a3f --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__disabled_mouse_hover_effect.snap @@ -0,0 +1,24 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────┐┌ Pane #2 ──────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────────┘└───────────────────────────────────────────────────────────────┘ diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__mouse_hover_effect-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__mouse_hover_effect-2.snap new file mode 100644 index 00000000..0b636b2e --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__mouse_hover_effect-2.snap @@ -0,0 +1,24 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────┐┌ Pane #2 ──────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────────┘└ Alt - group, Alt - ungroup all ─────────┘ diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__mouse_hover_effect.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__mouse_hover_effect.snap new file mode 100644 index 00000000..9be95a3f --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__mouse_hover_effect.snap @@ -0,0 +1,24 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ──────────────────────────────────────────────────────┐┌ Pane #2 ──────────────────────────────────────────────────────┐ +01 (C): │ ││ │ +02 (C): │ ││ │ +03 (C): │ ││ │ +04 (C): │ ││ │ +05 (C): │ ││ │ +06 (C): │ ││ │ +07 (C): │ ││ │ +08 (C): │ ││ │ +09 (C): │ ││ │ +10 (C): │ ││ │ +11 (C): │ ││ │ +12 (C): │ ││ │ +13 (C): │ ││ │ +14 (C): │ ││ │ +15 (C): │ ││ │ +16 (C): │ ││ │ +17 (C): │ ││ │ +18 (C): │ ││ │ +19 (C): └───────────────────────────────────────────────────────────────┘└───────────────────────────────────────────────────────────────┘ diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_stack_panes_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_stack_panes_action-2.snap index c26d308f..042e0d34 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_stack_panes_action-2.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_stack_panes_action-2.snap @@ -1,16 +1,14 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 3687 expression: "format!(\"{}\", snapshot)" --- 00 (C): ┌ Pane #1 ─────┐┌ Pane #2 ─────────────────────────────────────┐┌ Pane #5 ─────┐ -01 (C): │ │┌ Pane #3 ─────────────────────────────────────┐│ │ -02 (C): │ │┌ Pane #4 ─────────────────────────────────────┐│ │ +01 (C): │ ││ ││ │ +02 (C): │ ││ ││ │ 03 (C): │ ││ ││ │ 04 (C): │ ││ ││ │ 05 (C): │ ││ ││ │ 06 (C): │ ││ ││ │ -07 (C): │ ││ ││ │ -08 (C): │ ││ ││ │ -09 (C): └──────────────┘└──────────────────────────────────────────────┘└──────────────┘ - +07 (C): │ │└──────────────────────────────────────────────┘│ │ +08 (C): │ │└ Pane #3 ─────────────────────────────────────┘│ │ +09 (C): └──────────────┘└ Pane #4 ─────────────────────────────────────┘└──────────────┘ diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap index a75f02a9..dab5b941 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap @@ -1,6 +1,5 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 2183 -expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())" --- -[StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ApplyCachedResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([102, 111, 111], 0), Write([102, 111, 111], 1), ApplyCachedResizes, Exit] +[StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ApplyCachedResizes, ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ApplyCachedResizes, ApplyCachedResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([102, 111, 111], 0), Write([102, 111, 111], 1), ApplyCachedResizes, Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_action_to_screen.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_action_to_screen.snap index 153ca11f..79a0b61c 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_action_to_screen.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_action_to_screen.snap @@ -1,6 +1,5 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 1423 -expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())" --- -[StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ApplyCachedResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([102, 111, 111], 0), ApplyCachedResizes, Exit] +[StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ApplyCachedResizes, ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ApplyCachedResizes, ApplyCachedResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([102, 111, 111], 0), ApplyCachedResizes, Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_chars_action_to_screen.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_chars_action_to_screen.snap index 34a3eb4e..995d89bc 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_chars_action_to_screen.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_write_chars_action_to_screen.snap @@ -1,6 +1,5 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 1397 -expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())" +expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())" --- -[StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ApplyCachedResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([105, 110, 112, 117, 116, 32, 102, 114, 111, 109, 32, 116, 104, 101, 32, 99, 108, 105], 0), ApplyCachedResizes, Exit] +[StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ApplyCachedResizes, ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ApplyCachedResizes, ApplyCachedResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([105, 110, 112, 117, 116, 32, 102, 114, 111, 109, 32, 116, 104, 101, 32, 99, 108, 105], 0), ApplyCachedResizes, Exit] diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index bdb6c672..db6ea180 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -1273,6 +1273,46 @@ pub fn change_floating_panes_coordinates( unsafe { host_run_plugin_command() }; } +pub fn group_and_ungroup_panes(pane_ids_to_group: Vec, pane_ids_to_ungroup: Vec) { + let plugin_command = + PluginCommand::GroupAndUngroupPanes(pane_ids_to_group, pane_ids_to_ungroup); + let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); + object_to_stdout(&protobuf_plugin_command.encode_to_vec()); + unsafe { host_run_plugin_command() }; +} + +pub fn highlight_and_unhighlight_panes( + pane_ids_to_highlight: Vec, + pane_ids_to_unhighlight: Vec, +) { + let plugin_command = + PluginCommand::HighlightAndUnhighlightPanes(pane_ids_to_highlight, pane_ids_to_unhighlight); + let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); + object_to_stdout(&protobuf_plugin_command.encode_to_vec()); + unsafe { host_run_plugin_command() }; +} + +pub fn close_multiple_panes(pane_ids: Vec) { + let plugin_command = PluginCommand::CloseMultiplePanes(pane_ids); + let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); + object_to_stdout(&protobuf_plugin_command.encode_to_vec()); + unsafe { host_run_plugin_command() }; +} + +pub fn float_multiple_panes(pane_ids: Vec) { + let plugin_command = PluginCommand::FloatMultiplePanes(pane_ids); + let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); + object_to_stdout(&protobuf_plugin_command.encode_to_vec()); + unsafe { host_run_plugin_command() }; +} + +pub fn embed_multiple_panes(pane_ids: Vec) { + let plugin_command = PluginCommand::EmbedMultiplePanes(pane_ids); + 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)] diff --git a/zellij-tile/src/ui_components/text.rs b/zellij-tile/src/ui_components/text.rs index 4b49b5cc..228b8975 100644 --- a/zellij-tile/src/ui_components/text.rs +++ b/zellij-tile/src/ui_components/text.rs @@ -84,14 +84,14 @@ impl Text { let mut prefix = "".to_owned(); - if self.opaque { - prefix = format!("z{}", prefix); - } - if self.selected { prefix = format!("x{}", prefix); } + if self.opaque { + prefix = format!("z{}", prefix); + } + format!("{}{}{}", prefix, indices, text) } pub fn len(&self) -> usize { diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index 298127b0..bdfedc86 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -181,6 +181,14 @@ keybinds { bind "Alt -" { Resize "Decrease"; } bind "Alt [" { PreviousSwapLayout; } bind "Alt ]" { NextSwapLayout; } + bind "Alt m" { + LaunchOrFocusPlugin "zellij:multiple-select" { + floating true + move_to_focused_tab true + } + } + bind "Alt p" { TogglePaneInGroup; } + bind "Alt Shift p" { ToggleGroupMarking; } } shared_except "normal" "locked" { bind "Enter" "Esc" { SwitchToMode "Normal"; } @@ -427,7 +435,7 @@ load_plugins { // // show_release_notes false -// Whether to show startup tips on session start +// Whether to enable mouse hover effects and pane grouping functionality // Default: true // -// show_startup_tips false +// advanced_mouse_actions false diff --git a/zellij-utils/assets/plugins/about.wasm b/zellij-utils/assets/plugins/about.wasm index 24e8a3ff..c7355f84 100755 Binary files a/zellij-utils/assets/plugins/about.wasm and b/zellij-utils/assets/plugins/about.wasm differ diff --git a/zellij-utils/assets/plugins/compact-bar.wasm b/zellij-utils/assets/plugins/compact-bar.wasm index b6be5a06..fab6fbac 100755 Binary files a/zellij-utils/assets/plugins/compact-bar.wasm and b/zellij-utils/assets/plugins/compact-bar.wasm differ diff --git a/zellij-utils/assets/plugins/configuration.wasm b/zellij-utils/assets/plugins/configuration.wasm index f78e01c4..0fd47e93 100755 Binary files a/zellij-utils/assets/plugins/configuration.wasm and b/zellij-utils/assets/plugins/configuration.wasm differ diff --git a/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm b/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm index 032a1ea0..e3135dea 100755 Binary files a/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm and b/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm differ diff --git a/zellij-utils/assets/plugins/multiple-select.wasm b/zellij-utils/assets/plugins/multiple-select.wasm new file mode 100755 index 00000000..c3b73e96 Binary files /dev/null and b/zellij-utils/assets/plugins/multiple-select.wasm differ diff --git a/zellij-utils/assets/plugins/plugin-manager.wasm b/zellij-utils/assets/plugins/plugin-manager.wasm index eb737b69..87617d79 100755 Binary files a/zellij-utils/assets/plugins/plugin-manager.wasm and b/zellij-utils/assets/plugins/plugin-manager.wasm differ diff --git a/zellij-utils/assets/plugins/session-manager.wasm b/zellij-utils/assets/plugins/session-manager.wasm index a12ad35a..fb2ac62b 100755 Binary files a/zellij-utils/assets/plugins/session-manager.wasm and b/zellij-utils/assets/plugins/session-manager.wasm differ diff --git a/zellij-utils/assets/plugins/status-bar.wasm b/zellij-utils/assets/plugins/status-bar.wasm index b052ca29..4465559e 100755 Binary files a/zellij-utils/assets/plugins/status-bar.wasm and b/zellij-utils/assets/plugins/status-bar.wasm differ diff --git a/zellij-utils/assets/plugins/strider.wasm b/zellij-utils/assets/plugins/strider.wasm index 58a25484..5e57d392 100755 Binary files a/zellij-utils/assets/plugins/strider.wasm and b/zellij-utils/assets/plugins/strider.wasm differ diff --git a/zellij-utils/assets/plugins/tab-bar.wasm b/zellij-utils/assets/plugins/tab-bar.wasm index b9ef60d3..07ba9f24 100755 Binary files a/zellij-utils/assets/plugins/tab-bar.wasm and b/zellij-utils/assets/plugins/tab-bar.wasm differ diff --git a/zellij-utils/assets/prost/api.action.rs b/zellij-utils/assets/prost/api.action.rs index a93dde46..97b88452 100644 --- a/zellij-utils/assets/prost/api.action.rs +++ b/zellij-utils/assets/prost/api.action.rs @@ -1,311 +1,308 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Action { - #[prost(enumeration = "ActionName", tag = "1")] + #[prost(enumeration="ActionName", tag="1")] pub name: i32, - #[prost( - oneof = "action::OptionalPayload", - 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, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49" - )] + #[prost(oneof="action::OptionalPayload", 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, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49")] pub optional_payload: ::core::option::Option, } /// Nested message and enum types in `Action`. pub mod action { #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] +#[derive(Clone, PartialEq, ::prost::Oneof)] pub enum OptionalPayload { - #[prost(message, tag = "2")] + #[prost(message, tag="2")] SwitchToModePayload(super::SwitchToModePayload), - #[prost(message, tag = "3")] + #[prost(message, tag="3")] WritePayload(super::WritePayload), - #[prost(message, tag = "4")] + #[prost(message, tag="4")] WriteCharsPayload(super::WriteCharsPayload), - #[prost(message, tag = "5")] + #[prost(message, tag="5")] SwitchModeForAllClientsPayload(super::SwitchToModePayload), - #[prost(message, tag = "6")] + #[prost(message, tag="6")] ResizePayload(super::super::resize::Resize), - #[prost(enumeration = "super::super::resize::ResizeDirection", tag = "7")] + #[prost(enumeration="super::super::resize::ResizeDirection", tag="7")] MoveFocusPayload(i32), - #[prost(enumeration = "super::super::resize::ResizeDirection", tag = "8")] + #[prost(enumeration="super::super::resize::ResizeDirection", tag="8")] MoveFocusOrTabPayload(i32), - #[prost(message, tag = "9")] + #[prost(message, tag="9")] MovePanePayload(super::MovePanePayload), - #[prost(message, tag = "10")] + #[prost(message, tag="10")] DumpScreenPayload(super::DumpScreenPayload), - #[prost(message, tag = "11")] + #[prost(message, tag="11")] ScrollUpAtPayload(super::ScrollAtPayload), - #[prost(message, tag = "12")] + #[prost(message, tag="12")] ScrollDownAtPayload(super::ScrollAtPayload), - #[prost(message, tag = "13")] + #[prost(message, tag="13")] NewPanePayload(super::NewPanePayload), - #[prost(message, tag = "14")] + #[prost(message, tag="14")] EditFilePayload(super::EditFilePayload), - #[prost(message, tag = "15")] + #[prost(message, tag="15")] NewFloatingPanePayload(super::NewFloatingPanePayload), - #[prost(message, tag = "16")] + #[prost(message, tag="16")] NewTiledPanePayload(super::NewTiledPanePayload), - #[prost(bytes, tag = "17")] + #[prost(bytes, tag="17")] PaneNameInputPayload(::prost::alloc::vec::Vec), - #[prost(uint32, tag = "18")] + #[prost(uint32, tag="18")] GoToTabPayload(u32), - #[prost(message, tag = "19")] + #[prost(message, tag="19")] GoToTabNamePayload(super::GoToTabNamePayload), - #[prost(bytes, tag = "20")] + #[prost(bytes, tag="20")] TabNameInputPayload(::prost::alloc::vec::Vec), - #[prost(message, tag = "21")] + #[prost(message, tag="21")] RunPayload(super::RunCommandAction), - #[prost(message, tag = "22")] + #[prost(message, tag="22")] LeftClickPayload(super::Position), - #[prost(message, tag = "23")] + #[prost(message, tag="23")] RightClickPayload(super::Position), - #[prost(message, tag = "24")] + #[prost(message, tag="24")] MiddleClickPayload(super::Position), - #[prost(message, tag = "25")] + #[prost(message, tag="25")] LaunchOrFocusPluginPayload(super::LaunchOrFocusPluginPayload), - #[prost(message, tag = "26")] + #[prost(message, tag="26")] LeftMouseReleasePayload(super::Position), - #[prost(message, tag = "27")] + #[prost(message, tag="27")] RightMouseReleasePayload(super::Position), - #[prost(message, tag = "28")] + #[prost(message, tag="28")] MiddleMouseReleasePayload(super::Position), - #[prost(bytes, tag = "32")] + #[prost(bytes, tag="32")] SearchInputPayload(::prost::alloc::vec::Vec), - #[prost(enumeration = "super::SearchDirection", tag = "33")] + #[prost(enumeration="super::SearchDirection", tag="33")] SearchPayload(i32), - #[prost(enumeration = "super::SearchOption", tag = "34")] + #[prost(enumeration="super::SearchOption", tag="34")] SearchToggleOptionPayload(i32), - #[prost(message, tag = "35")] + #[prost(message, tag="35")] NewTiledPluginPanePayload(super::NewPluginPanePayload), - #[prost(message, tag = "36")] + #[prost(message, tag="36")] NewFloatingPluginPanePayload(super::NewPluginPanePayload), - #[prost(string, tag = "37")] + #[prost(string, tag="37")] StartOrReloadPluginPayload(::prost::alloc::string::String), - #[prost(uint32, tag = "38")] + #[prost(uint32, tag="38")] CloseTerminalPanePayload(u32), - #[prost(uint32, tag = "39")] + #[prost(uint32, tag="39")] ClosePluginPanePayload(u32), - #[prost(message, tag = "40")] + #[prost(message, tag="40")] FocusTerminalPaneWithIdPayload(super::PaneIdAndShouldFloat), - #[prost(message, tag = "41")] + #[prost(message, tag="41")] FocusPluginPaneWithIdPayload(super::PaneIdAndShouldFloat), - #[prost(message, tag = "42")] + #[prost(message, tag="42")] RenameTerminalPanePayload(super::IdAndName), - #[prost(message, tag = "43")] + #[prost(message, tag="43")] RenamePluginPanePayload(super::IdAndName), - #[prost(message, tag = "44")] + #[prost(message, tag="44")] RenameTabPayload(super::IdAndName), - #[prost(string, tag = "45")] + #[prost(string, tag="45")] RenameSessionPayload(::prost::alloc::string::String), - #[prost(message, tag = "46")] + #[prost(message, tag="46")] LaunchPluginPayload(super::LaunchOrFocusPluginPayload), - #[prost(message, tag = "47")] + #[prost(message, tag="47")] MessagePayload(super::CliPipePayload), - #[prost(enumeration = "super::MoveTabDirection", tag = "48")] + #[prost(enumeration="super::MoveTabDirection", tag="48")] MoveTabPayload(i32), - #[prost(message, tag = "49")] + #[prost(message, tag="49")] MouseEventPayload(super::MouseEventPayload), } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CliPipePayload { - #[prost(string, optional, tag = "1")] + #[prost(string, optional, tag="1")] pub name: ::core::option::Option<::prost::alloc::string::String>, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub payload: ::prost::alloc::string::String, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub args: ::prost::alloc::vec::Vec, - #[prost(string, optional, tag = "4")] + #[prost(string, optional, tag="4")] pub plugin: ::core::option::Option<::prost::alloc::string::String>, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct IdAndName { - #[prost(bytes = "vec", tag = "1")] + #[prost(bytes="vec", tag="1")] pub name: ::prost::alloc::vec::Vec, - #[prost(uint32, tag = "2")] + #[prost(uint32, tag="2")] pub id: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaneIdAndShouldFloat { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub pane_id: u32, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub should_float: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct NewPluginPanePayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub plugin_url: ::prost::alloc::string::String, - #[prost(string, optional, tag = "2")] + #[prost(string, optional, tag="2")] pub pane_name: ::core::option::Option<::prost::alloc::string::String>, - #[prost(bool, tag = "3")] + #[prost(bool, tag="3")] pub skip_plugin_cache: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct LaunchOrFocusPluginPayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub plugin_url: ::prost::alloc::string::String, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub should_float: bool, - #[prost(message, optional, tag = "3")] + #[prost(message, optional, tag="3")] pub plugin_configuration: ::core::option::Option, - #[prost(bool, tag = "4")] + #[prost(bool, tag="4")] pub move_to_focused_tab: bool, - #[prost(bool, tag = "5")] + #[prost(bool, tag="5")] pub should_open_in_place: bool, - #[prost(bool, tag = "6")] + #[prost(bool, tag="6")] pub skip_plugin_cache: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct GoToTabNamePayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub tab_name: ::prost::alloc::string::String, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub create: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct NewFloatingPanePayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub command: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct NewTiledPanePayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub command: ::core::option::Option, - #[prost(enumeration = "super::resize::ResizeDirection", optional, tag = "2")] + #[prost(enumeration="super::resize::ResizeDirection", optional, tag="2")] pub direction: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MovePanePayload { - #[prost(enumeration = "super::resize::ResizeDirection", optional, tag = "1")] + #[prost(enumeration="super::resize::ResizeDirection", optional, tag="1")] pub direction: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EditFilePayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub file_to_edit: ::prost::alloc::string::String, - #[prost(uint32, optional, tag = "2")] + #[prost(uint32, optional, tag="2")] pub line_number: ::core::option::Option, - #[prost(string, optional, tag = "3")] + #[prost(string, optional, tag="3")] pub cwd: ::core::option::Option<::prost::alloc::string::String>, - #[prost(enumeration = "super::resize::ResizeDirection", optional, tag = "4")] + #[prost(enumeration="super::resize::ResizeDirection", optional, tag="4")] pub direction: ::core::option::Option, - #[prost(bool, tag = "5")] + #[prost(bool, tag="5")] pub should_float: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ScrollAtPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub position: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct NewPanePayload { - #[prost(enumeration = "super::resize::ResizeDirection", optional, tag = "1")] + #[prost(enumeration="super::resize::ResizeDirection", optional, tag="1")] pub direction: ::core::option::Option, - #[prost(string, optional, tag = "2")] + #[prost(string, optional, tag="2")] pub pane_name: ::core::option::Option<::prost::alloc::string::String>, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SwitchToModePayload { - #[prost(enumeration = "super::input_mode::InputMode", tag = "1")] + #[prost(enumeration="super::input_mode::InputMode", tag="1")] pub input_mode: i32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct WritePayload { - #[prost(bytes = "vec", tag = "1")] + #[prost(bytes="vec", tag="1")] pub bytes_to_write: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct WriteCharsPayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub chars: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct DumpScreenPayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub file_path: ::prost::alloc::string::String, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub include_scrollback: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Position { - #[prost(int64, tag = "1")] + #[prost(int64, tag="1")] pub line: i64, - #[prost(int64, tag = "2")] + #[prost(int64, tag="2")] pub column: i64, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MouseEventPayload { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub event_type: u32, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub left: bool, - #[prost(bool, tag = "3")] + #[prost(bool, tag="3")] pub right: bool, - #[prost(bool, tag = "4")] + #[prost(bool, tag="4")] pub middle: bool, - #[prost(bool, tag = "5")] + #[prost(bool, tag="5")] pub wheel_up: bool, - #[prost(bool, tag = "6")] + #[prost(bool, tag="6")] pub wheel_down: bool, - #[prost(bool, tag = "7")] + #[prost(bool, tag="7")] pub shift: bool, - #[prost(bool, tag = "8")] + #[prost(bool, tag="8")] pub alt: bool, - #[prost(bool, tag = "9")] + #[prost(bool, tag="9")] pub ctrl: bool, - #[prost(int64, tag = "10")] + #[prost(int64, tag="10")] pub line: i64, - #[prost(int64, tag = "11")] + #[prost(int64, tag="11")] pub column: i64, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct RunCommandAction { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub command: ::prost::alloc::string::String, - #[prost(string, repeated, tag = "2")] + #[prost(string, repeated, tag="2")] pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - #[prost(string, optional, tag = "3")] + #[prost(string, optional, tag="3")] pub cwd: ::core::option::Option<::prost::alloc::string::String>, - #[prost(enumeration = "super::resize::ResizeDirection", optional, tag = "4")] + #[prost(enumeration="super::resize::ResizeDirection", optional, tag="4")] pub direction: ::core::option::Option, - #[prost(string, optional, tag = "5")] + #[prost(string, optional, tag="5")] pub pane_name: ::core::option::Option<::prost::alloc::string::String>, - #[prost(bool, tag = "6")] + #[prost(bool, tag="6")] pub hold_on_close: bool, - #[prost(bool, tag = "7")] + #[prost(bool, tag="7")] pub hold_on_start: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PluginConfiguration { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub name_and_value: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct NameAndValue { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub name: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub value: ::prost::alloc::string::String, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] @@ -476,6 +473,8 @@ pub enum ActionName { KeybindPipe = 84, TogglePanePinned = 85, MouseEvent = 86, + TogglePaneInGroup = 87, + ToggleGroupMarking = 88, } impl ActionName { /// String value of the enum field names used in the ProtoBuf definition. @@ -568,6 +567,8 @@ impl ActionName { ActionName::KeybindPipe => "KeybindPipe", ActionName::TogglePanePinned => "TogglePanePinned", ActionName::MouseEvent => "MouseEvent", + ActionName::TogglePaneInGroup => "TogglePaneInGroup", + ActionName::ToggleGroupMarking => "ToggleGroupMarking", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -657,6 +658,8 @@ impl ActionName { "KeybindPipe" => Some(Self::KeybindPipe), "TogglePanePinned" => Some(Self::TogglePanePinned), "MouseEvent" => Some(Self::MouseEvent), + "TogglePaneInGroup" => Some(Self::TogglePaneInGroup), + "ToggleGroupMarking" => Some(Self::ToggleGroupMarking), _ => None, } } diff --git a/zellij-utils/assets/prost/api.command.rs b/zellij-utils/assets/prost/api.command.rs index d337af63..8864c375 100644 --- a/zellij-utils/assets/prost/api.command.rs +++ b/zellij-utils/assets/prost/api.command.rs @@ -1,10 +1,10 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Command { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub path: ::prost::alloc::string::String, - #[prost(string, repeated, tag = "2")] + #[prost(string, repeated, tag="2")] pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - #[prost(string, optional, tag = "3")] + #[prost(string, optional, tag="3")] pub cwd: ::core::option::Option<::prost::alloc::string::String>, } diff --git a/zellij-utils/assets/prost/api.event.rs b/zellij-utils/assets/prost/api.event.rs index 46372db5..258dba41 100644 --- a/zellij-utils/assets/prost/api.event.rs +++ b/zellij-utils/assets/prost/api.event.rs @@ -1,476 +1,483 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EventNameList { - #[prost(enumeration = "EventType", repeated, tag = "1")] + #[prost(enumeration="EventType", repeated, tag="1")] pub event_types: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Event { - #[prost(enumeration = "EventType", tag = "1")] + #[prost(enumeration="EventType", tag="1")] pub name: i32, - #[prost( - oneof = "event::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" - )] + #[prost(oneof="event::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")] pub payload: ::core::option::Option, } /// Nested message and enum types in `Event`. pub mod event { #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] +#[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Payload { - #[prost(message, tag = "2")] + #[prost(message, tag="2")] ModeUpdatePayload(super::ModeUpdatePayload), - #[prost(message, tag = "3")] + #[prost(message, tag="3")] TabUpdatePayload(super::TabUpdatePayload), - #[prost(message, tag = "4")] + #[prost(message, tag="4")] PaneUpdatePayload(super::PaneUpdatePayload), - #[prost(message, tag = "5")] + #[prost(message, tag="5")] KeyPayload(super::super::key::Key), - #[prost(message, tag = "6")] + #[prost(message, tag="6")] MouseEventPayload(super::MouseEventPayload), - #[prost(float, tag = "7")] + #[prost(float, tag="7")] TimerPayload(f32), - #[prost(enumeration = "super::CopyDestination", tag = "8")] + #[prost(enumeration="super::CopyDestination", tag="8")] CopyToClipboardPayload(i32), - #[prost(bool, tag = "9")] + #[prost(bool, tag="9")] VisiblePayload(bool), - #[prost(message, tag = "10")] + #[prost(message, tag="10")] CustomMessagePayload(super::CustomMessagePayload), - #[prost(message, tag = "11")] + #[prost(message, tag="11")] FileListPayload(super::FileListPayload), - #[prost(message, tag = "12")] + #[prost(message, tag="12")] PermissionRequestResultPayload(super::PermissionRequestResultPayload), - #[prost(message, tag = "13")] + #[prost(message, tag="13")] SessionUpdatePayload(super::SessionUpdatePayload), - #[prost(message, tag = "14")] + #[prost(message, tag="14")] RunCommandResultPayload(super::RunCommandResultPayload), - #[prost(message, tag = "15")] + #[prost(message, tag="15")] WebRequestResultPayload(super::WebRequestResultPayload), - #[prost(message, tag = "16")] + #[prost(message, tag="16")] CommandPaneOpenedPayload(super::CommandPaneOpenedPayload), - #[prost(message, tag = "17")] + #[prost(message, tag="17")] CommandPaneExitedPayload(super::CommandPaneExitedPayload), - #[prost(message, tag = "18")] + #[prost(message, tag="18")] PaneClosedPayload(super::PaneClosedPayload), - #[prost(message, tag = "19")] + #[prost(message, tag="19")] EditPaneOpenedPayload(super::EditPaneOpenedPayload), - #[prost(message, tag = "20")] + #[prost(message, tag="20")] EditPaneExitedPayload(super::EditPaneExitedPayload), - #[prost(message, tag = "21")] + #[prost(message, tag="21")] CommandPaneRerunPayload(super::CommandPaneReRunPayload), - #[prost(message, tag = "22")] + #[prost(message, tag="22")] FailedToWriteConfigToDiskPayload(super::FailedToWriteConfigToDiskPayload), - #[prost(message, tag = "23")] + #[prost(message, tag="23")] ListClientsPayload(super::ListClientsPayload), - #[prost(message, tag = "24")] + #[prost(message, tag="24")] HostFolderChangedPayload(super::HostFolderChangedPayload), - #[prost(message, tag = "25")] + #[prost(message, tag="25")] FailedToChangeHostFolderPayload(super::FailedToChangeHostFolderPayload), - #[prost(message, tag = "26")] + #[prost(message, tag="26")] PastedTextPayload(super::PastedTextPayload), } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PastedTextPayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub pasted_text: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FailedToChangeHostFolderPayload { - #[prost(string, optional, tag = "1")] + #[prost(string, optional, tag="1")] pub error_message: ::core::option::Option<::prost::alloc::string::String>, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct HostFolderChangedPayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub new_host_folder_path: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ListClientsPayload { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub client_info: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ClientInfo { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub client_id: u32, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub pane_id: ::core::option::Option, - #[prost(string, tag = "3")] + #[prost(string, tag="3")] pub running_command: ::prost::alloc::string::String, - #[prost(bool, tag = "4")] + #[prost(bool, tag="4")] pub is_current_client: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FailedToWriteConfigToDiskPayload { - #[prost(string, optional, tag = "1")] + #[prost(string, optional, tag="1")] pub file_path: ::core::option::Option<::prost::alloc::string::String>, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CommandPaneReRunPayload { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub terminal_pane_id: u32, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaneClosedPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } /// duplicate of plugin_command.PaneId because protobuffs don't like recursive imports #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaneId { - #[prost(enumeration = "PaneType", tag = "1")] + #[prost(enumeration="PaneType", tag="1")] pub pane_type: i32, - #[prost(uint32, tag = "2")] + #[prost(uint32, tag="2")] pub id: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CommandPaneOpenedPayload { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub terminal_pane_id: u32, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EditPaneOpenedPayload { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub terminal_pane_id: u32, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CommandPaneExitedPayload { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub terminal_pane_id: u32, - #[prost(int32, optional, tag = "2")] + #[prost(int32, optional, tag="2")] pub exit_code: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EditPaneExitedPayload { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub terminal_pane_id: u32, - #[prost(int32, optional, tag = "2")] + #[prost(int32, optional, tag="2")] pub exit_code: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SessionUpdatePayload { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub session_manifests: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub resurrectable_sessions: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct RunCommandResultPayload { - #[prost(int32, optional, tag = "1")] + #[prost(int32, optional, tag="1")] pub exit_code: ::core::option::Option, - #[prost(bytes = "vec", tag = "2")] + #[prost(bytes="vec", tag="2")] pub stdout: ::prost::alloc::vec::Vec, - #[prost(bytes = "vec", tag = "3")] + #[prost(bytes="vec", tag="3")] pub stderr: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "4")] + #[prost(message, repeated, tag="4")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct WebRequestResultPayload { - #[prost(int32, tag = "1")] + #[prost(int32, tag="1")] pub status: i32, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub headers: ::prost::alloc::vec::Vec
, - #[prost(bytes = "vec", tag = "3")] + #[prost(bytes="vec", tag="3")] pub body: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "4")] + #[prost(message, repeated, tag="4")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ContextItem { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub name: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub value: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Header { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub name: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub value: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PermissionRequestResultPayload { - #[prost(bool, tag = "1")] + #[prost(bool, tag="1")] pub granted: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FileListPayload { - #[prost(string, repeated, tag = "1")] + #[prost(string, repeated, tag="1")] pub paths: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub paths_metadata: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FileMetadata { /// if this is false, the metadata for this file has not been read - #[prost(bool, tag = "1")] + #[prost(bool, tag="1")] pub metadata_is_set: bool, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub is_dir: bool, - #[prost(bool, tag = "3")] + #[prost(bool, tag="3")] pub is_file: bool, - #[prost(bool, tag = "4")] + #[prost(bool, tag="4")] pub is_symlink: bool, - #[prost(uint64, tag = "5")] + #[prost(uint64, tag="5")] pub len: u64, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CustomMessagePayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub message_name: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub payload: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MouseEventPayload { - #[prost(enumeration = "MouseEventName", tag = "1")] + #[prost(enumeration="MouseEventName", tag="1")] pub mouse_event_name: i32, - #[prost(oneof = "mouse_event_payload::MouseEventPayload", tags = "2, 3")] - pub mouse_event_payload: ::core::option::Option< - mouse_event_payload::MouseEventPayload, - >, + #[prost(oneof="mouse_event_payload::MouseEventPayload", tags="2, 3")] + pub mouse_event_payload: ::core::option::Option, } /// Nested message and enum types in `MouseEventPayload`. pub mod mouse_event_payload { #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] +#[derive(Clone, PartialEq, ::prost::Oneof)] pub enum MouseEventPayload { - #[prost(uint32, tag = "2")] + #[prost(uint32, tag="2")] LineCount(u32), - #[prost(message, tag = "3")] + #[prost(message, tag="3")] Position(super::super::action::Position), } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct TabUpdatePayload { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub tab_info: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaneUpdatePayload { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub pane_manifest: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaneManifest { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub tab_index: u32, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub panes: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SessionManifest { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub name: ::prost::alloc::string::String, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub tabs: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub panes: ::prost::alloc::vec::Vec, - #[prost(uint32, tag = "4")] + #[prost(uint32, tag="4")] pub connected_clients: u32, - #[prost(bool, tag = "5")] + #[prost(bool, tag="5")] pub is_current_session: bool, - #[prost(message, repeated, tag = "6")] + #[prost(message, repeated, tag="6")] pub available_layouts: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "7")] + #[prost(message, repeated, tag="7")] pub plugins: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "8")] + #[prost(message, repeated, tag="8")] pub tab_history: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ClientTabHistory { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub client_id: u32, - #[prost(uint32, repeated, tag = "2")] + #[prost(uint32, repeated, tag="2")] pub tab_history: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PluginInfo { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub plugin_id: u32, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub plugin_url: ::prost::alloc::string::String, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub plugin_config: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct LayoutInfo { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub name: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub source: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ResurrectableSession { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub name: ::prost::alloc::string::String, - #[prost(uint64, tag = "2")] + #[prost(uint64, tag="2")] pub creation_time: u64, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaneInfo { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub id: u32, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub is_plugin: bool, - #[prost(bool, tag = "3")] + #[prost(bool, tag="3")] pub is_focused: bool, - #[prost(bool, tag = "4")] + #[prost(bool, tag="4")] pub is_fullscreen: bool, - #[prost(bool, tag = "5")] + #[prost(bool, tag="5")] pub is_floating: bool, - #[prost(bool, tag = "6")] + #[prost(bool, tag="6")] pub is_suppressed: bool, - #[prost(string, tag = "7")] + #[prost(string, tag="7")] pub title: ::prost::alloc::string::String, - #[prost(bool, tag = "8")] + #[prost(bool, tag="8")] pub exited: bool, - #[prost(int32, optional, tag = "9")] + #[prost(int32, optional, tag="9")] pub exit_status: ::core::option::Option, - #[prost(bool, tag = "10")] + #[prost(bool, tag="10")] pub is_held: bool, - #[prost(uint32, tag = "11")] + #[prost(uint32, tag="11")] pub pane_x: u32, - #[prost(uint32, tag = "12")] + #[prost(uint32, tag="12")] pub pane_content_x: u32, - #[prost(uint32, tag = "13")] + #[prost(uint32, tag="13")] pub pane_y: u32, - #[prost(uint32, tag = "14")] + #[prost(uint32, tag="14")] pub pane_content_y: u32, - #[prost(uint32, tag = "15")] + #[prost(uint32, tag="15")] pub pane_rows: u32, - #[prost(uint32, tag = "16")] + #[prost(uint32, tag="16")] pub pane_content_rows: u32, - #[prost(uint32, tag = "17")] + #[prost(uint32, tag="17")] pub pane_columns: u32, - #[prost(uint32, tag = "18")] + #[prost(uint32, tag="18")] pub pane_content_columns: u32, - #[prost(message, optional, tag = "19")] + #[prost(message, optional, tag="19")] pub cursor_coordinates_in_pane: ::core::option::Option, - #[prost(string, optional, tag = "20")] + #[prost(string, optional, tag="20")] pub terminal_command: ::core::option::Option<::prost::alloc::string::String>, - #[prost(string, optional, tag = "21")] + #[prost(string, optional, tag="21")] pub plugin_url: ::core::option::Option<::prost::alloc::string::String>, - #[prost(bool, tag = "22")] + #[prost(bool, tag="22")] pub is_selectable: bool, + #[prost(message, repeated, tag="23")] + pub index_in_pane_group: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IndexInPaneGroup { + #[prost(uint32, tag="1")] + pub client_id: u32, + #[prost(uint32, tag="2")] + pub index: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct TabInfo { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub position: u32, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub name: ::prost::alloc::string::String, - #[prost(bool, tag = "3")] + #[prost(bool, tag="3")] pub active: bool, - #[prost(uint32, tag = "4")] + #[prost(uint32, tag="4")] pub panes_to_hide: u32, - #[prost(bool, tag = "5")] + #[prost(bool, tag="5")] pub is_fullscreen_active: bool, - #[prost(bool, tag = "6")] + #[prost(bool, tag="6")] pub is_sync_panes_active: bool, - #[prost(bool, tag = "7")] + #[prost(bool, tag="7")] pub are_floating_panes_visible: bool, - #[prost(uint32, repeated, tag = "8")] + #[prost(uint32, repeated, tag="8")] pub other_focused_clients: ::prost::alloc::vec::Vec, - #[prost(string, optional, tag = "9")] + #[prost(string, optional, tag="9")] pub active_swap_layout_name: ::core::option::Option<::prost::alloc::string::String>, - #[prost(bool, tag = "10")] + #[prost(bool, tag="10")] pub is_swap_layout_dirty: bool, - #[prost(uint32, tag = "11")] + #[prost(uint32, tag="11")] pub viewport_rows: u32, - #[prost(uint32, tag = "12")] + #[prost(uint32, tag="12")] pub viewport_columns: u32, - #[prost(uint32, tag = "13")] + #[prost(uint32, tag="13")] pub display_area_rows: u32, - #[prost(uint32, tag = "14")] + #[prost(uint32, tag="14")] pub display_area_columns: u32, - #[prost(uint32, tag = "15")] + #[prost(uint32, tag="15")] pub selectable_tiled_panes_count: u32, - #[prost(uint32, tag = "16")] + #[prost(uint32, tag="16")] pub selectable_floating_panes_count: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ModeUpdatePayload { - #[prost(enumeration = "super::input_mode::InputMode", tag = "1")] + #[prost(enumeration="super::input_mode::InputMode", tag="1")] pub current_mode: i32, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub keybinds: ::prost::alloc::vec::Vec, - #[prost(message, optional, tag = "3")] + #[prost(message, optional, tag="3")] pub style: ::core::option::Option, - #[prost(bool, tag = "4")] + #[prost(bool, tag="4")] pub arrow_fonts_support: bool, - #[prost(string, optional, tag = "5")] + #[prost(string, optional, tag="5")] pub session_name: ::core::option::Option<::prost::alloc::string::String>, - #[prost(enumeration = "super::input_mode::InputMode", optional, tag = "6")] + #[prost(enumeration="super::input_mode::InputMode", optional, tag="6")] pub base_mode: ::core::option::Option, - #[prost(string, optional, tag = "7")] + #[prost(string, optional, tag="7")] pub editor: ::core::option::Option<::prost::alloc::string::String>, - #[prost(string, optional, tag = "8")] + #[prost(string, optional, tag="8")] pub shell: ::core::option::Option<::prost::alloc::string::String>, + #[prost(bool, optional, tag="9")] + pub currently_marking_pane_group: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct InputModeKeybinds { - #[prost(enumeration = "super::input_mode::InputMode", tag = "1")] + #[prost(enumeration="super::input_mode::InputMode", tag="1")] pub mode: i32, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub key_bind: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct KeyBind { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub key: ::core::option::Option, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub action: ::prost::alloc::vec::Vec, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] @@ -522,6 +529,7 @@ pub enum EventType { FailedToChangeHostFolder = 28, PastedText = 29, ConfigWasWrittenToDisk = 30, + BeforeClose = 31, } impl EventType { /// String value of the enum field names used in the ProtoBuf definition. @@ -561,6 +569,7 @@ impl EventType { EventType::FailedToChangeHostFolder => "FailedToChangeHostFolder", EventType::PastedText => "PastedText", EventType::ConfigWasWrittenToDisk => "ConfigWasWrittenToDisk", + EventType::BeforeClose => "BeforeClose", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -597,6 +606,7 @@ impl EventType { "FailedToChangeHostFolder" => Some(Self::FailedToChangeHostFolder), "PastedText" => Some(Self::PastedText), "ConfigWasWrittenToDisk" => Some(Self::ConfigWasWrittenToDisk), + "BeforeClose" => Some(Self::BeforeClose), _ => None, } } diff --git a/zellij-utils/assets/prost/api.file.rs b/zellij-utils/assets/prost/api.file.rs index 12aa7293..7bccc521 100644 --- a/zellij-utils/assets/prost/api.file.rs +++ b/zellij-utils/assets/prost/api.file.rs @@ -1,10 +1,10 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct File { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub path: ::prost::alloc::string::String, - #[prost(int32, optional, tag = "2")] + #[prost(int32, optional, tag="2")] pub line_number: ::core::option::Option, - #[prost(string, optional, tag = "3")] + #[prost(string, optional, tag="3")] pub cwd: ::core::option::Option<::prost::alloc::string::String>, } diff --git a/zellij-utils/assets/prost/api.input_mode.rs b/zellij-utils/assets/prost/api.input_mode.rs index 97e46481..c87f1bb9 100644 --- a/zellij-utils/assets/prost/api.input_mode.rs +++ b/zellij-utils/assets/prost/api.input_mode.rs @@ -1,7 +1,7 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct InputModeMessage { - #[prost(enumeration = "InputMode", tag = "1")] + #[prost(enumeration="InputMode", tag="1")] pub input_mode: i32, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] diff --git a/zellij-utils/assets/prost/api.key.rs b/zellij-utils/assets/prost/api.key.rs index 92c47492..32ab4b84 100644 --- a/zellij-utils/assets/prost/api.key.rs +++ b/zellij-utils/assets/prost/api.key.rs @@ -1,26 +1,16 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Key { - #[prost(enumeration = "key::KeyModifier", optional, tag = "1")] + #[prost(enumeration="key::KeyModifier", optional, tag="1")] pub modifier: ::core::option::Option, - #[prost(enumeration = "key::KeyModifier", repeated, tag = "4")] + #[prost(enumeration="key::KeyModifier", repeated, tag="4")] pub additional_modifiers: ::prost::alloc::vec::Vec, - #[prost(oneof = "key::MainKey", tags = "2, 3")] + #[prost(oneof="key::MainKey", tags="2, 3")] pub main_key: ::core::option::Option, } /// Nested message and enum types in `Key`. pub mod key { - #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - ::prost::Enumeration - )] + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum KeyModifier { Ctrl = 0, @@ -52,17 +42,7 @@ pub mod key { } } } - #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - ::prost::Enumeration - )] + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum NamedKey { PageDown = 0, @@ -178,17 +158,7 @@ pub mod key { } } } - #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - ::prost::Enumeration - )] + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum Char { A = 0, @@ -317,11 +287,11 @@ pub mod key { } } #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] +#[derive(Clone, PartialEq, ::prost::Oneof)] pub enum MainKey { - #[prost(enumeration = "NamedKey", tag = "2")] + #[prost(enumeration="NamedKey", tag="2")] Key(i32), - #[prost(enumeration = "Char", tag = "3")] + #[prost(enumeration="Char", tag="3")] Char(i32), } } diff --git a/zellij-utils/assets/prost/api.message.rs b/zellij-utils/assets/prost/api.message.rs index e040e13f..49fb032e 100644 --- a/zellij-utils/assets/prost/api.message.rs +++ b/zellij-utils/assets/prost/api.message.rs @@ -1,10 +1,10 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Message { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub name: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub payload: ::prost::alloc::string::String, - #[prost(string, optional, tag = "3")] + #[prost(string, optional, tag="3")] pub worker_name: ::core::option::Option<::prost::alloc::string::String>, } diff --git a/zellij-utils/assets/prost/api.pipe_message.rs b/zellij-utils/assets/prost/api.pipe_message.rs index a9987563..19372325 100644 --- a/zellij-utils/assets/prost/api.pipe_message.rs +++ b/zellij-utils/assets/prost/api.pipe_message.rs @@ -1,27 +1,27 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PipeMessage { - #[prost(enumeration = "PipeSource", tag = "1")] + #[prost(enumeration="PipeSource", tag="1")] pub source: i32, - #[prost(string, optional, tag = "2")] + #[prost(string, optional, tag="2")] pub cli_source_id: ::core::option::Option<::prost::alloc::string::String>, - #[prost(uint32, optional, tag = "3")] + #[prost(uint32, optional, tag="3")] pub plugin_source_id: ::core::option::Option, - #[prost(string, tag = "4")] + #[prost(string, tag="4")] pub name: ::prost::alloc::string::String, - #[prost(string, optional, tag = "5")] + #[prost(string, optional, tag="5")] pub payload: ::core::option::Option<::prost::alloc::string::String>, - #[prost(message, repeated, tag = "6")] + #[prost(message, repeated, tag="6")] pub args: ::prost::alloc::vec::Vec, - #[prost(bool, tag = "7")] + #[prost(bool, tag="7")] pub is_private: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Arg { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub key: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub value: ::prost::alloc::string::String, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] diff --git a/zellij-utils/assets/prost/api.plugin_command.rs b/zellij-utils/assets/prost/api.plugin_command.rs index 338af3c8..b7dd7a20 100644 --- a/zellij-utils/assets/prost/api.plugin_command.rs +++ b/zellij-utils/assets/prost/api.plugin_command.rs @@ -1,764 +1,787 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PluginCommand { - #[prost(enumeration = "CommandName", tag = "1")] + #[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" - )] + #[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")] pub payload: ::core::option::Option, } /// Nested message and enum types in `PluginCommand`. pub mod plugin_command { #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] +#[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Payload { - #[prost(message, tag = "2")] + #[prost(message, tag="2")] SubscribePayload(super::SubscribePayload), - #[prost(message, tag = "3")] + #[prost(message, tag="3")] UnsubscribePayload(super::UnsubscribePayload), - #[prost(bool, tag = "4")] + #[prost(bool, tag="4")] SetSelectablePayload(bool), - #[prost(message, tag = "5")] + #[prost(message, tag="5")] OpenFilePayload(super::OpenFilePayload), - #[prost(message, tag = "6")] + #[prost(message, tag="6")] OpenFileFloatingPayload(super::OpenFilePayload), - #[prost(message, tag = "7")] + #[prost(message, tag="7")] OpenTerminalPayload(super::OpenFilePayload), - #[prost(message, tag = "8")] + #[prost(message, tag="8")] OpenTerminalFloatingPayload(super::OpenFilePayload), - #[prost(message, tag = "9")] + #[prost(message, tag="9")] OpenCommandPanePayload(super::OpenCommandPanePayload), - #[prost(message, tag = "10")] + #[prost(message, tag="10")] OpenCommandPaneFloatingPayload(super::OpenCommandPanePayload), - #[prost(message, tag = "11")] + #[prost(message, tag="11")] SwitchTabToPayload(super::SwitchTabToPayload), - #[prost(message, tag = "12")] + #[prost(message, tag="12")] SetTimeoutPayload(super::SetTimeoutPayload), - #[prost(message, tag = "13")] + #[prost(message, tag="13")] ExecCmdPayload(super::ExecCmdPayload), - #[prost(message, tag = "14")] + #[prost(message, tag="14")] PostMessageToPayload(super::PluginMessagePayload), - #[prost(message, tag = "15")] + #[prost(message, tag="15")] PostMessageToPluginPayload(super::PluginMessagePayload), - #[prost(bool, tag = "16")] + #[prost(bool, tag="16")] ShowSelfPayload(bool), - #[prost(message, tag = "17")] + #[prost(message, tag="17")] SwitchToModePayload(super::super::action::SwitchToModePayload), - #[prost(string, tag = "18")] + #[prost(string, tag="18")] NewTabsWithLayoutPayload(::prost::alloc::string::String), - #[prost(message, tag = "19")] + #[prost(message, tag="19")] ResizePayload(super::ResizePayload), - #[prost(message, tag = "20")] + #[prost(message, tag="20")] ResizeWithDirectionPayload(super::ResizePayload), - #[prost(message, tag = "21")] + #[prost(message, tag="21")] MoveFocusPayload(super::MovePayload), - #[prost(message, tag = "22")] + #[prost(message, tag="22")] MoveFocusOrTabPayload(super::MovePayload), - #[prost(bytes, tag = "23")] + #[prost(bytes, tag="23")] WritePayload(::prost::alloc::vec::Vec), - #[prost(string, tag = "24")] + #[prost(string, tag="24")] WriteCharsPayload(::prost::alloc::string::String), - #[prost(message, tag = "25")] + #[prost(message, tag="25")] MovePaneWithDirectionPayload(super::MovePayload), - #[prost(string, tag = "26")] + #[prost(string, tag="26")] GoToTabNamePayload(::prost::alloc::string::String), - #[prost(string, tag = "27")] + #[prost(string, tag="27")] FocusOrCreateTabPayload(::prost::alloc::string::String), - #[prost(uint32, tag = "28")] + #[prost(uint32, tag="28")] GoToTabPayload(u32), - #[prost(string, tag = "29")] + #[prost(string, tag="29")] StartOrReloadPluginPayload(::prost::alloc::string::String), - #[prost(uint32, tag = "30")] + #[prost(uint32, tag="30")] CloseTerminalPanePayload(u32), - #[prost(uint32, tag = "31")] + #[prost(uint32, tag="31")] ClosePluginPanePayload(u32), - #[prost(message, tag = "32")] + #[prost(message, tag="32")] FocusTerminalPanePayload(super::super::action::PaneIdAndShouldFloat), - #[prost(message, tag = "33")] + #[prost(message, tag="33")] FocusPluginPanePayload(super::super::action::PaneIdAndShouldFloat), - #[prost(message, tag = "34")] + #[prost(message, tag="34")] RenameTerminalPanePayload(super::IdAndNewName), - #[prost(message, tag = "35")] + #[prost(message, tag="35")] RenamePluginPanePayload(super::IdAndNewName), - #[prost(message, tag = "36")] + #[prost(message, tag="36")] RenameTabPayload(super::IdAndNewName), - #[prost(string, tag = "37")] + #[prost(string, tag="37")] ReportCrashPayload(::prost::alloc::string::String), - #[prost(message, tag = "38")] + #[prost(message, tag="38")] RequestPluginPermissionPayload(super::RequestPluginPermissionPayload), - #[prost(message, tag = "39")] + #[prost(message, tag="39")] SwitchSessionPayload(super::SwitchSessionPayload), - #[prost(message, tag = "40")] + #[prost(message, tag="40")] OpenFileInPlacePayload(super::OpenFilePayload), - #[prost(message, tag = "41")] + #[prost(message, tag="41")] OpenTerminalInPlacePayload(super::OpenFilePayload), - #[prost(message, tag = "42")] + #[prost(message, tag="42")] OpenCommandPaneInPlacePayload(super::OpenCommandPanePayload), - #[prost(message, tag = "43")] + #[prost(message, tag="43")] RunCommandPayload(super::RunCommandPayload), - #[prost(message, tag = "44")] + #[prost(message, tag="44")] WebRequestPayload(super::WebRequestPayload), - #[prost(string, tag = "45")] + #[prost(string, tag="45")] DeleteDeadSessionPayload(::prost::alloc::string::String), - #[prost(string, tag = "46")] + #[prost(string, tag="46")] RenameSessionPayload(::prost::alloc::string::String), - #[prost(string, tag = "47")] + #[prost(string, tag="47")] UnblockCliPipeInputPayload(::prost::alloc::string::String), - #[prost(string, tag = "48")] + #[prost(string, tag="48")] BlockCliPipeInputPayload(::prost::alloc::string::String), - #[prost(message, tag = "49")] + #[prost(message, tag="49")] CliPipeOutputPayload(super::CliPipeOutputPayload), - #[prost(message, tag = "50")] + #[prost(message, tag="50")] MessageToPluginPayload(super::MessageToPluginPayload), - #[prost(message, tag = "60")] + #[prost(message, tag="60")] KillSessionsPayload(super::KillSessionsPayload), - #[prost(string, tag = "61")] + #[prost(string, tag="61")] ScanHostFolderPayload(::prost::alloc::string::String), - #[prost(message, tag = "62")] + #[prost(message, tag="62")] NewTabsWithLayoutInfoPayload(super::NewTabsWithLayoutInfoPayload), - #[prost(message, tag = "63")] + #[prost(message, tag="63")] ReconfigurePayload(super::ReconfigurePayload), - #[prost(message, tag = "64")] + #[prost(message, tag="64")] HidePaneWithIdPayload(super::HidePaneWithIdPayload), - #[prost(message, tag = "65")] + #[prost(message, tag="65")] ShowPaneWithIdPayload(super::ShowPaneWithIdPayload), - #[prost(message, tag = "66")] + #[prost(message, tag="66")] OpenCommandPaneBackgroundPayload(super::OpenCommandPanePayload), - #[prost(message, tag = "67")] + #[prost(message, tag="67")] RerunCommandPanePayload(super::RerunCommandPanePayload), - #[prost(message, tag = "68")] + #[prost(message, tag="68")] ResizePaneIdWithDirectionPayload(super::ResizePaneIdWithDirectionPayload), - #[prost(message, tag = "69")] + #[prost(message, tag="69")] EditScrollbackForPaneWithIdPayload(super::EditScrollbackForPaneWithIdPayload), - #[prost(message, tag = "70")] + #[prost(message, tag="70")] WriteToPaneIdPayload(super::WriteToPaneIdPayload), - #[prost(message, tag = "71")] + #[prost(message, tag="71")] WriteCharsToPaneIdPayload(super::WriteCharsToPaneIdPayload), - #[prost(message, tag = "72")] + #[prost(message, tag="72")] MovePaneWithPaneIdPayload(super::MovePaneWithPaneIdPayload), - #[prost(message, tag = "73")] - MovePaneWithPaneIdInDirectionPayload( - super::MovePaneWithPaneIdInDirectionPayload, - ), - #[prost(message, tag = "74")] + #[prost(message, tag="73")] + MovePaneWithPaneIdInDirectionPayload(super::MovePaneWithPaneIdInDirectionPayload), + #[prost(message, tag="74")] ClearScreenForPaneIdPayload(super::ClearScreenForPaneIdPayload), - #[prost(message, tag = "75")] + #[prost(message, tag="75")] ScrollUpInPaneIdPayload(super::ScrollUpInPaneIdPayload), - #[prost(message, tag = "76")] + #[prost(message, tag="76")] ScrollDownInPaneIdPayload(super::ScrollDownInPaneIdPayload), - #[prost(message, tag = "77")] + #[prost(message, tag="77")] ScrollToTopInPaneIdPayload(super::ScrollToTopInPaneIdPayload), - #[prost(message, tag = "78")] + #[prost(message, tag="78")] ScrollToBottomInPaneIdPayload(super::ScrollToBottomInPaneIdPayload), - #[prost(message, tag = "79")] + #[prost(message, tag="79")] PageScrollUpInPaneIdPayload(super::PageScrollUpInPaneIdPayload), - #[prost(message, tag = "80")] + #[prost(message, tag="80")] PageScrollDownInPaneIdPayload(super::PageScrollDownInPaneIdPayload), - #[prost(message, tag = "81")] + #[prost(message, tag="81")] TogglePaneIdFullscreenPayload(super::TogglePaneIdFullscreenPayload), - #[prost(message, tag = "82")] - TogglePaneEmbedOrEjectForPaneIdPayload( - super::TogglePaneEmbedOrEjectForPaneIdPayload, - ), - #[prost(message, tag = "83")] + #[prost(message, tag="82")] + TogglePaneEmbedOrEjectForPaneIdPayload(super::TogglePaneEmbedOrEjectForPaneIdPayload), + #[prost(message, tag="83")] CloseTabWithIndexPayload(super::CloseTabWithIndexPayload), - #[prost(message, tag = "84")] + #[prost(message, tag="84")] BreakPanesToNewTabPayload(super::BreakPanesToNewTabPayload), - #[prost(message, tag = "85")] + #[prost(message, tag="85")] BreakPanesToTabWithIndexPayload(super::BreakPanesToTabWithIndexPayload), - #[prost(message, tag = "86")] + #[prost(message, tag="86")] ReloadPluginPayload(super::ReloadPluginPayload), - #[prost(message, tag = "87")] + #[prost(message, tag="87")] LoadNewPluginPayload(super::LoadNewPluginPayload), - #[prost(message, tag = "88")] + #[prost(message, tag="88")] RebindKeysPayload(super::RebindKeysPayload), - #[prost(message, tag = "89")] + #[prost(message, tag="89")] ChangeHostFolderPayload(super::ChangeHostFolderPayload), - #[prost(message, tag = "90")] + #[prost(message, tag="90")] SetFloatingPanePinnedPayload(super::SetFloatingPanePinnedPayload), - #[prost(message, tag = "91")] + #[prost(message, tag="91")] StackPanesPayload(super::StackPanesPayload), - #[prost(message, tag = "92")] - ChangeFloatingPanesCoordinatesPayload( - super::ChangeFloatingPanesCoordinatesPayload, - ), - #[prost(message, tag = "93")] + #[prost(message, tag="92")] + ChangeFloatingPanesCoordinatesPayload(super::ChangeFloatingPanesCoordinatesPayload), + #[prost(message, tag="93")] OpenCommandPaneNearPluginPayload(super::OpenCommandPaneNearPluginPayload), - #[prost(message, tag = "94")] + #[prost(message, tag="94")] OpenTerminalNearPluginPayload(super::OpenTerminalNearPluginPayload), - #[prost(message, tag = "95")] - OpenTerminalFloatingNearPluginPayload( - super::OpenTerminalFloatingNearPluginPayload, - ), - #[prost(message, tag = "96")] + #[prost(message, tag="95")] + OpenTerminalFloatingNearPluginPayload(super::OpenTerminalFloatingNearPluginPayload), + #[prost(message, tag="96")] OpenTerminalInPlaceOfPluginPayload(super::OpenTerminalInPlaceOfPluginPayload), - #[prost(message, tag = "97")] - OpenCommandPaneFloatingNearPluginPayload( - super::OpenCommandPaneFloatingNearPluginPayload, - ), - #[prost(message, tag = "98")] - OpenCommandPaneInPlaceOfPluginPayload( - super::OpenCommandPaneInPlaceOfPluginPayload, - ), - #[prost(message, tag = "99")] + #[prost(message, tag="97")] + OpenCommandPaneFloatingNearPluginPayload(super::OpenCommandPaneFloatingNearPluginPayload), + #[prost(message, tag="98")] + OpenCommandPaneInPlaceOfPluginPayload(super::OpenCommandPaneInPlaceOfPluginPayload), + #[prost(message, tag="99")] OpenFileNearPluginPayload(super::OpenFileNearPluginPayload), - #[prost(message, tag = "100")] + #[prost(message, tag="100")] OpenFileFloatingNearPluginPayload(super::OpenFileFloatingNearPluginPayload), - #[prost(message, tag = "101")] + #[prost(message, tag="101")] OpenFileInPlaceOfPluginPayload(super::OpenFileInPlaceOfPluginPayload), + #[prost(message, tag="102")] + GroupAndUngroupPanesPayload(super::GroupAndUngroupPanesPayload), + #[prost(message, tag="103")] + HighlightAndUnhighlightPanesPayload(super::HighlightAndUnhighlightPanesPayload), + #[prost(message, tag="104")] + CloseMultiplePanesPayload(super::CloseMultiplePanesPayload), + #[prost(message, tag="105")] + FloatMultiplePanesPayload(super::FloatMultiplePanesPayload), + #[prost(message, tag="106")] + EmbedMultiplePanesPayload(super::EmbedMultiplePanesPayload), } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct EmbedMultiplePanesPayload { + #[prost(message, repeated, tag="1")] + pub pane_ids: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FloatMultiplePanesPayload { + #[prost(message, repeated, tag="1")] + pub pane_ids: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CloseMultiplePanesPayload { + #[prost(message, repeated, tag="1")] + pub pane_ids: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct HighlightAndUnhighlightPanesPayload { + #[prost(message, repeated, tag="1")] + pub pane_ids_to_highlight: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="2")] + pub pane_ids_to_unhighlight: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GroupAndUngroupPanesPayload { + #[prost(message, repeated, tag="1")] + pub pane_ids_to_group: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="2")] + pub pane_ids_to_ungroup: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct OpenFileInPlaceOfPluginPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub file_to_open: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub floating_pane_coordinates: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, - #[prost(bool, tag = "4")] + #[prost(bool, tag="4")] pub close_plugin_after_replace: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OpenFileFloatingNearPluginPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub file_to_open: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub floating_pane_coordinates: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OpenFileNearPluginPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub file_to_open: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub floating_pane_coordinates: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OpenCommandPaneInPlaceOfPluginPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub command_to_run: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, - #[prost(bool, tag = "4")] + #[prost(bool, tag="4")] pub close_plugin_after_replace: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OpenCommandPaneFloatingNearPluginPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub command_to_run: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub floating_pane_coordinates: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OpenTerminalInPlaceOfPluginPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub file_to_open: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, - #[prost(bool, tag = "4")] + #[prost(bool, tag="4")] pub close_plugin_after_replace: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OpenTerminalFloatingNearPluginPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub file_to_open: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub floating_pane_coordinates: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OpenTerminalNearPluginPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub file_to_open: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OpenCommandPaneNearPluginPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub command_to_run: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub floating_pane_coordinates: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ChangeFloatingPanesCoordinatesPayload { - #[prost(message, repeated, tag = "1")] - pub pane_ids_and_floating_panes_coordinates: ::prost::alloc::vec::Vec< - PaneIdAndFloatingPaneCoordinates, - >, + #[prost(message, repeated, tag="1")] + pub pane_ids_and_floating_panes_coordinates: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct StackPanesPayload { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub pane_ids: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SetFloatingPanePinnedPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub should_be_pinned: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ChangeHostFolderPayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub new_host_folder: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct RebindKeysPayload { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub keys_to_rebind: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub keys_to_unbind: ::prost::alloc::vec::Vec, - #[prost(bool, tag = "3")] + #[prost(bool, tag="3")] pub write_config_to_disk: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct KeyToRebind { - #[prost(enumeration = "super::input_mode::InputMode", tag = "1")] + #[prost(enumeration="super::input_mode::InputMode", tag="1")] pub input_mode: i32, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub key: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub actions: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct KeyToUnbind { - #[prost(enumeration = "super::input_mode::InputMode", tag = "1")] + #[prost(enumeration="super::input_mode::InputMode", tag="1")] pub input_mode: i32, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub key: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct LoadNewPluginPayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub plugin_url: ::prost::alloc::string::String, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub plugin_config: ::prost::alloc::vec::Vec, - #[prost(bool, tag = "3")] + #[prost(bool, tag="3")] pub should_load_plugin_in_background: bool, - #[prost(bool, tag = "4")] + #[prost(bool, tag="4")] pub should_skip_plugin_cache: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ReloadPluginPayload { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub plugin_id: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct BreakPanesToTabWithIndexPayload { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub pane_ids: ::prost::alloc::vec::Vec, - #[prost(uint32, tag = "2")] + #[prost(uint32, tag="2")] pub tab_index: u32, - #[prost(bool, tag = "3")] + #[prost(bool, tag="3")] pub should_change_focus_to_target_tab: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct BreakPanesToNewTabPayload { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub pane_ids: ::prost::alloc::vec::Vec, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub should_change_focus_to_new_tab: bool, - #[prost(string, optional, tag = "3")] + #[prost(string, optional, tag="3")] pub new_tab_name: ::core::option::Option<::prost::alloc::string::String>, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MovePaneWithPaneIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MovePaneWithPaneIdInDirectionPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub direction: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ClearScreenForPaneIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ScrollUpInPaneIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ScrollDownInPaneIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ScrollToTopInPaneIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ScrollToBottomInPaneIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PageScrollUpInPaneIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PageScrollDownInPaneIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct TogglePaneIdFullscreenPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct TogglePaneEmbedOrEjectForPaneIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CloseTabWithIndexPayload { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub tab_index: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct WriteCharsToPaneIdPayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub chars_to_write: ::prost::alloc::string::String, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct WriteToPaneIdPayload { - #[prost(bytes = "vec", tag = "1")] + #[prost(bytes="vec", tag="1")] pub bytes_to_write: ::prost::alloc::vec::Vec, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EditScrollbackForPaneWithIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ResizePaneIdWithDirectionPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub resize: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ReconfigurePayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub config: ::prost::alloc::string::String, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub write_to_disk: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct RerunCommandPanePayload { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub terminal_pane_id: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct HidePaneWithIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ShowPaneWithIdPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub should_float_if_hidden: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct NewTabsWithLayoutInfoPayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub layout_info: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct KillSessionsPayload { - #[prost(string, repeated, tag = "1")] + #[prost(string, repeated, tag="1")] pub session_names: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CliPipeOutputPayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub pipe_name: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub output: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MessageToPluginPayload { - #[prost(string, optional, tag = "1")] + #[prost(string, optional, tag="1")] pub plugin_url: ::core::option::Option<::prost::alloc::string::String>, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub plugin_config: ::prost::alloc::vec::Vec, - #[prost(string, tag = "3")] + #[prost(string, tag="3")] pub message_name: ::prost::alloc::string::String, - #[prost(string, optional, tag = "4")] + #[prost(string, optional, tag="4")] pub message_payload: ::core::option::Option<::prost::alloc::string::String>, - #[prost(message, repeated, tag = "5")] + #[prost(message, repeated, tag="5")] pub message_args: ::prost::alloc::vec::Vec, - #[prost(message, optional, tag = "6")] + #[prost(message, optional, tag="6")] pub new_plugin_args: ::core::option::Option, - #[prost(uint32, optional, tag = "7")] + #[prost(uint32, optional, tag="7")] pub destination_plugin_id: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct NewPluginArgs { - #[prost(bool, optional, tag = "1")] + #[prost(bool, optional, tag="1")] pub should_float: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub pane_id_to_replace: ::core::option::Option, - #[prost(string, optional, tag = "3")] + #[prost(string, optional, tag="3")] pub pane_title: ::core::option::Option<::prost::alloc::string::String>, - #[prost(string, optional, tag = "4")] + #[prost(string, optional, tag="4")] pub cwd: ::core::option::Option<::prost::alloc::string::String>, - #[prost(bool, tag = "5")] + #[prost(bool, tag="5")] pub skip_cache: bool, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaneId { - #[prost(enumeration = "PaneType", tag = "1")] + #[prost(enumeration="PaneType", tag="1")] pub pane_type: i32, - #[prost(uint32, tag = "2")] + #[prost(uint32, tag="2")] pub id: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SwitchSessionPayload { - #[prost(string, optional, tag = "1")] + #[prost(string, optional, tag="1")] pub name: ::core::option::Option<::prost::alloc::string::String>, - #[prost(uint32, optional, tag = "2")] + #[prost(uint32, optional, tag="2")] pub tab_position: ::core::option::Option, - #[prost(uint32, optional, tag = "3")] + #[prost(uint32, optional, tag="3")] pub pane_id: ::core::option::Option, - #[prost(bool, optional, tag = "4")] + #[prost(bool, optional, tag="4")] pub pane_id_is_plugin: ::core::option::Option, - #[prost(message, optional, tag = "5")] + #[prost(message, optional, tag="5")] pub layout: ::core::option::Option, - #[prost(string, optional, tag = "6")] + #[prost(string, optional, tag="6")] pub cwd: ::core::option::Option<::prost::alloc::string::String>, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct RequestPluginPermissionPayload { - #[prost( - enumeration = "super::plugin_permission::PermissionType", - repeated, - tag = "1" - )] + #[prost(enumeration="super::plugin_permission::PermissionType", repeated, tag="1")] pub permissions: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SubscribePayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub subscriptions: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct UnsubscribePayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub subscriptions: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OpenFilePayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub file_to_open: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub floating_pane_coordinates: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct OpenCommandPanePayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub command_to_run: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub floating_pane_coordinates: ::core::option::Option, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SwitchTabToPayload { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub tab_index: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct SetTimeoutPayload { - #[prost(double, tag = "1")] + #[prost(double, tag="1")] pub seconds: f64, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ExecCmdPayload { - #[prost(string, repeated, tag = "1")] + #[prost(string, repeated, tag="1")] pub command_line: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct RunCommandPayload { - #[prost(string, repeated, tag = "1")] + #[prost(string, repeated, tag="1")] pub command_line: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub env_variables: ::prost::alloc::vec::Vec, - #[prost(string, tag = "3")] + #[prost(string, tag="3")] pub cwd: ::prost::alloc::string::String, - #[prost(message, repeated, tag = "4")] + #[prost(message, repeated, tag="4")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct WebRequestPayload { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub url: ::prost::alloc::string::String, - #[prost(enumeration = "HttpVerb", tag = "2")] + #[prost(enumeration="HttpVerb", tag="2")] pub verb: i32, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub headers: ::prost::alloc::vec::Vec, - #[prost(bytes = "vec", tag = "4")] + #[prost(bytes="vec", tag="4")] pub body: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "5")] + #[prost(message, repeated, tag="5")] pub context: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct EnvVariable { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub name: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub value: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ContextItem { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub name: ::prost::alloc::string::String, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub value: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PluginMessagePayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub message: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ResizePayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub resize: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MovePayload { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub direction: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaneIdAndFloatingPaneCoordinates { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub pane_id: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub floating_pane_coordinates: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct IdAndNewName { /// pane id or tab index - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub id: u32, - #[prost(string, tag = "2")] + #[prost(string, tag="2")] pub new_name: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FloatingPaneCoordinates { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub x: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub y: ::core::option::Option, - #[prost(message, optional, tag = "3")] + #[prost(message, optional, tag="3")] pub width: ::core::option::Option, - #[prost(message, optional, tag = "4")] + #[prost(message, optional, tag="4")] pub height: ::core::option::Option, - #[prost(bool, optional, tag = "5")] + #[prost(bool, optional, tag="5")] pub pinned: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct FixedOrPercentValue { - #[prost(enumeration = "FixedOrPercent", tag = "1")] + #[prost(enumeration="FixedOrPercent", tag="1")] pub r#type: i32, - #[prost(uint32, tag = "2")] + #[prost(uint32, tag="2")] pub value: u32, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] @@ -891,6 +914,11 @@ pub enum CommandName { OpenFileNearPlugin = 124, OpenFileFloatingNearPlugin = 125, OpenFileInPlaceOfPlugin = 126, + GroupAndUngroupPanes = 127, + HighlightAndUnhighlightPanes = 128, + CloseMultiplePanes = 129, + FloatMultiplePanes = 130, + EmbedMultiplePanes = 131, } impl CommandName { /// String value of the enum field names used in the ProtoBuf definition. @@ -1005,9 +1033,7 @@ impl CommandName { CommandName::PageScrollUpInPaneId => "PageScrollUpInPaneId", CommandName::PageScrollDownInPaneId => "PageScrollDownInPaneId", CommandName::TogglePaneIdFullscreen => "TogglePaneIdFullscreen", - CommandName::TogglePaneEmbedOrEjectForPaneId => { - "TogglePaneEmbedOrEjectForPaneId" - } + CommandName::TogglePaneEmbedOrEjectForPaneId => "TogglePaneEmbedOrEjectForPaneId", CommandName::CloseTabWithIndex => "CloseTabWithIndex", CommandName::BreakPanesToNewTab => "BreakPanesToNewTab", CommandName::BreakPanesToTabWithIndex => "BreakPanesToTabWithIndex", @@ -1018,24 +1044,21 @@ impl CommandName { CommandName::ChangeHostFolder => "ChangeHostFolder", CommandName::SetFloatingPanePinned => "SetFloatingPanePinned", CommandName::StackPanes => "StackPanes", - CommandName::ChangeFloatingPanesCoordinates => { - "ChangeFloatingPanesCoordinates" - } + CommandName::ChangeFloatingPanesCoordinates => "ChangeFloatingPanesCoordinates", CommandName::OpenCommandPaneNearPlugin => "OpenCommandPaneNearPlugin", CommandName::OpenTerminalNearPlugin => "OpenTerminalNearPlugin", - CommandName::OpenTerminalFloatingNearPlugin => { - "OpenTerminalFloatingNearPlugin" - } + CommandName::OpenTerminalFloatingNearPlugin => "OpenTerminalFloatingNearPlugin", CommandName::OpenTerminalInPlaceOfPlugin => "OpenTerminalInPlaceOfPlugin", - CommandName::OpenCommandPaneFloatingNearPlugin => { - "OpenCommandPaneFloatingNearPlugin" - } - CommandName::OpenCommandPaneInPlaceOfPlugin => { - "OpenCommandPaneInPlaceOfPlugin" - } + CommandName::OpenCommandPaneFloatingNearPlugin => "OpenCommandPaneFloatingNearPlugin", + CommandName::OpenCommandPaneInPlaceOfPlugin => "OpenCommandPaneInPlaceOfPlugin", CommandName::OpenFileNearPlugin => "OpenFileNearPlugin", CommandName::OpenFileFloatingNearPlugin => "OpenFileFloatingNearPlugin", CommandName::OpenFileInPlaceOfPlugin => "OpenFileInPlaceOfPlugin", + CommandName::GroupAndUngroupPanes => "GroupAndUngroupPanes", + CommandName::HighlightAndUnhighlightPanes => "HighlightAndUnhighlightPanes", + CommandName::CloseMultiplePanes => "CloseMultiplePanes", + CommandName::FloatMultiplePanes => "FloatMultiplePanes", + CommandName::EmbedMultiplePanes => "EmbedMultiplePanes", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -1147,9 +1170,7 @@ impl CommandName { "PageScrollUpInPaneId" => Some(Self::PageScrollUpInPaneId), "PageScrollDownInPaneId" => Some(Self::PageScrollDownInPaneId), "TogglePaneIdFullscreen" => Some(Self::TogglePaneIdFullscreen), - "TogglePaneEmbedOrEjectForPaneId" => { - Some(Self::TogglePaneEmbedOrEjectForPaneId) - } + "TogglePaneEmbedOrEjectForPaneId" => Some(Self::TogglePaneEmbedOrEjectForPaneId), "CloseTabWithIndex" => Some(Self::CloseTabWithIndex), "BreakPanesToNewTab" => Some(Self::BreakPanesToNewTab), "BreakPanesToTabWithIndex" => Some(Self::BreakPanesToTabWithIndex), @@ -1160,24 +1181,21 @@ impl CommandName { "ChangeHostFolder" => Some(Self::ChangeHostFolder), "SetFloatingPanePinned" => Some(Self::SetFloatingPanePinned), "StackPanes" => Some(Self::StackPanes), - "ChangeFloatingPanesCoordinates" => { - Some(Self::ChangeFloatingPanesCoordinates) - } + "ChangeFloatingPanesCoordinates" => Some(Self::ChangeFloatingPanesCoordinates), "OpenCommandPaneNearPlugin" => Some(Self::OpenCommandPaneNearPlugin), "OpenTerminalNearPlugin" => Some(Self::OpenTerminalNearPlugin), - "OpenTerminalFloatingNearPlugin" => { - Some(Self::OpenTerminalFloatingNearPlugin) - } + "OpenTerminalFloatingNearPlugin" => Some(Self::OpenTerminalFloatingNearPlugin), "OpenTerminalInPlaceOfPlugin" => Some(Self::OpenTerminalInPlaceOfPlugin), - "OpenCommandPaneFloatingNearPlugin" => { - Some(Self::OpenCommandPaneFloatingNearPlugin) - } - "OpenCommandPaneInPlaceOfPlugin" => { - Some(Self::OpenCommandPaneInPlaceOfPlugin) - } + "OpenCommandPaneFloatingNearPlugin" => Some(Self::OpenCommandPaneFloatingNearPlugin), + "OpenCommandPaneInPlaceOfPlugin" => Some(Self::OpenCommandPaneInPlaceOfPlugin), "OpenFileNearPlugin" => Some(Self::OpenFileNearPlugin), "OpenFileFloatingNearPlugin" => Some(Self::OpenFileFloatingNearPlugin), "OpenFileInPlaceOfPlugin" => Some(Self::OpenFileInPlaceOfPlugin), + "GroupAndUngroupPanes" => Some(Self::GroupAndUngroupPanes), + "HighlightAndUnhighlightPanes" => Some(Self::HighlightAndUnhighlightPanes), + "CloseMultiplePanes" => Some(Self::CloseMultiplePanes), + "FloatMultiplePanes" => Some(Self::FloatMultiplePanes), + "EmbedMultiplePanes" => Some(Self::EmbedMultiplePanes), _ => None, } } diff --git a/zellij-utils/assets/prost/api.plugin_ids.rs b/zellij-utils/assets/prost/api.plugin_ids.rs index 972bc949..165b7b71 100644 --- a/zellij-utils/assets/prost/api.plugin_ids.rs +++ b/zellij-utils/assets/prost/api.plugin_ids.rs @@ -1,16 +1,18 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PluginIds { - #[prost(int32, tag = "1")] + #[prost(int32, tag="1")] pub plugin_id: i32, - #[prost(int32, tag = "2")] + #[prost(int32, tag="2")] pub zellij_pid: i32, - #[prost(string, tag = "3")] + #[prost(string, tag="3")] pub initial_cwd: ::prost::alloc::string::String, + #[prost(uint32, tag="4")] + pub client_id: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ZellijVersion { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub version: ::prost::alloc::string::String, } diff --git a/zellij-utils/assets/prost/api.plugin_permission.rs b/zellij-utils/assets/prost/api.plugin_permission.rs index 54b7de32..27eba0d4 100644 --- a/zellij-utils/assets/prost/api.plugin_permission.rs +++ b/zellij-utils/assets/prost/api.plugin_permission.rs @@ -28,9 +28,7 @@ impl PermissionType { PermissionType::WriteToStdin => "WriteToStdin", PermissionType::WebAccess => "WebAccess", PermissionType::ReadCliPipes => "ReadCliPipes", - PermissionType::MessageAndLaunchOtherPlugins => { - "MessageAndLaunchOtherPlugins" - } + PermissionType::MessageAndLaunchOtherPlugins => "MessageAndLaunchOtherPlugins", PermissionType::Reconfigure => "Reconfigure", PermissionType::FullHdAccess => "FullHdAccess", } diff --git a/zellij-utils/assets/prost/api.resize.rs b/zellij-utils/assets/prost/api.resize.rs index 22f01246..ca15e53b 100644 --- a/zellij-utils/assets/prost/api.resize.rs +++ b/zellij-utils/assets/prost/api.resize.rs @@ -1,15 +1,15 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Resize { - #[prost(enumeration = "ResizeAction", tag = "1")] + #[prost(enumeration="ResizeAction", tag="1")] pub resize_action: i32, - #[prost(enumeration = "ResizeDirection", optional, tag = "2")] + #[prost(enumeration="ResizeDirection", optional, tag="2")] pub direction: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MoveDirection { - #[prost(enumeration = "ResizeDirection", tag = "1")] + #[prost(enumeration="ResizeDirection", tag="1")] pub direction: i32, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] diff --git a/zellij-utils/assets/prost/api.style.rs b/zellij-utils/assets/prost/api.style.rs index 62080a5f..16f7979e 100644 --- a/zellij-utils/assets/prost/api.style.rs +++ b/zellij-utils/assets/prost/api.style.rs @@ -2,116 +2,116 @@ #[derive(Clone, PartialEq, ::prost::Message)] pub struct Style { #[deprecated] - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub palette: ::core::option::Option, - #[prost(bool, tag = "2")] + #[prost(bool, tag="2")] pub rounded_corners: bool, - #[prost(bool, tag = "3")] + #[prost(bool, tag="3")] pub hide_session_name: bool, - #[prost(message, optional, tag = "4")] + #[prost(message, optional, tag="4")] pub styling: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Palette { - #[prost(enumeration = "ThemeHue", tag = "1")] + #[prost(enumeration="ThemeHue", tag="1")] pub theme_hue: i32, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub fg: ::core::option::Option, - #[prost(message, optional, tag = "3")] + #[prost(message, optional, tag="3")] pub bg: ::core::option::Option, - #[prost(message, optional, tag = "4")] + #[prost(message, optional, tag="4")] pub black: ::core::option::Option, - #[prost(message, optional, tag = "5")] + #[prost(message, optional, tag="5")] pub red: ::core::option::Option, - #[prost(message, optional, tag = "6")] + #[prost(message, optional, tag="6")] pub green: ::core::option::Option, - #[prost(message, optional, tag = "7")] + #[prost(message, optional, tag="7")] pub yellow: ::core::option::Option, - #[prost(message, optional, tag = "8")] + #[prost(message, optional, tag="8")] pub blue: ::core::option::Option, - #[prost(message, optional, tag = "9")] + #[prost(message, optional, tag="9")] pub magenta: ::core::option::Option, - #[prost(message, optional, tag = "10")] + #[prost(message, optional, tag="10")] pub cyan: ::core::option::Option, - #[prost(message, optional, tag = "11")] + #[prost(message, optional, tag="11")] pub white: ::core::option::Option, - #[prost(message, optional, tag = "12")] + #[prost(message, optional, tag="12")] pub orange: ::core::option::Option, - #[prost(message, optional, tag = "13")] + #[prost(message, optional, tag="13")] pub gray: ::core::option::Option, - #[prost(message, optional, tag = "14")] + #[prost(message, optional, tag="14")] pub purple: ::core::option::Option, - #[prost(message, optional, tag = "15")] + #[prost(message, optional, tag="15")] pub gold: ::core::option::Option, - #[prost(message, optional, tag = "16")] + #[prost(message, optional, tag="16")] pub silver: ::core::option::Option, - #[prost(message, optional, tag = "17")] + #[prost(message, optional, tag="17")] pub pink: ::core::option::Option, - #[prost(message, optional, tag = "18")] + #[prost(message, optional, tag="18")] pub brown: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Color { - #[prost(enumeration = "ColorType", tag = "1")] + #[prost(enumeration="ColorType", tag="1")] pub color_type: i32, - #[prost(oneof = "color::Payload", tags = "2, 3")] + #[prost(oneof="color::Payload", tags="2, 3")] pub payload: ::core::option::Option, } /// Nested message and enum types in `Color`. pub mod color { #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] +#[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Payload { - #[prost(message, tag = "2")] + #[prost(message, tag="2")] RgbColorPayload(super::RgbColorPayload), - #[prost(uint32, tag = "3")] + #[prost(uint32, tag="3")] EightBitColorPayload(u32), } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct RgbColorPayload { - #[prost(uint32, tag = "1")] + #[prost(uint32, tag="1")] pub red: u32, - #[prost(uint32, tag = "2")] + #[prost(uint32, tag="2")] pub green: u32, - #[prost(uint32, tag = "3")] + #[prost(uint32, tag="3")] pub blue: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Styling { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub text_unselected: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub text_selected: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "3")] + #[prost(message, repeated, tag="3")] pub ribbon_unselected: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "4")] + #[prost(message, repeated, tag="4")] pub ribbon_selected: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "5")] + #[prost(message, repeated, tag="5")] pub table_title: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "6")] + #[prost(message, repeated, tag="6")] pub table_cell_unselected: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "7")] + #[prost(message, repeated, tag="7")] pub table_cell_selected: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "8")] + #[prost(message, repeated, tag="8")] pub list_unselected: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "9")] + #[prost(message, repeated, tag="9")] pub list_selected: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "10")] + #[prost(message, repeated, tag="10")] pub frame_unselected: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "11")] + #[prost(message, repeated, tag="11")] pub frame_selected: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "12")] + #[prost(message, repeated, tag="12")] pub frame_highlight: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "13")] + #[prost(message, repeated, tag="13")] pub exit_code_success: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "14")] + #[prost(message, repeated, tag="14")] pub exit_code_error: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "15")] + #[prost(message, repeated, tag="15")] pub multiplayer_user_colors: ::prost::alloc::vec::Vec, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] diff --git a/zellij-utils/assets/themes/catppuccin-latte.kdl b/zellij-utils/assets/themes/catppuccin-latte.kdl index f2255dca..9ea747e3 100644 --- a/zellij-utils/assets/themes/catppuccin-latte.kdl +++ b/zellij-utils/assets/themes/catppuccin-latte.kdl @@ -6,7 +6,7 @@ themes { emphasis_0 254 100 11 emphasis_1 4 165 229 emphasis_2 64 160 43 - emphasis_3 234 118 203 + emphasis_3 210 15 57 } text_selected { base 76 79 105 @@ -27,7 +27,7 @@ themes { ribbon_unselected { base 220 224 232 background 92 95 119 - emphasis_0 210 15 57 + emphasis_0 234 118 203 emphasis_1 76 79 105 emphasis_2 30 102 245 emphasis_3 234 118 203 @@ -83,8 +83,8 @@ themes { frame_highlight { base 254 100 11 background 0 - emphasis_0 254 100 11 - emphasis_1 254 100 11 + emphasis_0 210 15 57 + emphasis_1 136 57 239 emphasis_2 254 100 11 emphasis_3 254 100 11 } diff --git a/zellij-utils/src/consts.rs b/zellij-utils/src/consts.rs index f8e8ca60..66428917 100644 --- a/zellij-utils/src/consts.rs +++ b/zellij-utils/src/consts.rs @@ -131,6 +131,7 @@ mod not_wasm { add_plugin!(assets, "configuration.wasm"); add_plugin!(assets, "plugin-manager.wasm"); add_plugin!(assets, "about.wasm"); + add_plugin!(assets, "multiple-select.wasm"); assets }; } diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 368b3e2a..3e888a63 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -494,6 +494,10 @@ impl KeyWithModifier { pub fn is_key_with_super_modifier(&self, key: BareKey) -> bool { self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Super) } + pub fn is_cancel_key(&self) -> bool { + // self.bare_key == BareKey::Esc || self.is_key_with_ctrl_modifier(BareKey::Char('c')) + self.bare_key == BareKey::Esc + } #[cfg(not(target_family = "wasm"))] pub fn to_termwiz_modifiers(&self) -> Modifiers { let mut modifiers = Modifiers::empty(); @@ -933,6 +937,7 @@ pub enum Event { FailedToChangeHostFolder(Option), // String -> the error we got when changing PastedText(String), ConfigWasWrittenToDisk, + BeforeClose, } #[derive( @@ -1274,8 +1279,8 @@ pub const DEFAULT_STYLES: Styling = Styling { }, frame_highlight: StyleDeclaration { base: PaletteColor::EightBit(default_colors::ORANGE), - emphasis_0: PaletteColor::EightBit(default_colors::GREEN), - emphasis_1: PaletteColor::EightBit(default_colors::GREEN), + emphasis_0: PaletteColor::EightBit(default_colors::MAGENTA), + emphasis_1: PaletteColor::EightBit(default_colors::PURPLE), emphasis_2: PaletteColor::EightBit(default_colors::GREEN), emphasis_3: PaletteColor::EightBit(default_colors::GREEN), background: PaletteColor::EightBit(default_colors::GREEN), @@ -1432,8 +1437,8 @@ impl From for Styling { }, frame_highlight: StyleDeclaration { base: palette.orange, - emphasis_0: palette.orange, - emphasis_1: palette.orange, + emphasis_0: palette.magenta, + emphasis_1: palette.purple, emphasis_2: palette.orange, emphasis_3: palette.orange, background: Default::default(), @@ -1508,6 +1513,7 @@ pub struct ModeInfo { pub session_name: Option, pub editor: Option, pub shell: Option, + pub currently_marking_pane_group: Option, } impl ModeInfo { @@ -1733,6 +1739,9 @@ pub struct PaneInfo { /// Unselectable panes are often used for UI elements that do not have direct user interaction /// (eg. the default `status-bar` or `tab-bar`). pub is_selectable: bool, + /// Grouped panes (usually through an explicit user action) that are staged for a bulk action + /// the index is kept track of in order to preserve the pane group order + pub index_in_pane_group: BTreeMap, } #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub struct ClientInfo { @@ -1763,6 +1772,7 @@ pub struct PluginIds { pub plugin_id: u32, pub zellij_pid: u32, pub initial_cwd: PathBuf, + pub client_id: ClientId, } /// Tag used to identify the plugin in layout and config kdl files @@ -2307,4 +2317,10 @@ pub enum PluginCommand { OpenFileNearPlugin(FileToOpen, Context), OpenFileFloatingNearPlugin(FileToOpen, Option, Context), OpenFileInPlaceOfPlugin(FileToOpen, bool, Context), // bool -> close_plugin_after_replace + GroupAndUngroupPanes(Vec, Vec), // panes to group, panes to ungroup + HighlightAndUnhighlightPanes(Vec, Vec), // panes to highlight, panes to + // unhighlight + CloseMultiplePanes(Vec), + FloatMultiplePanes(Vec), + EmbedMultiplePanes(Vec), } diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index 73e97e02..cf417ad4 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -375,6 +375,13 @@ pub enum ScreenContext { SetFloatingPanePinned, StackPanes, ChangeFloatingPanesCoordinates, + AddHighlightPaneFrameColorOverride, + GroupAndUngroupPanes, + HighlightAndUnhighlightPanes, + FloatMultiplePanes, + EmbedMultiplePanes, + TogglePaneInGroup, + ToggleGroupMarking, } /// Stack call representations corresponding to the different types of [`PtyInstruction`]s. @@ -512,6 +519,7 @@ pub enum BackgroundJobContext { WebRequest, ReportPluginList, RenderToClients, + HighlightPanesWithMessage, Exit, } diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index f5d16174..7202f82f 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -296,6 +296,8 @@ pub enum Action { TogglePanePinned, StackPanes(Vec), ChangeFloatingPaneCoordinates(PaneId, FloatingPaneCoordinates), + TogglePaneInGroup, + ToggleGroupMarking, } impl Action { diff --git a/zellij-utils/src/input/mod.rs b/zellij-utils/src/input/mod.rs index 66d02624..89ecee82 100644 --- a/zellij-utils/src/input/mod.rs +++ b/zellij-utils/src/input/mod.rs @@ -45,6 +45,7 @@ mod not_wasm { session_name, editor: None, shell: None, + currently_marking_pane_group: None, } } diff --git a/zellij-utils/src/input/mouse.rs b/zellij-utils/src/input/mouse.rs index 4f89cce8..d920495d 100644 --- a/zellij-utils/src/input/mouse.rs +++ b/zellij-utils/src/input/mouse.rs @@ -208,4 +208,34 @@ impl MouseEvent { }; event } + pub fn new_left_press_with_alt_event(position: Position) -> Self { + let event = MouseEvent { + event_type: MouseEventType::Press, + left: true, + right: false, + middle: false, + wheel_up: false, + wheel_down: false, + shift: false, + alt: true, + ctrl: false, + position, + }; + event + } + pub fn new_right_press_with_alt_event(position: Position) -> Self { + let event = MouseEvent { + event_type: MouseEventType::Press, + left: false, + right: true, + middle: false, + wheel_up: false, + wheel_down: false, + shift: false, + alt: true, + ctrl: false, + position, + }; + event + } } diff --git a/zellij-utils/src/input/options.rs b/zellij-utils/src/input/options.rs index a9f82f02..5b8b1f51 100644 --- a/zellij-utils/src/input/options.rs +++ b/zellij-utils/src/input/options.rs @@ -179,6 +179,12 @@ pub struct Options { #[clap(long, value_parser)] #[serde(default)] pub show_release_notes: Option, + + /// Whether to enable mouse hover effects and pane grouping functionality + /// default is true + #[clap(long, value_parser)] + #[serde(default)] + pub advanced_mouse_actions: Option, } #[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] @@ -260,6 +266,7 @@ impl Options { let stacked_resize = other.stacked_resize.or(self.stacked_resize); let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips); let show_release_notes = other.show_release_notes.or(self.show_release_notes); + let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions); Options { simplified_ui, @@ -292,6 +299,7 @@ impl Options { stacked_resize, show_startup_tips, show_release_notes, + advanced_mouse_actions, } } @@ -353,6 +361,7 @@ impl Options { let stacked_resize = other.stacked_resize.or(self.stacked_resize); let show_startup_tips = other.show_startup_tips.or(self.show_startup_tips); let show_release_notes = other.show_release_notes.or(self.show_release_notes); + let advanced_mouse_actions = other.advanced_mouse_actions.or(self.advanced_mouse_actions); Options { simplified_ui, @@ -385,6 +394,7 @@ impl Options { stacked_resize, show_startup_tips, show_release_notes, + advanced_mouse_actions, } } @@ -451,8 +461,9 @@ impl From for Options { serialization_interval: opts.serialization_interval, support_kitty_keyboard_protocol: opts.support_kitty_keyboard_protocol, stacked_resize: opts.stacked_resize, - show_release_notes: opts.show_release_notes, show_startup_tips: opts.show_startup_tips, + show_release_notes: opts.show_release_notes, + advanced_mouse_actions: opts.advanced_mouse_actions, ..Default::default() } } diff --git a/zellij-utils/src/input/plugins.rs b/zellij-utils/src/input/plugins.rs index d75deb33..be57adb9 100644 --- a/zellij-utils/src/input/plugins.rs +++ b/zellij-utils/src/input/plugins.rs @@ -65,6 +65,7 @@ impl PluginConfig { || tag == "configuration" || tag == "plugin-manager" || tag == "about" + || tag == "multiple-select" { Some(PluginConfig { path: PathBuf::from(&tag), diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap index 7fa1a55d..6eace5af 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__theme__theme_test__dracula_theme_from_file.snap @@ -449,16 +449,12 @@ expression: "format!(\"{:#?}\", theme)" emphasis_0: Rgb( ( 255, - 184, - 108, + 121, + 198, ), ), - emphasis_1: Rgb( - ( - 255, - 184, - 108, - ), + emphasis_1: EightBit( + 0, ), emphasis_2: Rgb( ( diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 93b111f4..e54fb2f5 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -1103,6 +1103,8 @@ impl Action { Some(node) }, Action::TogglePanePinned => Some(KdlNode::new("TogglePanePinned")), + Action::TogglePaneInGroup => Some(KdlNode::new("TogglePaneInGroup")), + Action::ToggleGroupMarking => Some(KdlNode::new("ToggleGroupMarking")), _ => None, } } @@ -1798,6 +1800,8 @@ impl TryFrom<(&KdlNode, &Options)> for Action { }) }, "TogglePanePinned" => Ok(Action::TogglePanePinned), + "TogglePaneInGroup" => Ok(Action::TogglePaneInGroup), + "ToggleGroupMarking" => Ok(Action::ToggleGroupMarking), _ => Err(ConfigError::new_kdl_error( format!("Unsupported action: {}", action_name).into(), kdl_action.span().offset(), @@ -2299,6 +2303,9 @@ impl Options { let show_release_notes = kdl_property_first_arg_as_bool_or_error!(kdl_options, "show_release_notes") .map(|(v, _)| v); + let advanced_mouse_actions = + kdl_property_first_arg_as_bool_or_error!(kdl_options, "advanced_mouse_actions") + .map(|(v, _)| v); Ok(Options { simplified_ui, theme, @@ -2330,6 +2337,7 @@ impl Options { stacked_resize, show_startup_tips, show_release_notes, + advanced_mouse_actions, }) } pub fn from_string(stringified_keybindings: &String) -> Result { @@ -3207,6 +3215,33 @@ impl Options { None } } + fn advanced_mouse_actions_to_kdl(&self, add_comments: bool) -> Option { + let comment_text = format!( + "{}\n{}\n{}", + " ", + "// Whether to enable mouse hover effects and pane grouping functionality", + "// default is true", + ); + + let create_node = |node_value: bool| -> KdlNode { + let mut node = KdlNode::new("advanced_mouse_actions"); + node.push(KdlValue::Bool(node_value)); + node + }; + if let Some(advanced_mouse_actions) = self.advanced_mouse_actions { + let mut node = create_node(advanced_mouse_actions); + if add_comments { + node.set_leading(format!("{}\n", comment_text)); + } + Some(node) + } else if add_comments { + let mut node = create_node(false); + node.set_leading(format!("{}\n// ", comment_text)); + Some(node) + } else { + None + } + } pub fn to_kdl(&self, add_comments: bool) -> Vec { let mut nodes = vec![]; if let Some(simplified_ui_node) = self.simplified_ui_to_kdl(add_comments) { @@ -3303,6 +3338,9 @@ impl Options { if let Some(show_release_notes) = self.show_release_notes_to_kdl(add_comments) { nodes.push(show_release_notes); } + if let Some(advanced_mouse_actions) = self.advanced_mouse_actions_to_kdl(add_comments) { + nodes.push(advanced_mouse_actions); + } nodes } } @@ -4886,6 +4924,7 @@ impl PaneInfo { terminal_command, plugin_url, is_selectable, + index_in_pane_group: Default::default(), // we don't serialize this }; Ok((tab_position, pane_info)) } @@ -5033,6 +5072,7 @@ fn serialize_and_deserialize_session_info_with_data() { terminal_command: Some("foo".to_owned()), plugin_url: None, is_selectable: true, + index_in_pane_group: Default::default(), // we don't serialize this }, PaneInfo { id: 1, @@ -5057,6 +5097,7 @@ fn serialize_and_deserialize_session_info_with_data() { terminal_command: None, plugin_url: Some("i_am_a_fake_plugin".to_owned()), is_selectable: true, + index_in_pane_group: Default::default(), // we don't serialize this }, ]; let mut panes = HashMap::new(); diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap index c5e53358..0a78ce71 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 5922 expression: fake_config_stringified --- keybinds clear-defaults=true { @@ -147,8 +146,16 @@ keybinds clear-defaults=true { bind "Alt j" { MoveFocus "down"; } bind "Alt k" { MoveFocus "up"; } bind "Alt l" { MoveFocusOrTab "right"; } + bind "Alt m" { + LaunchOrFocusPlugin "zellij:multiple-select" { + floating true + move_to_focused_tab true + } + } bind "Alt n" { NewPane; } bind "Alt o" { MoveTab "right"; } + bind "Alt p" { TogglePaneInGroup; } + bind "Alt Shift p" { ToggleGroupMarking; } bind "Ctrl q" { Quit; } } shared_except "locked" "move" { @@ -256,4 +263,3 @@ plugins { } load_plugins { } - diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap index 5ace8251..81ca2f2d 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 5934 expression: fake_config_stringified --- keybinds clear-defaults=true { @@ -147,8 +146,16 @@ keybinds clear-defaults=true { bind "Alt j" { MoveFocus "down"; } bind "Alt k" { MoveFocus "up"; } bind "Alt l" { MoveFocusOrTab "right"; } + bind "Alt m" { + LaunchOrFocusPlugin "zellij:multiple-select" { + floating true + move_to_focused_tab true + } + } bind "Alt n" { NewPane; } bind "Alt o" { MoveTab "right"; } + bind "Alt p" { TogglePaneInGroup; } + bind "Alt Shift p" { ToggleGroupMarking; } bind "Ctrl q" { Quit; } } shared_except "locked" "move" { @@ -455,4 +462,7 @@ load_plugins { // Default: true // // show_release_notes false - + +// Whether to enable mouse hover effects and pane grouping functionality +// default is true +// advanced_mouse_actions false diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap index e3c4368e..d6750cad 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__config_options_to_string_with_comments.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 5873 expression: fake_document.to_string() --- @@ -195,4 +194,7 @@ support_kitty_keyboard_protocol false // Default: true // // show_release_notes false - + +// Whether to enable mouse hover effects and pane grouping functionality +// default is true +// advanced_mouse_actions false diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string.snap index f1381601..f0eff7d8 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string.snap @@ -87,8 +87,8 @@ themes { frame_highlight { base 255 184 108 background 0 - emphasis_0 255 184 108 - emphasis_1 255 184 108 + emphasis_0 255 121 198 + emphasis_1 0 emphasis_2 255 184 108 emphasis_3 255 184 108 } diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_combined_definitions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_combined_definitions.snap index ae92e207..1da7649f 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_combined_definitions.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_combined_definitions.snap @@ -87,8 +87,8 @@ themes { frame_highlight { base 208 135 112 background 0 - emphasis_0 208 135 112 - emphasis_1 208 135 112 + emphasis_0 70 + emphasis_1 0 emphasis_2 208 135 112 emphasis_3 208 135 112 } diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_eight_bit_definitions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_eight_bit_definitions.snap index 12696b9c..73765c7f 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_eight_bit_definitions.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_eight_bit_definitions.snap @@ -87,8 +87,8 @@ themes { frame_highlight { base 254 background 0 - emphasis_0 254 - emphasis_1 254 + emphasis_0 70 + emphasis_1 0 emphasis_2 254 emphasis_3 254 } diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_hex_definitions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_hex_definitions.snap index 81d041ac..c996a131 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_hex_definitions.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_hex_definitions.snap @@ -87,8 +87,8 @@ themes { frame_highlight { base 208 135 112 background 0 - emphasis_0 208 135 112 - emphasis_1 208 135 112 + emphasis_0 180 142 173 + emphasis_1 0 emphasis_2 208 135 112 emphasis_3 208 135 112 } diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_multiple_theme_definitions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_multiple_theme_definitions.snap index abae11d8..cb289642 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_multiple_theme_definitions.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__themes_to_string_with_multiple_theme_definitions.snap @@ -87,8 +87,8 @@ themes { frame_highlight { base 255 184 108 background 0 - emphasis_0 255 184 108 - emphasis_1 255 184 108 + emphasis_0 255 121 198 + emphasis_1 0 emphasis_2 255 184 108 emphasis_3 255 184 108 } @@ -205,8 +205,8 @@ themes { frame_highlight { base 208 135 112 background 0 - emphasis_0 208 135 112 - emphasis_1 208 135 112 + emphasis_0 180 142 173 + emphasis_1 0 emphasis_2 208 135 112 emphasis_3 208 135 112 } diff --git a/zellij-utils/src/plugin_api/action.proto b/zellij-utils/src/plugin_api/action.proto index 3c4948cf..40a6481f 100644 --- a/zellij-utils/src/plugin_api/action.proto +++ b/zellij-utils/src/plugin_api/action.proto @@ -241,6 +241,8 @@ enum ActionName { KeybindPipe = 84; TogglePanePinned = 85; MouseEvent = 86; + TogglePaneInGroup = 87; + ToggleGroupMarking = 88; } message Position { diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs index 6d1fa9cd..857bb385 100644 --- a/zellij-utils/src/plugin_api/action.rs +++ b/zellij-utils/src/plugin_api/action.rs @@ -692,6 +692,16 @@ impl TryFrom for Action { Some(_) => Err("TogglePanePinned should not have a payload"), None => Ok(Action::TogglePanePinned), }, + Some(ProtobufActionName::TogglePaneInGroup) => match protobuf_action.optional_payload { + Some(_) => Err("TogglePaneInGroup should not have a payload"), + None => Ok(Action::TogglePaneInGroup), + }, + Some(ProtobufActionName::ToggleGroupMarking) => { + match protobuf_action.optional_payload { + Some(_) => Err("ToggleGroupMarking should not have a payload"), + None => Ok(Action::ToggleGroupMarking), + } + }, Some(ProtobufActionName::KeybindPipe) => match protobuf_action.optional_payload { Some(_) => Err("KeybindPipe should not have a payload"), // TODO: at some point we might want to support a payload here @@ -1236,6 +1246,14 @@ impl TryFrom for ProtobufAction { name: ProtobufActionName::TogglePanePinned as i32, optional_payload: None, }), + Action::TogglePaneInGroup { .. } => Ok(ProtobufAction { + name: ProtobufActionName::TogglePaneInGroup as i32, + optional_payload: None, + }), + Action::ToggleGroupMarking { .. } => Ok(ProtobufAction { + name: ProtobufActionName::ToggleGroupMarking as i32, + optional_payload: None, + }), Action::NoOp | Action::Confirm | Action::NewInPlacePane(..) diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto index 14eb0c40..9b0e4d2f 100644 --- a/zellij-utils/src/plugin_api/event.proto +++ b/zellij-utils/src/plugin_api/event.proto @@ -54,6 +54,7 @@ enum EventType { FailedToChangeHostFolder = 28; PastedText = 29; ConfigWasWrittenToDisk = 30; + BeforeClose = 31; } message EventNameList { @@ -304,6 +305,12 @@ message PaneInfo { optional string terminal_command = 20; optional string plugin_url = 21; bool is_selectable = 22; + repeated IndexInPaneGroup index_in_pane_group = 23; +} + +message IndexInPaneGroup { + uint32 client_id = 1; + uint32 index = 2; } message TabInfo { @@ -334,6 +341,7 @@ message ModeUpdatePayload { optional input_mode.InputMode base_mode = 6; optional string editor = 7; optional string shell = 8; + optional bool currently_marking_pane_group = 9; } message InputModeKeybinds { diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs index 4707d657..88c55827 100644 --- a/zellij-utils/src/plugin_api/event.rs +++ b/zellij-utils/src/plugin_api/event.rs @@ -359,6 +359,10 @@ impl TryFrom for Event { None => Ok(Event::ConfigWasWrittenToDisk), _ => Err("Malformed payload for the ConfigWasWrittenToDisk Event"), }, + Some(ProtobufEventType::BeforeClose) => match protobuf_event.payload { + None => Ok(Event::BeforeClose), + _ => Err("Malformed payload for the BeforeClose Event"), + }, None => Err("Unknown Protobuf Event"), } } @@ -733,6 +737,10 @@ impl TryFrom for ProtobufEvent { name: ProtobufEventType::ConfigWasWrittenToDisk as i32, payload: None, }), + Event::BeforeClose => Ok(ProtobufEvent { + name: ProtobufEventType::BeforeClose as i32, + payload: None, + }), } } } @@ -1072,6 +1080,16 @@ impl TryFrom for PaneInfo { terminal_command: protobuf_pane_info.terminal_command, plugin_url: protobuf_pane_info.plugin_url, is_selectable: protobuf_pane_info.is_selectable, + index_in_pane_group: protobuf_pane_info + .index_in_pane_group + .iter() + .map(|index_in_pane_group| { + ( + index_in_pane_group.client_id as u16, + index_in_pane_group.index as usize, + ) + }) + .collect(), }) } } @@ -1107,6 +1125,14 @@ impl TryFrom for ProtobufPaneInfo { terminal_command: pane_info.terminal_command, plugin_url: pane_info.plugin_url, is_selectable: pane_info.is_selectable, + index_in_pane_group: pane_info + .index_in_pane_group + .iter() + .map(|(&client_id, &index)| IndexInPaneGroup { + client_id: client_id as u32, + index: index as u32, + }) + .collect(), }) } } @@ -1216,6 +1242,8 @@ impl TryFrom for ModeInfo { let capabilities = PluginCapabilities { arrow_fonts: protobuf_mode_update_payload.arrow_fonts_support, }; + let currently_marking_pane_group = + protobuf_mode_update_payload.currently_marking_pane_group; let mode_info = ModeInfo { mode: current_mode, keybinds, @@ -1225,6 +1253,7 @@ impl TryFrom for ModeInfo { base_mode, editor, shell, + currently_marking_pane_group, }; Ok(mode_info) } @@ -1242,6 +1271,7 @@ impl TryFrom for ProtobufModeUpdatePayload { let session_name = mode_info.session_name; let editor = mode_info.editor.map(|e| e.display().to_string()); let shell = mode_info.shell.map(|s| s.display().to_string()); + let currently_marking_pane_group = mode_info.currently_marking_pane_group; let mut protobuf_input_mode_keybinds: Vec = vec![]; for (input_mode, input_mode_keybinds) in mode_info.keybinds { let mode: ProtobufInputMode = input_mode.try_into()?; @@ -1275,6 +1305,7 @@ impl TryFrom for ProtobufModeUpdatePayload { base_mode: base_mode.map(|b_m| b_m as i32), editor, shell, + currently_marking_pane_group, }) } } @@ -1344,6 +1375,7 @@ impl TryFrom for EventType { ProtobufEventType::FailedToChangeHostFolder => EventType::FailedToChangeHostFolder, ProtobufEventType::PastedText => EventType::PastedText, ProtobufEventType::ConfigWasWrittenToDisk => EventType::ConfigWasWrittenToDisk, + ProtobufEventType::BeforeClose => EventType::BeforeClose, }) } } @@ -1383,6 +1415,7 @@ impl TryFrom for ProtobufEventType { EventType::FailedToChangeHostFolder => ProtobufEventType::FailedToChangeHostFolder, EventType::PastedText => ProtobufEventType::PastedText, EventType::ConfigWasWrittenToDisk => ProtobufEventType::ConfigWasWrittenToDisk, + EventType::BeforeClose => ProtobufEventType::BeforeClose, }) } } @@ -1523,6 +1556,7 @@ fn serialize_mode_update_event_with_non_default_values() { base_mode: Some(InputMode::Locked), editor: Some(PathBuf::from("my_awesome_editor")), shell: Some(PathBuf::from("my_awesome_shell")), + currently_marking_pane_group: Some(false), }); let protobuf_event: ProtobufEvent = mode_update_event.clone().try_into().unwrap(); let serialized_protobuf_event = protobuf_event.encode_to_vec(); @@ -1884,6 +1918,14 @@ fn serialize_session_update_event_with_non_default_values() { TabInfo::default(), ]; let mut panes = HashMap::new(); + let mut index_in_pane_group_1 = BTreeMap::new(); + index_in_pane_group_1.insert(1, 0); + index_in_pane_group_1.insert(2, 0); + index_in_pane_group_1.insert(3, 0); + let mut index_in_pane_group_2 = BTreeMap::new(); + index_in_pane_group_2.insert(1, 1); + index_in_pane_group_2.insert(2, 1); + index_in_pane_group_2.insert(3, 1); let panes_list = vec![ PaneInfo { id: 1, @@ -1908,6 +1950,7 @@ fn serialize_session_update_event_with_non_default_values() { terminal_command: Some("foo".to_owned()), plugin_url: None, is_selectable: true, + index_in_pane_group: index_in_pane_group_1, }, PaneInfo { id: 1, @@ -1932,6 +1975,7 @@ fn serialize_session_update_event_with_non_default_values() { terminal_command: None, plugin_url: Some("i_am_a_fake_plugin".to_owned()), is_selectable: true, + index_in_pane_group: index_in_pane_group_2, }, ]; panes.insert(0, panes_list); diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto index 3fa321cc..3a63b0c8 100644 --- a/zellij-utils/src/plugin_api/plugin_command.proto +++ b/zellij-utils/src/plugin_api/plugin_command.proto @@ -140,6 +140,11 @@ enum CommandName { OpenFileNearPlugin = 124; OpenFileFloatingNearPlugin = 125; OpenFileInPlaceOfPlugin = 126; + GroupAndUngroupPanes = 127; + HighlightAndUnhighlightPanes = 128; + CloseMultiplePanes = 129; + FloatMultiplePanes = 130; + EmbedMultiplePanes = 131; } message PluginCommand { @@ -236,9 +241,36 @@ message PluginCommand { OpenFileNearPluginPayload open_file_near_plugin_payload = 99; OpenFileFloatingNearPluginPayload open_file_floating_near_plugin_payload = 100; OpenFileInPlaceOfPluginPayload open_file_in_place_of_plugin_payload = 101; + GroupAndUngroupPanesPayload group_and_ungroup_panes_payload = 102; + HighlightAndUnhighlightPanesPayload highlight_and_unhighlight_panes_payload = 103; + CloseMultiplePanesPayload close_multiple_panes_payload = 104; + FloatMultiplePanesPayload float_multiple_panes_payload = 105; + EmbedMultiplePanesPayload embed_multiple_panes_payload = 106; } } +message EmbedMultiplePanesPayload { + repeated PaneId pane_ids = 1; +} + +message FloatMultiplePanesPayload { + repeated PaneId pane_ids = 1; +} + +message CloseMultiplePanesPayload { + repeated PaneId pane_ids = 1; +} + +message HighlightAndUnhighlightPanesPayload { + repeated PaneId pane_ids_to_highlight = 1; + repeated PaneId pane_ids_to_unhighlight = 2; +} + +message GroupAndUngroupPanesPayload { + repeated PaneId pane_ids_to_group = 1; + repeated PaneId pane_ids_to_ungroup = 2; +} + message OpenFileInPlaceOfPluginPayload { file.File file_to_open = 1; optional FloatingPaneCoordinates floating_pane_coordinates = 2; diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs index 0de590e7..833d6270 100644 --- a/zellij-utils/src/plugin_api/plugin_command.rs +++ b/zellij-utils/src/plugin_api/plugin_command.rs @@ -5,17 +5,19 @@ pub use super::generated_api::api::{ plugin_command::{ plugin_command::Payload, BreakPanesToNewTabPayload, BreakPanesToTabWithIndexPayload, ChangeFloatingPanesCoordinatesPayload, ChangeHostFolderPayload, - ClearScreenForPaneIdPayload, CliPipeOutputPayload, CloseTabWithIndexPayload, CommandName, - ContextItem, EditScrollbackForPaneWithIdPayload, EnvVariable, ExecCmdPayload, + ClearScreenForPaneIdPayload, CliPipeOutputPayload, CloseMultiplePanesPayload, + CloseTabWithIndexPayload, CommandName, ContextItem, EditScrollbackForPaneWithIdPayload, + EmbedMultiplePanesPayload, EnvVariable, ExecCmdPayload, FixedOrPercent as ProtobufFixedOrPercent, - FixedOrPercentValue as ProtobufFixedOrPercentValue, - FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, HidePaneWithIdPayload, - HttpVerb as ProtobufHttpVerb, IdAndNewName, KeyToRebind, KeyToUnbind, KillSessionsPayload, - LoadNewPluginPayload, MessageToPluginPayload, MovePaneWithPaneIdInDirectionPayload, - MovePaneWithPaneIdPayload, MovePayload, NewPluginArgs as ProtobufNewPluginArgs, - NewTabsWithLayoutInfoPayload, OpenCommandPaneFloatingNearPluginPayload, - OpenCommandPaneInPlaceOfPluginPayload, OpenCommandPaneNearPluginPayload, - OpenCommandPanePayload, OpenFileFloatingNearPluginPayload, OpenFileInPlaceOfPluginPayload, + FixedOrPercentValue as ProtobufFixedOrPercentValue, FloatMultiplePanesPayload, + FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, GroupAndUngroupPanesPayload, + HidePaneWithIdPayload, HighlightAndUnhighlightPanesPayload, HttpVerb as ProtobufHttpVerb, + IdAndNewName, KeyToRebind, KeyToUnbind, KillSessionsPayload, LoadNewPluginPayload, + MessageToPluginPayload, MovePaneWithPaneIdInDirectionPayload, MovePaneWithPaneIdPayload, + MovePayload, NewPluginArgs as ProtobufNewPluginArgs, NewTabsWithLayoutInfoPayload, + OpenCommandPaneFloatingNearPluginPayload, OpenCommandPaneInPlaceOfPluginPayload, + OpenCommandPaneNearPluginPayload, OpenCommandPanePayload, + OpenFileFloatingNearPluginPayload, OpenFileInPlaceOfPluginPayload, OpenFileNearPluginPayload, OpenFilePayload, OpenTerminalFloatingNearPluginPayload, OpenTerminalInPlaceOfPluginPayload, OpenTerminalNearPluginPayload, PageScrollDownInPaneIdPayload, PageScrollUpInPaneIdPayload, PaneId as ProtobufPaneId, @@ -1540,6 +1542,78 @@ impl TryFrom for PluginCommand { }, _ => Err("Mismatched payload for OpenFileInPlaceOfPlugin"), }, + Some(CommandName::GroupAndUngroupPanes) => match protobuf_plugin_command.payload { + Some(Payload::GroupAndUngroupPanesPayload(group_and_ungroup_panes_payload)) => { + Ok(PluginCommand::GroupAndUngroupPanes( + group_and_ungroup_panes_payload + .pane_ids_to_group + .into_iter() + .filter_map(|p| p.try_into().ok()) + .collect(), + group_and_ungroup_panes_payload + .pane_ids_to_ungroup + .into_iter() + .filter_map(|p| p.try_into().ok()) + .collect(), + )) + }, + _ => Err("Mismatched payload for GroupAndUngroupPanes"), + }, + Some(CommandName::HighlightAndUnhighlightPanes) => { + match protobuf_plugin_command.payload { + Some(Payload::HighlightAndUnhighlightPanesPayload( + highlight_and_unhighlight_panes_payload, + )) => Ok(PluginCommand::HighlightAndUnhighlightPanes( + highlight_and_unhighlight_panes_payload + .pane_ids_to_highlight + .into_iter() + .filter_map(|p| p.try_into().ok()) + .collect(), + highlight_and_unhighlight_panes_payload + .pane_ids_to_unhighlight + .into_iter() + .filter_map(|p| p.try_into().ok()) + .collect(), + )), + _ => Err("Mismatched payload for HighlightAndUnhighlightPanes"), + } + }, + Some(CommandName::CloseMultiplePanes) => match protobuf_plugin_command.payload { + Some(Payload::CloseMultiplePanesPayload(close_multiple_panes_payload)) => { + Ok(PluginCommand::CloseMultiplePanes( + close_multiple_panes_payload + .pane_ids + .into_iter() + .filter_map(|p| p.try_into().ok()) + .collect(), + )) + }, + _ => Err("Mismatched payload for CloseMultiplePanes"), + }, + Some(CommandName::FloatMultiplePanes) => match protobuf_plugin_command.payload { + Some(Payload::FloatMultiplePanesPayload(float_multiple_panes_payload)) => { + Ok(PluginCommand::FloatMultiplePanes( + float_multiple_panes_payload + .pane_ids + .into_iter() + .filter_map(|p| p.try_into().ok()) + .collect(), + )) + }, + _ => Err("Mismatched payload for FloatMultiplePanes"), + }, + Some(CommandName::EmbedMultiplePanes) => match protobuf_plugin_command.payload { + Some(Payload::EmbedMultiplePanesPayload(embed_multiple_panes_payload)) => { + Ok(PluginCommand::EmbedMultiplePanes( + embed_multiple_panes_payload + .pane_ids + .into_iter() + .filter_map(|p| p.try_into().ok()) + .collect(), + )) + }, + _ => Err("Mismatched payload for EmbedMultiplePanes"), + }, None => Err("Unrecognized plugin command"), } } @@ -2551,6 +2625,65 @@ impl TryFrom for ProtobufPluginCommand { }, )), }), + PluginCommand::GroupAndUngroupPanes(panes_to_group, panes_to_ungroup) => { + Ok(ProtobufPluginCommand { + name: CommandName::GroupAndUngroupPanes as i32, + payload: Some(Payload::GroupAndUngroupPanesPayload( + GroupAndUngroupPanesPayload { + pane_ids_to_group: panes_to_group + .iter() + .filter_map(|&p| p.try_into().ok()) + .collect(), + pane_ids_to_ungroup: panes_to_ungroup + .iter() + .filter_map(|&p| p.try_into().ok()) + .collect(), + }, + )), + }) + }, + PluginCommand::HighlightAndUnhighlightPanes( + panes_to_highlight, + panes_to_unhighlight, + ) => Ok(ProtobufPluginCommand { + name: CommandName::HighlightAndUnhighlightPanes as i32, + payload: Some(Payload::HighlightAndUnhighlightPanesPayload( + HighlightAndUnhighlightPanesPayload { + pane_ids_to_highlight: panes_to_highlight + .iter() + .filter_map(|&p| p.try_into().ok()) + .collect(), + pane_ids_to_unhighlight: panes_to_unhighlight + .iter() + .filter_map(|&p| p.try_into().ok()) + .collect(), + }, + )), + }), + PluginCommand::CloseMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand { + name: CommandName::CloseMultiplePanes as i32, + payload: Some(Payload::CloseMultiplePanesPayload( + CloseMultiplePanesPayload { + pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(), + }, + )), + }), + PluginCommand::FloatMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand { + name: CommandName::FloatMultiplePanes as i32, + payload: Some(Payload::FloatMultiplePanesPayload( + FloatMultiplePanesPayload { + pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(), + }, + )), + }), + PluginCommand::EmbedMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand { + name: CommandName::EmbedMultiplePanes as i32, + payload: Some(Payload::EmbedMultiplePanesPayload( + EmbedMultiplePanesPayload { + pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(), + }, + )), + }), } } } diff --git a/zellij-utils/src/plugin_api/plugin_ids.proto b/zellij-utils/src/plugin_api/plugin_ids.proto index 09d30bd0..c2c7870c 100644 --- a/zellij-utils/src/plugin_api/plugin_ids.proto +++ b/zellij-utils/src/plugin_api/plugin_ids.proto @@ -6,6 +6,7 @@ message PluginIds { int32 plugin_id = 1; int32 zellij_pid = 2; string initial_cwd = 3; + uint32 client_id = 4; } message ZellijVersion { diff --git a/zellij-utils/src/plugin_api/plugin_ids.rs b/zellij-utils/src/plugin_api/plugin_ids.rs index 749ccf22..9a750973 100644 --- a/zellij-utils/src/plugin_api/plugin_ids.rs +++ b/zellij-utils/src/plugin_api/plugin_ids.rs @@ -13,6 +13,7 @@ impl TryFrom for PluginIds { plugin_id: protobuf_plugin_ids.plugin_id as u32, zellij_pid: protobuf_plugin_ids.zellij_pid as u32, initial_cwd: PathBuf::from(protobuf_plugin_ids.initial_cwd), + client_id: protobuf_plugin_ids.client_id as u16, }) } } @@ -24,6 +25,7 @@ impl TryFrom for ProtobufPluginIds { plugin_id: plugin_ids.plugin_id as i32, zellij_pid: plugin_ids.zellij_pid as i32, initial_cwd: plugin_ids.initial_cwd.display().to_string(), + client_id: plugin_ids.client_id as u32, }) } } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap index 51f0eff1..50be8410 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_config_options.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 770 expression: "format!(\"{:#?}\", options)" --- Options { @@ -36,4 +35,5 @@ Options { stacked_resize: None, show_startup_tips: None, show_release_notes: None, + advanced_mouse_actions: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap index ca547f2d..201b5e64 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__cli_arguments_override_layout_options.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 798 expression: "format!(\"{:#?}\", options)" --- Options { @@ -36,4 +35,5 @@ Options { stacked_resize: None, show_startup_tips: None, show_release_notes: None, + advanced_mouse_actions: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap index 79043ec6..94e7a87e 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-3.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 757 expression: "format!(\"{:#?}\", options)" --- Options { @@ -34,4 +33,5 @@ Options { stacked_resize: None, show_startup_tips: None, show_release_notes: None, + advanced_mouse_actions: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap index 7fd6ab84..ca465e22 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 755 expression: "format!(\"{:#?}\", config)" --- Config { @@ -211,6 +210,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -273,6 +301,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -709,6 +758,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -771,6 +849,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -1165,6 +1264,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -1250,6 +1378,27 @@ Config { Normal, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -1763,6 +1912,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -1843,6 +2021,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -2251,6 +2450,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -2313,6 +2541,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -2605,6 +2854,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -2667,6 +2945,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3031,6 +3330,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3123,6 +3451,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3410,6 +3759,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3472,6 +3850,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3742,6 +4141,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3804,6 +4232,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4128,6 +4577,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4218,6 +4696,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4591,6 +5090,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4671,6 +5199,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4936,6 +5485,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4998,6 +5576,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -5446,6 +6045,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -5538,6 +6166,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -5643,6 +6292,7 @@ Config { stacked_resize: None, show_startup_tips: None, show_release_notes: None, + advanced_mouse_actions: None, }, themes: {}, plugins: PluginAliases { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap index cc836b3e..3af974e0 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 813 expression: "format!(\"{:#?}\", config)" --- Config { @@ -211,6 +210,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -273,6 +301,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -709,6 +758,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -771,6 +849,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -1165,6 +1264,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -1250,6 +1378,27 @@ Config { Normal, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -1763,6 +1912,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -1843,6 +2021,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -2251,6 +2450,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -2313,6 +2541,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -2605,6 +2854,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -2667,6 +2945,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3031,6 +3330,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3123,6 +3451,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3410,6 +3759,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3472,6 +3850,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3742,6 +4141,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3804,6 +4232,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4128,6 +4577,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4218,6 +4696,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4591,6 +5090,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4671,6 +5199,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4936,6 +5485,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4998,6 +5576,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -5446,6 +6045,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -5538,6 +6166,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -5643,6 +6292,7 @@ Config { stacked_resize: None, show_startup_tips: None, show_release_notes: None, + advanced_mouse_actions: None, }, themes: {}, plugins: PluginAliases { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap index c13ecea9..14e792a3 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_keybinds_override_config_keybinds.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 855 expression: "format!(\"{:#?}\", config)" --- Config { @@ -121,6 +120,7 @@ Config { stacked_resize: None, show_startup_tips: None, show_release_notes: None, + advanced_mouse_actions: None, }, themes: {}, plugins: PluginAliases { diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap index 59a8e93a..ff8ddd98 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_options_override_config_options.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 780 expression: "format!(\"{:#?}\", options)" --- Options { @@ -36,4 +35,5 @@ Options { stacked_resize: None, show_startup_tips: None, show_release_notes: None, + advanced_mouse_actions: None, } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap index c70aff9b..5281896f 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 841 expression: "format!(\"{:#?}\", config)" --- Config { @@ -211,6 +210,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -273,6 +301,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -709,6 +758,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -771,6 +849,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -1165,6 +1264,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -1250,6 +1378,27 @@ Config { Normal, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -1763,6 +1912,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -1843,6 +2021,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -2251,6 +2450,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -2313,6 +2541,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -2605,6 +2854,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -2667,6 +2945,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3031,6 +3330,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3123,6 +3451,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3410,6 +3759,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3472,6 +3850,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3742,6 +4141,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3804,6 +4232,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4128,6 +4577,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4218,6 +4696,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4591,6 +5090,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4671,6 +5199,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4936,6 +5485,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4998,6 +5576,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -5446,6 +6045,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -5538,6 +6166,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -5643,6 +6292,7 @@ Config { stacked_resize: None, show_startup_tips: None, show_release_notes: None, + advanced_mouse_actions: None, }, themes: { "other-theme-from-config": Theme { @@ -6095,12 +6745,8 @@ Config { 2, ), ), - emphasis_1: Rgb( - ( - 2, - 2, - 2, - ), + emphasis_1: EightBit( + 0, ), emphasis_2: Rgb( ( @@ -6689,12 +7335,8 @@ Config { 1, ), ), - emphasis_1: Rgb( - ( - 1, - 1, - 1, - ), + emphasis_1: EightBit( + 0, ), emphasis_2: Rgb( ( @@ -7283,12 +7925,8 @@ Config { 1, ), ), - emphasis_1: Rgb( - ( - 1, - 1, - 1, - ), + emphasis_1: EightBit( + 0, ), emphasis_2: Rgb( ( diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap index be3ec13f..a56b840e 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 827 expression: "format!(\"{:#?}\", config)" --- Config { @@ -211,6 +210,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -273,6 +301,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -709,6 +758,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -771,6 +849,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -1165,6 +1264,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -1250,6 +1378,27 @@ Config { Normal, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -1763,6 +1912,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -1843,6 +2021,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -2251,6 +2450,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -2313,6 +2541,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -2605,6 +2854,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -2667,6 +2945,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3031,6 +3330,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3123,6 +3451,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3410,6 +3759,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3472,6 +3850,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -3742,6 +4141,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -3804,6 +4232,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4128,6 +4577,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4218,6 +4696,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4591,6 +5090,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4671,6 +5199,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -4936,6 +5485,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -4998,6 +5576,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -5446,6 +6045,35 @@ Config { Right, ), ], + KeyWithModifier { + bare_key: Char( + 'm', + ), + key_modifiers: { + Alt, + }, + }: [ + LaunchOrFocusPlugin( + RunPlugin( + RunPlugin { + _allow_exec_host_cmd: false, + location: Zellij( + PluginTag( + "multiple-select", + ), + ), + configuration: PluginUserConfiguration( + {}, + ), + initial_cwd: None, + }, + ), + true, + true, + false, + false, + ), + ], KeyWithModifier { bare_key: Char( 'n', @@ -5538,6 +6166,27 @@ Config { Pane, ), ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + }, + }: [ + TogglePaneInGroup, + ], + KeyWithModifier { + bare_key: Char( + 'p', + ), + key_modifiers: { + Alt, + Shift, + }, + }: [ + ToggleGroupMarking, + ], KeyWithModifier { bare_key: Char( 'q', @@ -5643,6 +6292,7 @@ Config { stacked_resize: None, show_startup_tips: None, show_release_notes: None, + advanced_mouse_actions: None, }, themes: {}, plugins: PluginAliases {