feat(ux): improve multiple select (#4221)

* intercept/clear-intercept key APIs

* allow opening a pinned unfocused floating pane

* rework plugin

* improve some apis

* fix tests

* tests for pane groups

* more exact placement and tests

* plugin command permission and cleanup

* improve some multiselect ux

* improve plugin ui

* remove old status indicator

* allow moving plugin out of the way

* style(fmt): rustfmt

* update plugins

* remove old keybinding

* cleanups

* fix: only rename pane if needed

* changelog and some cleanups

* style(fmt): rustfmt
This commit is contained in:
Aram Drevekenin 2025-06-03 17:15:32 +02:00 committed by GitHub
parent 226f5dc854
commit a9f8bbcd19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 2192 additions and 3972 deletions

View file

@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
## [Unreleased] ## [Unreleased]
* feat: multiple select and bulk pane actions (https://github.com/zellij-org/zellij/pull/4169 and https://github.com/zellij-org/zellij/pull/4171) * feat: multiple select and bulk pane actions (https://github.com/zellij-org/zellij/pull/4169 and https://github.com/zellij-org/zellij/pull/4171 and https://github.com/zellij-org/zellij/pull/4221)
## [0.42.2] - 2025-04-15 ## [0.42.2] - 2025-04-15
* refactor(terminal): track scroll_region as tuple rather than Option (https://github.com/zellij-org/zellij/pull/4082) * refactor(terminal): track scroll_region as tuple rather than Option (https://github.com/zellij-org/zellij/pull/4082)

View file

@ -2,7 +2,6 @@ use ansi_term::ANSIStrings;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use crate::{LinePart, ARROW_SEPARATOR}; use crate::{LinePart, ARROW_SEPARATOR};
use zellij_tile::prelude::actions::Action;
use zellij_tile::prelude::*; use zellij_tile::prelude::*;
use zellij_tile_utils::style; use zellij_tile_utils::style;
@ -252,8 +251,6 @@ pub fn tab_line(
mode: InputMode, mode: InputMode,
active_swap_layout_name: &Option<String>, active_swap_layout_name: &Option<String>,
is_swap_layout_dirty: bool, is_swap_layout_dirty: bool,
mode_info: &ModeInfo,
grouped_pane_count: Option<usize>,
) -> Vec<LinePart> { ) -> Vec<LinePart> {
let mut tabs_after_active = all_tabs.split_off(active_tab_index); let mut tabs_after_active = all_tabs.split_off(active_tab_index);
let mut tabs_before_active = all_tabs; let mut tabs_before_active = all_tabs;
@ -289,21 +286,15 @@ pub fn tab_line(
if current_title_len < cols { if current_title_len < cols {
let mut remaining_space = cols - current_title_len; let mut remaining_space = cols - current_title_len;
let remaining_bg = palette.text_unselected.background; let remaining_bg = palette.text_unselected.background;
let right_side_component = match grouped_pane_count { if let Some(swap_layout_status) = swap_layout_status(
Some(grouped_pane_count) => {
render_group_controls(mode_info, grouped_pane_count, remaining_space)
},
None => swap_layout_status(
remaining_space, remaining_space,
active_swap_layout_name, active_swap_layout_name,
is_swap_layout_dirty, is_swap_layout_dirty,
mode, mode,
&palette, &palette,
tab_separator(capabilities), tab_separator(capabilities),
), ) {
}; remaining_space -= swap_layout_status.len;
if let Some(right_side_component) = right_side_component {
remaining_space -= right_side_component.len;
let mut buffer = String::new(); let mut buffer = String::new();
for _ in 0..remaining_space { for _ in 0..remaining_space {
buffer.push_str(&style!(remaining_bg, remaining_bg).paint(" ").to_string()); buffer.push_str(&style!(remaining_bg, remaining_bg).paint(" ").to_string());
@ -313,7 +304,7 @@ pub fn tab_line(
len: remaining_space, len: remaining_space,
tab_index: None, tab_index: None,
}); });
prefix.push(right_side_component); prefix.push(swap_layout_status);
} }
} }
@ -382,274 +373,3 @@ fn swap_layout_status(
None => None, None => None,
} }
} }
fn render_group_controls(
help: &ModeInfo,
grouped_pane_count: usize,
max_len: usize,
) -> Option<LinePart> {
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<KeyWithModifier> = multiple_select_key
.iter()
.map(|k| k.strip_common_modifiers(&common_modifiers))
.collect();
let pane_group_toggle_key: Vec<KeyWithModifier> = pane_group_toggle_key
.iter()
.map(|k| k.strip_common_modifiers(&common_modifiers))
.collect();
let group_mark_toggle_key: Vec<KeyWithModifier> = 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::<Vec<_>>()
.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<Action>)]) -> Vec<KeyWithModifier> {
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: &[Action],
) -> Vec<KeyWithModifier> {
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<KeyModifier> {
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()
}

View file

@ -26,8 +26,6 @@ struct State {
tab_line: Vec<LinePart>, tab_line: Vec<LinePart>,
text_copy_destination: Option<CopyDestination>, text_copy_destination: Option<CopyDestination>,
display_system_clipboard_failure: bool, display_system_clipboard_failure: bool,
own_client_id: Option<ClientId>,
grouped_panes_count: Option<usize>,
} }
static ARROW_SEPARATOR: &str = ""; static ARROW_SEPARATOR: &str = "";
@ -46,7 +44,6 @@ impl ZellijPlugin for State {
EventType::SystemClipboardFailure, EventType::SystemClipboardFailure,
EventType::PaneUpdate, EventType::PaneUpdate,
]); ]);
self.own_client_id = Some(get_plugin_ids().client_id);
} }
fn update(&mut self, event: Event) -> bool { fn update(&mut self, event: Event) -> bool {
@ -112,28 +109,6 @@ impl ZellijPlugin for State {
self.text_copy_destination = None; self.text_copy_destination = None;
self.display_system_clipboard_failure = false; 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); eprintln!("Got unrecognized event: {:?}", event);
}, },
@ -207,8 +182,6 @@ impl ZellijPlugin for State {
self.mode_info.mode, self.mode_info.mode,
&active_swap_layout_name, &active_swap_layout_name,
is_swap_layout_dirty, is_swap_layout_dirty,
&self.mode_info,
self.grouped_panes_count,
); );
let output = self let output = self
.tab_line .tab_line

View file

@ -168,12 +168,6 @@ keybinds clear-defaults=true {{
bind "{secondary_modifier} -" {{ Resize "Decrease"; }} bind "{secondary_modifier} -" {{ Resize "Decrease"; }}
bind "{secondary_modifier} [" {{ PreviousSwapLayout; }} bind "{secondary_modifier} [" {{ PreviousSwapLayout; }}
bind "{secondary_modifier} ]" {{ NextSwapLayout; }} 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} p" {{ TogglePaneInGroup; }}
bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }} bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }}
}} }}
@ -400,12 +394,6 @@ keybinds clear-defaults=true {{
bind "{secondary_modifier} -" {{ Resize "Decrease"; }} bind "{secondary_modifier} -" {{ Resize "Decrease"; }}
bind "{secondary_modifier} [" {{ PreviousSwapLayout; }} bind "{secondary_modifier} [" {{ PreviousSwapLayout; }}
bind "{secondary_modifier} ]" {{ NextSwapLayout; }} 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} p" {{ TogglePaneInGroup; }}
bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }} bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }}
}} }}
@ -611,12 +599,6 @@ keybinds clear-defaults=true {{
bind "{secondary_modifier} -" {{ Resize "Decrease"; }} bind "{secondary_modifier} -" {{ Resize "Decrease"; }}
bind "{secondary_modifier} [" {{ PreviousSwapLayout; }} bind "{secondary_modifier} [" {{ PreviousSwapLayout; }}
bind "{secondary_modifier} ]" {{ NextSwapLayout; }} 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} p" {{ TogglePaneInGroup; }}
bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }} bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }}
}} }}
@ -1182,12 +1164,6 @@ keybinds clear-defaults=true {{
bind "{secondary_modifier} -" {{ Resize "Decrease"; }} bind "{secondary_modifier} -" {{ Resize "Decrease"; }}
bind "{secondary_modifier} [" {{ PreviousSwapLayout; }} bind "{secondary_modifier} [" {{ PreviousSwapLayout; }}
bind "{secondary_modifier} ]" {{ NextSwapLayout; }} 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} p" {{ TogglePaneInGroup; }}
bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }} bind "{secondary_modifier} Shift p" {{ ToggleGroupMarking; }}
}} }}

View file

