aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2024-08-29 17:35:21 +0200
committerGitHub <noreply@github.com>2024-08-29 17:35:21 +0200
commit1776d240dade5ef1d254651ba53d0ab6d99e10ff (patch)
treece86a9254abdd7d21682999d81111cf2443db09e
parentdocs(changelog): reload config options at runtime (diff)
downloadsl0ppij-1776d240dade5ef1d254651ba53d0ab6d99e10ff.tar.gz
sl0ppij-1776d240dade5ef1d254651ba53d0ab6d99e10ff.tar.xz
sl0ppij-1776d240dade5ef1d254651ba53d0ab6d99e10ff.zip
feat(plugins): add plugin APIs to affect other panes (#3576)
* resize_pane_with_id and close_pane_with_id * focus_pane_with_id and edit_scrollback_for_pane_with_id * write_to_pane_id and write_chars_to_pane_id * lots more commands * style(fmt): rustfmt
-rw-r--r--default-plugins/fixture-plugin-for-tests/src/main.rs51
-rw-r--r--zellij-server/src/panes/floating_panes/mod.rs152
-rw-r--r--zellij-server/src/panes/grid.rs2
-rw-r--r--zellij-server/src/panes/terminal_pane.rs2
-rw-r--r--zellij-server/src/panes/tiled_panes/mod.rs597
-rw-r--r--zellij-server/src/plugins/unit/plugin_tests.rs1136
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__clear_screen_for_pane_id_plugin_command.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_tab_with_index_plugin_command.snap10
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__edit_scrollback_for_pane_with_id_plugin_command.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_pane_id_in_direction_plugin_command.snap13
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_pane_id_plugin_command.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_down_in_pane_id_plugin_command.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_up_in_pane_id_plugin_command.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_pane_with_id_plugin_command.snap19
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_down_in_pane_id_plugin_command.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_bottom_in_pane_id_plugin_command.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_top_in_pane_id_plugin_command.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_up_in_pane_id_plugin_command.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_embed_or_eject_for_pane_id_plugin_command.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_id_fullscreen_plugin_command.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_to_pane_id_plugin_command.snap18
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_to_pane_id_plugin_command.snap17
-rw-r--r--zellij-server/src/plugins/zellij_exports.rs166
-rw-r--r--zellij-server/src/pty.rs23
-rw-r--r--zellij-server/src/screen.rs274
-rw-r--r--zellij-server/src/tab/mod.rs430
-rw-r--r--zellij-server/src/unit/screen_tests.rs7
-rw-r--r--zellij-tile/src/shim.rs173
-rw-r--r--zellij-utils/assets/prost/api.plugin_command.rs194
-rw-r--r--zellij-utils/src/data.rs16
-rw-r--r--zellij-utils/src/errors.rs15
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.proto100
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.rs333
33 files changed, 3459 insertions, 421 deletions
diff --git a/default-plugins/fixture-plugin-for-tests/src/main.rs b/default-plugins/fixture-plugin-for-tests/src/main.rs
index b0915f29..85300612 100644
--- a/default-plugins/fixture-plugin-for-tests/src/main.rs
+++ b/default-plugins/fixture-plugin-for-tests/src/main.rs
@@ -364,6 +364,57 @@ impl ZellijPlugin for State {
BareKey::Char('d') if key.has_modifiers(&[KeyModifier::Alt]) => {
rerun_command_pane(1);
},
+ BareKey::Char('e') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ resize_pane_with_id(
+ ResizeStrategy::new(Resize::Increase, Some(Direction::Left)),
+ PaneId::Terminal(2),
+ );
+ },
+ BareKey::Char('f') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ edit_scrollback_for_pane_with_id(PaneId::Terminal(2));
+ },
+ BareKey::Char('g') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ write_to_pane_id(vec![102, 111, 111], PaneId::Terminal(2));
+ },
+ BareKey::Char('h') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ write_chars_to_pane_id("foo\n", PaneId::Terminal(2));
+ },
+ BareKey::Char('i') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ move_pane_with_pane_id(PaneId::Terminal(2));
+ },
+ BareKey::Char('j') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ move_pane_with_pane_id_in_direction(PaneId::Terminal(2), Direction::Left);
+ },
+ BareKey::Char('k') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ clear_screen_for_pane_id(PaneId::Terminal(2));
+ },
+ BareKey::Char('l') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ scroll_up_in_pane_id(PaneId::Terminal(2));
+ },
+ BareKey::Char('m') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ scroll_down_in_pane_id(PaneId::Terminal(2));
+ },
+ BareKey::Char('n') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ scroll_to_top_in_pane_id(PaneId::Terminal(2));
+ },
+ BareKey::Char('o') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ scroll_to_bottom_in_pane_id(PaneId::Terminal(2));
+ },
+ BareKey::Char('p') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ page_scroll_up_in_pane_id(PaneId::Terminal(2));
+ },
+ BareKey::Char('q') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ page_scroll_down_in_pane_id(PaneId::Terminal(2));
+ },
+ BareKey::Char('r') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ toggle_pane_id_fullscreen(PaneId::Terminal(2));
+ },
+ BareKey::Char('s') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ toggle_pane_embed_or_eject_for_pane_id(PaneId::Terminal(2));
+ },
+ BareKey::Char('t') if key.has_modifiers(&[KeyModifier::Alt]) => {
+ close_tab_with_index(2);
+ },
_ => {},
},
Event::CustomMessage(message, payload) => {
diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs
index 2b586bbc..1d65e749 100644
--- a/zellij-server/src/panes/floating_panes/mod.rs
+++ b/zellij-server/src/panes/floating_panes/mod.rs
@@ -408,34 +408,40 @@ impl FloatingPanes {
strategy: &ResizeStrategy,
) -> Result<bool> {
// true => successfully resized
- let err_context =
- || format!("failed to {strategy} for active floating pane for client {client_id}");
-
+ if let Some(active_floating_pane_id) = self.active_panes.get(&client_id) {
+ return self.resize_pane_with_id(*strategy, *active_floating_pane_id);
+ }
+ Ok(false)
+ }
+ pub fn resize_pane_with_id(
+ &mut self,
+ strategy: ResizeStrategy,
+ pane_id: PaneId,
+ ) -> Result<bool> {
+ // true => successfully resized
+ let err_context = || format!("Failed to resize pane with id: {:?}", pane_id);
let display_area = *self.display_area.borrow();
let viewport = *self.viewport.borrow();
- if let Some(active_floating_pane_id) = self.active_panes.get(&client_id) {
- let mut floating_pane_grid = FloatingPaneGrid::new(
- &mut self.panes,
- &mut self.desired_pane_positions,
- display_area,
- viewport,
- );
- floating_pane_grid
- .change_pane_size(
- active_floating_pane_id,
- strategy,
- (RESIZE_INCREMENT_WIDTH, RESIZE_INCREMENT_HEIGHT),
- )
- .with_context(err_context)?;
+ let mut floating_pane_grid = FloatingPaneGrid::new(
+ &mut self.panes,
+ &mut self.desired_pane_positions,
+ display_area,
+ viewport,
+ );
+ floating_pane_grid
+ .change_pane_size(
+ &pane_id,
+ &strategy,
+ (RESIZE_INCREMENT_WIDTH, RESIZE_INCREMENT_HEIGHT),
+ )
+ .with_context(err_context)?;
- for pane in self.panes.values_mut() {
- resize_pty!(pane, os_api, self.senders, self.character_cell_size)
- .with_context(err_context)?;
- }
- self.set_force_render();
- return Ok(true);
+ for pane in self.panes.values_mut() {
+ resize_pty!(pane, os_api, self.senders, self.character_cell_size)
+ .with_context(err_context)?;
}
- Ok(false)
+ self.set_force_render();
+ Ok(true)
}
fn set_pane_active_at(&mut self, pane_id: PaneId) {
@@ -545,61 +551,73 @@ impl FloatingPanes {
}
pub fn move_active_pane_down(&mut self, client_id: ClientId) {
- let display_area = *self.display_area.borrow();
- let viewport = *self.viewport.borrow();
if let Some(active_pane_id) = self.active_panes.get(&client_id) {
- let mut floating_pane_grid = FloatingPaneGrid::new(
- &mut self.panes,
- &mut self.desired_pane_positions,
- display_area,
- viewport,
- );
- floating_pane_grid.move_pane_down(active_pane_id).unwrap();
- self.set_force_render();
+ self.move_pane_down(*active_pane_id);
}
}
- pub fn move_active_pane_up(&mut self, client_id: ClientId) {
+ pub fn move_pane_down(&mut self, pane_id: PaneId) {
let display_area = *self.display_area.borrow();
let viewport = *self.viewport.borrow();
+ let mut floating_pane_grid = FloatingPaneGrid::new(
+ &mut self.panes,
+ &mut self.desired_pane_positions,
+ display_area,
+ viewport,
+ );
+ floating_pane_grid.move_pane_down(&pane_id).non_fatal();
+ self.set_force_render();
+ }
+ pub fn move_active_pane_up(&mut self, client_id: ClientId) {
if let Some(active_pane_id) = self.active_panes.get(&client_id) {
- let mut floating_pane_grid = FloatingPaneGrid::new(
- &mut self.panes,
- &mut self.desired_pane_positions,
- display_area,
- viewport,
- );
- floating_pane_grid.move_pane_up(active_pane_id).unwrap();
- self.set_force_render();
+ self.move_pane_up(*active_pane_id);
}
}
- pub fn move_active_pane_left(&mut self, client_id: ClientId) {
+ pub fn move_pane_up(&mut self, pane_id: PaneId) {
let display_area = *self.display_area.borrow();
let viewport = *self.viewport.borrow();
+ let mut floating_pane_grid = FloatingPaneGrid::new(
+ &mut self.panes,
+ &mut self.desired_pane_positions,
+ display_area,
+ viewport,
+ );
+ floating_pane_grid.move_pane_up(&pane_id).non_fatal();
+ self.set_force_render();
+ }
+ pub fn move_active_pane_left(&mut self, client_id: ClientId) {
if let Some(active_pane_id) = self.active_panes.get(&client_id) {
- let mut floating_pane_grid = FloatingPaneGrid::new(
- &mut self.panes,
- &mut self.desired_pane_positions,
- display_area,
- viewport,
- );
- floating_pane_grid.move_pane_left(active_pane_id).unwrap();
- self.set_force_render();
+ self.move_pane_left(*active_pane_id);
}
}
- pub fn move_active_pane_right(&mut self, client_id: ClientId) {
+ pub fn move_pane_left(&mut self, pane_id: PaneId) {
let display_area = *self.display_area.borrow();
let viewport = *self.viewport.borrow();
+ let mut floating_pane_grid = FloatingPaneGrid::new(
+ &mut self.panes,
+ &mut self.desired_pane_positions,
+ display_area,
+ viewport,
+ );
+ floating_pane_grid.move_pane_left(&pane_id).unwrap();
+ self.set_force_render();
+ }
+ pub fn move_active_pane_right(&mut self, client_id: ClientId) {
if let Some(active_pane_id) = self.active_panes.get(&client_id) {
- let mut floating_pane_grid = FloatingPaneGrid::new(
- &mut self.panes,
- &mut self.desired_pane_positions,
- display_area,
- viewport,
- );
- floating_pane_grid.move_pane_right(active_pane_id).unwrap();
- self.set_force_render();
+ self.move_pane_right(*active_pane_id);
}
}
+ pub fn move_pane_right(&mut self, pane_id: PaneId) {
+ let display_area = *self.display_area.borrow();
+ let viewport = *self.viewport.borrow();
+ let mut floating_pane_grid = FloatingPaneGrid::new(
+ &mut self.panes,
+ &mut self.desired_pane_positions,
+ display_area,
+ viewport,
+ );
+ floating_pane_grid.move_pane_right(&pane_id).unwrap();
+ self.set_force_render();
+ }
pub fn move_active_pane(
&mut self,
search_backwards: bool,
@@ -607,7 +625,9 @@ impl FloatingPanes {
client_id: ClientId,
) {
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
-
+ self.move_pane(search_backwards, active_pane_id)
+ }
+ pub fn move_pane(&mut self, search_backwards: bool, pane_id: PaneId) {
let new_position_id = {
let pane_grid = FloatingPaneGrid::new(
&mut self.panes,
@@ -616,13 +636,13 @@ impl FloatingPanes {
*self.viewport.borrow(),
);
if search_backwards {
- pane_grid.previous_selectable_pane_id(&active_pane_id)
+ pane_grid.previous_selectable_pane_id(&pane_id)
} else {
- pane_grid.next_selectable_pane_id(&active_pane_id)
+ pane_grid.next_selectable_pane_id(&pane_id)
}
};
if let Some(new_position_id) = new_position_id {
- let current_position = self.panes.get(&active_pane_id).unwrap();
+ let current_position = self.panes.get(&pane_id).unwrap();
let prev_geom = current_position.position_and_size();
let prev_geom_override = current_position.geom_override();
@@ -635,7 +655,7 @@ impl FloatingPanes {
}
new_position.set_should_render(true);
- let current_position = self.panes.get_mut(&active_pane_id).unwrap();
+ let current_position = self.panes.get_mut(&pane_id).unwrap();
current_position.set_geom(next_geom);
if let Some(geom) = next_geom_override {
current_position.set_geom_override(geom);
diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs
index 05d127a6..8a603f4b 100644
--- a/zellij-server/src/panes/grid.rs
+++ b/zellij-server/src/panes/grid.rs
@@ -1155,7 +1155,7 @@ impl Grid {
self.mark_for_rerender();
}
/// Dumps all lines above terminal vieport and the viewport itself to a string
- pub fn dump_screen(&mut self, full: bool) -> String {
+ pub fn dump_screen(&self, full: bool) -> String {
let viewport: String = dump_screen!(self.viewport);
if !full {
return viewport;
diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs
index 872928c3..c7e96eb6 100644
--- a/zellij-server/src/panes/terminal_pane.rs
+++ b/zellij-server/src/panes/terminal_pane.rs
@@ -515,7 +515,7 @@ impl Pane for TerminalPane {
self.geom.y -= count;
self.reflow_lines();
}
- fn dump_screen(&mut self, _client_id: ClientId, full: bool) -> String {
+ fn dump_screen(&self, full: bool) -> String {
self.grid.dump_screen(full)
}
fn clear_screen(&mut self) {
diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs
index 7a528f2b..931e2594 100644
--- a/zellij-server/src/panes/tiled_panes/mod.rs
+++ b/zellij-server/src/panes/tiled_panes/mod.rs
@@ -67,7 +67,7 @@ pub struct TiledPanes {
active_panes: ActivePanes,
draw_pane_frames: bool,
panes_to_hide: HashSet<PaneId>,
- fullscreen_is_active: bool,
+ fullscreen_is_active: Option<PaneId>,
senders: ThreadSenders,
window_title: Option<String>,
client_id_to_boundaries: HashMap<ClientId, Boundaries>,
@@ -103,7 +103,7 @@ impl TiledPanes {
active_panes: ActivePanes::new(&os_api),
draw_pane_frames,
panes_to_hide: HashSet::new(),
- fullscreen_is_active: false,
+ fullscreen_is_active: None,
senders,
window_title: None,
client_id_to_boundaries: HashMap::new(),
@@ -833,56 +833,57 @@ impl TiledPanes {
client_id: ClientId,
strategy: &ResizeStrategy,
) -> Result<()> {
- let err_context =
- || format!("failed to {strategy} for active tiled pane for client {client_id}");
-
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
- let mut pane_grid = TiledPaneGrid::new(
- &mut self.panes,
- &self.panes_to_hide,
- *self.display_area.borrow(),
- *self.viewport.borrow(),
- );
+ self.resize_pane_with_id(*strategy, active_pane_id)?;
+ }
- match pane_grid
- .change_pane_size(&active_pane_id, strategy, (RESIZE_PERCENT, RESIZE_PERCENT))
- .with_context(err_context)
- {
- Ok(_) => {},
- Err(err) => match err.downcast_ref::<ZellijError>() {
- Some(ZellijError::PaneSizeUnchanged) => {
- // try once more with double the resize percent, but let's keep it at that
- match pane_grid
- .change_pane_size(
- &active_pane_id,
- strategy,
- (RESIZE_PERCENT * 2.0, RESIZE_PERCENT * 2.0),
- )
- .with_context(err_context)
- {
- Ok(_) => {},
- Err(err) => match err.downcast_ref::<ZellijError>() {
- Some(ZellijError::PaneSizeUnchanged) => {
- Err::<(), _>(err).non_fatal()
- },
- _ => {
- return Err(err);
- },
+ Ok(())
+ }
+ pub fn resize_pane_with_id(&mut self, strategy: ResizeStrategy, pane_id: PaneId) -> Result<()> {
+ let err_context = || format!("failed to resize pand with id: {:?}", pane_id);
+
+ let mut pane_grid = TiledPaneGrid::new(
+ &mut self.panes,
+ &self.panes_to_hide,
+ *self.display_area.borrow(),
+ *self.viewport.borrow(),
+ );
+
+ match pane_grid
+ .change_pane_size(&pane_id, &strategy, (RESIZE_PERCENT, RESIZE_PERCENT))
+ .with_context(err_context)
+ {
+ Ok(_) => {},
+ Err(err) => match err.downcast_ref::<ZellijError>() {
+ Some(ZellijError::PaneSizeUnchanged) => {
+ // try once more with double the resize percent, but let's keep it at that
+ match pane_grid
+ .change_pane_size(
+ &pane_id,
+ &strategy,
+ (RESIZE_PERCENT * 2.0, RESIZE_PERCENT * 2.0),
+ )
+ .with_context(err_context)
+ {
+ Ok(_) => {},
+ Err(err) => match err.downcast_ref::<ZellijError>() {
+ Some(ZellijError::PaneSizeUnchanged) => Err::<(), _>(err).non_fatal(),
+ _ => {
+ return Err(err);
},
- }
- },
- _ => {
- return Err(err);
- },
+ },
+ }
},
- }
-
- for pane in self.panes.values_mut() {
- resize_pty!(pane, self.os_api, self.senders, self.character_cell_size).unwrap();
- }
- self.reset_boundaries();
+ _ => {
+ return Err(err);
+ },
+ },
}
+ for pane in self.panes.values_mut() {
+ resize_pty!(pane, self.os_api, self.senders, self.character_cell_size).unwrap();
+ }
+ self.reset_boundaries();
Ok(())
}
@@ -1225,7 +1226,9 @@ impl TiledPanes {
}
pub fn move_active_pane(&mut self, search_backwards: bool, client_id: ClientId) {
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
-
+ self.move_pane(search_backwards, active_pane_id)
+ }
+ pub fn move_pane(&mut self, search_backwards: bool, pane_id: PaneId) {
let new_position_id = {
let pane_grid = TiledPaneGrid::new(
&mut self.panes,
@@ -1234,9 +1237,9 @@ impl TiledPanes {
*self.viewport.borrow(),
);
if search_backwards {
- pane_grid.previous_selectable_pane_id(&active_pane_id)
+ pane_grid.previous_selectable_pane_id(&pane_id)
} else {
- pane_grid.next_selectable_pane_id(&active_pane_id)
+ pane_grid.next_selectable_pane_id(&pane_id)
}
};
if self
@@ -1250,7 +1253,7 @@ impl TiledPanes {
self.reapply_pane_frames();
}
- let current_position = self.panes.get(&active_pane_id).unwrap();
+ let current_position = self.panes.get(&pane_id).unwrap();
let prev_geom = current_position.position_and_size();
let prev_geom_override = current_position.geom_override();
@@ -1270,7 +1273,7 @@ impl TiledPanes {
.unwrap();
new_position.set_should_render(true);
- let current_position = self.panes.get_mut(&active_pane_id).unwrap();
+ let current_position = self.panes.get_mut(&pane_id).unwrap();
current_position.set_geom(next_geom);
if let Some(geom) = next_geom_override {
current_position.set_geom_override(geom);
@@ -1283,202 +1286,215 @@ impl TiledPanes {
)
.unwrap();
current_position.set_should_render(true);
+ self.reapply_pane_focus();
self.set_pane_frames(self.draw_pane_frames);
}
pub fn move_active_pane_down(&mut self, client_id: ClientId) {
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
- let mut pane_grid = TiledPaneGrid::new(
- &mut self.panes,
- &self.panes_to_hide,
- *self.display_area.borrow(),
- *self.viewport.borrow(),
- );
- let next_index = pane_grid
- .next_selectable_pane_id_below(&active_pane_id)
- .or_else(|| pane_grid.progress_stack_down_if_in_stack(&active_pane_id));
- if let Some(p) = next_index {
- let active_pane_id = self.active_panes.get(&client_id).unwrap();
- let current_position = self.panes.get(active_pane_id).unwrap();
- let prev_geom = current_position.position_and_size();
- let prev_geom_override = current_position.geom_override();
+ self.move_pane_down(active_pane_id);
+ }
+ }
+ pub fn move_pane_down(&mut self, pane_id: PaneId) {
+ let mut pane_grid = TiledPaneGrid::new(
+ &mut self.panes,
+ &self.panes_to_hide,
+ *self.display_area.borrow(),
+ *self.viewport.borrow(),
+ );
+ let next_index = pane_grid
+ .next_selectable_pane_id_below(&pane_id)
+ .or_else(|| pane_grid.progress_stack_down_if_in_stack(&pane_id));
+ if let Some(p) = next_index {
+ let current_position = self.panes.get(&pane_id).unwrap();
+ let prev_geom = current_position.position_and_size();
+ let prev_geom_override = current_position.geom_override();
- let new_position = self.panes.get_mut(&p).unwrap();
- let next_geom = new_position.position_and_size();
- let next_geom_override = new_position.geom_override();
- new_position.set_geom(prev_geom);
- if let Some(geom) = prev_geom_override {
- new_position.set_geom_override(geom);
- }
- resize_pty!(
- new_position,
- self.os_api,
- self.senders,
- self.character_cell_size
- )
- .unwrap();
- new_position.set_should_render(true);
+ let new_position = self.panes.get_mut(&p).unwrap();
+ let next_geom = new_position.position_and_size();
+ let next_geom_override = new_position.geom_override();
+ new_position.set_geom(prev_geom);
+ if let Some(geom) = prev_geom_override {
+ new_position.set_geom_override(geom);
+ }
+ resize_pty!(
+ new_position,
+ self.os_api,
+ self.senders,
+ self.character_cell_size
+ )
+ .unwrap();
+ new_position.set_should_render(true);
- let current_position = self.panes.get_mut(active_pane_id).unwrap();
- current_position.set_geom(next_geom);
- if let Some(geom) = next_geom_override {
- current_position.set_geom_override(geom);
- }
- resize_pty!(
- current_position,
- self.os_api,
- self.senders,
- self.character_cell_size
- )
- .unwrap();
- current_position.set_should_render(true);
- self.set_pane_frames(self.draw_pane_frames);
+ let current_position = self.panes.get_mut(&pane_id).unwrap();
+ current_position.set_geom(next_geom);
+ if let Some(geom) = next_geom_override {
+ current_position.set_geom_override(geom);
}
+ resize_pty!(
+ current_position,
+ self.os_api,
+ self.senders,
+ self.character_cell_size
+ )
+ .unwrap();
+ current_position.set_should_render(true);
+ self.reapply_pane_focus();
+ self.set_pane_frames(self.draw_pane_frames);
}
}
pub fn move_active_pane_left(&mut self, client_id: ClientId) {
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
- let pane_grid = TiledPaneGrid::new(
- &mut self.panes,
- &self.panes_to_hide,
- *self.display_area.borrow(),
- *self.viewport.borrow(),
- );
- let next_index = pane_grid.next_selectable_pane_id_to_the_left(&active_pane_id);
- if let Some(p) = next_index {
- let active_pane_id = self.active_panes.get(&client_id).unwrap();
- let current_position = self.panes.get(active_pane_id).unwrap();
- let prev_geom = current_position.position_and_size();
- let prev_geom_override = current_position.geom_override();
+ self.move_pane_left(active_pane_id);
+ }
+ }
+ pub fn move_pane_left(&mut self, pane_id: PaneId) {
+ let pane_grid = TiledPaneGrid::new(
+ &mut self.panes,
+ &self.panes_to_hide,
+ *self.display_area.borrow(),
+ *self.viewport.borrow(),
+ );
+ let next_index = pane_grid.next_selectable_pane_id_to_the_left(&pane_id);
+ if let Some(p) = next_index {
+ let current_position = self.panes.get(&pane_id).unwrap();
+ let prev_geom = current_position.position_and_size();
+ let prev_geom_override = current_position.geom_override();
- let new_position = self.panes.get_mut(&p).unwrap();
- let next_geom = new_position.position_and_size();
- let next_geom_override = new_position.geom_override();
- new_position.set_geom(prev_geom);
- if let Some(geom) = prev_geom_override {
- new_position.set_geom_override(geom);
- }
- resize_pty!(
- new_position,
- self.os_api,
- self.senders,
- self.character_cell_size
- )
- .unwrap();
- new_position.set_should_render(true);
+ let new_position = self.panes.get_mut(&p).unwrap();
+ let next_geom = new_position.position_and_size();
+ let next_geom_override = new_position.geom_override();
+ new_position.set_geom(prev_geom);
+ if let Some(geom) = prev_geom_override {
+ new_position.set_geom_override(geom);
+ }
+ resize_pty!(
+ new_position,
+ self.os_api,
+ self.senders,
+ self.character_cell_size
+ )
+ .unwrap();
+ new_position.set_should_render(true);
- let current_position = self.panes.get_mut(active_pane_id).unwrap();
- current_position.set_geom(next_geom);
- if let Some(geom) = next_geom_override {
- current_position.set_geom_override(geom);
- }
- resize_pty!(
- current_position,
- self.os_api,
- self.senders,
- self.character_cell_size
- )
- .unwrap();
- current_position.set_should_render(true);
- self.set_pane_frames(self.draw_pane_frames);
+ let current_position = self.panes.get_mut(&pane_id).unwrap();
+ current_position.set_geom(next_geom);
+ if let Some(geom) = next_geom_override {
+ current_position.set_geom_override(geom);
}
+ resize_pty!(
+ current_position,
+ self.os_api,
+ self.senders,
+ self.character_cell_size
+ )
+ .unwrap();
+ current_position.set_should_render(true);
+ self.reapply_pane_focus();
+ self.set_pane_frames(self.draw_pane_frames);
}
}
pub fn move_active_pane_right(&mut self, client_id: ClientId) {
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
- let pane_grid = TiledPaneGrid::new(
- &mut self.panes,
- &self.panes_to_hide,
- *self.display_area.borrow(),
- *self.viewport.borrow(),
- );
- let next_index = pane_grid.next_selectable_pane_id_to_the_right(&active_pane_id);
- if let Some(p) = next_index {
- let active_pane_id = self.active_panes.get(&client_id).unwrap();
- let current_position = self.panes.get(active_pane_id).unwrap();
- let prev_geom = current_position.position_and_size();
- let prev_geom_override = current_position.geom_override();
+ self.move_pane_right(active_pane_id);
+ }
+ }
+ pub fn move_pane_right(&mut self, pane_id: PaneId) {
+ let pane_grid = TiledPaneGrid::new(
+ &mut self.panes,
+ &self.panes_to_hide,
+ *self.display_area.borrow(),
+ *self.viewport.borrow(),
+ );
+ let next_index = pane_grid.next_selectable_pane_id_to_the_right(&pane_id);
+ if let Some(p) = next_index {
+ let current_position = self.panes.get(&pane_id).unwrap();
+ let prev_geom = current_position.position_and_size();
+ let prev_geom_override = current_position.geom_override();
- let new_position = self.panes.get_mut(&p).unwrap();
- let next_geom = new_position.position_and_size();
- let next_geom_override = new_position.geom_override();
- new_position.set_geom(prev_geom);
- if let Some(geom) = prev_geom_override {
- new_position.set_geom_override(geom);
- }
- resize_pty!(
- new_position,
- self.os_api,
- self.senders,
- self.character_cell_size
- )
- .unwrap();
- new_position.set_should_render(true);
+ let new_position = self.panes.get_mut(&p).unwrap();
+ let next_geom = new_position.position_and_size();
+ let next_geom_override = new_position.geom_override();
+ new_position.set_geom(prev_geom);
+ if let Some(geom) = prev_geom_override {
+ new_position.set_geom_override(geom);
+ }
+ resize_pty!(
+ new_position,
+ self.os_api,
+ self.senders,
+ self.character_cell_size
+ )
+ .unwrap();
+ new_position.set_should_render(true);
- let current_position = self.panes.get_mut(active_pane_id).unwrap();
- current_position.set_geom(next_geom);
- if let Some(geom) = next_geom_override {
- current_position.set_geom_override(geom);
- }
- resize_pty!(
- current_position,
- self.os_api,
- self.senders,
- self.character_cell_size
- )
- .unwrap();
- current_position.set_should_render(true);
- self.set_pane_frames(self.draw_pane_frames);
+ let current_position = self.panes.get_mut(&pane_id).unwrap();
+ current_position.set_geom(next_geom);
+ if let Some(geom) = next_geom_override {
+ current_position.set_geom_override(geom);
}
+ resize_pty!(
+ current_position,
+ self.os_api,
+ self.senders,
+ self.character_cell_size
+ )
+ .unwrap();
+ current_position.set_should_render(true);
+ self.reapply_pane_focus();
+ self.set_pane_frames(self.draw_pane_frames);
}
}
pub fn move_active_pane_up(&mut self, client_id: ClientId) {
if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
- let mut pane_grid = TiledPaneGrid::new(
- &mut self.panes,
- &self.panes_to_hide,
- *self.display_area.borrow(),
- *self.viewport.borrow(),
- );
- let next_index = pane_grid
- .next_selectable_pane_id_above(&active_pane_id)
- .or_else(|| pane_grid.progress_stack_up_if_in_stack(&active_pane_id));
- if let Some(p) = next_index {
- let active_pane_id = self.active_panes.get(&client_id).unwrap();
- let current_position = self.panes.get(active_pane_id).unwrap();
- let prev_geom = current_position.position_and_size();
- let prev_geom_override = current_position.geom_override();
+ self.move_pane_up(active_pane_id);
+ }
+ }
+ pub fn move_pane_up(&mut self, pane_id: PaneId) {
+ let mut pane_grid = TiledPaneGrid::new(
+ &mut self.panes,
+ &self.panes_to_hide,
+ *self.display_area.borrow(),
+ *self.viewport.borrow(),
+ );
+ let next_index = pane_grid
+ .next_selectable_pane_id_above(&pane_id)
+ .or_else(|| pane_grid.progress_stack_up_if_in_stack(&pane_id));
+ if let Some(p) = next_index {
+ let current_position = self.panes.get(&pane_id).unwrap();
+ let prev_geom = current_position.position_and_size();
+ let prev_geom_override = current_position.geom_override();
- let new_position = self.panes.get_mut(&p).unwrap();
- let next_geom = new_position.position_and_size();
- let next_geom_override = new_position.geom_override();
- new_position.set_geom(prev_geom);
- if let Some(geom) = prev_geom_override {
- new_position.set_geom_override(geom);
- }
- resize_pty!(
- new_position,
- self.os_api,
- self.senders,
- self.character_cell_size
- )
- .unwrap();
- new_position.set_should_render(true);
+ let new_position = self.panes.get_mut(&p).unwrap();
+ let next_geom = new_position.position_and_size();
+ let next_geom_override = new_position.geom_override();
+ new_position.set_geom(prev_geom);
+ if let Some(geom) = prev_geom_override {
+ new_position.set_geom_override(geom);
+ }
+ resize_pty!(
+ new_position,
+ self.os_api,
+ self.senders,
+ self.character_cell_size
+ )
+ .unwrap();
+ new_position.set_should_render(true);
- let current_position = self.panes.get_mut(active_pane_id).unwrap();
- current_position.set_geom(next_geom);
- if let Some(geom) = next_geom_override {
- current_position.set_geom_override(geom);
- }
- resize_pty!(
- current_position,
- self.os_api,
- self.senders,
- self.character_cell_size
- )
- .unwrap();
- current_position.set_should_render(true);
- self.set_pane_frames(self.draw_pane_frames);
+ let current_position = self.panes.get_mut(&pane_id).unwrap();
+ current_position.set_geom(next_geom);
+ if let Some(geom) = next_geom_override {
+ current_position.set_geom_override(geom);
}
+ resize_pty!(
+ current_position,
+ self.os_api,
+ self.senders,
+ self.character_cell_size
+ )
+ .unwrap();
+ current_position.set_should_render(true);
+ self.reapply_pane_focus();
+ self.set_pane_frames(self.draw_pane_frames);
}
}
pub fn move_clients_out_of_pane(&mut self, pane_id: PaneId) {
@@ -1565,23 +1581,65 @@ impl TiledPanes {
self.panes_to_hide.contains(&pane_id)
}
pub fn fullscreen_is_active(&self) -> bool {
- self.fullscreen_is_active
+ self.fullscreen_is_active.is_some()
}
pub fn unset_fullscreen(&mut self) {
- if self.fullscreen_is_active {
- let first_client_id = {
- let connected_clients = self.connected_clients.borrow();
- connected_clients.iter().next().copied()
- };
- if let Some(active_pane_id) =
- first_client_id.and_then(|first_client_id| self.get_active_pane_id(first_client_id))
- {
- let panes_to_hide: Vec<_> = self.panes_to_hide.iter().copied().collect();
- for pane_id in panes_to_hide {
- let pane = self.get_pane_mut(pane_id).unwrap();
- pane.set_should_render(true);
- pane.set_should_render_boundaries(true);
+ if let Some(fullscreen_pane_id) = self.fullscreen_is_active {
+ let panes_to_hide: Vec<_> = self.panes_to_hide.iter().copied().collect();
+ for pane_id in panes_to_hide {
+ let pane = self.get_pane_mut(pane_id).unwrap();
+ pane.set_should_render(true);
+ pane.set_should_render_boundaries(true);
+ }
+ let viewport_pane_ids: Vec<_> = self
+ .panes
+ .keys()
+ .copied()
+ .into_iter()
+ .filter(|id| {
+ !is_inside_viewport(&*self.viewport.borrow(), self.get_pane(*id).unwrap())
+ })
+ .collect();
+ for pid in viewport_pane_ids {
+ let viewport_pane = self.get_pane_mut(pid).unwrap();
+ viewport_pane.reset_size_and_position_override();
+ }
+ self.panes_to_hide.clear();
+ let fullscreen_pane = self.get_pane_mut(fullscreen_pane_id).unwrap();
+ fullscreen_pane.reset_size_and_position_override();
+ self.set_force_render();
+ let display_area = *self.display_area.borrow();
+ self.resize(display_area);
+ self.fullscreen_is_active = None;
+ }
+ }
+ pub fn toggle_active_pane_fullscreen(&mut self, client_id: ClientId) {
+ if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
+ self.toggle_pane_fullscreen(active_pane_id);
+ }
+ }
+
+ pub fn toggle_pane_fullscreen(&mut self, pane_id: PaneId) {
+ if self.fullscreen_is_active.is_some() {
+ self.unset_fullscreen();
+ } else {
+ let pane_ids_to_hide = self.panes.iter().filter_map(|(&id, _pane)| {
+ if id != pane_id
+ && is_inside_viewport(&*self.viewport.borrow(), self.get_pane(id).unwrap())
+ {
+ Some(id)
+ } else {
+ None
}
+ });
+ self.panes_to_hide = pane_ids_to_hide.collect();
+ if self.panes_to_hide.is_empty() {
+ // nothing to do, pane is already as fullscreen as it can be, let's bail
+ return;
+ } else {
+ // For all of the panes outside of the viewport staying on the fullscreen
+ // screen, switch them to using override positions as well so that the resize
+ // system doesn't get confused by viewport and old panes that no longer line up
let viewport_pane_ids: Vec<_> = self
.panes
.keys()
@@ -1592,59 +1650,12 @@ impl TiledPanes {
})
.collect();
for pid in viewport_pane_ids {
- let viewport_pane = self.get_pane_mut(pid).unwrap();
- viewport_pane.reset_size_and_position_override();
- }
- self.panes_to_hide.clear();
- let active_terminal = self.get_pane_mut(active_pane_id).unwrap();
- active_terminal.reset_size_and_position_override();
- self.set_force_render();
- let display_area = *self.display_area.borrow();
- self.resize(display_area);
- self.fullscreen_is_active = false;
- }
- }
- }
- pub fn toggle_active_pane_fullscreen(&mut self, client_id: ClientId) {
- if let Some(active_pane_id) = self.get_active_pane_id(client_id) {
- if self.fullscreen_is_active {
- self.unset_fullscreen();
- } else {
- let pane_ids_to_hide = self.panes.iter().filter_map(|(&id, _pane)| {
- if id != active_pane_id
- && is_inside_viewport(&*self.viewport.borrow(), self.get_pane(id).unwrap())
- {
- Some(id)
- } else {
- None
- }
- });
- self.panes_to_hide = pane_ids_to_hide.collect();
- if self.panes_to_hide.is_empty() {
- // nothing to do, pane is already as fullscreen as it can be, let's bail
- return;
- } else {
- // For all of the panes outside of the viewport staying on the fullscreen
- // screen, switch them to using override positions as well so that the resize
- // system doesn't get confused by viewport and old panes that no longer line up
- let viewport_pane_ids: Vec<_> = self
- .panes
- .keys()
- .copied()
- .into_iter()
- .filter(|id| {
- !is_inside_viewport(
- &*self.viewport.borrow(),
- self.get_pane(*id).unwrap(),
- )
- })
- .collect();
- for pid in viewport_pane_ids {
- let viewport_pane = self.get_pane_mut(pid).unwrap();
+ if let Some(viewport_pane) = self.get_pane_mut(pid) {
viewport_pane.set_geom_override(viewport_pane.position_and_size());
}
- let viewport = { *self.viewport.borrow() };
- let active_pane = self.get_pane_mut(active_pane_id).unwrap();
+ }
+ let viewport = { *self.viewport.borrow() };
+ if let Some(active_pane) = self.get_pane_mut(pane_id) {
let full_screen_geom = PaneGeom {
x: viewport.x,
y: viewport.y,
@@ -1652,16 +1663,16 @@ impl TiledPanes {
};
active_pane.set_geom_override(full_screen_geom);
}
- let connected_client_list: Vec<ClientId> =
- { self.connected_clients.borrow().iter().copied().collect() };
- for client_id in connected_client_list {
- self.focus_pane(active_pane_id, client_id);
- }
- self.set_force_render();
- let display_area = *self.display_area.borrow();
- self.resize(display_area);
- self.fullscreen_is_active = true;
}
+ let connected_client_list: Vec<ClientId> =
+ { self.connected_clients.borrow().iter().copied().collect() };
+ for client_id in connected_client_list {
+ self.focus_pane(pane_id, client_id);
+ }
+ self.set_force_render();
+ let display_area = *self.display_area.borrow();
+ self.resize(display_area);
+ self.fullscreen_is_active = Some(pane_id);
}
}
diff --git a/zellij-server/src/plugins/unit/plugin_tests.rs b/zellij-server/src/plugins/unit/plugin_tests.rs
index 18df6159..392985a1 100644
--- a/zellij-server/src/plugins/unit/plugin_tests.rs
+++ b/zellij-server/src/plugins/unit/plugin_tests.rs
@@ -6984,3 +6984,1139 @@ pub fn rerun_command_pane_plugin_command() {
.clone();
assert_snapshot!(format!("{:#?}", rerun_command_pane_event));
}
+
+#[test]
+#[ignore]
+pub fn resize_pane_with_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::ResizePaneWithId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('e')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let rerun_command_pane_event = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::ResizePaneWithId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", rerun_command_pane_event));
+}
+
+#[test]
+#[ignore]
+pub fn edit_scrollback_for_pane_with_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::EditScrollbackForPaneWithId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('f')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let rerun_command_pane_event = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::EditScrollbackForPaneWithId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", rerun_command_pane_event));
+}
+
+#[test]
+#[ignore]
+pub fn write_to_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::WriteToPaneId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('g')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let rerun_command_pane_event = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::WriteToPaneId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", rerun_command_pane_event));
+}
+
+#[test]
+#[ignore]
+pub fn write_chars_to_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::WriteToPaneId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('h')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let rerun_command_pane_event = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::WriteToPaneId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", rerun_command_pane_event));
+}
+
+#[test]
+#[ignore]
+pub fn move_pane_with_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::MovePaneWithPaneId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('i')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::MovePaneWithPaneId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn move_pane_with_pane_id_in_direction_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::MovePaneWithPaneIdInDirection,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('j')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::MovePaneWithPaneIdInDirection(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn clear_screen_for_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::ClearScreenForPaneId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('k')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::ClearScreenForPaneId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn scroll_up_in_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::ScrollUpInPaneId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('l')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::ScrollUpInPaneId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn scroll_down_in_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::ScrollDownInPaneId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('m')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::ScrollDownInPaneId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn scroll_to_top_in_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::ScrollToTopInPaneId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('n')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::ScrollToTopInPaneId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn scroll_to_bottom_in_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::ScrollToBottomInPaneId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('o')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::ScrollToBottomInPaneId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn page_scroll_up_in_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::PageScrollUpInPaneId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('p')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::PageScrollUpInPaneId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn page_scroll_down_in_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::PageScrollDownInPaneId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('q')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::PageScrollDownInPaneId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn toggle_pane_id_fullscreen_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::TogglePaneIdFullscreen,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('r')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::TogglePaneIdFullscreen(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn toggle_pane_embed_or_eject_for_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::TogglePaneEmbedOrEjectForPaneId,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('s')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::TogglePaneEmbedOrEjectForPaneId(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn close_tab_with_index_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::CloseTabWithIndex,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ None,
+ client_id,
+ size,
+ None,
+ false,
+ ));
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('t')).with_alt_modifier()), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let screen_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::CloseTabWithIndex(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", screen_instruction));
+}
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__clear_screen_for_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__clear_screen_for_pane_id_plugin_command.snap
new file mode 100644
index 00000000..bd9a6796
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__clear_screen_for_pane_id_plugin_command.snap
@@ -0,0 +1,12 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7482
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ ClearScreenForPaneId(
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_tab_with_index_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_tab_with_index_plugin_command.snap
new file mode 100644
index 00000000..32b2ac8a
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_tab_with_index_plugin_command.snap
@@ -0,0 +1,10 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 8121
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ CloseTabWithIndex(
+ 2,
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__edit_scrollback_for_pane_with_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__edit_scrollback_for_pane_with_id_plugin_command.snap
new file mode 100644
index 00000000..47285b57
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__edit_scrollback_for_pane_with_id_plugin_command.snap
@@ -0,0 +1,12 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7127
+expression: "format!(\"{:#?}\", rerun_command_pane_event)"
+---
+Some(
+ EditScrollbackForPaneWithId(
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_pane_id_in_direction_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_pane_id_in_direction_plugin_command.snap
new file mode 100644
index 00000000..5fedc503
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_pane_id_in_direction_plugin_command.snap
@@ -0,0 +1,13 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7411
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ MovePaneWithPaneIdInDirection(
+ Terminal(
+ 2,
+ ),
+ Left,
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_pane_id_plugin_command.snap
new file mode 100644
index 00000000..ed8d393b
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_pane_id_plugin_command.snap
@@ -0,0 +1,12 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7340
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ MovePaneWithPaneId(
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_down_in_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_down_in_pane_id_plugin_command.snap
new file mode 100644
index 00000000..e436c892
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_down_in_pane_id_plugin_command.snap
@@ -0,0 +1,12 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7908
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ PageScrollDownInPaneId(
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_up_in_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_up_in_pane_id_plugin_command.snap
new file mode 100644
index 00000000..31c9d2f9
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_up_in_pane_id_plugin_command.snap
@@ -0,0 +1,12 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7837
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ PageScrollUpInPaneId(
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_pane_with_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_pane_with_id_plugin_command.snap
new file mode 100644
index 00000000..69c25e26
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_pane_with_id_plugin_command.snap
@@ -0,0 +1,19 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7056
+expression: "format!(\"{:#?}\", rerun_command_pane_event)"
+---
+Some(
+ ResizePaneWithId(
+ ResizeStrategy {
+ resize: Increase,
+ direction: Some(
+ Left,
+ ),
+ invert_on_boundaries: false,
+ },
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_down_in_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_down_in_pane_id_plugin_command.snap
new file mode 100644
index 00000000..df8f5b6f
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_down_in_pane_id_plugin_command.snap
@@ -0,0 +1,12 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7624
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ ScrollDownInPaneId(
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_bottom_in_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_bottom_in_pane_id_plugin_command.snap
new file mode 100644
index 00000000..3a72d183
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_bottom_in_pane_id_plugin_command.snap
@@ -0,0 +1,12 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7766
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ ScrollToBottomInPaneId(
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_top_in_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_top_in_pane_id_plugin_command.snap
new file mode 100644
index 00000000..bb111aea
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_top_in_pane_id_plugin_command.snap
@@ -0,0 +1,12 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7695
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ ScrollToTopInPaneId(
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_up_in_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_up_in_pane_id_plugin_command.snap
new file mode 100644
index 00000000..f6de5eb8
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_up_in_pane_id_plugin_command.snap
@@ -0,0 +1,12 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7553
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ ScrollUpInPaneId(
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_embed_or_eject_for_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_embed_or_eject_for_pane_id_plugin_command.snap
new file mode 100644
index 00000000..816c3576
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_embed_or_eject_for_pane_id_plugin_command.snap
@@ -0,0 +1,12 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 8050
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ TogglePaneEmbedOrEjectForPaneId(
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_id_fullscreen_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_id_fullscreen_plugin_command.snap
new file mode 100644
index 00000000..1d64a184
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_id_fullscreen_plugin_command.snap
@@ -0,0 +1,12 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7979
+expression: "format!(\"{:#?}\", screen_instruction)"
+---
+Some(
+ TogglePaneIdFullscreen(
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_to_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_to_pane_id_plugin_command.snap
new file mode 100644
index 00000000..60904fab
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_to_pane_id_plugin_command.snap
@@ -0,0 +1,18 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7269
+expression: "format!(\"{:#?}\", rerun_command_pane_event)"
+---
+Some(
+ WriteToPaneId(
+ [
+ 102,
+ 111,
+ 111,
+ 10,
+ ],
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_to_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_to_pane_id_plugin_command.snap
new file mode 100644
index 00000000..6d9b051e
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_to_pane_id_plugin_command.snap
@@ -0,0 +1,17 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 7198
+expression: "format!(\"{:#?}\", rerun_command_pane_event)"
+---
+Some(
+ WriteToPaneId(
+ [
+ 102,
+ 111,
+ 111,
+ ],
+ Terminal(
+ 2,
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs
index 26b6388a..89093c50 100644
--- a/zellij-server/src/plugins/zellij_exports.rs
+++ b/zellij-server/src/plugins/zellij_exports.rs
@@ -272,6 +272,54 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) {
PluginCommand::RerunCommandPane(terminal_pane_id) => {
rerun_command_pane(env, terminal_pane_id)
},
+ PluginCommand::ResizePaneIdWithDirection(resize, pane_id) => {
+ resize_pane_with_id(env, resize, pane_id.into())
+ },
+ PluginCommand::EditScrollbackForPaneWithId(pane_id) => {
+ edit_scrollback_for_pane_with_id(env, pane_id.into())
+ },
+ PluginCommand::WriteToPaneId(bytes, pane_id) => {
+ write_to_pane_id(env, bytes, pane_id.into())
+ },
+ PluginCommand::WriteCharsToPaneId(chars, pane_id) => {
+ write_chars_to_pane_id(env, chars, pane_id.into())
+ },
+ PluginCommand::MovePaneWithPaneId(pane_id) => {
+ move_pane_with_pane_id(env, pane_id.into())
+ },
+ PluginCommand::MovePaneWithPaneIdInDirection(pane_id, direction) => {
+ move_pane_with_pane_id_in_direction(env, pane_id.into(), direction)
+ },
+ PluginCommand::ClearScreenForPaneId(pane_id) => {
+ clear_screen_for_pane_id(env, pane_id.into())
+ },
+ PluginCommand::ScrollUpInPaneId(pane_id) => {
+ scroll_up_in_pane_id(env, pane_id.into())
+ },
+ PluginCommand::ScrollDownInPaneId(pane_id) => {
+ scroll_down_in_pane_id(env, pane_id.into())
+ },
+ PluginCommand::ScrollToTopInPaneId(pane_id) => {
+ scroll_to_top_in_pane_id(env, pane_id.into())
+ },
+ PluginCommand::ScrollToBottomInPaneId(pane_id) => {
+ scroll_to_bottom_in_pane_id(env, pane_id.into())
+ },
+ PluginCommand::PageScrollUpInPaneId(pane_id) => {
+ page_scroll_up_in_pane_id(env, pane_id.into())
+ },
+ PluginCommand::PageScrollDownInPaneId(pane_id) => {
+ page_scroll_down_in_pane_id(env, pane_id.into())
+ },
+ PluginCommand::TogglePaneIdFullscreen(pane_id) => {
+ toggle_pane_id_fullscreen(env, pane_id.into())
+ },
+ PluginCommand::TogglePaneEmbedOrEjectForPaneId(pane_id) => {
+ toggle_pane_embed_or_eject_for_pane_id(env, pane_id.into())
+ },
+ PluginCommand::CloseTabWithIndex(tab_index) => {
+ close_tab_with_index(env, tab_index)
+ },
},
(PermissionStatus::Denied, permission) => {
log::error!(
@@ -1442,6 +1490,105 @@ fn scan_host_folder(env: &PluginEnv, folder_to_scan: PathBuf) {
}
}
+fn resize_pane_with_id(env: &PluginEnv, resize: ResizeStrategy, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::ResizePaneWithId(resize, pane_id));
+}
+
+fn edit_scrollback_for_pane_with_id(env: &PluginEnv, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::EditScrollbackForPaneWithId(pane_id));
+}
+
+fn write_to_pane_id(env: &PluginEnv, bytes: Vec<u8>, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::WriteToPaneId(bytes, pane_id));
+}
+
+fn write_chars_to_pane_id(env: &PluginEnv, chars: String, pane_id: PaneId) {
+ let bytes = chars.into_bytes();
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::WriteToPaneId(bytes, pane_id));
+}
+
+fn move_pane_with_pane_id(env: &PluginEnv, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::MovePaneWithPaneId(pane_id));
+}
+
+fn move_pane_with_pane_id_in_direction(env: &PluginEnv, pane_id: PaneId, direction: Direction) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::MovePaneWithPaneIdInDirection(
+ pane_id, direction,
+ ));
+}
+
+fn clear_screen_for_pane_id(env: &PluginEnv, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::ClearScreenForPaneId(pane_id));
+}
+
+fn scroll_up_in_pane_id(env: &PluginEnv, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::ScrollUpInPaneId(pane_id));
+}
+
+fn scroll_down_in_pane_id(env: &PluginEnv, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::ScrollDownInPaneId(pane_id));
+}
+
+fn scroll_to_top_in_pane_id(env: &PluginEnv, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::ScrollToTopInPaneId(pane_id));
+}
+
+fn scroll_to_bottom_in_pane_id(env: &PluginEnv, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::ScrollToBottomInPaneId(pane_id));
+}
+
+fn page_scroll_up_in_pane_id(env: &PluginEnv, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::PageScrollUpInPaneId(pane_id));
+}
+
+fn page_scroll_down_in_pane_id(env: &PluginEnv, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::PageScrollDownInPaneId(pane_id));
+}
+
+fn toggle_pane_id_fullscreen(env: &PluginEnv, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::TogglePaneIdFullscreen(pane_id));
+}
+
+fn toggle_pane_embed_or_eject_for_pane_id(env: &PluginEnv, pane_id: PaneId) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::TogglePaneEmbedOrEjectForPaneId(pane_id));
+}
+
+fn close_tab_with_index(env: &PluginEnv, tab_index: usize) {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::CloseTabWithIndex(tab_index));
+}
+
// Custom panic handler for plugins.
//
// This is called when a panic occurs in a plugin. Since most panics will likely originate in the
@@ -1516,7 +1663,10 @@ fn check_command_permission(
| PluginCommand::RunCommand(..)
| PluginCommand::ExecCmd(..) => PermissionType::RunCommands,
PluginCommand::WebRequest(..) => PermissionType::WebAccess,
- PluginCommand::Write(..) | PluginCommand::WriteChars(..) => PermissionType::WriteToStdin,
+ PluginCommand::Write(..)
+ | PluginCommand::WriteChars(..)
+ | PluginCommand::WriteToPaneId(..)
+ | PluginCommand::WriteCharsToPaneId(..) => PermissionType::WriteToStdin,
PluginCommand::SwitchTabTo(..)
| PluginCommand::SwitchToMode(..)
| PluginCommand::NewTabsWithLayout(..)
@@ -1531,19 +1681,31 @@ fn check_command_permission(
| PluginCommand::MoveFocusOrTab(..)
| PluginCommand::Detach
| PluginCommand::EditScrollback
+ | PluginCommand::EditScrollbackForPaneWithId(..)
| PluginCommand::ToggleTab
| PluginCommand::MovePane
| PluginCommand::MovePaneWithDirection(..)
+ | PluginCommand::MovePaneWithPaneId(..)
+ | PluginCommand::MovePaneWithPaneIdInDirection(..)
| PluginCommand::ClearScreen
+ | PluginCommand::ClearScreenForPaneId(..)
| PluginCommand::ScrollUp
+ | PluginCommand::ScrollUpInPaneId(..)
| PluginCommand::ScrollDown
+ | PluginCommand::ScrollDownInPaneId(..)
| PluginCommand::ScrollToTop
+ | PluginCommand::ScrollToTopInPaneId(..)
| PluginCommand::ScrollToBottom
+ | PluginCommand::ScrollToBottomInPaneId(..)
| PluginCommand::PageScrollUp
+ | PluginCommand::PageScrollUpInPaneId(..)
| PluginCommand::PageScrollDown
+ | PluginCommand::PageScrollDownInPaneId(..)
| PluginCommand::ToggleFocusFullscreen
+ | PluginCommand::TogglePaneIdFullscreen(..)
| PluginCommand::TogglePaneFrames
| PluginCommand::TogglePaneEmbedOrEject
+ | PluginCommand::TogglePaneEmbedOrEjectForPaneId(..)
| PluginCommand::UndoRenamePane
| PluginCommand::CloseFocus
| PluginCommand::ToggleActiveTabSync
@@ -1570,6 +1732,8 @@ fn check_command_permission(
| PluginCommand::ShowPaneWithId(..)
| PluginCommand::HidePaneWithId(..)
| PluginCommand::RerunCommandPane(..)
+ | PluginCommand::ResizePaneIdWithDirection(..)
+ | PluginCommand::CloseTabWithIndex(..)
| PluginCommand::KillSessions(..) => PermissionType::ChangeApplicationState,
PluginCommand::UnblockCliPipeInput(..)
| PluginCommand::BlockCliPipeInput(..)
diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs
index 1ad549cd..a03464fa 100644
--- a/zellij-server/src/pty.rs
+++ b/zellij-server/src/pty.rs
@@ -10,11 +10,7 @@ use crate::{
};
use async_std::task::{self, JoinHandle};
use std::sync::Arc;
-use std::{
- collections::{BTreeMap, HashMap},
- os::unix::io::RawFd,
- path::PathBuf,
-};
+use std::{collections::HashMap, os::unix::io::RawFd, path::PathBuf};
use zellij_utils::nix::unistd::Pid;
use zellij_utils::{
async_std,
@@ -32,7 +28,7 @@ use zellij_utils::{
pub type VteBytes = Vec<u8>;
pub type TabIndex = u32;
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ClientTabIndexOrPaneId {
ClientId(ClientId),
TabIndex(usize),
@@ -51,7 +47,7 @@ pub enum PtyInstruction {
ClientTabIndexOrPaneId,
), // bool (if Some) is
// should_float, String is an optional pane name
- OpenInPlaceEditor(PathBuf, Option<usize>, ClientId), // Option<usize> is the optional line number
+ OpenInPlaceEditor(PathBuf, Option<usize>, ClientTabIndexOrPaneId), // Option<usize> is the optional line number
SpawnTerminalVertically(Option<TerminalAction>, Option<String>, ClientId), // String is an
// optional pane
// name
@@ -357,9 +353,12 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
},
}
},
- PtyInstruction::OpenInPlaceEditor(temp_file, line_number, client_id) => {
- let err_context =
- || format!("failed to open in-place editor for client {}", client_id);
+ PtyInstruction::OpenInPlaceEditor(
+ temp_file,
+ line_number,
+ client_tab_index_or_pane_id,
+ ) => {
+ let err_context = || format!("failed to open in-place editor for client");
match pty.spawn_terminal(
Some(TerminalAction::OpenFile(OpenFilePayload::new(
@@ -367,14 +366,14 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
line_number,
None,
))),
- ClientTabIndexOrPaneId::ClientId(client_id),
+ client_tab_index_or_pane_id,
) {
Ok((pid, _starts_held)) => {
pty.bus
.senders
.send_to_screen(ScreenInstruction::OpenInPlaceEditor(
PaneId::Terminal(pid),
- client_id,
+ client_tab_index_or_pane_id,
))
.with_context(err_context)?;
},
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index 9c88396a..1bbec5cf 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -156,7 +156,7 @@ pub enum ScreenInstruction {
bool, // start suppressed
ClientTabIndexOrPaneId,
),
- OpenInPlaceEditor(PaneId, ClientId),
+ OpenInPlaceEditor(PaneId, ClientTabIndexOrPaneId),
TogglePaneEmbedOrFloating(ClientId),
ToggleFloatingPanes(ClientId, Option<TerminalAction>),
HorizontalSplit(PaneId, Option<InitialTitle>, HoldForCommand, ClientId),
@@ -380,6 +380,21 @@ pub enum ScreenInstruction {
hide_session_name: bool,
},
RerunCommandPane(u32), // u32 - terminal pane id
+ ResizePaneWithId(ResizeStrategy, PaneId),
+ EditScrollbackForPaneWithId(PaneId),
+ WriteToPaneId(Vec<u8>, PaneId),
+ MovePaneWithPaneId(PaneId),
+ MovePaneWithPaneIdInDirection(PaneId, Direction),
+ ClearScreenForPaneId(PaneId),
+ ScrollUpInPaneId(PaneId),
+ ScrollDownInPaneId(PaneId),
+ ScrollToTopInPaneId(PaneId),
+ ScrollToBottomInPaneId(PaneId),
+ PageScrollUpInPaneId(PaneId),
+ PageScrollDownInPaneId(PaneId),
+ TogglePaneIdFullscreen(PaneId),
+ TogglePaneEmbedOrEjectForPaneId(PaneId),
+ CloseTabWithIndex(usize),
}
impl From<&ScreenInstruction> for ScreenContext {
@@ -566,6 +581,27 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::ListClientsMetadata(..) => ScreenContext::ListClientsMetadata,
ScreenInstruction::Reconfigure { .. } => ScreenContext::Reconfigure,
ScreenInstruction::RerunCommandPane { .. } => ScreenContext::RerunCommandPane,
+ ScreenInstruction::ResizePaneWithId(..) => ScreenContext::ResizePaneWithId,
+ ScreenInstruction::EditScrollbackForPaneWithId(..) => {
+ ScreenContext::EditScrollbackForPaneWithId
+ },
+ ScreenInstruction::WriteToPaneId(..) => ScreenContext::WriteToPaneId,
+ ScreenInstruction::MovePaneWithPaneId(..) => ScreenContext::MovePaneWithPaneId,
+ ScreenInstruction::MovePaneWithPaneIdInDirection(..) => {
+ ScreenContext::MovePaneWithPaneIdInDirection
+ },
+ ScreenInstruction::ClearScreenForPaneId(..) => ScreenContext::ClearScreenForPaneId,
+ ScreenInstruction::ScrollUpInPaneId(..) => ScreenContext::ScrollUpInPaneId,
+ ScreenInstruction::ScrollDownInPaneId(..) => ScreenContext::ScrollDownInPaneId,
+ ScreenInstruction::ScrollToTopInPaneId(..) => ScreenContext::ScrollToTopInPaneId,
+ ScreenInstruction::ScrollToBottomInPaneId(..) => ScreenContext::ScrollToBottomInPaneId,
+ ScreenInstruction::PageScrollUpInPaneId(..) => ScreenContext::PageScrollUpInPaneId,
+ ScreenInstruction::PageScrollDownInPaneId(..) => ScreenContext::PageScrollDownInPaneId,
+ ScreenInstruction::TogglePaneIdFullscreen(..) => ScreenContext::TogglePaneIdFullscreen,
+ ScreenInstruction::TogglePaneEmbedOrEjectForPaneId(..) => {
+ ScreenContext::TogglePaneEmbedOrEjectForPaneId
+ },
+ ScreenInstruction::CloseTabWithIndex(..) => ScreenContext::CloseTabWithIndex,
}
}
}
@@ -2010,6 +2046,19 @@ impl Screen {
);
}
}
+ pub fn resize_pane_with_id(&mut self, resize: ResizeStrategy, pane_id: PaneId) {
+ let mut found = false;
+ for tab in self.tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ tab.resize_pane_with_id(resize, pane_id);
+ found = true;
+ break;
+ }
+ }
+ if !found {
+ log::error!("Failed to find pane with id: {:?} to resize", pane_id);
+ }
+ }
pub fn break_pane(
&mut self,
default_shell: Option<TerminalAction>,
@@ -2586,11 +2635,35 @@ pub(crate) fn screen_thread_main(
screen.render(None)?;
},
- ScreenInstruction::OpenInPlaceEditor(pid, client_id) => {
- active_tab!(screen, client_id, |tab: &mut Tab| tab
- .replace_active_pane_with_editor_pane(pid, client_id), ?);
- screen.unblock_input()?;
- screen.log_and_report_session_state()?;
+ ScreenInstruction::OpenInPlaceEditor(pid, client_tab_index_or_pane_id) => {
+ match client_tab_index_or_pane_id {
+ ClientTabIndexOrPaneId::ClientId(client_id) => {
+ active_tab!(screen, client_id, |tab: &mut Tab| tab
+ .replace_active_pane_with_editor_pane(pid, client_id), ?);
+ screen.unblock_input()?;
+ screen.log_and_report_session_state()?;
+ },
+ ClientTabIndexOrPaneId::TabIndex(tab_index) => {
+ log::error!("Cannot OpenInPlaceEditor with a TabIndex");
+ },
+ ClientTabIndexOrPaneId::PaneId(pane_id_to_replace) => {
+ let mut found = false;
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id_to_replace) {
+ tab.replace_pane_with_editor_pane(pid, pane_id_to_replace);
+ found = true;
+ break;
+ }
+ }
+ if !found {
+ log::error!(
+ "Could not find pane with id {:?} to replace",
+ pane_id_to_replace
+ );
+ }
+ },
+ }
screen.render(None)?;
},
@@ -4165,6 +4238,195 @@ pub(crate) fn screen_thread_main(
ScreenInstruction::RerunCommandPane(terminal_pane_id) => {
screen.rerun_command_pane_with_id(terminal_pane_id)
},
+ ScreenInstruction::ResizePaneWithId(resize, pane_id) => {
+ screen.resize_pane_with_id(resize, pane_id)
+ },
+ ScreenInstruction::EditScrollbackForPaneWithId(pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ tab.edit_scrollback_for_pane_with_id(pane_id).non_fatal();
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::WriteToPaneId(bytes, pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ tab.write_to_pane_id(&None, bytes, false, pane_id, None)
+ .non_fatal();
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::MovePaneWithPaneId(pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ tab.move_pane(pane_id);
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::MovePaneWithPaneIdInDirection(pane_id, direction) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ match direction {
+ Direction::Down => tab.move_pane_down(pane_id),
+ Direction::Up => tab.move_pane_up(pane_id),
+ Direction::Left => tab.move_pane_left(pane_id),
+ Direction::Right => tab.move_pane_right(pane_id),
+ }
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::ClearScreenForPaneId(pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ tab.clear_screen_for_pane_id(pane_id);
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::ScrollUpInPaneId(pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ if let PaneId::Terminal(terminal_pane_id) = pane_id {
+ tab.scroll_terminal_up(terminal_pane_id);
+ } else {
+ // this is because to do this with plugins, we need the client_id -
+ // which we do not have (yet?) in this context...
+ log::error!(
+ "Currently only terminal panes are supported for scrolling up"
+ );
+ }
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::ScrollDownInPaneId(pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ if let PaneId::Terminal(terminal_pane_id) = pane_id {
+ tab.scroll_terminal_down(terminal_pane_id);
+ } else {
+ // this is because to do this with plugins, we need the client_id -
+ // which we do not have (yet?) in this context...
+ log::error!(
+ "Currently only terminal panes are supported for scrolling down"
+ );
+ }
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::ScrollToTopInPaneId(pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ if let PaneId::Terminal(terminal_pane_id) = pane_id {
+ tab.scroll_terminal_to_top(terminal_pane_id);
+ } else {
+ // this is because to do this with plugins, we need the client_id -
+ // which we do not have (yet?) in this context...
+ log::error!(
+ "Currently only terminal panes are supported for scrolling to top"
+ );
+ }
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::ScrollToBottomInPaneId(pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ if let PaneId::Terminal(terminal_pane_id) = pane_id {
+ tab.scroll_terminal_to_bottom(terminal_pane_id);
+ } else {
+ // this is because to do this with plugins, we need the client_id -
+ // which we do not have (yet?) in this context...
+ log::error!("Currently only terminal panes are supported for scrolling to bottom");
+ }
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::PageScrollUpInPaneId(pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ if let PaneId::Terminal(terminal_pane_id) = pane_id {
+ tab.scroll_terminal_page_up(terminal_pane_id);
+ } else {
+ // this is because to do this with plugins, we need the client_id -
+ // which we do not have (yet?) in this context...
+ log::error!(
+ "Currently only terminal panes are supported for scrolling"
+ );
+ }
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::PageScrollDownInPaneId(pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ if let PaneId::Terminal(terminal_pane_id) = pane_id {
+ tab.scroll_terminal_page_down(terminal_pane_id);
+ } else {
+ // this is because to do this with plugins, we need the client_id -
+ // which we do not have (yet?) in this context...
+ log::error!(
+ "Currently only terminal panes are supported for scrolling"
+ );
+ }
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::TogglePaneIdFullscreen(pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ tab.toggle_pane_fullscreen(pane_id);
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::TogglePaneEmbedOrEjectForPaneId(pane_id) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ tab.toggle_pane_embed_or_floating_for_pane_id(pane_id)
+ .non_fatal();
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
+ ScreenInstruction::CloseTabWithIndex(tab_index) => {
+ screen.close_tab_at_index(tab_index).non_fatal()
+ },
}
}
Ok(())
diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs
index 4d507d46..7b8e01e4 100644
--- a/zellij-server/src/tab/mod.rs
+++ b/zellij-server/src/tab/mod.rs
@@ -265,7 +265,7 @@ pub trait Pane {
fn pull_left(&mut self, count: usize);
fn pull_up(&mut self, count: usize);
fn clear_screen(&mut self);
- fn dump_screen(&mut self, _client_id: ClientId, _full: bool) -> String {
+ fn dump_screen(&self, _full: bool) -> String {
"".to_owned()
}
fn scroll_up(&mut self, count: usize, client_id: ClientId);
@@ -1024,6 +1024,38 @@ impl Tab {
}
Ok(())
}
+ pub fn toggle_pane_embed_or_floating_for_pane_id(&mut self, pane_id: PaneId) -> Result<()> {
+ let err_context = || {
+ format!(
+ "failed to toggle embedded/floating pane for pane_id {:?}",
+ pane_id
+ )
+ };
+ if self.tiled_panes.fullscreen_is_active() {
+ self.tiled_panes.unset_fullscreen();
+ }
+ if self.floating_panes.panes_contain(&pane_id) {
+ if self.tiled_panes.has_room_for_new_pane() {
+ let floating_pane_to_embed = self
+ .extract_pane(pane_id, true, None)
+ .with_context(|| {
+ format!("failed to find floating pane (ID: {pane_id:?}) to embed",)
+ })
+ .with_context(err_context)?;
+ self.add_tiled_pane(floating_pane_to_embed, pane_id, None)?;
+ }
+ } else if self.tiled_panes.panes_contain(&pane_id) {
+ if self.get_selectable_tiled_panes().count() <= 1 {
+ log::error!("Cannot float the last tiled pane...");
+ // don't close the only pane on screen...
+ return Ok(());
+ }
+ if let Some(embedded_pane_to_float) = self.extract_pane(pane_id, true, None) {
+ self.add_floating_pane(embedded_pane_to_float, pane_id, None, None)?;
+ }
+ }
+ Ok(())
+ }
pub fn toggle_floating_panes(
&mut self,
client_id: Option<ClientId>,
@@ -1181,28 +1213,7 @@ impl Tab {
match pid {
PaneId::Terminal(pid) => {
- let next_terminal_position = self.get_next_terminal_position(); // TODO: this is not accurate in this case
- let mut new_pane = TerminalPane::new(
- pid,
- PaneGeom::default(), // the initial size will be set later
- self.style,
- next_terminal_position,
- String::new(),
- self.link_handler.clone(),
- self.character_cell_size.clone(),
- self.sixel_image_store.clone(),
- self.terminal_emulator_colors.clone(),
- self.terminal_emulator_color_codes.clone(),
- None,
- None,
- self.debug,
- self.arrow_fonts,
- self.styled_underlines,
- self.explicitly_disable_kitty_keyboard_protocol,
- );
- new_pane.update_name("EDITING SCROLLBACK"); // we do this here and not in the
- // constructor so it won't be overrided
- // by the editor
+ let new_pane = self.new_scrollback_editor_pane(pid);
let replaced_pane = if self.floating_panes.panes_are_visible() {
self.floating_panes
.replace_active_pane(Box::new(new_pane), client_id)
@@ -1213,9 +1224,7 @@ impl Tab {
};
match replaced_pane {
Some(replaced_pane) => {
- let is_scrollback_editor = true;
- self.suppressed_panes
- .insert(PaneId::Terminal(pid), (is_scrollback_editor, replaced_pane));
+ self.insert_scrollback_editor_replaced_pane(replaced_pane, pid);
self.get_active_pane(client_id)
.with_context(|| format!("no active pane found for client {client_id}"))
.and_then(|current_active_pane| {
@@ -1243,6 +1252,60 @@ impl Tab {
}
Ok(())
}
+ pub fn replace_pane_with_editor_pane(
+ &mut self,
+ pid: PaneId,
+ pane_id_to_replace: PaneId,
+ ) -> Result<()> {
+ // this method creates a new pane from pid and replaces it with the pane iwth the given pane_id_to_replace
+ // the pane with the given pane_id_to_replace is then suppressed (hidden and not rendered) until the current
+ // created pane is closed, in which case it will be replaced back by it
+ let err_context = || format!("failed to suppress pane");
+
+ match pid {
+ PaneId::Terminal(pid) => {
+ let new_pane = self.new_scrollback_editor_pane(pid);
+ let replaced_pane = if self.floating_panes.panes_contain(&pane_id_to_replace) {
+ self.floating_panes
+ .replace_pane(pane_id_to_replace, Box::new(new_pane))
+ .ok()
+ } else if self.tiled_panes.panes_contain(&pane_id_to_replace) {
+ self.tiled_panes
+ .replace_pane(pane_id_to_replace, Box::new(new_pane))
+ } else if self
+ .suppressed_panes
+ .values()
+ .any(|s_p| s_p.1.pid() == pane_id_to_replace)
+ {
+ log::error!("Cannot replace suppressed pane");
+ None
+ } else {
+ // not a thing
+ None
+ };
+ match replaced_pane {
+ Some(replaced_pane) => {
+ resize_pty!(
+ replaced_pane,
+ self.os_api,
+ self.senders,
+ self.character_cell_size
+ );
+ self.insert_scrollback_editor_replaced_pane(replaced_pane, pid);
+ },
+ None => {
+ Err::<(), _>(anyhow!("Could not find editor pane to replace"))
+ .with_context(err_context)
+ .non_fatal();
+ },
+ }
+ },
+ PaneId::Plugin(_pid) => {
+ // TBD, currently unsupported
+ },
+ }
+ Ok(())
+ }
pub fn suppress_pane_and_replace_with_pid(
&mut self,
old_pane_id: PaneId,
@@ -1493,6 +1556,23 @@ impl Tab {
}
})
}
+ pub fn get_pane_with_id(&self, pane_id: PaneId) -> Option<&dyn Pane> {
+ self.floating_panes
+ .get_pane(pane_id)
+ .map(Box::as_ref)
+ .or_else(|| self.tiled_panes.get_pane(pane_id).map(Box::as_ref))
+ .or_else(|| self.suppressed_panes.get(&pane_id).map(|p| p.1.as_ref()))
+ }
+ pub fn get_pane_with_id_mut(&mut self, pane_id: PaneId) -> Option<&mut Box<dyn Pane>> {
+ self.floating_panes
+ .get_pane_mut(pane_id)
+ .or_else(|| self.tiled_panes.get_pane_mut(pane_id))
+ .or_else(|| {
+ self.suppressed_panes
+ .get_mut(&pane_id)
+ .map(|(_, pane)| pane)
+ })
+ }
pub fn get_active_pane_mut(&mut self, client_id: ClientId) -> Option<&mut Box<dyn Pane>> {
self.get_active_pane_id(client_id).and_then(|ap| {
if self.floating_panes.panes_are_visible() {
@@ -1948,6 +2028,13 @@ impl Tab {
}
self.tiled_panes.toggle_active_pane_fullscreen(client_id);
}
+ pub fn toggle_pane_fullscreen(&mut self, pane_id: PaneId) {
+ if self.tiled_panes.panes_contain(&pane_id) {
+ self.tiled_panes.toggle_pane_fullscreen(pane_id);
+ } else {
+ log::error!("No tiled pane with id: {:?} found", pane_id);
+ }
+ }
pub fn is_fullscreen_active(&self) -> bool {
self.tiled_panes.fullscreen_is_active()
}
@@ -2391,6 +2478,20 @@ impl Tab {
.move_active_pane(search_backwards, client_id);
}
}
+ pub fn move_pane(&mut self, pane_id: PaneId) {
+ if !self.has_selectable_panes() {
+ return;
+ }
+ if self.tiled_panes.fullscreen_is_active() {
+ return;
+ }
+ let search_backwards = false;
+ if self.floating_panes.panes_are_visible() {
+ self.floating_panes.move_pane(search_backwards, pane_id);
+ } else {
+ self.tiled_panes.move_pane(search_backwards, pane_id);
+ }
+ }
pub fn move_active_pane_backwards(&mut self, client_id: ClientId) {
if !self.has_selectable_panes() {
return;
@@ -2422,6 +2523,21 @@ impl Tab {
self.tiled_panes.move_active_pane_down(client_id);
}
}
+ pub fn move_pane_down(&mut self, pane_id: PaneId) {
+ if self.floating_panes.panes_are_visible() {
+ self.floating_panes.move_pane_down(pane_id);
+ self.swap_layouts.set_is_floating_damaged();
+ self.set_force_render(); // we force render here to make sure the panes under the floating pane render and don't leave "garbage" behind
+ } else {
+ if !self.has_selectable_panes() {
+ return;
+ }
+ if self.tiled_panes.fullscreen_is_active() {
+ return;
+ }
+ self.tiled_panes.move_pane_down(pane_id);
+ }
+ }
pub fn move_active_pane_up(&mut self, client_id: ClientId) {
if self.floating_panes.panes_are_visible() {
self.floating_panes.move_active_pane_up(client_id);
@@ -2437,6 +2553,21 @@ impl Tab {
self.tiled_panes.move_active_pane_up(client_id);
}
}
+ pub fn move_pane_up(&mut self, pane_id: PaneId) {
+ if self.floating_panes.panes_are_visible() {
+ self.floating_panes.move_pane_up(pane_id);
+ self.swap_layouts.set_is_floating_damaged();
+ self.set_force_render(); // we force render here to make sure the panes under the floating pane render and don't leave "garbage" behind
+ } else {
+ if !self.has_selectable_panes() {
+ return;
+ }
+ if self.tiled_panes.fullscreen_is_active() {
+ return;
+ }
+ self.tiled_panes.move_pane_up(pane_id);
+ }
+ }
pub fn move_active_pane_right(&mut self, client_id: ClientId) {
if self.floating_panes.panes_are_visible() {
self.floating_panes.move_active_pane_right(client_id);
@@ -2452,6 +2583,21 @@ impl Tab {
self.tiled_panes.move_active_pane_right(client_id);
}
}
+ pub fn move_pane_right(&mut self, pane_id: PaneId) {
+ if self.floating_panes.panes_are_visible() {
+ self.floating_panes.move_pane_right(pane_id);
+ self.swap_layouts.set_is_floating_damaged();
+ self.set_force_render(); // we force render here to make sure the panes under the floating pane render and don't leave "garbage" behind
+ } else {
+ if !self.has_selectable_panes() {
+ return;
+ }
+ if self.tiled_panes.fullscreen_is_active() {
+ return;
+ }
+ self.tiled_panes.move_pane_right(pane_id);
+ }
+ }
pub fn move_active_pane_left(&mut self, client_id: ClientId) {
if self.floating_panes.panes_are_visible() {
self.floating_panes.move_active_pane_left(client_id);
@@ -2467,6 +2613,21 @@ impl Tab {
self.tiled_panes.move_active_pane_left(client_id);
}
}
+ pub fn move_pane_left(&mut self, pane_id: PaneId) {
+ if self.floating_panes.panes_are_visible() {
+ self.floating_panes.move_pane_left(pane_id);
+ self.swap_layouts.set_is_floating_damaged();
+ self.set_force_render(); // we force render here to make sure the panes under the floating pane render and don't leave "garbage" behind
+ } else {
+ if !self.has_selectable_panes() {
+ return;
+ }
+ if self.tiled_panes.fullscreen_is_active() {
+ return;
+ }
+ self.tiled_panes.move_pane_left(pane_id);
+ }
+ }
fn close_down_to_max_terminals(&mut self) -> Result<()> {
if let Some(max_panes) = self.max_panes {
let terminals = self.get_tiled_pane_ids();
@@ -2744,6 +2905,11 @@ impl Tab {
}
Ok(())
}
+ pub fn clear_screen_for_pane_id(&mut self, pane_id: PaneId) {
+ if let Some(pane) = self.get_pane_with_id_mut(pane_id) {
+ pane.clear_screen();
+ }
+ }
pub fn dump_active_terminal_screen(
&mut self,
file: Option<String>,
@@ -2754,13 +2920,25 @@ impl Tab {
|| format!("failed to dump active terminal screen for client {client_id}");
if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) {
- let dump = active_pane.dump_screen(client_id, full);
+ let dump = active_pane.dump_screen(full);
self.os_api
.write_to_file(dump, file)
.with_context(err_context)?;
}
Ok(())
}
+ pub fn dump_terminal_screen(
+ &mut self,
+ file: Option<String>,
+ pane_id: PaneId,
+ full: bool,
+ ) -> Result<()> {
+ if let Some(pane) = self.get_pane_with_id(pane_id) {
+ let dump = pane.dump_screen(full);
+ self.os_api.write_to_file(dump, file).non_fatal()
+ }
+ Ok(())
+ }
pub fn edit_scrollback(&mut self, client_id: ClientId) -> Result<()> {
let err_context = || format!("failed to edit scrollback for client {client_id}");
@@ -2779,16 +2957,44 @@ impl Tab {
.send_to_pty(PtyInstruction::OpenInPlaceEditor(
file,
line_number,
- client_id,
+ ClientTabIndexOrPaneId::ClientId(client_id),
))
.with_context(err_context)
}
+ pub fn edit_scrollback_for_pane_with_id(&mut self, pane_id: PaneId) -> Result<()> {
+ if let PaneId::Terminal(_terminal_pane_id) = pane_id {
+ let mut file = temp_dir();
+ file.push(format!("{}.dump", Uuid::new_v4()));
+ self.dump_terminal_screen(Some(String::from(file.to_string_lossy())), pane_id, true)
+ .non_fatal();
+ let line_number = self
+ .get_pane_with_id(pane_id)
+ .and_then(|a_t| a_t.get_line_number());
+ self.senders.send_to_pty(PtyInstruction::OpenInPlaceEditor(
+ file,
+ line_number,
+ ClientTabIndexOrPaneId::PaneId(pane_id),
+ ))
+ } else {
+ log::error!("Editing plugin pane scrollback is currently unsupported.");
+ Ok(())
+ }
+ }
pub fn scroll_active_terminal_up(&mut self, client_id: ClientId) {
if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) {
active_pane.scroll_up(1, client_id);
}
}
+ pub fn scroll_terminal_up(&mut self, terminal_pane_id: u32) {
+ if let Some(terminal_pane) = self.get_pane_with_id_mut(PaneId::Terminal(terminal_pane_id)) {
+ let fictitious_client_id = 1; // this is not checked for terminal panes and we
+ // don't have an actual client id here
+ // TODO: traits were a mistake
+ terminal_pane.scroll_up(1, fictitious_client_id);
+ }
+ }
+
pub fn scroll_active_terminal_down(&mut self, client_id: ClientId) -> Result<()> {
let err_context = || format!("failed to scroll down active pane for client {client_id}");
@@ -2804,14 +3010,34 @@ impl Tab {
Ok(())
}
+ pub fn scroll_terminal_down(&mut self, terminal_pane_id: u32) {
+ if let Some(terminal_pane) = self.get_pane_with_id_mut(PaneId::Terminal(terminal_pane_id)) {
+ let fictitious_client_id = 1; // this is not checked for terminal panes and we
+ // don't have an actual client id here
+ // TODO: traits were a mistake
+ terminal_pane.scroll_down(1, fictitious_client_id);
+ }
+ }
+
pub fn scroll_active_terminal_up_page(&mut self, client_id: ClientId) {
if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) {
// prevent overflow when row == 0
- let scroll_rows = active_pane.rows().max(1) - 1;
+ let scroll_rows = active_pane.rows().max(1).saturating_sub(1);
active_pane.scroll_up(scroll_rows, client_id);
}
}
+ pub fn scroll_terminal_page_up(&mut self, terminal_pane_id: u32) {
+ if let Some(terminal_pane) = self.get_pane_with_id_mut(PaneId::Terminal(terminal_pane_id)) {
+ let fictitious_client_id = 1; // this is not checked for terminal panes and we
+ // don't have an actual client id here
+ // TODO: traits were a mistake
+ // prevent overflow when row == 0
+ let scroll_rows = terminal_pane.rows().max(1).saturating_sub(1);
+ terminal_pane.scroll_up(scroll_rows, fictitious_client_id);
+ }
+ }
+
pub fn scroll_active_terminal_down_page(&mut self, client_id: ClientId) -> Result<()> {
let err_context =
|| format!("failed to scroll down one page in active pane for client {client_id}");
@@ -2829,14 +3055,40 @@ impl Tab {
Ok(())
}
+ pub fn scroll_terminal_page_down(&mut self, terminal_pane_id: u32) {
+ if let Some(terminal_pane) = self.get_pane_with_id_mut(PaneId::Terminal(terminal_pane_id)) {
+ let fictitious_client_id = 1; // this is not checked for terminal panes and we
+ // don't have an actual client id here
+ // TODO: traits were a mistake
+ let scroll_rows = terminal_pane.get_content_rows();
+ terminal_pane.scroll_down(scroll_rows, fictitious_client_id);
+ if !terminal_pane.is_scrolled() {
+ if let PaneId::Terminal(raw_fd) = terminal_pane.pid() {
+ self.process_pending_vte_events(raw_fd).non_fatal()
+ }
+ }
+ }
+ }
+
pub fn scroll_active_terminal_up_half_page(&mut self, client_id: ClientId) {
if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) {
// prevent overflow when row == 0
- let scroll_rows = (active_pane.rows().max(1) - 1) / 2;
+ let scroll_rows = (active_pane.rows().max(1).saturating_sub(1)) / 2;
active_pane.scroll_up(scroll_rows, client_id);
}
}
+ pub fn scroll_terminal_half_page_up(&mut self, terminal_pane_id: u32) {
+ if let Some(terminal_pane) = self.get_pane_with_id_mut(PaneId::Terminal(terminal_pane_id)) {
+ let fictitious_client_id = 1; // this is not checked for terminal panes and we
+ // don't have an actual client id here
+ // TODO: traits were a mistake
+ // prevent overflow when row == 0
+ let scroll_rows = (terminal_pane.rows().max(1).saturating_sub(1)) / 2;
+ terminal_pane.scroll_down(scroll_rows, fictitious_client_id);
+ }
+ }
+
pub fn scroll_active_terminal_down_half_page(&mut self, client_id: ClientId) -> Result<()> {
let err_context =
|| format!("failed to scroll down half a page in active pane for client {client_id}");
@@ -2854,6 +3106,21 @@ impl Tab {
Ok(())
}
+ pub fn scroll_terminal_half_page_down(&mut self, terminal_pane_id: u32) {
+ if let Some(terminal_pane) = self.get_pane_with_id_mut(PaneId::Terminal(terminal_pane_id)) {
+ let fictitious_client_id = 1; // this is not checked for terminal panes and we
+ // don't have an actual client id here
+ // TODO: traits were a mistake
+ let scroll_rows = (terminal_pane.rows().max(1) - 1) / 2;
+ terminal_pane.scroll_down(scroll_rows, fictitious_client_id);
+ if !terminal_pane.is_scrolled() {
+ if let PaneId::Terminal(raw_fd) = terminal_pane.pid() {
+ self.process_pending_vte_events(raw_fd).non_fatal();
+ }
+ }
+ }
+ }
+
pub fn scroll_active_terminal_to_bottom(&mut self, client_id: ClientId) -> Result<()> {
let err_context =
|| format!("failed to scroll to bottom in active pane for client {client_id}");
@@ -2870,6 +3137,17 @@ impl Tab {
Ok(())
}
+ pub fn scroll_terminal_to_bottom(&mut self, terminal_pane_id: u32) {
+ if let Some(terminal_pane) = self.get_pane_with_id_mut(PaneId::Terminal(terminal_pane_id)) {
+ terminal_pane.clear_scroll();
+ if !terminal_pane.is_scrolled() {
+ if let PaneId::Terminal(raw_fd) = terminal_pane.pid() {
+ self.process_pending_vte_events(raw_fd).non_fatal();
+ }
+ }
+ }
+ }
+
pub fn scroll_active_terminal_to_top(&mut self, client_id: ClientId) -> Result<()> {
if let Some(active_pane) = self.get_active_pane_or_floating_pane_mut(client_id) {
active_pane.clear_scroll();
@@ -2880,6 +3158,18 @@ impl Tab {
Ok(())
}
+ pub fn scroll_terminal_to_top(&mut self, terminal_pane_id: u32) {
+ if let Some(terminal_pane) = self.get_pane_with_id_mut(PaneId::Terminal(terminal_pane_id)) {
+ terminal_pane.clear_scroll();
+ if let Some(size) = terminal_pane.get_line_number() {
+ let fictitious_client_id = 1; // this is not checked for terminal panes and we
+ // don't have an actual client id here
+ // TODO: traits were a mistake
+ terminal_pane.scroll_up(size, fictitious_client_id);
+ }
+ }
+ }
+
pub fn clear_active_terminal_scroll(&mut self, client_id: ClientId) -> Result<()> {
// TODO: is this a thing?
let err_context =
@@ -3954,6 +4244,50 @@ impl Tab {
},
}
}
+ pub fn resize_pane_with_id(&mut self, strategy: ResizeStrategy, pane_id: PaneId) -> Result<()> {
+ let err_context = || format!("unable to resize pane");
+ if self.floating_panes.panes_contain(&pane_id) {
+ let successfully_resized = self
+ .floating_panes
+ .resize_pane_with_id(strategy, pane_id)
+ .with_context(err_context)?;
+ if successfully_resized {
+ self.swap_layouts.set_is_floating_damaged();
+ self.swap_layouts.set_is_tiled_damaged();
+ self.set_force_render(); // we force render here to make sure the panes under the floating pane render and don't leave "garbage" in case of a decrease
+ }
+ } else if self.tiled_panes.panes_contain(&pane_id) {
+ match self.tiled_panes.resize_pane_with_id(strategy, pane_id) {
+ Ok(_) => {},
+ Err(err) => match err.downcast_ref::<ZellijError>() {
+ Some(ZellijError::CantResizeFixedPanes { pane_ids }) => {
+ let mut pane_ids_to_error = vec![];
+ for (id, is_terminal) in pane_ids {
+ if *is_terminal {
+ pane_ids_to_error.push(PaneId::Terminal(*id));
+ } else {
+ pane_ids_to_error.push(PaneId::Plugin(*id));
+ };
+ }
+ self.senders
+ .send_to_background_jobs(BackgroundJob::DisplayPaneError(
+ pane_ids_to_error,
+ "FIXED!".into(),
+ ))
+ .with_context(err_context)?;
+ },
+ _ => Err::<(), _>(err).fatal(),
+ },
+ }
+ } else if self
+ .suppressed_panes
+ .values()
+ .any(|s_p| s_p.1.pid() == pane_id)
+ {
+ log::error!("Cannot resize suppressed panes");
+ }
+ Ok(())
+ }
pub fn update_theme(&mut self, theme: Palette) {
self.style.colors = theme;
self.floating_panes.update_pane_themes(theme);
@@ -3995,6 +4329,42 @@ impl Tab {
pub fn update_auto_layout(&mut self, auto_layout: bool) {
self.auto_layout = auto_layout;
}
+ fn new_scrollback_editor_pane(&self, pid: u32) -> TerminalPane {
+ let next_terminal_position = self.get_next_terminal_position();
+ let mut new_pane = TerminalPane::new(
+ pid,
+ PaneGeom::default(), // the initial size will be set later
+ self.style,
+ next_terminal_position,
+ String::new(),
+ self.link_handler.clone(),
+ self.character_cell_size.clone(),
+ self.sixel_image_store.clone(),
+ self.terminal_emulator_colors.clone(),
+ self.terminal_emulator_color_codes.clone(),
+ None,
+ None,
+ self.debug,
+ self.arrow_fonts,
+ self.styled_underlines,
+ self.explicitly_disable_kitty_keyboard_protocol,
+ );
+ new_pane.update_name("EDITING SCROLLBACK"); // we do this here and not in the
+ // constructor so it won't be overrided
+ // by the editor
+ new_pane
+ }
+ fn insert_scrollback_editor_replaced_pane(
+ &mut self,
+ replaced_pane: Box<dyn Pane>,
+ terminal_pane_id: u32,
+ ) {
+ let is_scrollback_editor = true;
+ self.suppressed_panes.insert(
+ PaneId::Terminal(terminal_pane_id),
+ (is_scrollback_editor, replaced_pane),
+ );
+ }
}
pub fn pane_info_for_pane(pane_id: &PaneId, pane: &Box<dyn Pane>) -> PaneInfo {
diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs
index 1d8a7c81..f3554286 100644
--- a/zellij-server/src/unit/screen_tests.rs
+++ b/zellij-server/src/unit/screen_tests.rs
@@ -29,7 +29,10 @@ use std::env::set_var;
use std::os::unix::io::RawFd;
use std::sync::{Arc, Mutex};
-use crate::{plugins::PluginInstruction, pty::PtyInstruction};
+use crate::{
+ plugins::PluginInstruction,
+ pty::{ClientTabIndexOrPaneId, PtyInstruction},
+};
use zellij_utils::ipc::PixelDimensions;
use zellij_utils::{
@@ -1699,7 +1702,7 @@ pub fn send_cli_edit_scrollback_action() {
{
assert_eq!(scrollback_contents_file, &PathBuf::from(&dumped_file_name));
assert_eq!(terminal_id, &Some(1));
- assert_eq!(client_id, &1);
+ assert_eq!(client_id, &ClientTabIndexOrPaneId::ClientId(1));
found_instruction = true;
}
}
diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs
index 2c5e8580..9b57d1a9 100644
--- a/zellij-tile/src/shim.rs
+++ b/zellij-tile/src/shim.rs
@@ -857,6 +857,179 @@ pub fn rerun_command_pane(terminal_pane_id: u32) {
unsafe { host_run_plugin_command() };
}
+/// Sugar for close_terminal_pane and close_plugin_pane
+pub fn close_pane_with_id(pane_id: PaneId) {
+ let plugin_command = match pane_id {
+ PaneId::Terminal(terminal_pane_id) => PluginCommand::CloseTerminalPane(terminal_pane_id),
+ PaneId::Plugin(plugin_pane_id) => PluginCommand::ClosePluginPane(plugin_pane_id),
+ };
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Resize the specified pane (increase/decrease) with an optional direction (left/right/up/down)
+pub fn resize_pane_with_id(resize_strategy: ResizeStrategy, pane_id: PaneId) {
+ let plugin_command = PluginCommand::ResizePaneIdWithDirection(resize_strategy, pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Changes the focus to the pane with the specified id, unsuppressing it if it was suppressed and switching to its tab and layer (eg. floating/tiled).
+pub fn focus_pane_with_id(pane_id: PaneId, should_float_if_hidden: bool) {
+ let plugin_command = match pane_id {
+ PaneId::Terminal(terminal_pane_id) => {
+ PluginCommand::FocusTerminalPane(terminal_pane_id, should_float_if_hidden)
+ },
+ PaneId::Plugin(plugin_pane_id) => {
+ PluginCommand::FocusPluginPane(plugin_pane_id, should_float_if_hidden)
+ },
+ };
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Edit the scrollback of the specified pane in the user's default `$EDITOR` (currently only works
+/// for terminal panes)
+pub fn edit_scrollback_for_pane_with_id(pane_id: PaneId) {
+ let plugin_command = PluginCommand::EditScrollbackForPaneWithId(pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Write bytes to the `STDIN` of the specified pane
+pub fn write_to_pane_id(bytes: Vec<u8>, pane_id: PaneId) {
+ let plugin_command = PluginCommand::WriteToPaneId(bytes, pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Write characters to the `STDIN` of the specified pane
+pub fn write_chars_to_pane_id(chars: &str, pane_id: PaneId) {
+ let plugin_command = PluginCommand::WriteCharsToPaneId(chars.to_owned(), pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Switch the position of the pane with this id with a different pane
+pub fn move_pane_with_pane_id(pane_id: PaneId) {
+ let plugin_command = PluginCommand::MovePaneWithPaneId(pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Switch the position of the pane with this id with a different pane in the specified direction (eg. `Down`, `Up`, `Left`, `Right`).
+pub fn move_pane_with_pane_id_in_direction(pane_id: PaneId, direction: Direction) {
+ let plugin_command = PluginCommand::MovePaneWithPaneIdInDirection(pane_id, direction);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Clear the scroll buffer of the specified pane
+pub fn clear_screen_for_pane_id(pane_id: PaneId) {
+ let plugin_command = PluginCommand::ClearScreenForPaneId(pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Scroll the specified pane up 1 line
+pub fn scroll_up_in_pane_id(pane_id: PaneId) {
+ let plugin_command = PluginCommand::ScrollUpInPaneId(pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Scroll the specified pane down 1 line
+pub fn scroll_down_in_pane_id(pane_id: PaneId) {
+ let plugin_command = PluginCommand::ScrollDownInPaneId(pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Scroll the specified pane all the way to the top of the scrollbuffer
+pub fn scroll_to_top_in_pane_id(pane_id: PaneId) {
+ let plugin_command = PluginCommand::ScrollToTopInPaneId(pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Scroll the specified pane all the way to the bottom of the scrollbuffer
+pub fn scroll_to_bottom_in_pane_id(pane_id: PaneId) {
+ let plugin_command = PluginCommand::ScrollToBottomInPaneId(pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Scroll the specified pane up one page
+pub fn page_scroll_up_in_pane_id(pane_id: PaneId) {
+ let plugin_command = PluginCommand::PageScrollUpInPaneId(pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Scroll the specified pane down one page
+pub fn page_scroll_down_in_pane_id(pane_id: PaneId) {
+ let plugin_command = PluginCommand::PageScrollDownInPaneId(pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Toggle the specified pane to be fullscreen or normal sized
+pub fn toggle_pane_id_fullscreen(pane_id: PaneId) {
+ let plugin_command = PluginCommand::TogglePaneIdFullscreen(pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Embed the specified pane (make it stop floating) or turn it to a float pane if it is not
+pub fn toggle_pane_embed_or_eject_for_pane_id(pane_id: PaneId) {
+ let plugin_command = PluginCommand::TogglePaneEmbedOrEjectForPaneId(pane_id);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Close the focused tab
+pub fn close_tab_with_index(tab_index: usize) {
+ let plugin_command = PluginCommand::CloseTabWithIndex(tab_index);
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
+/// Rename the specified pane
+pub fn rename_pane_with_id<S: AsRef<str>>(pane_id: PaneId, new_name: S)
+where
+ S: ToString,
+{
+ let plugin_command = match pane_id {
+ PaneId::Terminal(terminal_pane_id) => {
+ PluginCommand::RenameTerminalPane(terminal_pane_id, new_name.to_string())
+ },
+ PaneId::Plugin(plugin_pane_id) => {
+ PluginCommand::RenamePluginPane(plugin_pane_id, new_name.to_string())
+ },
+ };
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
// Utility Functions
#[allow(unused)]
diff --git a/zellij-utils/assets/prost/api.plugin_command.rs b/zellij-utils/assets/prost/api.plugin_command.rs
index 2290aab4..7ebbf11d 100644
--- a/zellij-utils/assets/prost/api.plugin_command.rs
+++ b/zellij-utils/assets/prost/api.plugin_command.rs
@@ -5,7 +5,7 @@ pub struct PluginCommand {
pub name: i32,
#[prost(
oneof = "plugin_command::Payload",
- tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67"
+ tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83"
)]
pub payload: ::core::option::Option<plugin_command::Payload>,
}
@@ -128,10 +128,150 @@ pub mod plugin_command {
OpenCommandPaneBackgroundPayload(super::OpenCommandPanePayload),
#[prost(message, tag = "67")]
RerunCommandPanePayload(super::RerunCommandPanePayload),
+ #[prost(message, tag = "68")]
+ ResizePaneIdWithDirectionPayload(super::ResizePaneIdWithDirectionPayload),
+ #[prost(message, tag = "69")]
+ EditScrollbackForPaneWithIdPayload(super::EditScrollbackForPaneWithIdPayload),
+ #[prost(message, tag = "70")]
+ WriteToPaneIdPayload(super::WriteToPaneIdPayload),
+ #[prost(message, tag = "71")]
+ WriteCharsToPaneIdPayload(super::WriteCharsToPaneIdPayload),
+ #[prost(message, tag = "72")]
+ MovePaneWithPaneIdPayload(super::MovePaneWithPaneIdPayload),
+ #[prost(message, tag = "73")]
+ MovePaneWithPaneIdInDirectionPayload(
+ super::MovePaneWithPaneIdInDirectionPayload,
+ ),
+ #[prost(message, tag = "74")]
+ ClearScreenForPaneIdPayload(super::ClearScreenForPaneIdPayload),
+ #[prost(message, tag = "75")]
+ ScrollUpInPaneIdPayload(super::ScrollUpInPaneIdPayload),
+ #[prost(message, tag = "76")]
+ ScrollDownInPaneIdPayload(super::ScrollDownInPaneIdPayload),
+ #[prost(message, tag = "77")]
+ ScrollToTopInPaneIdPayload(super::ScrollToTopInPaneIdPayload),
+ #[prost(message, tag = "78")]
+ ScrollToBottomInPaneIdPayload(super::ScrollToBottomInPaneIdPayload),
+ #[prost(message, tag = "79")]
+ PageScrollUpInPaneIdPayload(super::PageScrollUpInPaneIdPayload),
+ #[prost(message, tag = "80")]
+ PageScrollDownInPaneIdPayload(super::PageScrollDownInPaneIdPayload),
+ #[prost(message, tag = "81")]
+ TogglePaneIdFullscreenPayload(super::TogglePaneIdFullscreenPayload),
+ #[prost(message, tag = "82")]
+ TogglePaneEmbedOrEjectForPaneIdPayload(
+ super::TogglePaneEmbedOrEjectForPaneIdPayload,
+ ),
+ #[prost(message, tag = "83")]
+ CloseTabWithIndexPayload(super::CloseTabWithIndexPayload),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct MovePaneWithPaneIdPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct MovePaneWithPaneIdInDirectionPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+ #[prost(message, optional, tag = "2")]
+ pub direction: ::core::option::Option<super::resize::MoveDirection>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ClearScreenForPaneIdPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ScrollUpInPaneIdPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ScrollDownInPaneIdPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ScrollToTopInPaneIdPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ScrollToBottomInPaneIdPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct PageScrollUpInPaneIdPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct PageScrollDownInPaneIdPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct TogglePaneIdFullscreenPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct TogglePaneEmbedOrEjectForPaneIdPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct CloseTabWithIndexPayload {
+ #[prost(uint32, tag = "1")]
+ pub tab_index: u32,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct WriteCharsToPaneIdPayload {
+ #[prost(string, tag = "1")]
+ pub chars_to_write: ::prost::alloc::string::String,
+ #[prost(message, optional, tag = "2")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct WriteToPaneIdPayload {
+ #[prost(bytes = "vec", tag = "1")]
+ pub bytes_to_write: ::prost::alloc::vec::Vec<u8>,
+ #[prost(message, optional, tag = "2")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct EditScrollbackForPaneWithIdPayload {
+ #[prost(message, optional, tag = "1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ResizePaneIdWithDirectionPayload {
+ #[prost(message, optional, tag = "1")]
+ pub resize: ::core::option::Option<super::resize::Resize>,
+ #[prost(message, optional, tag = "2")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ReconfigurePayload {
#[prost(string, tag = "1")]
pub config: ::prost::alloc::string::String,
@@ -478,6 +618,22 @@ pub enum CommandName {
ShowPaneWithId = 89,
OpenCommandPaneBackground = 90,
RerunCommandPane = 91,
+ ResizePaneIdWithDirection = 92,
+ EditScrollbackForPaneWithId = 93,
+ WriteToPaneId = 94,
+ WriteCharsToPaneId = 95,
+ MovePaneWithPaneId = 96,
+ MovePaneWithPaneIdInDirection = 97,
+ ClearScreenForPaneId = 98,
+ ScrollUpInPaneId = 99,
+ ScrollDownInPaneId = 100,
+ ScrollToTopInPaneId = 101,
+ ScrollToBottomInPaneId = 102,
+ PageScrollUpInPaneId = 103,
+ PageScrollDownInPaneId = 104,
+ TogglePaneIdFullscreen = 105,
+ TogglePaneEmbedOrEjectForPaneId = 106,
+ CloseTabWithIndex = 107,
}
impl CommandName {
/// String value of the enum field names used in the ProtoBuf definition.
@@ -578,6 +734,24 @@ impl CommandName {
CommandName::ShowPaneWithId => "ShowPaneWithId",
CommandName::OpenCommandPaneBackground => "OpenCommandPaneBackground",
CommandName::RerunCommandPane => "RerunCommandPane",
+ CommandName::ResizePaneIdWithDirection => "ResizePaneIdWithDirection",
+ CommandName::EditScrollbackForPaneWithId => "EditScrollbackForPaneWithId",
+ CommandName::WriteToPaneId => "WriteToPaneId",
+ CommandName::WriteCharsToPaneId => "WriteCharsToPaneId",
+ CommandName::MovePaneWithPaneId => "MovePaneWithPaneId",
+ CommandName::MovePaneWithPaneIdInDirection => "MovePaneWithPaneIdInDirection",
+ CommandName::ClearScreenForPaneId => "ClearScreenForPaneId",
+ CommandName::ScrollUpInPaneId => "ScrollUpInPaneId",
+ CommandName::ScrollDownInPaneId => "ScrollDownInPaneId",
+ CommandName::ScrollToTopInPaneId => "ScrollToTopInPaneId",
+ CommandName::ScrollToBottomInPaneId => "ScrollToBottomInPaneId",
+ CommandName::PageScrollUpInPaneId => "PageScrollUpInPaneId",
+ CommandName::PageScrollDownInPaneId => "PageScrollDownInPaneId",
+ CommandName::TogglePaneIdFullscreen => "TogglePaneIdFullscreen",
+ CommandName::TogglePaneEmbedOrEjectForPaneId => {
+ "TogglePaneEmbedOrEjectForPaneId"
+ }
+ CommandName::CloseTabWithIndex => "CloseTabWithIndex",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@@ -675,6 +849,24 @@ impl CommandName {
"ShowPaneWithId" => Some(Self::ShowPaneWithId),
"OpenCommandPaneBackground" => Some(Self::OpenCommandPaneBackground),
"RerunCommandPane" => Some(Self::RerunCommandPane),
+ "ResizePaneIdWithDirection" => Some(Self::ResizePaneIdWithDirection),
+ "EditScrollbackForPaneWithId" => Some(Self::EditScrollbackForPaneWithId),
+ "WriteToPaneId" => Some(Self::WriteToPaneId),
+ "WriteCharsToPaneId" => Some(Self::WriteCharsToPaneId),
+ "MovePaneWithPaneId" => Some(Self::MovePaneWithPaneId),
+ "MovePaneWithPaneIdInDirection" => Some(Self::MovePaneWithPaneIdInDirection),
+ "ClearScreenForPaneId" => Some(Self::ClearScreenForPaneId),
+ "ScrollUpInPaneId" => Some(Self::ScrollUpInPaneId),
+ "ScrollDownInPaneId" => Some(Self::ScrollDownInPaneId),
+ "ScrollToTopInPaneId" => Some(Self::ScrollToTopInPaneId),
+ "ScrollToBottomInPaneId" => Some(Self::ScrollToBottomInPaneId),
+ "PageScrollUpInPaneId" => Some(Self::PageScrollUpInPaneId),
+ "PageScrollDownInPaneId" => Some(Self::PageScrollDownInPaneId),
+ "TogglePaneIdFullscreen" => Some(Self::TogglePaneIdFullscreen),
+ "TogglePaneEmbedOrEjectForPaneId" => {
+ Some(Self::TogglePaneEmbedOrEjectForPaneId)
+ }
+ "CloseTabWithIndex" => Some(Self::CloseTabWithIndex),
_ => None,
}
}
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs
index 85897839..2087977b 100644
--- a/zellij-utils/src/data.rs
+++ b/zellij-utils/src/data.rs
@@ -1820,4 +1820,20 @@ pub enum PluginCommand {
ShowPaneWithId(PaneId, bool), // bool -> should_float_if_hidden
OpenCommandPaneBackground(CommandToRun, Context),
RerunCommandPane(u32), // u32 - terminal pane id
+ ResizePaneIdWithDirection(ResizeStrategy, PaneId),
+ EditScrollbackForPaneWithId(PaneId),
+ WriteToPaneId(Vec<u8>, PaneId),
+ WriteCharsToPaneId(String, PaneId),
+ MovePaneWithPaneId(PaneId),
+ MovePaneWithPaneIdInDirection(PaneId, Direction),
+ ClearScreenForPaneId(PaneId),
+ ScrollUpInPaneId(PaneId),
+ ScrollDownInPaneId(PaneId),
+ ScrollToTopInPaneId(PaneId),
+ ScrollToBottomInPaneId(PaneId),
+ PageScrollUpInPaneId(PaneId),
+ PageScrollDownInPaneId(PaneId),
+ TogglePaneIdFullscreen(PaneId),
+ TogglePaneEmbedOrEjectForPaneId(PaneId),
+ CloseTabWithIndex(usize), // usize - tab_index
}
diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs
index 44dd206d..f86595a3 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -355,6 +355,21 @@ pub enum ScreenContext {
ListClientsMetadata,
Reconfigure,
RerunCommandPane,
+ ResizePaneWithId,
+ EditScrollbackForPaneWithId,
+ WriteToPaneId,
+ MovePaneWithPaneId,
+ MovePaneWithPaneIdInDirection,
+ ClearScreenForPaneId,
+ ScrollUpInPaneId,
+ ScrollDownInPaneId,
+ ScrollToTopInPaneId,
+ ScrollToBottomInPaneId,
+ PageScrollUpInPaneId,
+ PageScrollDownInPaneId,
+ TogglePaneIdFullscreen,
+ TogglePaneEmbedOrEjectForPaneId,
+ CloseTabWithIndex,
}
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto
index be47f567..6e77643b 100644
--- a/zellij-utils/src/plugin_api/plugin_command.proto
+++ b/zellij-utils/src/plugin_api/plugin_command.proto
@@ -103,6 +103,22 @@ enum CommandName {
ShowPaneWithId = 89;
OpenCommandPaneBackground = 90;
RerunCommandPane = 91;
+ ResizePaneIdWithDirection = 92;
+ EditScrollbackForPaneWithId = 93;
+ WriteToPaneId = 94;
+ WriteCharsToPaneId = 95;
+ MovePaneWithPaneId = 96;
+ MovePaneWithPaneIdInDirection = 97;
+ ClearScreenForPaneId = 98;
+ ScrollUpInPaneId = 99;
+ ScrollDownInPaneId = 100;
+ ScrollToTopInPaneId = 101;
+ ScrollToBottomInPaneId = 102;
+ PageScrollUpInPaneId = 103;
+ PageScrollDownInPaneId = 104;
+ TogglePaneIdFullscreen = 105;
+ TogglePaneEmbedOrEjectForPaneId = 106;
+ CloseTabWithIndex = 107;
}
message PluginCommand {
@@ -165,9 +181,93 @@ message PluginCommand {
ShowPaneWithIdPayload show_pane_with_id_payload = 65;
OpenCommandPanePayload open_command_pane_background_payload = 66;
RerunCommandPanePayload rerun_command_pane_payload = 67;
+ ResizePaneIdWithDirectionPayload resize_pane_id_with_direction_payload = 68;
+ EditScrollbackForPaneWithIdPayload edit_scrollback_for_pane_with_id_payload = 69;
+ WriteToPaneIdPayload write_to_pane_id_payload = 70;
+ WriteCharsToPaneIdPayload write_chars_to_pane_id_payload = 71;
+ MovePaneWithPaneIdPayload move_pane_with_pane_id_payload = 72;
+ MovePaneWithPaneIdInDirectionPayload move_pane_with_pane_id_in_direction_payload = 73;
+ ClearScreenForPaneIdPayload clear_screen_for_pane_id_payload = 74;
+ ScrollUpInPaneIdPayload scroll_up_in_pane_id_payload = 75;
+ ScrollDownInPaneIdPayload scroll_down_in_pane_id_payload = 76;
+ ScrollToTopInPaneIdPayload scroll_to_top_in_pane_id_payload = 77;
+ ScrollToBottomInPaneIdPayload scroll_to_bottom_in_pane_id_payload = 78;
+ PageScrollUpInPaneIdPayload page_scroll_up_in_pane_id_payload = 79;
+ PageScrollDownInPaneIdPayload page_scroll_down_in_pane_id_payload = 80;
+ TogglePaneIdFullscreenPayload toggle_pane_id_fullscreen_payload = 81;
+ TogglePaneEmbedOrEjectForPaneIdPayload toggle_pane_embed_or_eject_for_pane_id_payload = 82;
+ CloseTabWithIndexPayload close_tab_with_index_payload = 83;
}
}
+message MovePaneWithPaneIdPayload {
+ PaneId pane_id = 1;
+}
+
+message MovePaneWithPaneIdInDirectionPayload {
+ PaneId pane_id = 1;
+ resize.MoveDirection direction = 2;
+}
+
+message ClearScreenForPaneIdPayload {
+ PaneId pane_id = 1;
+}
+
+message ScrollUpInPaneIdPayload {
+ PaneId pane_id = 1;
+}
+
+message ScrollDownInPaneIdPayload {
+ PaneId pane_id = 1;
+}
+
+message ScrollToTopInPaneIdPayload {
+ PaneId pane_id = 1;
+}
+
+message ScrollToBottomInPaneIdPayload {
+ PaneId pane_id = 1;
+}
+
+message PageScrollUpInPaneIdPayload {
+ PaneId pane_id = 1;
+}
+
+message PageScrollDownInPaneIdPayload {
+ PaneId pane_id = 1;
+}
+
+message TogglePaneIdFullscreenPayload {
+ PaneId pane_id = 1;
+}
+
+message TogglePaneEmbedOrEjectForPaneIdPayload {
+ PaneId pane_id = 1;
+}
+
+message CloseTabWithIndexPayload {
+ uint32 tab_index = 1;
+}
+
+message WriteCharsToPaneIdPayload {
+ string chars_to_write = 1;
+ PaneId pane_id = 2;
+}
+
+message WriteToPaneIdPayload {
+ bytes bytes_to_write = 1;
+ PaneId pane_id = 2;
+}
+
+message EditScrollbackForPaneWithIdPayload {
+ PaneId pane_id = 1;
+}
+
+message ResizePaneIdWithDirectionPayload {
+ resize.Resize resize = 1;
+ PaneId pane_id = 2;
+}
+
message ReconfigurePayload {
string config = 1;
bool write_to_disk = 2;
diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs
index 4ffabbe4..155907d3 100644
--- a/zellij-utils/src/plugin_api/plugin_command.rs
+++ b/zellij-utils/src/plugin_api/plugin_command.rs
@@ -3,17 +3,23 @@ pub use super::generated_api::api::{
event::{EventNameList as ProtobufEventNameList, Header},
input_mode::InputMode as ProtobufInputMode,
plugin_command::{
- plugin_command::Payload, CliPipeOutputPayload, CommandName, ContextItem, EnvVariable,
- ExecCmdPayload, FixedOrPercent as ProtobufFixedOrPercent,
+ plugin_command::Payload, ClearScreenForPaneIdPayload, CliPipeOutputPayload,
+ CloseTabWithIndexPayload, CommandName, ContextItem, EditScrollbackForPaneWithIdPayload,
+ EnvVariable, ExecCmdPayload, FixedOrPercent as ProtobufFixedOrPercent,
FixedOrPercentValue as ProtobufFixedOrPercentValue,
FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, HidePaneWithIdPayload,
HttpVerb as ProtobufHttpVerb, IdAndNewName, KillSessionsPayload, MessageToPluginPayload,
- MovePayload, NewPluginArgs as ProtobufNewPluginArgs, NewTabsWithLayoutInfoPayload,
- OpenCommandPanePayload, OpenFilePayload, PaneId as ProtobufPaneId,
- PaneType as ProtobufPaneType, PluginCommand as ProtobufPluginCommand, PluginMessagePayload,
- ReconfigurePayload, RequestPluginPermissionPayload, RerunCommandPanePayload, ResizePayload,
- RunCommandPayload, SetTimeoutPayload, ShowPaneWithIdPayload, SubscribePayload,
- SwitchSessionPayload, SwitchTabToPayload, UnsubscribePayload, WebRequestPayload,
+ MovePaneWithPaneIdInDirectionPayload, MovePaneWithPaneIdPayload, MovePayload,
+ NewPluginArgs as ProtobufNewPluginArgs, NewTabsWithLayoutInfoPayload,
+ OpenCommandPanePayload, OpenFilePayload, PageScrollDownInPaneIdPayload,
+ PageScrollUpInPaneIdPayload, PaneId as ProtobufPaneId, PaneType as ProtobufPaneType,
+ PluginCommand as ProtobufPluginCommand, PluginMessagePayload, ReconfigurePayload,
+ RequestPluginPermissionPayload, RerunCommandPanePayload, ResizePaneIdWithDirectionPayload,
+ ResizePayload, RunCommandPayload, ScrollDownInPaneIdPayload, ScrollToBottomInPaneIdPayload,
+ ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload, SetTimeoutPayload,
+ ShowPaneWithIdPayload, SubscribePayload, SwitchSessionPayload, SwitchTabToPayload,
+ TogglePaneEmbedOrEjectForPaneIdPayload, TogglePaneIdFullscreenPayload, UnsubscribePayload,
+ WebRequestPayload, WriteCharsToPaneIdPayload, WriteToPaneIdPayload,
},
plugin_permission::PermissionType as ProtobufPermissionType,
resize::ResizeAction as ProtobufResizeAction,
@@ -991,6 +997,183 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
),
_ => Err("Mismatched payload for RerunCommandPane"),
},
+ Some(CommandName::ResizePaneIdWithDirection) => match protobuf_plugin_command.payload {
+ Some(Payload::ResizePaneIdWithDirectionPayload(resize_with_direction_payload)) => {
+ match (
+ resize_with_direction_payload.resize,
+ resize_with_direction_payload.pane_id,
+ ) {
+ (Some(resize), Some(pane_id)) => {
+ Ok(PluginCommand::ResizePaneIdWithDirection(
+ resize.try_into()?,
+ pane_id.try_into()?,
+ ))
+ },
+ _ => Err("Malformed resize_pane_with_id payload"),
+ }
+ },
+ _ => Err("Mismatched payload for Resize"),
+ },
+ Some(CommandName::EditScrollbackForPaneWithId) => match protobuf_plugin_command.payload
+ {
+ Some(Payload::EditScrollbackForPaneWithIdPayload(
+ edit_scrollback_for_pane_with_id_payload,
+ )) => match edit_scrollback_for_pane_with_id_payload.pane_id {
+ Some(pane_id) => Ok(PluginCommand::EditScrollbackForPaneWithId(
+ pane_id.try_into()?,
+ )),
+ _ => Err("Malformed edit_scrollback_for_pane_with_id payload"),
+ },
+ _ => Err("Mismatched payload for EditScrollback"),
+ },
+ Some(CommandName::WriteToPaneId) => match protobuf_plugin_command.payload {
+ Some(Payload::WriteToPaneIdPayload(write_to_pane_id_payload)) => {
+ match write_to_pane_id_payload.pane_id {
+ Some(pane_id) => Ok(PluginCommand::WriteToPaneId(
+ write_to_pane_id_payload.bytes_to_write,
+ pane_id.try_into()?,
+ )),
+ _ => Err("Malformed write_to_pane_id payload"),
+ }
+ },
+ _ => Err("Mismatched payload for WriteToPaneId"),
+ },
+ Some(CommandName::WriteCharsToPaneId) => match protobuf_plugin_command.payload {
+ Some(Payload::WriteCharsToPaneIdPayload(write_chars_to_pane_id_payload)) => {
+ match write_chars_to_pane_id_payload.pane_id {
+ Some(pane_id) => Ok(PluginCommand::WriteCharsToPaneId(
+ write_chars_to_pane_id_payload.chars_to_write,
+ pane_id.try_into()?,
+ )),
+ _ => Err("Malformed write_chars_to_pane_id payload"),
+ }
+ },
+ _ => Err("Mismatched payload for WriteCharsCharsToPaneId"),
+ },
+ Some(CommandName::MovePaneWithPaneId) => match protobuf_plugin_command.payload {
+ Some(Payload::MovePaneWithPaneIdPayload(move_pane_with_pane_id_payload)) => {
+ match move_pane_with_pane_id_payload.pane_id {
+ Some(pane_id) => Ok(PluginCommand::MovePaneWithPaneId(pane_id.try_into()?)),
+ _ => Err("Malformed move_pane_with_pane_id payload"),
+ }
+ },
+ _ => Err("Mismatched payload for MovePaneWithPaneId"),
+ },
+ Some(CommandName::MovePaneWithPaneIdInDirection) => {
+ match protobuf_plugin_command.payload {
+ Some(Payload::MovePaneWithPaneIdInDirectionPayload(move_payload)) => {
+ match (move_payload.direction, move_payload.pane_id) {
+ (Some(direction), Some(pane_id)) => {
+ Ok(PluginCommand::MovePaneWithPaneIdInDirection(
+ pane_id.try_into()?,
+ direction.try_into()?,
+ ))
+ },
+ _ => Err("Malformed MovePaneWithPaneIdInDirection payload"),
+ }
+ },
+ _ => Err("Mismatched payload for MovePaneWithDirection"),
+ }
+ },
+ Some(CommandName::ClearScreenForPaneId) => match protobuf_plugin_command.payload {
+ Some(Payload::ClearScreenForPaneIdPayload(clear_screen_for_pane_id_payload)) => {
+ match clear_screen_for_pane_id_payload.pane_id {
+ Some(pane_id) => {
+ Ok(PluginCommand::ClearScreenForPaneId(pane_id.try_into()?))
+ },
+ _ => Err("Malformed clear_screen_for_pane_id_payload payload"),
+ }
+ },
+ _ => Err("Mismatched payload for ClearScreenForPaneId"),
+ },
+ Some(CommandName::ScrollUpInPaneId) => match protobuf_plugin_command.payload {
+ Some(Payload::ScrollUpInPaneIdPayload(scroll_up_in_pane_id_payload)) => {
+ match scroll_up_in_pane_id_payload.pane_id {
+ Some(pane_id) => Ok(PluginCommand::ScrollUpInPaneId(pane_id.try_into()?)),
+ _ => Err("Malformed scroll_up_in_pane_id_payload payload"),
+ }
+ },
+ _ => Err("Mismatched payload for ScrollUpInPaneId"),
+ },
+ Some(CommandName::ScrollDownInPaneId) => match protobuf_plugin_command.payload {
+ Some(Payload::ScrollDownInPaneIdPayload(scroll_down_in_pane_id_payload)) => {
+ match scroll_down_in_pane_id_payload.pane_id {
+ Some(pane_id) => Ok(PluginCommand::ScrollDownInPaneId(pane_id.try_into()?)),
+ _ => Err("Malformed scroll_down_in_pane_id_payload payload"),
+ }
+ },
+ _ => Err("Mismatched payload for ScrollDownInPaneId"),
+ },
+ Some(CommandName::ScrollToTopInPaneId) => match protobuf_plugin_command.payload {
+ Some(Payload::ScrollToTopInPaneIdPayload(scroll_to_top_in_pane_id_payload)) => {
+ match scroll_to_top_in_pane_id_payload.pane_id {
+ Some(pane_id) => {
+ Ok(PluginCommand::ScrollToTopInPaneId(pane_id.try_into()?))
+ },
+ _ => Err("Malformed scroll_to_top_in_pane_id_payload payload"),
+ }
+ },
+ _ => Err("Mismatched payload for ScrollToTopInPaneId"),
+ },
+ Some(CommandName::ScrollToBottomInPaneId) => match protobuf_plugin_command.payload {
+ Some(Payload::ScrollToBottomInPaneIdPayload(
+ scroll_to_bottom_in_pane_id_payload,
+ )) => match scroll_to_bottom_in_pane_id_payload.pane_id {
+ Some(pane_id) => Ok(PluginCommand::ScrollToBottomInPaneId(pane_id.try_into()?)),
+ _ => Err("Malformed scroll_to_bottom_in_pane_id_payload payload"),
+ },
+ _ => Err("Mismatched payload for ScrollToBottomInPaneId"),
+ },
+ Some(CommandName::PageScrollUpInPaneId) => match protobuf_plugin_command.payload {
+ Some(Payload::PageScrollUpInPaneIdPayload(page_scroll_up_in_pane_id_payload)) => {
+ match page_scroll_up_in_pane_id_payload.pane_id {
+ Some(pane_id) => {
+ Ok(PluginCommand::PageScrollUpInPaneId(pane_id.try_into()?))
+ },
+ _ => Err("Malformed page_scroll_up_in_pane_id_payload payload"),
+ }
+ },
+ _ => Err("Mismatched payload for PageScrollUpInPaneId"),
+ },
+ Some(CommandName::PageScrollDownInPaneId) => match protobuf_plugin_command.payload {
+ Some(Payload::PageScrollDownInPaneIdPayload(
+ page_scroll_down_in_pane_id_payload,
+ )) => match page_scroll_down_in_pane_id_payload.pane_id {
+ Some(pane_id) => Ok(PluginCommand::PageScrollDownInPaneId(pane_id.try_into()?)),
+ _ => Err("Malformed page_scroll_down_in_pane_id_payload payload"),
+ },
+ _ => Err("Mismatched payload for PageScrollDownInPaneId"),
+ },
+ Some(CommandName::TogglePaneIdFullscreen) => match protobuf_plugin_command.payload {
+ Some(Payload::TogglePaneIdFullscreenPayload(toggle_pane_id_fullscreen_payload)) => {
+ match toggle_pane_id_fullscreen_payload.pane_id {
+ Some(pane_id) => {
+ Ok(PluginCommand::TogglePaneIdFullscreen(pane_id.try_into()?))
+ },
+ _ => Err("Malformed toggle_pane_id_fullscreen_payload payload"),
+ }
+ },
+ _ => Err("Mismatched payload for TogglePaneIdFullscreen"),
+ },
+ Some(CommandName::TogglePaneEmbedOrEjectForPaneId) => {
+ match protobuf_plugin_command.payload {
+ Some(Payload::TogglePaneEmbedOrEjectForPaneIdPayload(
+ toggle_pane_embed_or_eject_payload,
+ )) => match toggle_pane_embed_or_eject_payload.pane_id {
+ Some(pane_id) => Ok(PluginCommand::TogglePaneEmbedOrEjectForPaneId(
+ pane_id.try_into()?,
+ )),
+ _ => Err("Malformed toggle_pane_embed_or_eject_payload payload"),
+ },
+ _ => Err("Mismatched payload for TogglePaneEmbedOrEjectForPaneId"),
+ }
+ },
+ Some(CommandName::CloseTabWithIndex) => match protobuf_plugin_command.payload {
+ Some(Payload::CloseTabWithIndexPayload(close_tab_index_payload)) => Ok(
+ PluginCommand::CloseTabWithIndex(close_tab_index_payload.tab_index as usize),
+ ),
+ _ => Err("Mismatched payload for CloseTabWithIndex"),
+ },
None => Err("Unrecognized plugin command"),
}
}
@@ -1605,6 +1788,140 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
terminal_pane_id,
})),
}),
+ PluginCommand::ResizePaneIdWithDirection(resize, pane_id) => {
+ Ok(ProtobufPluginCommand {
+ name: CommandName::ResizePaneIdWithDirection as i32,
+ payload: Some(Payload::ResizePaneIdWithDirectionPayload(
+ ResizePaneIdWithDirectionPayload {
+ resize: Some(resize.try_into()?),
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ })
+ },
+ PluginCommand::EditScrollbackForPaneWithId(pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::EditScrollbackForPaneWithId as i32,
+ payload: Some(Payload::EditScrollbackForPaneWithIdPayload(
+ EditScrollbackForPaneWithIdPayload {
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ }),
+ PluginCommand::WriteToPaneId(bytes_to_write, pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::WriteToPaneId as i32,
+ payload: Some(Payload::WriteToPaneIdPayload(WriteToPaneIdPayload {
+ bytes_to_write,
+ pane_id: Some(pane_id.try_into()?),
+ })),
+ }),
+ PluginCommand::WriteCharsToPaneId(chars_to_write, pane_id) => {
+ Ok(ProtobufPluginCommand {
+ name: CommandName::WriteCharsToPaneId as i32,
+ payload: Some(Payload::WriteCharsToPaneIdPayload(
+ WriteCharsToPaneIdPayload {
+ chars_to_write,
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ })
+ },
+ PluginCommand::MovePaneWithPaneId(pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::MovePaneWithPaneId as i32,
+ payload: Some(Payload::MovePaneWithPaneIdPayload(
+ MovePaneWithPaneIdPayload {
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ }),
+ PluginCommand::MovePaneWithPaneIdInDirection(pane_id, direction) => {
+ Ok(ProtobufPluginCommand {
+ name: CommandName::MovePaneWithPaneIdInDirection as i32,
+ payload: Some(Payload::MovePaneWithPaneIdInDirectionPayload(
+ MovePaneWithPaneIdInDirectionPayload {
+ pane_id: Some(pane_id.try_into()?),
+ direction: Some(direction.try_into()?),
+ },
+ )),
+ })
+ },
+ PluginCommand::ClearScreenForPaneId(pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::ClearScreenForPaneId as i32,
+ payload: Some(Payload::ClearScreenForPaneIdPayload(
+ ClearScreenForPaneIdPayload {
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ }),
+ PluginCommand::ScrollUpInPaneId(pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::ScrollUpInPaneId as i32,
+ payload: Some(Payload::ScrollUpInPaneIdPayload(ScrollUpInPaneIdPayload {
+ pane_id: Some(pane_id.try_into()?),
+ })),
+ }),
+ PluginCommand::ScrollDownInPaneId(pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::ScrollDownInPaneId as i32,
+ payload: Some(Payload::ScrollDownInPaneIdPayload(
+ ScrollDownInPaneIdPayload {
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ }),
+ PluginCommand::ScrollToTopInPaneId(pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::ScrollToTopInPaneId as i32,
+ payload: Some(Payload::ScrollToTopInPaneIdPayload(
+ ScrollToTopInPaneIdPayload {
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ }),
+ PluginCommand::ScrollToBottomInPaneId(pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::ScrollToBottomInPaneId as i32,
+ payload: Some(Payload::ScrollToBottomInPaneIdPayload(
+ ScrollToBottomInPaneIdPayload {
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ }),
+ PluginCommand::PageScrollUpInPaneId(pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::PageScrollUpInPaneId as i32,
+ payload: Some(Payload::PageScrollUpInPaneIdPayload(
+ PageScrollUpInPaneIdPayload {
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ }),
+ PluginCommand::PageScrollDownInPaneId(pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::PageScrollDownInPaneId as i32,
+ payload: Some(Payload::PageScrollDownInPaneIdPayload(
+ PageScrollDownInPaneIdPayload {
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ }),
+ PluginCommand::TogglePaneIdFullscreen(pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::TogglePaneIdFullscreen as i32,
+ payload: Some(Payload::TogglePaneIdFullscreenPayload(
+ TogglePaneIdFullscreenPayload {
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ }),
+ PluginCommand::TogglePaneEmbedOrEjectForPaneId(pane_id) => Ok(ProtobufPluginCommand {
+ name: CommandName::TogglePaneEmbedOrEjectForPaneId as i32,
+ payload: Some(Payload::TogglePaneEmbedOrEjectForPaneIdPayload(
+ TogglePaneEmbedOrEjectForPaneIdPayload {
+ pane_id: Some(pane_id.try_into()?),
+ },
+ )),
+ }),
+ PluginCommand::CloseTabWithIndex(tab_index) => Ok(ProtobufPluginCommand {
+ name: CommandName::CloseTabWithIndex as i32,
+ payload: Some(Payload::CloseTabWithIndexPayload(
+ CloseTabWithIndexPayload {
+ tab_index: tab_index as u32,
+ },
+ )),
+ }),
}
}
}