@ -1,9 +1,6 @@
pub mod state;
pub mod ui;
use state::{MarkedIndex, VisibilityAndFocus};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use ui::PaneItem; use std::time::Instant;
use zellij_tile::prelude::actions::Action;
use zellij_tile::prelude::*; use zellij_tile::prelude::*;
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -12,13 +9,17 @@ pub struct App {
own_client_id: Option<ClientId>, own_client_id: Option<ClientId>,
own_tab_index: Option<usize>, own_tab_index: Option<usize>,
total_tabs_in_session: Option<usize>, total_tabs_in_session: Option<usize>,
search_string: String, grouped_panes: Vec<PaneId>,
previous_search_string: String, // used eg. for the new tab title when breaking panes grouped_panes_count: usize,
left_side_panes: Vec<PaneItem>, mode_info: ModeInfo,
right_side_panes: Vec<PaneItem>, closing: bool,
search_results: Option<Vec<PaneItem>>, highlighted_at: Option<Instant>,
visibility_and_focus: VisibilityAndFocus, baseline_ui_width: usize,
marked_index: Option<MarkedIndex>, current_rows: usize,
current_cols: usize,
display_area_rows: usize,
display_area_cols: usize,
alternate_coordinates: bool,
} }
register_plugin!(App); register_plugin!(App);
@ -27,186 +28,597 @@ impl ZellijPlugin for App {
fn load(&mut self, _configuration: BTreeMap<String, String>) { fn load(&mut self, _configuration: BTreeMap<String, String>) {
subscribe(&[ subscribe(&[
EventType::Key, EventType::Key,
EventType::Mouse, EventType::InterceptedKeyPress,
EventType::ModeUpdate, EventType::ModeUpdate,
EventType::RunCommandResult,
EventType::TabUpdate,
EventType::PaneUpdate, EventType::PaneUpdate,
EventType::FailedToWriteConfigToDisk, EventType::TabUpdate,
EventType::ConfigWasWrittenToDisk, EventType::Timer,
EventType::BeforeClose,
]); ]);
let plugin_ids = get_plugin_ids(); let plugin_ids = get_plugin_ids();
self.own_plugin_id = Some(plugin_ids.plugin_id); self.own_plugin_id = Some(plugin_ids.plugin_id);
self.own_client_id = Some(plugin_ids.client_id); self.own_client_id = Some(plugin_ids.client_id);
rename_plugin_pane(plugin_ids.plugin_id, "Multiple Select");
intercept_key_presses();
set_selectable(false);
} }
fn update(&mut self, event: Event) -> bool { fn update(&mut self, event: Event) -> bool {
let mut should_render = false; if self.closing {
return false;
}
match event { match event {
Event::PaneUpdate(pane_manifest) => { Event::ModeUpdate(mode_info) => self.handle_mode_update(mode_info),
self.react_to_zellij_state_update(pane_manifest); Event::PaneUpdate(pane_manifest) => self.handle_pane_update(pane_manifest),
should_render = true; Event::TabUpdate(tab_infos) => self.handle_tab_update(tab_infos),
}, Event::InterceptedKeyPress(key) => self.handle_key_press(key),
Event::Key(key) => { Event::Timer(_) => self.handle_timer(),
match key.bare_key { _ => false,
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) { fn render(&mut self, rows: usize, cols: usize) {
self.render_close_shortcut(cols); self.update_current_size(rows, cols);
self.render_tab_shortcut(cols, rows); let ui_width = self.calculate_ui_width();
match self.visibility_and_focus { self.update_baseline_ui_width(ui_width);
VisibilityAndFocus::OnlyLeftSideVisible => self.render_left_side(rows, cols, true), let base_x = cols.saturating_sub(self.baseline_ui_width) / 2;
VisibilityAndFocus::OnlyRightSideVisible => self.render_right_side(rows, cols, true), let base_y = rows.saturating_sub(8) / 2;
VisibilityAndFocus::BothSidesVisibleLeftSideFocused => { self.render_header(base_x, base_y);
self.render_left_side(rows, cols, true); self.render_shortcuts(base_x, base_y + 2);
self.render_right_side(rows, cols, false); self.render_controls(base_x, base_y + 7);
}, }
VisibilityAndFocus::BothSidesVisibleRightSideFocused => { }
self.render_left_side(rows, cols, false);
self.render_right_side(rows, cols, true); impl App {
fn update_current_size(&mut self, new_rows: usize, new_cols: usize) {
let size_changed = new_rows != self.current_rows || new_cols != self.current_cols;
self.current_rows = new_rows;
self.current_cols = new_cols;
if size_changed {
self.baseline_ui_width = 0;
}
}
fn update_baseline_ui_width(&mut self, current_ui_width: usize) {
if current_ui_width > self.baseline_ui_width {
self.baseline_ui_width = current_ui_width;
}
}
fn calculate_ui_width(&self) -> usize {
let controls_width = group_controls_length(&self.mode_info);
let header_width = Self::header_text().0.len();
let shortcuts_max_width = Self::shortcuts_max_width();
std::cmp::max(
controls_width,
std::cmp::max(header_width, shortcuts_max_width),
)
}
fn header_text() -> (&'static str, Text) {
let header_text = "<ESC> - cancel, <TAB> - move";
let header_text_component = Text::new(header_text)
.color_substring(3, "<ESC>")
.color_substring(3, "<TAB>");
(header_text, header_text_component)
}
fn shortcuts_max_width() -> usize {
std::cmp::max(
std::cmp::max(
Self::group_actions_text().0.len(),
Self::shortcuts_line1_text().0.len(),
),
std::cmp::max(
Self::shortcuts_line2_text().0.len(),
Self::shortcuts_line3_text().0.len(),
),
)
}
fn group_actions_text() -> (&'static str, Text) {
let text = "GROUP ACTIONS";
let component = Text::new(text).color_all(2);
(text, component)
}
fn shortcuts_line1_text() -> (&'static str, Text) {
let text = "<b> - break out, <s> - stack, <c> - close";
let component = Text::new(text)
.color_substring(3, "<b>")
.color_substring(3, "<s>")
.color_substring(3, "<c>");
(text, component)
}
fn shortcuts_line2_text() -> (&'static str, Text) {
let text = "<r> - break right, <l> - break left";
let component = Text::new(text)
.color_substring(3, "<r>")
.color_substring(3, "<l>");
(text, component)
}
fn shortcuts_line3_text() -> (&'static str, Text) {
let text = "<e> - embed, <f> - float";
let component = Text::new(text)
.color_substring(3, "<e>")
.color_substring(3, "<f>");
(text, component)
}
fn handle_mode_update(&mut self, mode_info: ModeInfo) -> bool {
if self.mode_info != mode_info {
self.mode_info = mode_info;
let ui_width = self.calculate_ui_width();
self.update_baseline_ui_width(ui_width);
true
} else {
false
}
}
fn handle_pane_update(&mut self, pane_manifest: PaneManifest) -> bool {
let Some(own_client_id) = self.own_client_id else {
return false;
};
self.update_grouped_panes(&pane_manifest, own_client_id);
self.update_tab_info(&pane_manifest);
self.total_tabs_in_session = Some(pane_manifest.panes.keys().count());
true
}
fn handle_tab_update(&mut self, tab_infos: Vec<TabInfo>) -> bool {
for tab in tab_infos {
if tab.active {
self.display_area_rows = tab.display_area_rows;
self.display_area_cols = tab.display_area_columns;
break;
}
}
false
}
fn update_grouped_panes(&mut self, pane_manifest: &PaneManifest, own_client_id: ClientId) {
self.grouped_panes.clear();
let mut count = 0;
for (_tab_index, pane_infos) in &pane_manifest.panes {
for pane_info in pane_infos {
if pane_info.index_in_pane_group.get(&own_client_id).is_some() {
let pane_id = if pane_info.is_plugin {
PaneId::Plugin(pane_info.id)
} else {
PaneId::Terminal(pane_info.id)
};
self.grouped_panes.push(pane_id);
count += 1;
}
}
}
if count == 0 {
self.close_self();
}
let previous_count = self.grouped_panes_count;
self.grouped_panes_count = count;
if let Some(own_plugin_id) = self.own_plugin_id {
let title = if count == 1 {
"SELECTED PANE"
} else {
"SELECTED PANES"
};
if previous_count != count {
rename_plugin_pane(own_plugin_id, format!("{} {}", count, title));
}
if previous_count != 0 && count != 0 && previous_count != count {
if self.doherty_threshold_elapsed_since_highlight() {
self.highlighted_at = Some(Instant::now());
highlight_and_unhighlight_panes(vec![PaneId::Plugin(own_plugin_id)], vec![]);
set_timeout(0.4);
}
}
}
}
fn doherty_threshold_elapsed_since_highlight(&self) -> bool {
self.highlighted_at
.map(|h| h.elapsed() >= std::time::Duration::from_millis(400))
.unwrap_or(true)
}
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);
return;
}
}
}
}
fn handle_key_press(&mut self, key: KeyWithModifier) -> bool {
if !key.has_no_modifiers() {
return false;
}
match key.bare_key {
BareKey::Char('b') => self.break_grouped_panes_to_new_tab(),
BareKey::Char('s') => self.stack_grouped_panes(),
BareKey::Char('f') => self.float_grouped_panes(),
BareKey::Char('e') => self.embed_grouped_panes(),
BareKey::Char('r') => self.break_grouped_panes_right(),
BareKey::Char('l') => self.break_grouped_panes_left(),
BareKey::Char('c') => self.close_grouped_panes(),
BareKey::Tab => self.next_coordinates(),
BareKey::Esc => {
self.ungroup_panes_in_zellij(&self.grouped_panes.clone());
self.close_self();
}, },
_ => return false,
} }
self.render_focus_boundary(rows, cols); false
self.render_help_line(rows, cols); }
fn handle_timer(&mut self) -> bool {
if let Some(own_plugin_id) = self.own_plugin_id {
if self.doherty_threshold_elapsed_since_highlight() {
highlight_and_unhighlight_panes(vec![], vec![PaneId::Plugin(own_plugin_id)]);
} }
} }
false
}
fn render_header(&self, base_x: usize, base_y: usize) {
let header_text = Self::header_text();
print_text_with_coordinates(header_text.1, base_x, base_y, None, None);
}
fn render_shortcuts(&self, base_x: usize, base_y: usize) {
let mut running_y = base_y;
print_text_with_coordinates(Self::group_actions_text().1, base_x, running_y, None, None);
running_y += 1;
print_text_with_coordinates(
Self::shortcuts_line1_text().1,
base_x,
running_y,
None,
None,
);
running_y += 1;
print_text_with_coordinates(
Self::shortcuts_line2_text().1,
base_x,
running_y,
None,
None,
);
running_y += 1;
print_text_with_coordinates(
Self::shortcuts_line3_text().1,
base_x,
running_y,
None,
None,
);
}
fn render_controls(&self, base_x: usize, base_y: usize) {
render_group_controls(&self.mode_info, base_x, base_y);
}
fn execute_action_and_close<F>(&mut self, action: F)
where
F: FnOnce(&[PaneId]),
{
let pane_ids = self.grouped_panes.clone();
action(&pane_ids);
self.close_self();
}
pub fn break_grouped_panes_to_new_tab(&mut self) {
self.execute_action_and_close(|pane_ids| {
break_panes_to_new_tab(pane_ids, None, true);
});
self.ungroup_panes_in_zellij(&self.grouped_panes.clone());
}
pub fn stack_grouped_panes(&mut self) {
self.execute_action_and_close(|pane_ids| {
stack_panes(pane_ids.to_vec());
});
self.ungroup_panes_in_zellij(&self.grouped_panes.clone());
}
pub fn float_grouped_panes(&mut self) {
self.execute_action_and_close(|pane_ids| {
float_multiple_panes(pane_ids.to_vec());
});
self.ungroup_panes_in_zellij(&self.grouped_panes.clone());
}
pub fn embed_grouped_panes(&mut self) {
self.execute_action_and_close(|pane_ids| {
embed_multiple_panes(pane_ids.to_vec());
});
self.ungroup_panes_in_zellij(&self.grouped_panes.clone());
}
pub fn break_grouped_panes_right(&mut self) {
let Some(own_tab_index) = self.own_tab_index else {
return;
};
let pane_ids = self.grouped_panes.clone();
if Some(own_tab_index + 1) < self.total_tabs_in_session {
break_panes_to_tab_with_index(&pane_ids, own_tab_index + 1, true);
} else {
break_panes_to_new_tab(&pane_ids, None, true);
}
self.close_self();
}
pub fn break_grouped_panes_left(&mut self) {
let Some(own_tab_index) = self.own_tab_index else {
return;
};
let pane_ids = self.grouped_panes.clone();
if own_tab_index > 0 {
break_panes_to_tab_with_index(&pane_ids, own_tab_index - 1, true);
} else {
break_panes_to_new_tab(&pane_ids, None, true);
}
self.close_self();
}
pub fn close_grouped_panes(&mut self) {
self.execute_action_and_close(|pane_ids| {
close_multiple_panes(pane_ids.to_vec());
});
}
pub fn ungroup_panes_in_zellij(&mut self, pane_ids: &[PaneId]) {
group_and_ungroup_panes(vec![], pane_ids.to_vec());
}
pub fn close_self(&mut self) {
self.closing = true;
close_self();
}
pub fn next_coordinates(&mut self) {
let width_30_percent = (self.display_area_cols as f64 * 0.3) as usize;
let height_30_percent = (self.display_area_rows as f64 * 0.3) as usize;
let width = std::cmp::max(width_30_percent, 48);
let height = std::cmp::max(height_30_percent, 10);
let y_position = self.display_area_rows.saturating_sub(height + 2);
if let Some(own_plugin_id) = self.own_plugin_id {
if self.alternate_coordinates {
let x_position = 2;
let Some(next_coordinates) = FloatingPaneCoordinates::new(
Some(format!("{}", x_position)),
Some(format!("{}", y_position)),
Some(format!("{}", width)),
Some(format!("{}", height)),
Some(true),
) else {
return;
};
change_floating_panes_coordinates(vec![(
PaneId::Plugin(own_plugin_id),
next_coordinates,
)]);
self.alternate_coordinates = false;
} else {
let x_position = self
.display_area_cols
.saturating_sub(width)
.saturating_sub(2);
let Some(next_coordinates) = FloatingPaneCoordinates::new(
Some(format!("{}", x_position)),
Some(format!("{}", y_position)),
Some(format!("{}", width)),
Some(format!("{}", height)),
Some(true),
) else {
return;
};
change_floating_panes_coordinates(vec![(
PaneId::Plugin(own_plugin_id),
next_coordinates,
)]);
self.alternate_coordinates = true;
}
}
}
}
fn render_group_controls(mode_info: &ModeInfo, base_x: usize, base_y: usize) {
let keymap = mode_info.get_mode_keybinds();
let (common_modifiers, pane_group_key, group_mark_key) = extract_key_bindings(&keymap);
let pane_group_bound = pane_group_key != "UNBOUND";
let group_mark_bound = group_mark_key != "UNBOUND";
if !pane_group_bound && !group_mark_bound {
return;
}
render_common_modifiers(&common_modifiers, base_x, base_y);
let mut next_x = base_x + render_common_modifiers(&common_modifiers, base_x, base_y);
if pane_group_bound {
next_x = render_toggle_group_ribbon(&pane_group_key, next_x, base_y);
}
if group_mark_bound {
render_follow_focus_ribbon(&group_mark_key, next_x, base_y, mode_info);
}
}
fn group_controls_length(mode_info: &ModeInfo) -> usize {
let keymap = mode_info.get_mode_keybinds();
let (common_modifiers, pane_group_key, group_mark_key) = extract_key_bindings(&keymap);
let pane_group_bound = pane_group_key != "UNBOUND";
let group_mark_bound = group_mark_key != "UNBOUND";
let mut length = 0;
if !common_modifiers.is_empty() {
let modifiers_text = format!(
"{} + ",
common_modifiers
.iter()
.map(|m| m.to_string())
.collect::<Vec<_>>()
.join(" ")
);
length += modifiers_text.chars().count();
}
if pane_group_bound {
let toggle_text = format!("<{}> Toggle", pane_group_key);
length += toggle_text.chars().count() + 4;
}
if group_mark_bound {
let follow_text = format!("<{}> Follow Focus", group_mark_key);
length += follow_text.chars().count() + 4;
}
length
}
fn extract_key_bindings(
keymap: &[(KeyWithModifier, Vec<Action>)],
) -> (Vec<KeyModifier>, String, String) {
let pane_group_keys = get_key_for_action(keymap, &[Action::TogglePaneInGroup]);
let group_mark_keys = get_key_for_action(keymap, &[Action::ToggleGroupMarking]);
let key_refs: Vec<&KeyWithModifier> = [pane_group_keys.first(), group_mark_keys.first()]
.into_iter()
.flatten()
.collect();
let common_modifiers = get_common_modifiers(key_refs);
let pane_group_key = format_key_without_modifiers(&pane_group_keys, &common_modifiers);
let group_mark_key = format_key_without_modifiers(&group_mark_keys, &common_modifiers);
(common_modifiers, pane_group_key, group_mark_key)
}
fn format_key_without_modifiers(
keys: &[KeyWithModifier],
common_modifiers: &[KeyModifier],
) -> String {
keys.first()
.map(|key| format!("{}", key.strip_common_modifiers(&common_modifiers.to_vec())))
.unwrap_or_else(|| "UNBOUND".to_string())
}
fn render_common_modifiers(
common_modifiers: &[KeyModifier],
base_x: usize,
base_y: usize,
) -> usize {
if !common_modifiers.is_empty() {
let modifiers_text = format!(
"{} + ",
common_modifiers
.iter()
.map(|m| m.to_string())
.collect::<Vec<_>>()
.join(" ")
);
print_text_with_coordinates(
Text::new(&modifiers_text).color_all(0),
base_x,
base_y,
None,
None,
);
modifiers_text.chars().count()
} else {
0
}
}
fn get_key_for_action(
keymap: &[(KeyWithModifier, Vec<Action>)],
target_action: &[Action],
) -> Vec<KeyWithModifier> {
keymap
.iter()
.find_map(|(key, actions)| {
if actions.first() == target_action.first() {
Some(key.clone())
} else {
None
}
})
.map(|key| vec![key])
.unwrap_or_default()
}
fn get_common_modifiers(keys: Vec<&KeyWithModifier>) -> Vec<KeyModifier> {
if keys.is_empty() {
return vec![];
}
let mut common = keys[0].key_modifiers.clone();
for key in keys.iter().skip(1) {
common = common.intersection(&key.key_modifiers).cloned().collect();
}
common.into_iter().collect()
}
fn render_follow_focus_ribbon(
group_mark_key: &str,
x_position: usize,
base_y: usize,
mode_info: &ModeInfo,
) {
let follow_text = format!("<{}> Follow Focus", group_mark_key);
let key_highlight = format!("{}", group_mark_key);
let mut ribbon = Text::new(&follow_text).color_substring(0, &key_highlight);
if mode_info.currently_marking_pane_group.unwrap_or(false) {
ribbon = ribbon.selected();
}
print_ribbon_with_coordinates(ribbon, x_position, base_y, None, None);
}
fn render_toggle_group_ribbon(pane_group_key: &str, base_x: usize, base_y: usize) -> usize {
let toggle_text = format!("<{}> Toggle", pane_group_key);
let key_highlight = format!("{}", pane_group_key);
print_ribbon_with_coordinates(
Text::new(&toggle_text).color_substring(0, &key_highlight),
base_x,
base_y,
None,
None,
);
base_x + toggle_text.len() + 4
}

View file

@ -1,634 +0,0 @@
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<usize>,
}
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<usize, PaneItem> = 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<PaneId>) {
group_and_ungroup_panes(pane_ids, vec![]);
}
pub fn ungroup_panes_in_zellij(&mut self, pane_ids: Vec<PaneId>) {
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<usize> =
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<PaneId> = 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<usize> =
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<PaneItem>) {
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<PaneId> =
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<PaneId> =
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<PaneId> =
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<PaneId> =
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<PaneId> =
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<PaneId> =
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<PaneId> =
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<PaneId> =
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<PaneId> =
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<PaneId> =
self.right_side_panes.drain(..).map(|p| p.id).collect();
close_multiple_panes(pane_ids_to_close);
close_self();
}
}

View file

@ -1,873 +0,0 @@
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<usize>,
}
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::<Vec<_>>()
.iter()
.rev()
.collect();
let second_half_start_index = pane_item_text_len.saturating_sub(length_of_each_half);
let adjusted_indices: Vec<usize> = 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 = "<Ctrl c> - 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("<TAB> - 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 = "<TAB> - 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("<TAB> - 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::<Vec<_>>()
.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::<Vec<_>>()
.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<NestedListItem>) {
// 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 = "<ENTER> - select, <↓↑→> - navigate";
let enter_select_panes_text_short = "<ENTER> / <↓↑→>...";
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 = "<ENTER> - select all, <↓↑> - navigate";
let enter_select_panes_text_short = "<ENTER> / <↓↑>...";
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 = "<SPACE> - mark many,";
let space_shortcut_text_short = "<SPACE> /";
if self.marked_index.is_some() {
let escape_shortcut_text_full = "<Ctrl c> - remove marks";
let escape_shortcut_text_short = "<Ctrl c>...";
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() {
"<Ctrl c> - 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<NestedListItem>) {
// 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, <Ctrl c> - clear";
let right_side_controls_text_1_short = "<←↓↑>/<Ctrl c>...";
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 = "<b> - break out, <s> - stack, <c> - close";
let right_side_controls_text_2_short = "<b>/<s>/<c>...";
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 = "<r> - break right, <l> - break left";
let right_side_controls_text_3_short = "<r>/<l>...";
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 = "<e> - embed, <f> - float";
let right_side_controls_text_4_short = "<e>/<f>...";
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,
);
}
}

View file

@ -39,8 +39,6 @@ struct State {
display_system_clipboard_failure: bool, display_system_clipboard_failure: bool,
classic_ui: bool, classic_ui: bool,
base_mode_is_locked: bool, base_mode_is_locked: bool,
own_client_id: Option<ClientId>,
grouped_panes_count: Option<usize>,
} }
register_plugin!(State); register_plugin!(State);
@ -199,7 +197,6 @@ impl ZellijPlugin for State {
.get("classic") .get("classic")
.map(|c| c == "true") .map(|c| c == "true")
.unwrap_or(false); .unwrap_or(false);
self.own_client_id = Some(get_plugin_ids().client_id);
set_selectable(false); set_selectable(false);
subscribe(&[ subscribe(&[
EventType::ModeUpdate, EventType::ModeUpdate,
@ -227,28 +224,6 @@ impl ZellijPlugin for State {
} }
self.tabs = tabs; 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) => { Event::CopyToClipboard(copy_destination) => {
match self.text_copy_destination { match self.text_copy_destination {
Some(text_copy_destination) => { Some(text_copy_destination) => {
@ -306,7 +281,6 @@ impl ZellijPlugin for State {
self.base_mode_is_locked, self.base_mode_is_locked,
self.text_copy_destination, self.text_copy_destination,
self.display_system_clipboard_failure, self.display_system_clipboard_failure,
self.grouped_panes_count,
), ),
fill_bg, fill_bg,
); );

View file

@ -6,7 +6,7 @@ use ansi_term::{
use std::collections::HashMap; use std::collections::HashMap;
use zellij_tile::prelude::actions::Action; use zellij_tile::prelude::actions::Action;
use zellij_tile::prelude::*; use zellij_tile::prelude::*;
use zellij_tile_utils::{palette_match, style}; use zellij_tile_utils::palette_match;
use crate::first_line::{to_char, KeyAction, KeyMode, KeyShortcut}; use crate::first_line::{to_char, KeyAction, KeyMode, KeyShortcut};
use crate::second_line::{system_clipboard_error, text_copied_hint}; use crate::second_line::{system_clipboard_error, text_copied_hint};
@ -22,7 +22,6 @@ pub fn one_line_ui(
base_mode_is_locked: bool, base_mode_is_locked: bool,
text_copied_to_clipboard_destination: Option<CopyDestination>, text_copied_to_clipboard_destination: Option<CopyDestination>,
clipboard_failure: bool, clipboard_failure: bool,
grouped_pane_count: Option<usize>,
) -> LinePart { ) -> LinePart {
if let Some(text_copied_to_clipboard_destination) = text_copied_to_clipboard_destination { if let Some(text_copied_to_clipboard_destination) = text_copied_to_clipboard_destination {
return text_copied_hint(text_copied_to_clipboard_destination); return text_copied_hint(text_copied_to_clipboard_destination);
@ -36,17 +35,10 @@ pub fn one_line_ui(
*max_len = max_len.saturating_sub(line_part.len); *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) render_mode_key_indicators(help, max_len, separator, base_mode_is_locked)
.map(|mode_key_indicators| append(&mode_key_indicators, &mut max_len)) .map(|mode_key_indicators| append(&mode_key_indicators, &mut max_len))
.and_then(|_| match help.mode { .and_then(|_| match help.mode {
InputMode::Normal | InputMode::Locked => match grouped_pane_count { InputMode::Normal | InputMode::Locked => render_secondary_info(help, tab_info, max_len)
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)), .map(|secondary_info| append(&secondary_info, &mut max_len)),
_ => add_keygroup_separator(help, max_len) _ => add_keygroup_separator(help, max_len)
.map(|key_group_separator| append(&key_group_separator, &mut max_len)) .map(|key_group_separator| append(&key_group_separator, &mut max_len))
@ -664,225 +656,6 @@ fn render_common_modifiers(
line_part_to_render.len += prefix_text.chars().count() + separator.chars().count(); 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<LinePart> {
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<KeyWithModifier> = multiple_select_key
.iter()
.map(|k| k.strip_common_modifiers(&common_modifiers))
.collect();
let pane_group_toggle_key: Vec<KeyWithModifier> = pane_group_toggle_key
.iter()
.map(|k| k.strip_common_modifiers(&common_modifiers))
.collect();
let group_mark_toggle_key: Vec<KeyWithModifier> = 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::<Vec<_>>()
.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( fn render_secondary_info(
help: &ModeInfo, help: &ModeInfo,
tab_info: Option<&TabInfo>, tab_info: Option<&TabInfo>,
@ -1745,25 +1518,6 @@ fn configuration_key(keymap: &[(KeyWithModifier, Vec<Action>)]) -> Vec<KeyWithMo
} }
} }
fn multiple_select_key(keymap: &[(KeyWithModifier, Vec<Action>)]) -> Vec<KeyWithModifier> {
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<usize>) -> LinePart { fn style_key_with_modifier(keyvec: &[KeyWithModifier], color_index: Option<usize>) -> LinePart {
if keyvec.is_empty() { if keyvec.is_empty() {
return LinePart::default(); return LinePart::default();

View file

@ -138,12 +138,6 @@ keybinds {
bind "Alt k" "Alt Up" { MoveFocus "Up"; } bind "Alt k" "Alt Up" { MoveFocus "Up"; }
bind "Alt =" "Alt +" { Resize "Increase"; } bind "Alt =" "Alt +" { Resize "Increase"; }
bind "Alt -" { Resize "Decrease"; } bind "Alt -" { Resize "Decrease"; }
bind "Alt m" {
LaunchOrFocusPlugin "zellij:multiple-select" {
floating true
move_to_focused_tab true
}
}
bind "Alt p" { TogglePaneInGroup; } bind "Alt p" { TogglePaneInGroup; }
bind "Alt Shift p" { ToggleGroupMarking; } bind "Alt Shift p" { ToggleGroupMarking; }
} }

View file

@ -5,6 +5,7 @@ pub mod tab;
mod background_jobs; mod background_jobs;
mod logging_pipe; mod logging_pipe;
mod pane_groups;
mod plugins; mod plugins;
mod pty; mod pty;
mod pty_writer; mod pty_writer;

View file

@ -0,0 +1,469 @@
use std::collections::{HashMap, HashSet};
use zellij_utils::data::FloatingPaneCoordinates;
use zellij_utils::input::layout::{RunPluginOrAlias, SplitSize};
use zellij_utils::pane_size::Size;
use crate::{panes::PaneId, pty::PtyInstruction, thread_bus::ThreadSenders, ClientId};
pub struct PaneGroups {
panes_in_group: HashMap<ClientId, Vec<PaneId>>,
senders: ThreadSenders,
}
impl std::fmt::Debug for PaneGroups {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PaneGroups")
.field("panes_in_group", &self.panes_in_group)
.finish_non_exhaustive()
}
}
impl PaneGroups {
pub fn new(senders: ThreadSenders) -> Self {
PaneGroups {
panes_in_group: HashMap::new(),
senders,
}
}
pub fn clone_inner(&self) -> HashMap<ClientId, Vec<PaneId>> {
self.panes_in_group.clone()
}
pub fn get_client_pane_group(&self, client_id: &ClientId) -> HashSet<PaneId> {
self.panes_in_group
.get(client_id)
.map(|p| p.iter().copied().collect())
.unwrap_or_else(|| HashSet::new())
}
pub fn clear_pane_group(&mut self, client_id: &ClientId) {
self.panes_in_group.get_mut(client_id).map(|p| p.clear());
}
pub fn toggle_pane_id_in_group(
&mut self,
pane_id: PaneId,
screen_size: Size,
client_id: &ClientId,
) {
let previous_groups = self.clone_inner();
let client_pane_group = self
.panes_in_group
.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);
};
if self.should_launch_plugin(&previous_groups, client_id) {
self.launch_plugin(screen_size, client_id);
}
}
pub fn add_pane_id_to_group(
&mut self,
pane_id: PaneId,
screen_size: Size,
client_id: &ClientId,
) {
let previous_groups = self.clone_inner();
let client_pane_group = self
.panes_in_group
.entry(*client_id)
.or_insert_with(|| vec![]);
if !client_pane_group.contains(&pane_id) {
client_pane_group.push(pane_id);
}
if self.should_launch_plugin(&previous_groups, client_id) {
self.launch_plugin(screen_size, client_id);
}
}
pub fn group_and_ungroup_panes(
&mut self,
mut pane_ids_to_group: Vec<PaneId>,
pane_ids_to_ungroup: Vec<PaneId>,
screen_size: Size,
client_id: &ClientId,
) {
let previous_groups = self.clone_inner();
let client_pane_group = self
.panes_in_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));
if self.should_launch_plugin(&previous_groups, client_id) {
self.launch_plugin(screen_size, client_id);
}
}
pub fn override_groups_with(&mut self, new_pane_groups: HashMap<ClientId, Vec<PaneId>>) {
self.panes_in_group = new_pane_groups;
}
fn should_launch_plugin(
&self,
previous_groups: &HashMap<ClientId, Vec<PaneId>>,
client_id: &ClientId,
) -> bool {
let mut should_launch = false;
for (client_id, previous_panes) in previous_groups {
let previous_panes_has_panes = !previous_panes.is_empty();
let current_panes_has_panes = self
.panes_in_group
.get(&client_id)
.map(|g| !g.is_empty())
.unwrap_or(false);
if !previous_panes_has_panes && current_panes_has_panes {
should_launch = true;
}
}
should_launch || previous_groups.get(&client_id).is_none()
}
fn launch_plugin(&self, screen_size: Size, client_id: &ClientId) {
if let Ok(run_plugin) =
RunPluginOrAlias::from_url("zellij:multiple-select", &None, None, None)
{
let tab_index = 1;
let size = Size::default();
let should_float = Some(true);
let should_be_opened_in_place = false;
let pane_title = None;
let skip_cache = false;
let cwd = None;
let should_focus_plugin = Some(false);
let width_30_percent = (screen_size.cols as f64 * 0.3) as usize;
let height_30_percent = (screen_size.rows as f64 * 0.3) as usize;
let width = std::cmp::max(width_30_percent, 48);
let height = std::cmp::max(height_30_percent, 10);
let y_position = screen_size.rows.saturating_sub(height + 2);
let floating_pane_coordinates = FloatingPaneCoordinates {
x: Some(SplitSize::Fixed(2)),
y: Some(SplitSize::Fixed(y_position)),
width: Some(SplitSize::Fixed(width)),
height: Some(SplitSize::Fixed(height)),
pinned: Some(true),
};
let _ = self.senders.send_to_pty(PtyInstruction::FillPluginCwd(
should_float,
should_be_opened_in_place,
pane_title,
run_plugin,
tab_index,
None,
*client_id,
size,
skip_cache,
cwd,
should_focus_plugin,
Some(floating_pane_coordinates),
));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
fn create_mock_senders() -> ThreadSenders {
let mut mock = ThreadSenders::default();
mock.should_silently_fail = true;
mock
}
fn create_test_pane_groups() -> PaneGroups {
PaneGroups::new(create_mock_senders())
}
fn create_test_screen_size() -> Size {
Size { rows: 24, cols: 80 }
}
#[test]
fn new_creates_empty_pane_groups() {
let pane_groups = create_test_pane_groups();
assert!(pane_groups.panes_in_group.is_empty());
}
#[test]
fn clone_inner_returns_copy_of_internal_map() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let pane_id = PaneId::Terminal(10);
let screen_size = create_test_screen_size();
pane_groups.add_pane_id_to_group(pane_id, screen_size, &client_id);
let cloned = pane_groups.clone_inner();
assert_eq!(cloned.len(), 1);
assert!(cloned.contains_key(&client_id));
assert_eq!(cloned[&client_id], vec![pane_id]);
}
#[test]
fn get_client_pane_group_returns_empty_set_for_nonexistent_client() {
let pane_groups = create_test_pane_groups();
let client_id: ClientId = 999;
let result = pane_groups.get_client_pane_group(&client_id);
assert!(result.is_empty());
}
#[test]
fn get_client_pane_group_returns_correct_panes() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let pane_ids = vec![
PaneId::Terminal(10),
PaneId::Plugin(20),
PaneId::Terminal(30),
];
let screen_size = create_test_screen_size();
for pane_id in &pane_ids {
pane_groups.add_pane_id_to_group(*pane_id, screen_size, &client_id);
}
let result = pane_groups.get_client_pane_group(&client_id);
assert_eq!(result.len(), 3);
for pane_id in pane_ids {
assert!(result.contains(&pane_id));
}
}
#[test]
fn clear_pane_group_clears_existing_group() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let pane_ids = vec![
PaneId::Terminal(10),
PaneId::Plugin(20),
PaneId::Terminal(30),
];
let screen_size = create_test_screen_size();
for pane_id in pane_ids {
pane_groups.add_pane_id_to_group(pane_id, screen_size, &client_id);
}
assert!(!pane_groups.get_client_pane_group(&client_id).is_empty());
pane_groups.clear_pane_group(&client_id);
assert!(pane_groups.get_client_pane_group(&client_id).is_empty());
}
#[test]
fn clear_pane_group_handles_nonexistent_client() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 999;
pane_groups.clear_pane_group(&client_id);
assert!(pane_groups.get_client_pane_group(&client_id).is_empty());
}
#[test]
fn toggle_pane_id_adds_new_pane() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let pane_id = PaneId::Terminal(10);
let screen_size = create_test_screen_size();
pane_groups.toggle_pane_id_in_group(pane_id, screen_size, &client_id);
let result = pane_groups.get_client_pane_group(&client_id);
assert!(result.contains(&pane_id));
}
#[test]
fn toggle_pane_id_removes_existing_pane() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let pane_id = PaneId::Plugin(10);
let screen_size = create_test_screen_size();
pane_groups.add_pane_id_to_group(pane_id, screen_size, &client_id);
assert!(pane_groups
.get_client_pane_group(&client_id)
.contains(&pane_id));
pane_groups.toggle_pane_id_in_group(pane_id, screen_size, &client_id);
assert!(!pane_groups
.get_client_pane_group(&client_id)
.contains(&pane_id));
}
#[test]
fn add_pane_id_to_group_adds_new_pane() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let pane_id = PaneId::Terminal(10);
let screen_size = create_test_screen_size();
pane_groups.add_pane_id_to_group(pane_id, screen_size, &client_id);
let result = pane_groups.get_client_pane_group(&client_id);
assert!(result.contains(&pane_id));
}
#[test]
fn add_pane_id_to_group_does_not_duplicate() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let pane_id = PaneId::Plugin(10);
let screen_size = create_test_screen_size();
pane_groups.add_pane_id_to_group(pane_id, screen_size, &client_id);
pane_groups.add_pane_id_to_group(pane_id, screen_size, &client_id);
let result = pane_groups.get_client_pane_group(&client_id);
assert_eq!(result.len(), 1);
assert!(result.contains(&pane_id));
}
#[test]
fn group_and_ungroup_panes_adds_and_removes_correctly() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let screen_size = create_test_screen_size();
let initial_panes = vec![PaneId::Terminal(1), PaneId::Plugin(2), PaneId::Terminal(3)];
for pane_id in &initial_panes {
pane_groups.add_pane_id_to_group(*pane_id, screen_size, &client_id);
}
let panes_to_add = vec![PaneId::Plugin(4), PaneId::Terminal(5)];
let panes_to_remove = vec![PaneId::Plugin(2), PaneId::Terminal(3)];
pane_groups.group_and_ungroup_panes(panes_to_add, panes_to_remove, screen_size, &client_id);
let result = pane_groups.get_client_pane_group(&client_id);
assert!(result.contains(&PaneId::Terminal(1)));
assert!(result.contains(&PaneId::Plugin(4)));
assert!(result.contains(&PaneId::Terminal(5)));
assert!(!result.contains(&PaneId::Plugin(2)));
assert!(!result.contains(&PaneId::Terminal(3)));
assert_eq!(result.len(), 3);
}
#[test]
fn override_groups_with_replaces_all_groups() {
let mut pane_groups = create_test_pane_groups();
let client_id1: ClientId = 1;
let client_id2: ClientId = 2;
let screen_size = create_test_screen_size();
pane_groups.add_pane_id_to_group(PaneId::Terminal(10), screen_size, &client_id1);
let mut new_groups = HashMap::new();
new_groups.insert(client_id2, vec![PaneId::Plugin(20), PaneId::Terminal(30)]);
pane_groups.override_groups_with(new_groups);
assert!(pane_groups.get_client_pane_group(&client_id1).is_empty());
let result = pane_groups.get_client_pane_group(&client_id2);
assert!(result.contains(&PaneId::Plugin(20)));
assert!(result.contains(&PaneId::Terminal(30)));
assert_eq!(result.len(), 2);
}
#[test]
fn multiple_clients_independent_groups() {
let mut pane_groups = create_test_pane_groups();
let client_id1: ClientId = 1;
let client_id2: ClientId = 2;
let screen_size = create_test_screen_size();
pane_groups.add_pane_id_to_group(PaneId::Terminal(10), screen_size, &client_id1);
pane_groups.add_pane_id_to_group(PaneId::Plugin(20), screen_size, &client_id2);
let group1 = pane_groups.get_client_pane_group(&client_id1);
let group2 = pane_groups.get_client_pane_group(&client_id2);
assert!(group1.contains(&PaneId::Terminal(10)));
assert!(!group1.contains(&PaneId::Plugin(20)));
assert!(group2.contains(&PaneId::Plugin(20)));
assert!(!group2.contains(&PaneId::Terminal(10)));
}
#[test]
fn pane_id_variants_work_correctly() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let screen_size = create_test_screen_size();
let terminal_pane = PaneId::Terminal(100);
let plugin_pane = PaneId::Plugin(200);
pane_groups.add_pane_id_to_group(terminal_pane, screen_size, &client_id);
pane_groups.add_pane_id_to_group(plugin_pane, screen_size, &client_id);
let result = pane_groups.get_client_pane_group(&client_id);
assert!(result.contains(&terminal_pane));
assert!(result.contains(&plugin_pane));
assert_eq!(result.len(), 2);
let another_terminal = PaneId::Terminal(200);
assert!(!result.contains(&another_terminal));
}
#[test]
fn should_launch_plugin_returns_true_when_first_pane_added() {
let pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let previous_groups = HashMap::new();
assert!(pane_groups.should_launch_plugin(&previous_groups, &client_id));
}
#[test]
fn should_launch_plugin_returns_true_when_empty_to_non_empty() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let screen_size = create_test_screen_size();
let mut previous_groups = HashMap::new();
previous_groups.insert(client_id, vec![]);
pane_groups.add_pane_id_to_group(PaneId::Terminal(10), screen_size, &client_id);
assert!(pane_groups.should_launch_plugin(&previous_groups, &client_id));
}
#[test]
fn should_launch_plugin_returns_false_when_non_empty_to_non_empty() {
let mut pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let screen_size = create_test_screen_size();
pane_groups.add_pane_id_to_group(PaneId::Terminal(10), screen_size, &client_id);
let previous_groups = pane_groups.clone_inner();
pane_groups.add_pane_id_to_group(PaneId::Plugin(20), screen_size, &client_id);
assert!(!pane_groups.should_launch_plugin(&previous_groups, &client_id));
}
#[test]
fn should_launch_plugin_returns_false_when_non_empty_to_empty() {
let pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let mut previous_groups = HashMap::new();
previous_groups.insert(client_id, vec![PaneId::Terminal(10)]);
assert!(!pane_groups.should_launch_plugin(&previous_groups, &client_id));
}
#[test]
fn should_launch_plugin_returns_false_when_empty_to_empty() {
let pane_groups = create_test_pane_groups();
let client_id: ClientId = 1;
let mut previous_groups = HashMap::new();
previous_groups.insert(client_id, vec![]);
assert!(!pane_groups.should_launch_plugin(&previous_groups, &client_id));
}
}

View file

@ -316,6 +316,20 @@ impl FloatingPanes {
pub fn last_floating_pane_id(&self) -> Option<PaneId> { pub fn last_floating_pane_id(&self) -> Option<PaneId> {
self.panes.keys().last().copied() self.panes.keys().last().copied()
} }
pub fn last_selectable_floating_pane_id(&self) -> Option<PaneId> {
self.panes
.iter()
.filter(|(_p_id, p)| p.selectable())
.last()
.map(|(p_id, _p)| *p_id)
}
pub fn has_selectable_panes(&self) -> bool {
self.panes
.iter()
.filter(|(_p_id, p)| p.selectable())
.last()
.is_some()
}
pub fn first_active_floating_pane_id(&self) -> Option<PaneId> { pub fn first_active_floating_pane_id(&self) -> Option<PaneId> {
self.active_panes.values().next().copied() self.active_panes.values().next().copied()
} }
@ -388,6 +402,7 @@ impl FloatingPanes {
let multiple_users_exist_in_session = let multiple_users_exist_in_session =
{ self.connected_clients_in_app.borrow().len() > 1 }; { self.connected_clients_in_app.borrow().len() > 1 };
active_panes.retain(|c_id, _| self.connected_clients.borrow().contains(c_id)); active_panes.retain(|c_id, _| self.connected_clients.borrow().contains(c_id));
let pane_is_selectable = pane.selectable();
let mut pane_contents_and_ui = PaneContentsAndUi::new( let mut pane_contents_and_ui = PaneContentsAndUi::new(
pane, pane,
output, output,
@ -415,6 +430,7 @@ impl FloatingPanes {
client_mode, client_mode,
self.session_is_mirrored, self.session_is_mirrored,
is_floating, is_floating,
pane_is_selectable,
) )
.with_context(err_context)?; .with_context(err_context)?;
if let PaneId::Plugin(..) = kind { if let PaneId::Plugin(..) = kind {

View file

@ -967,6 +967,7 @@ impl TiledPanes {
let pane_is_stacked = pane.current_geom().is_stacked(); let pane_is_stacked = pane.current_geom().is_stacked();
let pane_is_one_liner_in_stack = let pane_is_one_liner_in_stack =
pane_is_stacked && pane.current_geom().rows.is_fixed(); pane_is_stacked && pane.current_geom().rows.is_fixed();
let pane_is_selectable = pane.selectable();
let mut pane_contents_and_ui = PaneContentsAndUi::new( let mut pane_contents_and_ui = PaneContentsAndUi::new(
pane, pane,
output, output,
@ -1004,6 +1005,7 @@ impl TiledPanes {
client_mode, client_mode,
self.session_is_mirrored, self.session_is_mirrored,
is_floating, is_floating,
pane_is_selectable,
) )
.with_context(err_context)?; .with_context(err_context)?;
} else if pane_is_stacked { } else if pane_is_stacked {
@ -1015,6 +1017,7 @@ impl TiledPanes {
client_mode, client_mode,
self.session_is_mirrored, self.session_is_mirrored,
is_floating, is_floating,
pane_is_selectable,
) )
.with_context(err_context)?; .with_context(err_context)?;
// we also need to render its boundaries as normal // we also need to render its boundaries as normal

View file

@ -25,8 +25,8 @@ use wasm_bridge::WasmBridge;
use async_std::{channel, future::timeout, task}; use async_std::{channel, future::timeout, task};
use zellij_utils::{ use zellij_utils::{
data::{ data::{
ClientInfo, Event, EventType, InputMode, MessageToPlugin, PermissionStatus, PermissionType, ClientInfo, Event, EventType, FloatingPaneCoordinates, InputMode, MessageToPlugin,
PipeMessage, PipeSource, PluginCapabilities, PermissionStatus, PermissionType, PipeMessage, PipeSource, PluginCapabilities,
}, },
errors::{prelude::*, ContextType, PluginContext}, errors::{prelude::*, ContextType, PluginContext},
input::{ input::{
@ -55,6 +55,8 @@ pub enum PluginInstruction {
Size, Size,
Option<PathBuf>, // cwd Option<PathBuf>, // cwd
bool, // skip cache bool, // skip cache
Option<bool>, // should focus plugin
Option<FloatingPaneCoordinates>,
), ),
LoadBackgroundPlugin(RunPluginOrAlias, ClientId), LoadBackgroundPlugin(RunPluginOrAlias, ClientId),
Update(Vec<(Option<PluginId>, Option<ClientId>, Event)>), // Focused plugin / broadcast, client_id, event data Update(Vec<(Option<PluginId>, Option<ClientId>, Event)>), // Focused plugin / broadcast, client_id, event data
@ -281,6 +283,8 @@ pub(crate) fn plugin_thread_main(
size, size,
cwd, cwd,
skip_cache, skip_cache,
should_focus_plugin,
floating_pane_coordinates,
) => { ) => {
run_plugin_or_alias.populate_run_plugin_if_needed(&plugin_aliases); run_plugin_or_alias.populate_run_plugin_if_needed(&plugin_aliases);
let cwd = run_plugin_or_alias.get_initial_cwd().or(cwd); let cwd = run_plugin_or_alias.get_initial_cwd().or(cwd);
@ -306,6 +310,8 @@ pub(crate) fn plugin_thread_main(
pane_id_to_replace, pane_id_to_replace,
cwd, cwd,
start_suppressed, start_suppressed,
floating_pane_coordinates,
should_focus_plugin,
Some(client_id), Some(client_id),
))); )));
}, },
@ -378,6 +384,8 @@ pub(crate) fn plugin_thread_main(
None, None,
start_suppressed, start_suppressed,
None, None,
None,
None,
), ),
)); ));
}, },
@ -1070,7 +1078,8 @@ fn load_background_plugin(
pane_id_to_replace, pane_id_to_replace,
cwd, cwd,
start_suppressed, start_suppressed,
// None, None,
None,
Some(client_id), Some(client_id),
))); )));
}, },

View file

@ -868,6 +868,7 @@ impl<'a> PluginLoader<'a> {
default_mode: self.default_mode.clone(), default_mode: self.default_mode.clone(),
subscriptions: Arc::new(Mutex::new(HashSet::new())), subscriptions: Arc::new(Mutex::new(HashSet::new())),
keybinds: self.keybinds.clone(), keybinds: self.keybinds.clone(),
intercepting_key_presses: false,
stdin_pipe, stdin_pipe,
stdout_pipe, stdout_pipe,
}; };

View file

@ -313,6 +313,7 @@ pub struct PluginEnv {
pub stdin_pipe: Arc<Mutex<VecDeque<u8>>>, pub stdin_pipe: Arc<Mutex<VecDeque<u8>>>,
pub stdout_pipe: Arc<Mutex<VecDeque<u8>>>, pub stdout_pipe: Arc<Mutex<VecDeque<u8>>>,
pub keybinds: Keybinds, pub keybinds: Keybinds,
pub intercepting_key_presses: bool,
} }
#[derive(Clone)] #[derive(Clone)]
@ -457,4 +458,7 @@ impl RunningPlugin {
pub fn update_default_shell(&mut self, default_shell: Option<TerminalAction>) { pub fn update_default_shell(&mut self, default_shell: Option<TerminalAction>) {
self.store.data_mut().default_shell = default_shell; self.store.data_mut().default_shell = default_shell;
} }
pub fn intercepting_key_presses(&self) -> bool {
self.store.data().intercepting_key_presses
}
} }

View file

@ -714,6 +714,8 @@ pub fn load_new_plugin_from_hd() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -794,6 +796,8 @@ pub fn load_new_plugin_with_plugin_alias() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -870,6 +874,8 @@ pub fn plugin_workers() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
// we send a SystemClipboardFailure to trigger the custom handler in the fixture plugin that // we send a SystemClipboardFailure to trigger the custom handler in the fixture plugin that
@ -950,6 +956,8 @@ pub fn plugin_workers_persist_state() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
// we send a SystemClipboardFailure to trigger the custom handler in the fixture plugin that // we send a SystemClipboardFailure to trigger the custom handler in the fixture plugin that
@ -1040,6 +1048,8 @@ pub fn can_subscribe_to_hd_events() {
size, size,
None, None,
false, false,
None,
None,
)); ));
// extra long time because we only start the fs watcher on plugin load // extra long time because we only start the fs watcher on plugin load
std::thread::sleep(std::time::Duration::from_millis(5000)); std::thread::sleep(std::time::Duration::from_millis(5000));
@ -1118,6 +1128,8 @@ pub fn switch_to_mode_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1190,6 +1202,8 @@ pub fn switch_to_mode_plugin_command_permission_denied() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1262,6 +1276,8 @@ pub fn new_tabs_with_layout_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1348,6 +1364,8 @@ pub fn new_tab_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1420,6 +1438,8 @@ pub fn go_to_next_tab_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1491,6 +1511,8 @@ pub fn go_to_previous_tab_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1562,6 +1584,8 @@ pub fn resize_focused_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1633,6 +1657,8 @@ pub fn resize_focused_pane_with_direction_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1704,6 +1730,8 @@ pub fn focus_next_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1775,6 +1803,8 @@ pub fn focus_previous_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1846,6 +1876,8 @@ pub fn move_focus_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1917,6 +1949,8 @@ pub fn move_focus_or_tab_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -1988,6 +2022,8 @@ pub fn edit_scrollback_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2059,6 +2095,8 @@ pub fn write_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2130,6 +2168,8 @@ pub fn write_chars_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2201,6 +2241,8 @@ pub fn toggle_tab_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2272,6 +2314,8 @@ pub fn move_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2343,6 +2387,8 @@ pub fn move_pane_with_direction_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2415,6 +2461,8 @@ pub fn clear_screen_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2487,6 +2535,8 @@ pub fn scroll_up_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2558,6 +2608,8 @@ pub fn scroll_down_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2629,6 +2681,8 @@ pub fn scroll_to_top_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2700,6 +2754,8 @@ pub fn scroll_to_bottom_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2771,6 +2827,8 @@ pub fn page_scroll_up_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2842,6 +2900,8 @@ pub fn page_scroll_down_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2913,6 +2973,8 @@ pub fn toggle_focus_fullscreen_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -2984,6 +3046,8 @@ pub fn toggle_pane_frames_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3055,6 +3119,8 @@ pub fn toggle_pane_embed_or_eject_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3126,6 +3192,8 @@ pub fn undo_rename_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3197,6 +3265,8 @@ pub fn close_focus_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3268,6 +3338,8 @@ pub fn toggle_active_tab_sync_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3339,6 +3411,8 @@ pub fn close_focused_tab_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3410,6 +3484,8 @@ pub fn undo_rename_tab_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3481,6 +3557,8 @@ pub fn previous_swap_layout_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3552,6 +3630,8 @@ pub fn next_swap_layout_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3623,6 +3703,8 @@ pub fn go_to_tab_name_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3694,6 +3776,8 @@ pub fn focus_or_create_tab_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3765,6 +3849,8 @@ pub fn go_to_tab() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3836,6 +3922,8 @@ pub fn start_or_reload_plugin() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3914,6 +4002,8 @@ pub fn quit_zellij_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -3992,6 +4082,8 @@ pub fn detach_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4070,6 +4162,8 @@ pub fn open_file_floating_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4152,6 +4246,8 @@ pub fn open_file_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4235,6 +4331,8 @@ pub fn open_file_with_line_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4317,6 +4415,8 @@ pub fn open_file_with_line_floating_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4399,6 +4499,8 @@ pub fn open_terminal_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4477,6 +4579,8 @@ pub fn open_terminal_floating_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4555,6 +4659,8 @@ pub fn open_command_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4633,6 +4739,8 @@ pub fn open_command_pane_floating_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4704,6 +4812,8 @@ pub fn switch_to_tab_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4775,6 +4885,8 @@ pub fn hide_self_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4845,6 +4957,8 @@ pub fn show_self_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4916,6 +5030,8 @@ pub fn close_terminal_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -4987,6 +5103,8 @@ pub fn close_plugin_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5058,6 +5176,8 @@ pub fn focus_terminal_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5129,6 +5249,8 @@ pub fn focus_plugin_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5200,6 +5322,8 @@ pub fn rename_terminal_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5271,6 +5395,8 @@ pub fn rename_plugin_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5342,6 +5468,8 @@ pub fn rename_tab_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5422,6 +5550,8 @@ pub fn send_configuration_to_plugins() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5490,6 +5620,8 @@ pub fn request_plugin_permissions() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5582,6 +5714,8 @@ pub fn granted_permission_request_result() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5673,6 +5807,8 @@ pub fn denied_permission_request_result() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5744,6 +5880,8 @@ pub fn run_command_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5822,6 +5960,8 @@ pub fn run_command_with_env_vars_and_cwd_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5900,6 +6040,8 @@ pub fn web_request_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -5971,6 +6113,8 @@ pub fn unblock_input_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
@ -6053,6 +6197,8 @@ pub fn block_input_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
// extra long time because we only start the fs watcher on plugin load // extra long time because we only start the fs watcher on plugin load
std::thread::sleep(std::time::Duration::from_millis(5000)); std::thread::sleep(std::time::Duration::from_millis(5000));
@ -6143,6 +6289,8 @@ pub fn pipe_output_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
@ -6226,6 +6374,8 @@ pub fn pipe_message_to_plugin_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::CliPipe { let _ = plugin_thread_sender.send(PluginInstruction::CliPipe {
@ -6320,6 +6470,8 @@ pub fn switch_session_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
@ -6401,6 +6553,8 @@ pub fn switch_session_with_layout_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
@ -6482,6 +6636,8 @@ pub fn switch_session_with_layout_and_cwd_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
@ -6563,6 +6719,8 @@ pub fn disconnect_other_clients_plugins_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
@ -6644,6 +6802,8 @@ pub fn reconfigure_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
@ -6729,6 +6889,8 @@ pub fn run_plugin_in_specific_cwd() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
@ -6803,6 +6965,8 @@ pub fn hide_pane_with_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -6874,6 +7038,8 @@ pub fn show_pane_with_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -6952,6 +7118,8 @@ pub fn open_command_pane_background_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7027,6 +7195,8 @@ pub fn rerun_command_pane_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7098,6 +7268,8 @@ pub fn resize_pane_with_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7169,6 +7341,8 @@ pub fn edit_scrollback_for_pane_with_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7240,6 +7414,8 @@ pub fn write_to_pane_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7311,6 +7487,8 @@ pub fn write_chars_to_pane_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7382,6 +7560,8 @@ pub fn move_pane_with_pane_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7453,6 +7633,8 @@ pub fn move_pane_with_pane_id_in_direction_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7524,6 +7706,8 @@ pub fn clear_screen_for_pane_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7595,6 +7779,8 @@ pub fn scroll_up_in_pane_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7666,6 +7852,8 @@ pub fn scroll_down_in_pane_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7737,6 +7925,8 @@ pub fn scroll_to_top_in_pane_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7808,6 +7998,8 @@ pub fn scroll_to_bottom_in_pane_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7879,6 +8071,8 @@ pub fn page_scroll_up_in_pane_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -7950,6 +8144,8 @@ pub fn page_scroll_down_in_pane_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -8021,6 +8217,8 @@ pub fn toggle_pane_id_fullscreen_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -8092,6 +8290,8 @@ pub fn toggle_pane_embed_or_eject_for_pane_id_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -8163,6 +8363,8 @@ pub fn close_tab_with_index_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -8234,6 +8436,8 @@ pub fn break_panes_to_new_tab_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -8305,6 +8509,8 @@ pub fn break_panes_to_tab_with_index_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -8376,6 +8582,8 @@ pub fn reload_plugin_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -8447,6 +8655,8 @@ pub fn load_new_plugin_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -8525,6 +8735,8 @@ pub fn rebind_keys_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
@ -8599,6 +8811,8 @@ pub fn list_clients_plugin_command() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
@ -8670,6 +8884,8 @@ pub fn before_close_plugin_event() {
size, size,
None, None,
false, false,
None,
None,
)); ));
std::thread::sleep(std::time::Duration::from_millis(5000)); 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) // here we send an unload to plugin id 0 (the first plugin id, presumably this plugin)

View file

@ -324,6 +324,15 @@ impl WasmBridge {
for (_worker_name, worker_sender) in workers { for (_worker_name, worker_sender) in workers {
drop(worker_sender.send(MessageToWorker::Exit)); drop(worker_sender.send(MessageToWorker::Exit));
} }
{
// if the plugin was intercepting key presses and for some reason did not clear
// this state, we make sure to do it ourselves so that the user will not get stuck
if running_plugin.lock().unwrap().intercepting_key_presses() {
let _ = self
.senders
.send_to_screen(ScreenInstruction::ClearKeyPressesIntercepts(client_id));
}
}
let subscriptions = subscriptions.lock().unwrap(); let subscriptions = subscriptions.lock().unwrap();
if subscriptions.contains(&EventType::BeforeClose) { if subscriptions.contains(&EventType::BeforeClose) {
let mut running_plugin = running_plugin.lock().unwrap(); let mut running_plugin = running_plugin.lock().unwrap();
@ -1436,6 +1445,8 @@ impl WasmBridge {
pane_id_to_replace, pane_id_to_replace,
cwd, cwd,
start_suppressed, start_suppressed,
None,
None,
Some(client_id), Some(client_id),
))); )));
vec![(plugin_id, Some(client_id))] vec![(plugin_id, Some(client_id))]

View file

@ -73,9 +73,10 @@ pub fn zellij_exports(linker: &mut Linker<PluginEnv>) {
.unwrap(); .unwrap();
} }
fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) { fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) {
let env = caller.data(); let mut env = caller.data_mut();
let err_context = || format!("failed to run plugin command {}", env.name()); let plugin_command = env.name();
let err_context = || format!("failed to run plugin command {}", plugin_command);
wasi_read_bytes(env) wasi_read_bytes(env)
.and_then(|bytes| { .and_then(|bytes| {
let command: ProtobufPluginCommand = ProtobufPluginCommand::decode(bytes.as_slice())?; let command: ProtobufPluginCommand = ProtobufPluginCommand::decode(bytes.as_slice())?;
@ -455,6 +456,10 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) {
PluginCommand::EmbedMultiplePanes(pane_ids) => { PluginCommand::EmbedMultiplePanes(pane_ids) => {
embed_multiple_panes(env, pane_ids.into_iter().map(|p| p.into()).collect()) embed_multiple_panes(env, pane_ids.into_iter().map(|p| p.into()).collect())
}, },
PluginCommand::InterceptKeyPresses => intercept_key_presses(&mut env),
PluginCommand::ClearKeyPressesIntercepts => {
clear_key_presses_intercepts(&mut env)
},
}, },
(PermissionStatus::Denied, permission) => { (PermissionStatus::Denied, permission) => {
log::error!( log::error!(
@ -2163,6 +2168,8 @@ fn load_new_plugin(
size, size,
cwd, cwd,
skip_cache, skip_cache,
None,
None,
)); ));
}, },
Err(e) => { Err(e) => {
@ -2231,6 +2238,23 @@ fn embed_multiple_panes(env: &PluginEnv, pane_ids: Vec<PaneId>) {
)); ));
} }
fn intercept_key_presses(env: &mut PluginEnv) {
env.intercepting_key_presses = true;
let _ = env
.senders
.send_to_screen(ScreenInstruction::InterceptKeyPresses(
env.plugin_id,
env.client_id,
));
}
fn clear_key_presses_intercepts(env: &mut PluginEnv) {
env.intercepting_key_presses = false;
let _ = env
.senders
.send_to_screen(ScreenInstruction::ClearKeyPressesIntercepts(env.client_id));
}
// Custom panic handler for plugins. // Custom panic handler for plugins.
// //
// This is called when a panic occurs in a plugin. Since most panics will likely originate in the // This is called when a panic occurs in a plugin. Since most panics will likely originate in the
@ -2409,6 +2433,9 @@ fn check_command_permission(
PermissionType::Reconfigure PermissionType::Reconfigure
}, },
PluginCommand::ChangeHostFolder(..) => PermissionType::FullHdAccess, PluginCommand::ChangeHostFolder(..) => PermissionType::FullHdAccess,
PluginCommand::InterceptKeyPresses | PluginCommand::ClearKeyPressesIntercepts => {
PermissionType::InterceptInput
},
_ => return (PermissionStatus::Granted, None), _ => return (PermissionStatus::Granted, None),
}; };

View file

@ -97,6 +97,7 @@ pub enum PtyInstruction {
Size, Size,
bool, // skip cache bool, // skip cache
Option<PathBuf>, // if Some, will not fill cwd but just forward the message Option<PathBuf>, // if Some, will not fill cwd but just forward the message
Option<bool>, // should focus plugin
Option<FloatingPaneCoordinates>, Option<FloatingPaneCoordinates>,
), ),
ListClientsMetadata(SessionLayoutMetadata, ClientId), ListClientsMetadata(SessionLayoutMetadata, ClientId),
@ -771,6 +772,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
size, size,
skip_cache, skip_cache,
cwd, cwd,
should_focus_plugin,
floating_pane_coordinates, floating_pane_coordinates,
) => { ) => {
pty.fill_plugin_cwd( pty.fill_plugin_cwd(
@ -784,6 +786,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
size, size,
skip_cache, skip_cache,
cwd, cwd,
should_focus_plugin,
floating_pane_coordinates, floating_pane_coordinates,
)?; )?;
}, },
@ -1527,9 +1530,8 @@ impl Pty {
size: Size, size: Size,
skip_cache: bool, skip_cache: bool,
cwd: Option<PathBuf>, cwd: Option<PathBuf>,
// left here for historical and potential future reasons since we might change the ordering should_focus_plugin: Option<bool>,
// of the pipeline between threads and end up needing to forward this floating_pane_coordinates: Option<FloatingPaneCoordinates>,
_floating_pane_coordinates: Option<FloatingPaneCoordinates>,
) -> Result<()> { ) -> Result<()> {
let get_focused_cwd = || { let get_focused_cwd = || {
self.active_panes self.active_panes
@ -1563,6 +1565,8 @@ impl Pty {
size, size,
cwd, cwd,
skip_cache, skip_cache,
should_focus_plugin,
floating_pane_coordinates,
))?; ))?;
Ok(()) Ok(())
} }

View file

@ -9,8 +9,8 @@ use std::time::Duration;
use log::{debug, warn}; use log::{debug, warn};
use zellij_utils::data::{ use zellij_utils::data::{
Direction, KeyWithModifier, PaneManifest, PluginPermission, Resize, ResizeStrategy, Direction, FloatingPaneCoordinates, KeyWithModifier, PaneManifest, PluginPermission, Resize,
SessionInfo, Styling, ResizeStrategy, SessionInfo, Styling,
}; };
use zellij_utils::errors::prelude::*; use zellij_utils::errors::prelude::*;
use zellij_utils::input::command::RunCommand; use zellij_utils::input::command::RunCommand;
@ -32,6 +32,7 @@ use zellij_utils::{
use crate::background_jobs::BackgroundJob; use crate::background_jobs::BackgroundJob;
use crate::os_input_output::ResizeCache; use crate::os_input_output::ResizeCache;
use crate::pane_groups::PaneGroups;
use crate::panes::alacritty_functions::xparse_color; use crate::panes::alacritty_functions::xparse_color;
use crate::panes::terminal_character::AnsiCode; use crate::panes::terminal_character::AnsiCode;
use crate::session_layout_metadata::{PaneLayoutMetadata, SessionLayoutMetadata}; use crate::session_layout_metadata::{PaneLayoutMetadata, SessionLayoutMetadata};
@ -51,10 +52,7 @@ use crate::{
ClientId, ServerInstruction, ClientId, ServerInstruction,
}; };
use zellij_utils::{ use zellij_utils::{
data::{ data::{Event, InputMode, ModeInfo, Palette, PaletteColor, PluginCapabilities, Style, TabInfo},
Event, FloatingPaneCoordinates, InputMode, ModeInfo, Palette, PaletteColor,
PluginCapabilities, Style, TabInfo,
},
errors::{ContextType, ScreenContext}, errors::{ContextType, ScreenContext},
input::get_mode_info, input::get_mode_info,
ipc::{ClientAttributes, PixelDimensions, ServerToClientMsg}, ipc::{ClientAttributes, PixelDimensions, ServerToClientMsg},
@ -305,6 +303,8 @@ pub enum ScreenInstruction {
Option<PaneId>, Option<PaneId>,
Option<PathBuf>, // cwd Option<PathBuf>, // cwd
bool, // start suppressed bool, // start suppressed
Option<FloatingPaneCoordinates>,
Option<bool>, // should focus plugin
Option<ClientId>, Option<ClientId>,
), ),
UpdatePluginLoadingStage(u32, LoadingIndication), // u32 - plugin_id UpdatePluginLoadingStage(u32, LoadingIndication), // u32 - plugin_id
@ -415,6 +415,8 @@ pub enum ScreenInstruction {
EmbedMultiplePanes(Vec<PaneId>, ClientId), EmbedMultiplePanes(Vec<PaneId>, ClientId),
TogglePaneInGroup(ClientId), TogglePaneInGroup(ClientId),
ToggleGroupMarking(ClientId), ToggleGroupMarking(ClientId),
InterceptKeyPresses(PluginId, ClientId),
ClearKeyPressesIntercepts(ClientId),
} }
impl From<&ScreenInstruction> for ScreenContext { impl From<&ScreenInstruction> for ScreenContext {
@ -636,6 +638,10 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::EmbedMultiplePanes(..) => ScreenContext::EmbedMultiplePanes, ScreenInstruction::EmbedMultiplePanes(..) => ScreenContext::EmbedMultiplePanes,
ScreenInstruction::TogglePaneInGroup(..) => ScreenContext::TogglePaneInGroup, ScreenInstruction::TogglePaneInGroup(..) => ScreenContext::TogglePaneInGroup,
ScreenInstruction::ToggleGroupMarking(..) => ScreenContext::ToggleGroupMarking, ScreenInstruction::ToggleGroupMarking(..) => ScreenContext::ToggleGroupMarking,
ScreenInstruction::InterceptKeyPresses(..) => ScreenContext::InterceptKeyPresses,
ScreenInstruction::ClearKeyPressesIntercepts(..) => {
ScreenContext::ClearKeyPressesIntercepts
},
} }
} }
} }
@ -717,7 +723,7 @@ pub(crate) struct Screen {
default_layout_name: Option<String>, default_layout_name: Option<String>,
explicitly_disable_kitty_keyboard_protocol: bool, explicitly_disable_kitty_keyboard_protocol: bool,
default_editor: Option<PathBuf>, default_editor: Option<PathBuf>,
current_pane_group: Rc<RefCell<HashMap<ClientId, Vec<PaneId>>>>, current_pane_group: Rc<RefCell<PaneGroups>>,
advanced_mouse_actions: bool, advanced_mouse_actions: bool,
currently_marking_pane_group: Rc<RefCell<HashMap<ClientId, bool>>>, currently_marking_pane_group: Rc<RefCell<HashMap<ClientId, bool>>>,
} }
@ -753,6 +759,7 @@ impl Screen {
let mut session_infos_on_machine = BTreeMap::new(); let mut session_infos_on_machine = BTreeMap::new();
let resurrectable_sessions = BTreeMap::new(); let resurrectable_sessions = BTreeMap::new();
session_infos_on_machine.insert(session_name.clone(), session_info); session_infos_on_machine.insert(session_name.clone(), session_info);
let current_pane_group = PaneGroups::new(bus.senders.clone());
Screen { Screen {
bus, bus,
max_panes, max_panes,
@ -790,7 +797,7 @@ impl Screen {
layout_dir, layout_dir,
explicitly_disable_kitty_keyboard_protocol, explicitly_disable_kitty_keyboard_protocol,
default_editor, default_editor,
current_pane_group: Rc::new(RefCell::new(HashMap::new())), current_pane_group: Rc::new(RefCell::new(current_pane_group)),
currently_marking_pane_group: Rc::new(RefCell::new(HashMap::new())), currently_marking_pane_group: Rc::new(RefCell::new(HashMap::new())),
advanced_mouse_actions, advanced_mouse_actions,
} }
@ -2897,15 +2904,12 @@ impl Screen {
fn get_client_pane_group(&self, client_id: &ClientId) -> HashSet<PaneId> { fn get_client_pane_group(&self, client_id: &ClientId) -> HashSet<PaneId> {
self.current_pane_group self.current_pane_group
.borrow() .borrow()
.get(client_id) .get_client_pane_group(client_id)
.map(|p| p.iter().copied().collect())
.unwrap_or_else(|| HashSet::new())
} }
fn clear_pane_group(&mut self, client_id: &ClientId) { fn clear_pane_group(&mut self, client_id: &ClientId) {
self.current_pane_group self.current_pane_group
.borrow_mut() .borrow_mut()
.get_mut(client_id) .clear_pane_group(client_id);
.map(|p| p.clear());
self.currently_marking_pane_group self.currently_marking_pane_group
.borrow_mut() .borrow_mut()
.remove(client_id); .remove(client_id);
@ -2913,22 +2917,14 @@ impl Screen {
fn toggle_pane_id_in_group(&mut self, pane_id: PaneId, client_id: &ClientId) { 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 mut pane_groups = self.current_pane_group.borrow_mut();
let client_pane_group = pane_groups.entry(*client_id).or_insert_with(|| vec![]); pane_groups.toggle_pane_id_in_group(pane_id, self.size, client_id);
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(); self.retain_only_existing_panes_in_pane_groups();
} }
fn add_pane_id_to_group(&mut self, pane_id: PaneId, client_id: &ClientId) { 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 mut pane_groups = self.current_pane_group.borrow_mut();
let client_pane_group = pane_groups.entry(*client_id).or_insert_with(|| vec![]); pane_groups.add_pane_id_to_group(pane_id, self.size, client_id);
if !client_pane_group.contains(&pane_id) {
client_pane_group.push(pane_id);
}
} }
self.retain_only_existing_panes_in_pane_groups(); self.retain_only_existing_panes_in_pane_groups();
} }
@ -2956,17 +2952,18 @@ impl Screen {
fn group_and_ungroup_panes( fn group_and_ungroup_panes(
&mut self, &mut self,
mut pane_ids_to_group: Vec<PaneId>, pane_ids_to_group: Vec<PaneId>,
pane_ids_to_ungroup: Vec<PaneId>, pane_ids_to_ungroup: Vec<PaneId>,
client_id: ClientId, client_id: ClientId,
) { ) {
{ {
let mut current_pane_group = self.current_pane_group.borrow_mut(); let mut current_pane_group = self.current_pane_group.borrow_mut();
let client_pane_group = current_pane_group current_pane_group.group_and_ungroup_panes(
.entry(client_id) pane_ids_to_group,
.or_insert_with(|| vec![]); pane_ids_to_ungroup,
client_pane_group.append(&mut pane_ids_to_group); self.size,
client_pane_group.retain(|p| !pane_ids_to_ungroup.contains(p)); &client_id,
);
} }
self.retain_only_existing_panes_in_pane_groups(); self.retain_only_existing_panes_in_pane_groups();
let _ = self.log_and_report_session_state(); let _ = self.log_and_report_session_state();
@ -2974,7 +2971,7 @@ impl Screen {
fn retain_only_existing_panes_in_pane_groups(&mut self) { fn retain_only_existing_panes_in_pane_groups(&mut self) {
let clients_with_empty_group = { let clients_with_empty_group = {
let mut clients_with_empty_group = vec![]; let mut clients_with_empty_group = vec![];
let mut current_pane_group = self.current_pane_group.borrow_mut(); let mut current_pane_group = { self.current_pane_group.borrow().clone_inner() };
for (client_id, panes_in_group) in current_pane_group.iter_mut() { for (client_id, panes_in_group) in current_pane_group.iter_mut() {
let all_tabs = self.get_tabs(); let all_tabs = self.get_tabs();
panes_in_group.retain(|p_id| { panes_in_group.retain(|p_id| {
@ -2991,6 +2988,9 @@ impl Screen {
clients_with_empty_group.push(*client_id) clients_with_empty_group.push(*client_id)
} }
} }
self.current_pane_group
.borrow_mut()
.override_groups_with(current_pane_group);
clients_with_empty_group clients_with_empty_group
}; };
for client_id in &clients_with_empty_group { for client_id in &clients_with_empty_group {
@ -3114,6 +3114,7 @@ pub(crate) fn screen_thread_main(
let mut pending_events_waiting_for_tab: Vec<ScreenInstruction> = vec![]; let mut pending_events_waiting_for_tab: Vec<ScreenInstruction> = vec![];
let mut pending_events_waiting_for_client: Vec<ScreenInstruction> = vec![]; let mut pending_events_waiting_for_client: Vec<ScreenInstruction> = vec![];
let mut plugin_loading_message_cache = HashMap::new(); let mut plugin_loading_message_cache = HashMap::new();
let mut keybind_intercepts = HashMap::new();
loop { loop {
let (event, mut err_ctx) = screen let (event, mut err_ctx) = screen
.bus .bus
@ -3178,6 +3179,7 @@ pub(crate) fn screen_thread_main(
invoked_with, invoked_with,
floating_pane_coordinates, floating_pane_coordinates,
start_suppressed, start_suppressed,
true,
Some(client_id) Some(client_id)
) )
}, ?); }, ?);
@ -3204,6 +3206,7 @@ pub(crate) fn screen_thread_main(
invoked_with, invoked_with,
floating_pane_coordinates, floating_pane_coordinates,
start_suppressed, start_suppressed,
true,
None, None,
)?; )?;
if let Some(hold_for_command) = hold_for_command { if let Some(hold_for_command) = hold_for_command {
@ -3226,6 +3229,7 @@ pub(crate) fn screen_thread_main(
invoked_with, invoked_with,
floating_pane_coordinates, floating_pane_coordinates,
start_suppressed, start_suppressed,
true,
None, None,
)?; )?;
if let Some(hold_for_command) = hold_for_command { if let Some(hold_for_command) = hold_for_command {
@ -3361,6 +3365,19 @@ pub(crate) fn screen_thread_main(
is_kitty_keyboard_protocol, is_kitty_keyboard_protocol,
client_id, client_id,
) => { ) => {
if let Some(plugin_id) = keybind_intercepts.get(&client_id) {
if let Some(key_with_modifier) = key_with_modifier {
let _ = screen
.bus
.senders
.send_to_plugin(PluginInstruction::Update(vec![(
Some(*plugin_id),
Some(client_id),
Event::InterceptedKeyPress(key_with_modifier),
)]));
continue;
}
}
let mut state_changed = false; let mut state_changed = false;
active_tab_and_connected_client_id!( active_tab_and_connected_client_id!(
screen, screen,
@ -4322,6 +4339,7 @@ pub(crate) fn screen_thread_main(
skip_cache, skip_cache,
cwd, cwd,
None, None,
None,
))?; ))?;
}, },
ScreenInstruction::NewFloatingPluginPane( ScreenInstruction::NewFloatingPluginPane(
@ -4350,6 +4368,7 @@ pub(crate) fn screen_thread_main(
size, size,
skip_cache, skip_cache,
cwd, cwd,
None,
floating_pane_coordinates, floating_pane_coordinates,
))?; ))?;
}, },
@ -4385,6 +4404,7 @@ pub(crate) fn screen_thread_main(
skip_cache, skip_cache,
None, None,
None, None,
None,
))?; ))?;
}, },
None => { None => {
@ -4419,6 +4439,8 @@ pub(crate) fn screen_thread_main(
pane_id_to_replace, pane_id_to_replace,
cwd, cwd,
start_suppressed, start_suppressed,
floating_pane_coordinates,
should_focus_plugin,
client_id, client_id,
) => { ) => {
if screen.active_tab_indices.is_empty() && tab_index.is_none() { if screen.active_tab_indices.is_empty() && tab_index.is_none() {
@ -4432,6 +4454,8 @@ pub(crate) fn screen_thread_main(
pane_id_to_replace, pane_id_to_replace,
cwd, cwd,
start_suppressed, start_suppressed,
floating_pane_coordinates,
should_focus_plugin,
client_id, client_id,
)); ));
continue; continue;
@ -4480,8 +4504,9 @@ pub(crate) fn screen_thread_main(
Some(pane_title), Some(pane_title),
should_float, should_float,
Some(run_plugin), Some(run_plugin),
None, floating_pane_coordinates,
start_suppressed, start_suppressed,
should_focus_plugin.unwrap_or(true),
Some(client_id), Some(client_id),
) )
}, ?); }, ?);
@ -4495,6 +4520,7 @@ pub(crate) fn screen_thread_main(
Some(run_plugin), Some(run_plugin),
None, None,
start_suppressed, start_suppressed,
should_focus_plugin.unwrap_or(true),
None, None,
)?; )?;
} else { } else {
@ -4571,6 +4597,7 @@ pub(crate) fn screen_thread_main(
skip_cache, skip_cache,
None, None,
None, None,
None,
))?; ))?;
}, },
None => { None => {
@ -4618,6 +4645,7 @@ pub(crate) fn screen_thread_main(
skip_cache, skip_cache,
None, None,
None, None,
None,
))?; ))?;
} }
}, },
@ -4654,6 +4682,7 @@ pub(crate) fn screen_thread_main(
skip_cache, skip_cache,
cwd, cwd,
None, None,
None,
))?; ))?;
}, },
None => { None => {
@ -4691,6 +4720,7 @@ pub(crate) fn screen_thread_main(
skip_cache, skip_cache,
cwd, cwd,
None, None,
None,
))?; ))?;
}, },
None => { None => {
@ -5271,6 +5301,12 @@ pub(crate) fn screen_thread_main(
} }
let _ = screen.log_and_report_session_state(); let _ = screen.log_and_report_session_state();
}, },
ScreenInstruction::InterceptKeyPresses(plugin_id, client_id) => {
keybind_intercepts.insert(client_id, plugin_id);
},
ScreenInstruction::ClearKeyPressesIntercepts(client_id) => {
keybind_intercepts.remove(&client_id);
},
} }
} }
Ok(()) Ok(())

View file

@ -22,6 +22,7 @@ use zellij_utils::position::Position;
use zellij_utils::position::{Column, Line}; use zellij_utils::position::{Column, Line};
use crate::background_jobs::BackgroundJob; use crate::background_jobs::BackgroundJob;
use crate::pane_groups::PaneGroups;
use crate::pty_writer::PtyWriteInstruction; use crate::pty_writer::PtyWriteInstruction;
use crate::screen::CopyOptions; use crate::screen::CopyOptions;
use crate::ui::{loading_indication::LoadingIndication, pane_boundaries_frame::FrameParams}; use crate::ui::{loading_indication::LoadingIndication, pane_boundaries_frame::FrameParams};
@ -262,7 +263,7 @@ pub(crate) struct Tab {
styled_underlines: bool, styled_underlines: bool,
explicitly_disable_kitty_keyboard_protocol: bool, explicitly_disable_kitty_keyboard_protocol: bool,
mouse_hover_pane_id: HashMap<ClientId, PaneId>, mouse_hover_pane_id: HashMap<ClientId, PaneId>,
current_pane_group: Rc<RefCell<HashMap<ClientId, Vec<PaneId>>>>, current_pane_group: Rc<RefCell<PaneGroups>>,
advanced_mouse_actions: bool, advanced_mouse_actions: bool,
currently_marking_pane_group: Rc<RefCell<HashMap<ClientId, bool>>>, currently_marking_pane_group: Rc<RefCell<HashMap<ClientId, bool>>>,
} }
@ -675,7 +676,7 @@ impl Tab {
styled_underlines: bool, styled_underlines: bool,
explicitly_disable_kitty_keyboard_protocol: bool, explicitly_disable_kitty_keyboard_protocol: bool,
default_editor: Option<PathBuf>, default_editor: Option<PathBuf>,
current_pane_group: Rc<RefCell<HashMap<ClientId, Vec<PaneId>>>>, current_pane_group: Rc<RefCell<PaneGroups>>,
currently_marking_pane_group: Rc<RefCell<HashMap<ClientId, bool>>>, currently_marking_pane_group: Rc<RefCell<HashMap<ClientId, bool>>>,
advanced_mouse_actions: bool, advanced_mouse_actions: bool,
) -> Self { ) -> Self {
@ -1187,17 +1188,17 @@ impl Tab {
self.set_force_render(); self.set_force_render();
} else { } else {
self.show_floating_panes(); self.show_floating_panes();
match self.floating_panes.last_floating_pane_id() { match self.floating_panes.last_selectable_floating_pane_id() {
Some(first_floating_pane_id) => match client_id { Some(last_selectable_floating_pane_id) => match client_id {
Some(client_id) => { Some(client_id) => {
if !self.floating_panes.active_panes_contain(&client_id) { if !self.floating_panes.active_panes_contain(&client_id) {
self.floating_panes self.floating_panes
.focus_pane(first_floating_pane_id, client_id); .focus_pane(last_selectable_floating_pane_id, client_id);
} }
}, },
None => { None => {
self.floating_panes self.floating_panes
.focus_pane_for_all_clients(first_floating_pane_id); .focus_pane_for_all_clients(last_selectable_floating_pane_id);
}, },
}, },
None => { None => {
@ -1234,14 +1235,17 @@ impl Tab {
invoked_with: Option<Run>, invoked_with: Option<Run>,
floating_pane_coordinates: Option<FloatingPaneCoordinates>, floating_pane_coordinates: Option<FloatingPaneCoordinates>,
start_suppressed: bool, start_suppressed: bool,
should_focus_pane: bool,
client_id: Option<ClientId>, client_id: Option<ClientId>,
) -> Result<()> { ) -> Result<()> {
let err_context = || format!("failed to create new pane with id {pid:?}"); let err_context = || format!("failed to create new pane with id {pid:?}");
if should_focus_pane {
match should_float { match should_float {
Some(true) => self.show_floating_panes(), Some(true) => self.show_floating_panes(),
Some(false) => self.hide_floating_panes(), Some(false) => self.hide_floating_panes(),
None => {}, None => {},
}; };
}
self.close_down_to_max_terminals() self.close_down_to_max_terminals()
.with_context(err_context)?; .with_context(err_context)?;
let mut new_pane = match pid { let mut new_pane = match pid {
@ -1316,11 +1320,27 @@ impl Tab {
self.suppressed_panes self.suppressed_panes
.insert(pid, (is_scrollback_editor, new_pane)); .insert(pid, (is_scrollback_editor, new_pane));
Ok(()) Ok(())
} else if self.floating_panes.panes_are_visible() { } else if should_focus_pane {
if self.floating_panes.panes_are_visible() {
self.add_floating_pane(new_pane, pid, floating_pane_coordinates, true) self.add_floating_pane(new_pane, pid, floating_pane_coordinates, true)
} else { } else {
self.add_tiled_pane(new_pane, pid, client_id) self.add_tiled_pane(new_pane, pid, client_id)
} }
} else {
match should_float {
Some(true) => {
self.add_floating_pane(new_pane, pid, floating_pane_coordinates, false)
},
Some(false) => self.add_tiled_pane(new_pane, pid, client_id),
None => {
if self.floating_panes.panes_are_visible() {
self.add_floating_pane(new_pane, pid, floating_pane_coordinates, false)
} else {
self.add_tiled_pane(new_pane, pid, client_id)
}
},
}
}
} }
pub fn replace_active_pane_with_editor_pane( pub fn replace_active_pane_with_editor_pane(
&mut self, &mut self,
@ -2277,7 +2297,7 @@ impl Tab {
); );
let current_pane_group: HashMap<ClientId, Vec<PaneId>> = let current_pane_group: HashMap<ClientId, Vec<PaneId>> =
{ self.current_pane_group.borrow().clone() }; { self.current_pane_group.borrow().clone_inner() };
self.tiled_panes self.tiled_panes
.render( .render(
output, output,
@ -2851,6 +2871,8 @@ impl Tab {
// is opened // is opened
self.tiled_panes.move_clients_out_of_pane(id); self.tiled_panes.move_clients_out_of_pane(id);
} }
} else if let Some(pane) = self.floating_panes.get_pane_mut(id) {
pane.set_selectable(selectable);
} }
// we do this here because if there is a non-selectable pane on the edge, we consider it // we do this here because if there is a non-selectable pane on the edge, we consider it
// outside the viewport (a ui-pane, eg. the status-bar and tab-bar) and need to adjust for it // outside the viewport (a ui-pane, eg. the status-bar and tab-bar) and need to adjust for it
@ -2877,7 +2899,7 @@ impl Tab {
if self.floating_panes.panes_contain(&id) { if self.floating_panes.panes_contain(&id) {
let _closed_pane = self.floating_panes.remove_pane(id); let _closed_pane = self.floating_panes.remove_pane(id);
self.floating_panes.move_clients_out_of_pane(id); self.floating_panes.move_clients_out_of_pane(id);
if !self.floating_panes.has_panes() { if !self.floating_panes.has_selectable_panes() {
self.swap_layouts.reset_floating_damage(); self.swap_layouts.reset_floating_damage();
self.hide_floating_panes(); self.hide_floating_panes();
} }
@ -3663,10 +3685,9 @@ impl Tab {
let err_context = let err_context =
|| format!("failed to handle mouse event {event:?} for client {client_id}"); || format!("failed to handle mouse event {event:?} for client {client_id}");
if !self.floating_panes.panes_are_visible() { if !self.floating_panes.panes_are_visible() {
let search_selectable = false;
if let Ok(Some(pane_id)) = self if let Ok(Some(pane_id)) = self
.floating_panes .floating_panes
.get_pinned_pane_id_at(&event.position, search_selectable) .get_pinned_pane_id_at(&event.position, true)
{ {
// here, the floating panes are not visible, but there is a pinned pane (always // here, the floating panes are not visible, but there is a pinned pane (always
// visible) that has been clicked on - so we make the entire surface visible and // visible) that has been clicked on - so we make the entire surface visible and
@ -3674,6 +3695,15 @@ impl Tab {
self.show_floating_panes(); self.show_floating_panes();
self.floating_panes.focus_pane(pane_id, client_id); self.floating_panes.focus_pane(pane_id, client_id);
return Ok(MouseEffect::state_changed()); return Ok(MouseEffect::state_changed());
} else if let Ok(Some(_pane_id)) = self
.floating_panes
.get_pinned_pane_id_at(&event.position, false)
{
// here, the floating panes are not visible, but there is a pinned pane (always
// visible) that has been clicked on - this pane however is not selectable
// (we know this because we passed "false" to get_pinned_pane_id_at)
// so we don't do anything
return Ok(MouseEffect::default());
} }
} }
let active_pane_id_before_click = self let active_pane_id_before_click = self
@ -3924,8 +3954,13 @@ impl Tab {
self.mouse_hover_pane_id.remove(&client_id); self.mouse_hover_pane_id.remove(&client_id);
} else { } else {
let pane_id = pane.pid(); let pane_id = pane.pid();
if self.advanced_mouse_actions { // if the pane is not selectable, we don't want to create a hover effect over it
// we do however want to remove the hover effect from other panes
let pane_is_selectable = pane.selectable();
if self.advanced_mouse_actions && pane_is_selectable {
self.mouse_hover_pane_id.insert(client_id, pane_id); self.mouse_hover_pane_id.insert(client_id, pane_id);
} else if self.advanced_mouse_actions {
self.mouse_hover_pane_id.remove(&client_id);
} }
} }
}; };
@ -4467,7 +4502,7 @@ impl Tab {
} }
pub fn pane_infos(&self) -> Vec<PaneInfo> { pub fn pane_infos(&self) -> Vec<PaneInfo> {
let mut pane_info = vec![]; let mut pane_info = vec![];
let current_pane_group = { self.current_pane_group.borrow().clone() }; let current_pane_group = { self.current_pane_group.borrow().clone_inner() };
let mut tiled_pane_info = self.tiled_panes.pane_info(&current_pane_group); let mut tiled_pane_info = self.tiled_panes.pane_info(&current_pane_group);
let mut floating_pane_info = self.floating_panes.pane_info(&current_pane_group); let mut floating_pane_info = self.floating_panes.pane_info(&current_pane_group);
pane_info.append(&mut tiled_pane_info); pane_info.append(&mut tiled_pane_info);

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
use super::Tab; use super::Tab;
use crate::pane_groups::PaneGroups;
use crate::panes::sixel::SixelImageStore; use crate::panes::sixel::SixelImageStore;
use crate::screen::CopyOptions; use crate::screen::CopyOptions;
use crate::{ use crate::{
@ -165,7 +166,7 @@ fn create_new_tab(size: Size, stacked_resize: bool) -> Tab {
let copy_options = CopyOptions::default(); let copy_options = CopyOptions::default();
let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default()));
let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new()));
let current_pane_group = Rc::new(RefCell::new(HashMap::new())); let current_pane_group = Rc::new(RefCell::new(PaneGroups::new(ThreadSenders::default())));
let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new()));
let debug = false; let debug = false;
let arrow_fonts = true; let arrow_fonts = true;
@ -238,7 +239,7 @@ fn create_new_tab_with_layout(size: Size, layout: TiledPaneLayout) -> Tab {
let copy_options = CopyOptions::default(); let copy_options = CopyOptions::default();
let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default()));
let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new()));
let current_pane_group = Rc::new(RefCell::new(HashMap::new())); let current_pane_group = Rc::new(RefCell::new(PaneGroups::new(ThreadSenders::default())));
let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new()));
let debug = false; let debug = false;
let arrow_fonts = true; let arrow_fonts = true;
@ -317,7 +318,7 @@ fn create_new_tab_with_cell_size(
let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default()));
let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new()));
let stacked_resize = Rc::new(RefCell::new(true)); let stacked_resize = Rc::new(RefCell::new(true));
let current_pane_group = Rc::new(RefCell::new(HashMap::new())); let current_pane_group = Rc::new(RefCell::new(PaneGroups::new(ThreadSenders::default())));
let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new())); let currently_marking_pane_group = Rc::new(RefCell::new(HashMap::new()));
let debug = false; let debug = false;
let arrow_fonts = true; let arrow_fonts = true;
@ -602,7 +603,7 @@ fn split_largest_pane() {
let mut tab = create_new_tab(size, stacked_resize); let mut tab = create_new_tab(size, stacked_resize);
for i in 2..5 { for i in 2..5 {
let new_pane_id = PaneId::Terminal(i); let new_pane_id = PaneId::Terminal(i);
tab.new_pane(new_pane_id, None, None, None, None, false, Some(1)) tab.new_pane(new_pane_id, None, None, None, None, false, true, Some(1))
.unwrap(); .unwrap();
} }
assert_eq!(tab.tiled_panes.panes.len(), 4, "The tab has four panes"); assert_eq!(tab.tiled_panes.panes.len(), 4, "The tab has four panes");
@ -811,7 +812,16 @@ pub fn cannot_split_largest_pane_when_there_is_no_room() {
let size = Size { cols: 8, rows: 4 }; let size = Size { cols: 8, rows: 4 };
let stacked_resize = true; let stacked_resize = true;
let mut tab = create_new_tab(size, stacked_resize); let mut tab = create_new_tab(size, stacked_resize);
tab.new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) tab.new_pane(
PaneId::Terminal(2),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
tab.tiled_panes.panes.len(), tab.tiled_panes.panes.len(),
@ -856,7 +866,7 @@ pub fn toggle_focused_pane_fullscreen() {
let mut tab = create_new_tab(size, stacked_resize); let mut tab = create_new_tab(size, stacked_resize);
for i in 2..5 { for i in 2..5 {
let new_pane_id = PaneId::Terminal(i); let new_pane_id = PaneId::Terminal(i);
tab.new_pane(new_pane_id, None, None, None, None, false, Some(1)) tab.new_pane(new_pane_id, None, None, None, None, false, true, Some(1))
.unwrap(); .unwrap();
} }
tab.toggle_active_pane_fullscreen(1); tab.toggle_active_pane_fullscreen(1);
@ -932,7 +942,7 @@ pub fn toggle_focused_pane_fullscreen_with_stacked_resizes() {
let mut tab = create_new_tab(size, stacked_resize); let mut tab = create_new_tab(size, stacked_resize);
for i in 2..5 { for i in 2..5 {
let new_pane_id = PaneId::Terminal(i); let new_pane_id = PaneId::Terminal(i);
tab.new_pane(new_pane_id, None, None, None, None, false, Some(1)) tab.new_pane(new_pane_id, None, None, None, None, false, true, Some(1))
.unwrap(); .unwrap();
} }
tab.toggle_active_pane_fullscreen(1); tab.toggle_active_pane_fullscreen(1);
@ -1008,16 +1018,52 @@ fn switch_to_next_pane_fullscreen() {
let mut active_tab = create_new_tab(size, stacked_resize); let mut active_tab = create_new_tab(size, stacked_resize);
active_tab active_tab
.new_pane(PaneId::Terminal(1), None, None, None, None, false, Some(1)) .new_pane(
PaneId::Terminal(1),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
active_tab active_tab
.new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) .new_pane(
PaneId::Terminal(2),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
active_tab active_tab
.new_pane(PaneId::Terminal(3), None, None, None, None, false, Some(1)) .new_pane(
PaneId::Terminal(3),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
active_tab active_tab
.new_pane(PaneId::Terminal(4), None, None, None, None, false, Some(1)) .new_pane(
PaneId::Terminal(4),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
active_tab.toggle_active_pane_fullscreen(1); active_tab.toggle_active_pane_fullscreen(1);
@ -1049,16 +1095,52 @@ fn switch_to_prev_pane_fullscreen() {
//testing four consecutive switches in fullscreen mode //testing four consecutive switches in fullscreen mode
active_tab active_tab
.new_pane(PaneId::Terminal(1), None, None, None, None, false, Some(1)) .new_pane(
PaneId::Terminal(1),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
active_tab active_tab
.new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) .new_pane(
PaneId::Terminal(2),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
active_tab active_tab
.new_pane(PaneId::Terminal(3), None, None, None, None, false, Some(1)) .new_pane(
PaneId::Terminal(3),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
active_tab active_tab
.new_pane(PaneId::Terminal(4), None, None, None, None, false, Some(1)) .new_pane(
PaneId::Terminal(4),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
active_tab.toggle_active_pane_fullscreen(1); active_tab.toggle_active_pane_fullscreen(1);
// order is now 1 2 3 4 // order is now 1 2 3 4
@ -14650,7 +14732,16 @@ fn correctly_resize_frameless_panes_on_pane_close() {
let content_size = (pane.get_content_columns(), pane.get_content_rows()); let content_size = (pane.get_content_columns(), pane.get_content_rows());
assert_eq!(content_size, (cols, rows)); assert_eq!(content_size, (cols, rows));
tab.new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) tab.new_pane(
PaneId::Terminal(2),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
tab.close_pane(PaneId::Terminal(2), true); tab.close_pane(PaneId::Terminal(2), true);

View file

@ -64,6 +64,7 @@ pub struct FrameParams {
pub pane_is_floating: bool, pub pane_is_floating: bool,
pub content_offset: Offset, pub content_offset: Offset,
pub mouse_is_hovering_over_pane: bool, pub mouse_is_hovering_over_pane: bool,
pub pane_is_selectable: bool,
} }
#[derive(Default, PartialEq)] #[derive(Default, PartialEq)]
@ -86,6 +87,7 @@ pub struct PaneFrame {
is_floating: bool, is_floating: bool,
content_offset: Offset, content_offset: Offset,
mouse_is_hovering_over_pane: bool, mouse_is_hovering_over_pane: bool,
is_selectable: bool,
} }
impl PaneFrame { impl PaneFrame {
@ -114,6 +116,7 @@ impl PaneFrame {
is_floating: frame_params.pane_is_floating, is_floating: frame_params.pane_is_floating,
content_offset: frame_params.content_offset, content_offset: frame_params.content_offset,
mouse_is_hovering_over_pane: frame_params.mouse_is_hovering_over_pane, mouse_is_hovering_over_pane: frame_params.mouse_is_hovering_over_pane,
is_selectable: frame_params.pane_is_selectable,
} }
} }
pub fn is_pinned(mut self, is_pinned: bool) -> Self { pub fn is_pinned(mut self, is_pinned: bool) -> Self {
@ -167,7 +170,7 @@ impl PaneFrame {
// string and length because of color // string and length because of color
let has_scroll = self.scroll_position.0 > 0 || self.scroll_position.1 > 0; let has_scroll = self.scroll_position.0 > 0 || self.scroll_position.1 > 0;
if has_scroll { if has_scroll {
let pin_indication = if self.is_floating { let pin_indication = if self.is_floating && self.is_selectable {
self.render_pinned_indication(max_length) self.render_pinned_indication(max_length)
} else { } else {
None None
@ -192,7 +195,7 @@ impl PaneFrame {
(None, Some(scroll_indication)) => Some(scroll_indication), (None, Some(scroll_indication)) => Some(scroll_indication),
_ => None, _ => None,
} }
} else if self.is_floating { } else if self.is_floating && self.is_selectable {
self.render_pinned_indication(max_length) self.render_pinned_indication(max_length)
} else { } else {
None None

View file

@ -207,6 +207,7 @@ impl<'a> PaneContentsAndUi<'a> {
client_mode: InputMode, client_mode: InputMode,
session_is_mirrored: bool, session_is_mirrored: bool,
pane_is_floating: bool, pane_is_floating: bool,
pane_is_selectable: bool,
) -> Result<()> { ) -> Result<()> {
let err_context = || format!("failed to render pane frame for client {client_id}"); let err_context = || format!("failed to render pane frame for client {client_id}");
@ -243,6 +244,7 @@ impl<'a> PaneContentsAndUi<'a> {
mouse_is_hovering_over_pane: self mouse_is_hovering_over_pane: self
.mouse_is_hovering_over_pane_for_clients .mouse_is_hovering_over_pane_for_clients
.contains(&client_id), .contains(&client_id),
pane_is_selectable,
} }
} else { } else {
FrameParams { FrameParams {
@ -260,6 +262,7 @@ impl<'a> PaneContentsAndUi<'a> {
mouse_is_hovering_over_pane: self mouse_is_hovering_over_pane: self
.mouse_is_hovering_over_pane_for_clients .mouse_is_hovering_over_pane_for_clients
.contains(&client_id), .contains(&client_id),
pane_is_selectable,
} }
}; };

View file

@ -1194,7 +1194,16 @@ fn switch_to_tab_with_fullscreen() {
{ {
let active_tab = screen.get_active_tab_mut(1).unwrap(); let active_tab = screen.get_active_tab_mut(1).unwrap();
active_tab active_tab
.new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) .new_pane(
PaneId::Terminal(2),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
active_tab.toggle_active_pane_fullscreen(1); active_tab.toggle_active_pane_fullscreen(1);
} }
@ -1309,7 +1318,16 @@ fn attach_after_first_tab_closed() {
{ {
let active_tab = screen.get_active_tab_mut(1).unwrap(); let active_tab = screen.get_active_tab_mut(1).unwrap();
active_tab active_tab
.new_pane(PaneId::Terminal(2), None, None, None, None, false, Some(1)) .new_pane(
PaneId::Terminal(2),
None,
None,
None,
None,
false,
true,
Some(1),
)
.unwrap(); .unwrap();
active_tab.toggle_active_pane_fullscreen(1); active_tab.toggle_active_pane_fullscreen(1);
} }
@ -1345,6 +1363,7 @@ fn open_new_floating_pane_with_custom_coordinates() {
pinned: None, pinned: None,
}), }),
false, false,
true,
Some(1), Some(1),
) )
.unwrap(); .unwrap();
@ -1380,6 +1399,7 @@ fn open_new_floating_pane_with_custom_coordinates_exceeding_viewport() {
pinned: None, pinned: None,
}), }),
false, false,
true,
Some(1), Some(1),
) )
.unwrap(); .unwrap();
@ -1478,7 +1498,11 @@ fn group_panes_with_mouse() {
); );
assert_eq!( assert_eq!(
screen.current_pane_group.borrow().get(&client_id), screen
.current_pane_group
.borrow()
.clone_inner()
.get(&client_id),
Some(&vec![PaneId::Terminal(2)]), Some(&vec![PaneId::Terminal(2)]),
"Pane Id added to client's pane group" "Pane Id added to client's pane group"
); );
@ -1489,7 +1513,11 @@ fn group_panes_with_mouse() {
); );
assert_eq!( assert_eq!(
screen.current_pane_group.borrow().get(&client_id), screen
.current_pane_group
.borrow()
.clone_inner()
.get(&client_id),
Some(&vec![]), Some(&vec![]),
"Pane Id removed from client's pane group" "Pane Id removed from client's pane group"
); );
@ -1509,7 +1537,11 @@ fn group_panes_with_keyboard() {
let _ = screen.toggle_pane_in_group(client_id); let _ = screen.toggle_pane_in_group(client_id);
assert_eq!( assert_eq!(
screen.current_pane_group.borrow().get(&client_id), screen
.current_pane_group
.borrow()
.clone_inner()
.get(&client_id),
Some(&vec![PaneId::Terminal(2)]), Some(&vec![PaneId::Terminal(2)]),
"Pane Id added to client's pane group" "Pane Id added to client's pane group"
); );
@ -1517,7 +1549,11 @@ fn group_panes_with_keyboard() {
let _ = screen.toggle_pane_in_group(client_id); let _ = screen.toggle_pane_in_group(client_id);
assert_eq!( assert_eq!(
screen.current_pane_group.borrow().get(&client_id), screen
.current_pane_group
.borrow()
.clone_inner()
.get(&client_id),
Some(&vec![]), Some(&vec![]),
"Pane Id removed from client's pane group" "Pane Id removed from client's pane group"
); );
@ -1546,6 +1582,7 @@ fn group_panes_following_focus() {
None, None,
None, None,
false, false,
true,
Some(client_id), Some(client_id),
) )
.unwrap(); .unwrap();
@ -1560,7 +1597,11 @@ fn group_panes_following_focus() {
.unwrap(); .unwrap();
screen.add_active_pane_to_group_if_marking(&client_id); screen.add_active_pane_to_group_if_marking(&client_id);
assert_eq!( assert_eq!(
screen.current_pane_group.borrow().get(&client_id), screen
.current_pane_group
.borrow()
.clone_inner()
.get(&client_id),
Some(&vec![PaneId::Terminal(4), PaneId::Terminal(3)]), Some(&vec![PaneId::Terminal(4), PaneId::Terminal(3)]),
"Pane Id of focused pane and newly focused pane above added to pane group" "Pane Id of focused pane and newly focused pane above added to pane group"
); );
@ -1573,7 +1614,7 @@ fn group_panes_following_focus() {
.move_focus_up(client_id) .move_focus_up(client_id)
.unwrap(); .unwrap();
let _ = screen.add_active_pane_to_group_if_marking(&client_id); 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"); assert_eq!(screen.current_pane_group.borrow().clone_inner().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");
} }
} }
@ -1600,6 +1641,7 @@ fn break_group_with_mouse() {
None, None,
None, None,
false, false,
true,
Some(client_id), Some(client_id),
) )
.unwrap(); .unwrap();
@ -1620,7 +1662,11 @@ fn break_group_with_mouse() {
.unwrap(); .unwrap();
screen.add_active_pane_to_group_if_marking(&client_id); screen.add_active_pane_to_group_if_marking(&client_id);
assert_eq!( assert_eq!(
screen.current_pane_group.borrow().get(&client_id), screen
.current_pane_group
.borrow()
.clone_inner()
.get(&client_id),
Some(&vec![ Some(&vec![
PaneId::Terminal(4), PaneId::Terminal(4),
PaneId::Terminal(3), PaneId::Terminal(3),
@ -1635,7 +1681,11 @@ fn break_group_with_mouse() {
client_id, client_id,
); );
assert_eq!( assert_eq!(
screen.current_pane_group.borrow().get(&client_id), screen
.current_pane_group
.borrow()
.clone_inner()
.get(&client_id),
Some(&vec![]), Some(&vec![]),
"Group cleared by mouse event" "Group cleared by mouse event"
); );

View file

@ -1,6 +1,5 @@
--- ---
source: zellij-server/src/./unit/screen_tests.rs source: zellij-server/src/./unit/screen_tests.rs
assertion_line: 2983
expression: "format!(\"{:#?}\", pty_fill_plugin_cwd_instruction)" expression: "format!(\"{:#?}\", pty_fill_plugin_cwd_instruction)"
--- ---
Some( Some(
@ -32,5 +31,6 @@ Some(
false, false,
None, None,
None, None,
None,
), ),
) )

View file

@ -1313,6 +1313,20 @@ pub fn embed_multiple_panes(pane_ids: Vec<PaneId>) {
unsafe { host_run_plugin_command() }; unsafe { host_run_plugin_command() };
} }
pub fn intercept_key_presses() {
let plugin_command = PluginCommand::InterceptKeyPresses;
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 clear_key_presses_intercepts() {
let plugin_command = PluginCommand::ClearKeyPressesIntercepts;
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 // Utility Functions
#[allow(unused)] #[allow(unused)]

View file

@ -54,6 +54,44 @@ impl Text {
.map(|i| i.append(&mut indices.into_iter().collect())); .map(|i| i.append(&mut indices.into_iter().collect()));
self self
} }
pub fn color_substring<S: AsRef<str>>(mut self, index_level: usize, substr: S) -> Self {
let substr = substr.as_ref();
let mut start = 0;
while let Some(pos) = self.text[start..].find(substr) {
let abs_pos = start + pos;
self = self.color_range(index_level, abs_pos..abs_pos + substr.len());
start = abs_pos + substr.len();
}
self
}
pub fn color_all(self, index_level: usize) -> Self {
self.color_range(index_level, ..)
}
pub fn color_nth_substring<S: AsRef<str>>(
self,
index_level: usize,
substr: S,
occurrence_index: usize,
) -> Self {
let substr = substr.as_ref();
let mut start = 0;
let mut count = 0;
while let Some(pos) = self.text[start..].find(substr) {
if count == occurrence_index {
let abs_pos = start + pos;
return self.color_range(index_level, abs_pos..abs_pos + substr.len());
}
count += 1;
start = start + pos + substr.len();
}
self
}
fn pad_indices(&mut self, index_level: usize) { fn pad_indices(&mut self, index_level: usize) {
if self.indices.get(index_level).is_none() { if self.indices.get(index_level).is_none() {
for _ in self.indices.len()..=index_level { for _ in self.indices.len()..=index_level {

View file

@ -181,12 +181,6 @@ keybinds {
bind "Alt -" { Resize "Decrease"; } bind "Alt -" { Resize "Decrease"; }
bind "Alt [" { PreviousSwapLayout; } bind "Alt [" { PreviousSwapLayout; }
bind "Alt ]" { NextSwapLayout; } bind "Alt ]" { NextSwapLayout; }
bind "Alt m" {
LaunchOrFocusPlugin "zellij:multiple-select" {
floating true
move_to_focused_tab true
}
}
bind "Alt p" { TogglePaneInGroup; } bind "Alt p" { TogglePaneInGroup; }
bind "Alt Shift p" { ToggleGroupMarking; } bind "Alt Shift p" { ToggleGroupMarking; }
} }

View file

@ -9,7 +9,7 @@ pub struct EventNameList {
pub struct Event { pub struct Event {
#[prost(enumeration="EventType", tag="1")] #[prost(enumeration="EventType", tag="1")]
pub name: i32, 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, 27")]
pub payload: ::core::option::Option<event::Payload>, pub payload: ::core::option::Option<event::Payload>,
} }
/// Nested message and enum types in `Event`. /// Nested message and enum types in `Event`.
@ -67,6 +67,8 @@ pub mod event {
FailedToChangeHostFolderPayload(super::FailedToChangeHostFolderPayload), FailedToChangeHostFolderPayload(super::FailedToChangeHostFolderPayload),
#[prost(message, tag="26")] #[prost(message, tag="26")]
PastedTextPayload(super::PastedTextPayload), PastedTextPayload(super::PastedTextPayload),
#[prost(message, tag="27")]
InterceptedKeyPayload(super::super::key::Key),
} }
} }
#[allow(clippy::derive_partial_eq_without_eq)] #[allow(clippy::derive_partial_eq_without_eq)]
@ -530,6 +532,7 @@ pub enum EventType {
PastedText = 29, PastedText = 29,
ConfigWasWrittenToDisk = 30, ConfigWasWrittenToDisk = 30,
BeforeClose = 31, BeforeClose = 31,
InterceptedKeyPress = 32,
} }
impl EventType { impl EventType {
/// String value of the enum field names used in the ProtoBuf definition. /// String value of the enum field names used in the ProtoBuf definition.
@ -570,6 +573,7 @@ impl EventType {
EventType::PastedText => "PastedText", EventType::PastedText => "PastedText",
EventType::ConfigWasWrittenToDisk => "ConfigWasWrittenToDisk", EventType::ConfigWasWrittenToDisk => "ConfigWasWrittenToDisk",
EventType::BeforeClose => "BeforeClose", EventType::BeforeClose => "BeforeClose",
EventType::InterceptedKeyPress => "InterceptedKeyPress",
} }
} }
/// Creates an enum from field names used in the ProtoBuf definition. /// Creates an enum from field names used in the ProtoBuf definition.
@ -607,6 +611,7 @@ impl EventType {
"PastedText" => Some(Self::PastedText), "PastedText" => Some(Self::PastedText),
"ConfigWasWrittenToDisk" => Some(Self::ConfigWasWrittenToDisk), "ConfigWasWrittenToDisk" => Some(Self::ConfigWasWrittenToDisk),
"BeforeClose" => Some(Self::BeforeClose), "BeforeClose" => Some(Self::BeforeClose),
"InterceptedKeyPress" => Some(Self::InterceptedKeyPress),
_ => None, _ => None,
} }
} }

View file

@ -919,6 +919,8 @@ pub enum CommandName {
CloseMultiplePanes = 129, CloseMultiplePanes = 129,
FloatMultiplePanes = 130, FloatMultiplePanes = 130,
EmbedMultiplePanes = 131, EmbedMultiplePanes = 131,
InterceptKeyPresses = 132,
ClearKeyPressesIntercepts = 133,
} }
impl CommandName { impl CommandName {
/// String value of the enum field names used in the ProtoBuf definition. /// String value of the enum field names used in the ProtoBuf definition.
@ -1059,6 +1061,8 @@ impl CommandName {
CommandName::CloseMultiplePanes => "CloseMultiplePanes", CommandName::CloseMultiplePanes => "CloseMultiplePanes",
CommandName::FloatMultiplePanes => "FloatMultiplePanes", CommandName::FloatMultiplePanes => "FloatMultiplePanes",
CommandName::EmbedMultiplePanes => "EmbedMultiplePanes", CommandName::EmbedMultiplePanes => "EmbedMultiplePanes",
CommandName::InterceptKeyPresses => "InterceptKeyPresses",
CommandName::ClearKeyPressesIntercepts => "ClearKeyPressesIntercepts",
} }
} }
/// Creates an enum from field names used in the ProtoBuf definition. /// Creates an enum from field names used in the ProtoBuf definition.
@ -1196,6 +1200,8 @@ impl CommandName {
"CloseMultiplePanes" => Some(Self::CloseMultiplePanes), "CloseMultiplePanes" => Some(Self::CloseMultiplePanes),
"FloatMultiplePanes" => Some(Self::FloatMultiplePanes), "FloatMultiplePanes" => Some(Self::FloatMultiplePanes),
"EmbedMultiplePanes" => Some(Self::EmbedMultiplePanes), "EmbedMultiplePanes" => Some(Self::EmbedMultiplePanes),
"InterceptKeyPresses" => Some(Self::InterceptKeyPresses),
"ClearKeyPressesIntercepts" => Some(Self::ClearKeyPressesIntercepts),
_ => None, _ => None,
} }
} }

View file

@ -12,6 +12,7 @@ pub enum PermissionType {
MessageAndLaunchOtherPlugins = 8, MessageAndLaunchOtherPlugins = 8,
Reconfigure = 9, Reconfigure = 9,
FullHdAccess = 10, FullHdAccess = 10,
InterceptInput = 11,
} }
impl PermissionType { impl PermissionType {
/// String value of the enum field names used in the ProtoBuf definition. /// String value of the enum field names used in the ProtoBuf definition.
@ -31,6 +32,7 @@ impl PermissionType {
PermissionType::MessageAndLaunchOtherPlugins => "MessageAndLaunchOtherPlugins", PermissionType::MessageAndLaunchOtherPlugins => "MessageAndLaunchOtherPlugins",
PermissionType::Reconfigure => "Reconfigure", PermissionType::Reconfigure => "Reconfigure",
PermissionType::FullHdAccess => "FullHdAccess", PermissionType::FullHdAccess => "FullHdAccess",
PermissionType::InterceptInput => "InterceptInput",
} }
} }
/// Creates an enum from field names used in the ProtoBuf definition. /// Creates an enum from field names used in the ProtoBuf definition.
@ -47,6 +49,7 @@ impl PermissionType {
"MessageAndLaunchOtherPlugins" => Some(Self::MessageAndLaunchOtherPlugins), "MessageAndLaunchOtherPlugins" => Some(Self::MessageAndLaunchOtherPlugins),
"Reconfigure" => Some(Self::Reconfigure), "Reconfigure" => Some(Self::Reconfigure),
"FullHdAccess" => Some(Self::FullHdAccess), "FullHdAccess" => Some(Self::FullHdAccess),
"InterceptInput" => Some(Self::InterceptInput),
_ => None, _ => None,
} }
} }

View file

@ -938,6 +938,7 @@ pub enum Event {
PastedText(String), PastedText(String),
ConfigWasWrittenToDisk, ConfigWasWrittenToDisk,
BeforeClose, BeforeClose,
InterceptedKeyPress(KeyWithModifier),
} }
#[derive( #[derive(
@ -969,6 +970,7 @@ pub enum Permission {
MessageAndLaunchOtherPlugins, MessageAndLaunchOtherPlugins,
Reconfigure, Reconfigure,
FullHdAccess, FullHdAccess,
InterceptInput,
} }
impl PermissionType { impl PermissionType {
@ -991,6 +993,7 @@ impl PermissionType {
}, },
PermissionType::Reconfigure => "Change Zellij runtime configuration".to_owned(), PermissionType::Reconfigure => "Change Zellij runtime configuration".to_owned(),
PermissionType::FullHdAccess => "Full access to the hard-drive".to_owned(), PermissionType::FullHdAccess => "Full access to the hard-drive".to_owned(),
PermissionType::InterceptInput => "Intercept Input (keyboard & mouse)".to_owned(),
} }
} }
} }
@ -2323,4 +2326,6 @@ pub enum PluginCommand {
CloseMultiplePanes(Vec<PaneId>), CloseMultiplePanes(Vec<PaneId>),
FloatMultiplePanes(Vec<PaneId>), FloatMultiplePanes(Vec<PaneId>),
EmbedMultiplePanes(Vec<PaneId>), EmbedMultiplePanes(Vec<PaneId>),
InterceptKeyPresses,
ClearKeyPressesIntercepts,
} }

View file

@ -382,6 +382,8 @@ pub enum ScreenContext {
EmbedMultiplePanes, EmbedMultiplePanes,
TogglePaneInGroup, TogglePaneInGroup,
ToggleGroupMarking, ToggleGroupMarking,
InterceptKeyPresses,
ClearKeyPressesIntercepts,
} }
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s. /// Stack call representations corresponding to the different types of [`PtyInstruction`]s.

View file

@ -146,12 +146,6 @@ keybinds clear-defaults=true {
bind "Alt j" { MoveFocus "down"; } bind "Alt j" { MoveFocus "down"; }
bind "Alt k" { MoveFocus "up"; } bind "Alt k" { MoveFocus "up"; }
bind "Alt l" { MoveFocusOrTab "right"; } 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 n" { NewPane; }
bind "Alt o" { MoveTab "right"; } bind "Alt o" { MoveTab "right"; }
bind "Alt p" { TogglePaneInGroup; } bind "Alt p" { TogglePaneInGroup; }

View file

@ -146,12 +146,6 @@ keybinds clear-defaults=true {
bind "Alt j" { MoveFocus "down"; } bind "Alt j" { MoveFocus "down"; }
bind "Alt k" { MoveFocus "up"; } bind "Alt k" { MoveFocus "up"; }
bind "Alt l" { MoveFocusOrTab "right"; } 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 n" { NewPane; }
bind "Alt o" { MoveTab "right"; } bind "Alt o" { MoveTab "right"; }
bind "Alt p" { TogglePaneInGroup; } bind "Alt p" { TogglePaneInGroup; }

View file

@ -55,6 +55,7 @@ enum EventType {
PastedText = 29; PastedText = 29;
ConfigWasWrittenToDisk = 30; ConfigWasWrittenToDisk = 30;
BeforeClose = 31; BeforeClose = 31;
InterceptedKeyPress = 32;
} }
message EventNameList { message EventNameList {
@ -89,6 +90,7 @@ message Event {
HostFolderChangedPayload host_folder_changed_payload = 24; HostFolderChangedPayload host_folder_changed_payload = 24;
FailedToChangeHostFolderPayload failed_to_change_host_folder_payload = 25; FailedToChangeHostFolderPayload failed_to_change_host_folder_payload = 25;
PastedTextPayload pasted_text_payload = 26; PastedTextPayload pasted_text_payload = 26;
key.Key intercepted_key_payload = 27;
} }
} }

View file

@ -363,6 +363,12 @@ impl TryFrom<ProtobufEvent> for Event {
None => Ok(Event::BeforeClose), None => Ok(Event::BeforeClose),
_ => Err("Malformed payload for the BeforeClose Event"), _ => Err("Malformed payload for the BeforeClose Event"),
}, },
Some(ProtobufEventType::InterceptedKeyPress) => match protobuf_event.payload {
Some(ProtobufEventPayload::KeyPayload(protobuf_key)) => {
Ok(Event::InterceptedKeyPress(protobuf_key.try_into()?))
},
_ => Err("Malformed payload for the InterceptedKeyPress Event"),
},
None => Err("Unknown Protobuf Event"), None => Err("Unknown Protobuf Event"),
} }
} }
@ -741,6 +747,10 @@ impl TryFrom<Event> for ProtobufEvent {
name: ProtobufEventType::BeforeClose as i32, name: ProtobufEventType::BeforeClose as i32,
payload: None, payload: None,
}), }),
Event::InterceptedKeyPress(key) => Ok(ProtobufEvent {
name: ProtobufEventType::InterceptedKeyPress as i32,
payload: Some(event::Payload::KeyPayload(key.try_into()?)),
}),
} }
} }
} }
@ -1376,6 +1386,7 @@ impl TryFrom<ProtobufEventType> for EventType {
ProtobufEventType::PastedText => EventType::PastedText, ProtobufEventType::PastedText => EventType::PastedText,
ProtobufEventType::ConfigWasWrittenToDisk => EventType::ConfigWasWrittenToDisk, ProtobufEventType::ConfigWasWrittenToDisk => EventType::ConfigWasWrittenToDisk,
ProtobufEventType::BeforeClose => EventType::BeforeClose, ProtobufEventType::BeforeClose => EventType::BeforeClose,
ProtobufEventType::InterceptedKeyPress => EventType::InterceptedKeyPress,
}) })
} }
} }
@ -1416,6 +1427,7 @@ impl TryFrom<EventType> for ProtobufEventType {
EventType::PastedText => ProtobufEventType::PastedText, EventType::PastedText => ProtobufEventType::PastedText,
EventType::ConfigWasWrittenToDisk => ProtobufEventType::ConfigWasWrittenToDisk, EventType::ConfigWasWrittenToDisk => ProtobufEventType::ConfigWasWrittenToDisk,
EventType::BeforeClose => ProtobufEventType::BeforeClose, EventType::BeforeClose => ProtobufEventType::BeforeClose,
EventType::InterceptedKeyPress => ProtobufEventType::InterceptedKeyPress,
}) })
} }
} }

View file

@ -145,6 +145,8 @@ enum CommandName {
CloseMultiplePanes = 129; CloseMultiplePanes = 129;
FloatMultiplePanes = 130; FloatMultiplePanes = 130;
EmbedMultiplePanes = 131; EmbedMultiplePanes = 131;
InterceptKeyPresses = 132;
ClearKeyPressesIntercepts = 133;
} }
message PluginCommand { message PluginCommand {

View file

@ -1614,6 +1614,14 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
}, },
_ => Err("Mismatched payload for EmbedMultiplePanes"), _ => Err("Mismatched payload for EmbedMultiplePanes"),
}, },
Some(CommandName::InterceptKeyPresses) => match protobuf_plugin_command.payload {
Some(_) => Err("InterceptKeyPresses should have no payload, found a payload"),
None => Ok(PluginCommand::InterceptKeyPresses),
},
Some(CommandName::ClearKeyPressesIntercepts) => match protobuf_plugin_command.payload {
Some(_) => Err("ClearKeyPressesIntercepts should have no payload, found a payload"),
None => Ok(PluginCommand::ClearKeyPressesIntercepts),
},
None => Err("Unrecognized plugin command"), None => Err("Unrecognized plugin command"),
} }
} }
@ -2684,6 +2692,14 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
}, },
)), )),
}), }),
PluginCommand::InterceptKeyPresses => Ok(ProtobufPluginCommand {
name: CommandName::InterceptKeyPresses as i32,
payload: None,
}),
PluginCommand::ClearKeyPressesIntercepts => Ok(ProtobufPluginCommand {
name: CommandName::ClearKeyPressesIntercepts as i32,
payload: None,
}),
} }
} }
} }

View file

@ -14,4 +14,5 @@ enum PermissionType {
MessageAndLaunchOtherPlugins = 8; MessageAndLaunchOtherPlugins = 8;
Reconfigure = 9; Reconfigure = 9;
FullHdAccess = 10; FullHdAccess = 10;
InterceptInput = 11;
} }

View file

@ -26,6 +26,7 @@ impl TryFrom<ProtobufPermissionType> for PermissionType {
}, },
ProtobufPermissionType::Reconfigure => Ok(PermissionType::Reconfigure), ProtobufPermissionType::Reconfigure => Ok(PermissionType::Reconfigure),
ProtobufPermissionType::FullHdAccess => Ok(PermissionType::FullHdAccess), ProtobufPermissionType::FullHdAccess => Ok(PermissionType::FullHdAccess),
ProtobufPermissionType::InterceptInput => Ok(PermissionType::InterceptInput),
} }
} }
} }
@ -53,6 +54,7 @@ impl TryFrom<PermissionType> for ProtobufPermissionType {
}, },
PermissionType::Reconfigure => Ok(ProtobufPermissionType::Reconfigure), PermissionType::Reconfigure => Ok(ProtobufPermissionType::Reconfigure),
PermissionType::FullHdAccess => Ok(ProtobufPermissionType::FullHdAccess), PermissionType::FullHdAccess => Ok(ProtobufPermissionType::FullHdAccess),
PermissionType::InterceptInput => Ok(ProtobufPermissionType::InterceptInput),
} }
} }
} }

View file

@ -210,35 +210,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -758,35 +729,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -1264,35 +1206,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -1912,35 +1825,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -2450,35 +2334,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -2854,35 +2709,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -3330,35 +3156,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -3759,35 +3556,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -4141,35 +3909,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -4577,35 +4316,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -5090,35 +4800,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -5485,35 +5166,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -6045,35 +5697,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',

View file

@ -210,35 +210,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -758,35 +729,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -1264,35 +1206,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -1912,35 +1825,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -2450,35 +2334,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -2854,35 +2709,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -3330,35 +3156,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -3759,35 +3556,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -4141,35 +3909,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -4577,35 +4316,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -5090,35 +4800,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -5485,35 +5166,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -6045,35 +5697,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',

View file

@ -210,35 +210,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -758,35 +729,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -1264,35 +1206,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -1912,35 +1825,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -2450,35 +2334,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -2854,35 +2709,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -3330,35 +3156,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -3759,35 +3556,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -4141,35 +3909,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -4577,35 +4316,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -5090,35 +4800,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -5485,35 +5166,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -6045,35 +5697,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',

View file

@ -210,35 +210,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -758,35 +729,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -1264,35 +1206,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -1912,35 +1825,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -2450,35 +2334,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -2854,35 +2709,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -3330,35 +3156,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -3759,35 +3556,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -4141,35 +3909,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -4577,35 +4316,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -5090,35 +4800,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -5485,35 +5166,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',
@ -6045,35 +5697,6 @@ Config {
Right, 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 { KeyWithModifier {
bare_key: Char( bare_key: Char(
'n', 'n',