summaryrefslogtreecommitdiffstats
path: root/zellij-server
diff options
context:
space:
mode:
Diffstat (limited to 'zellij-server')
-rw-r--r--zellij-server/Cargo.toml1
-rw-r--r--zellij-server/src/lib.rs1
-rw-r--r--zellij-server/src/os_input_output.rs69
-rw-r--r--zellij-server/src/panes/floating_panes/mod.rs60
-rw-r--r--zellij-server/src/panes/grid.rs3
-rw-r--r--zellij-server/src/panes/terminal_pane.rs4
-rw-r--r--zellij-server/src/panes/tiled_panes/mod.rs100
-rw-r--r--zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs7
-rw-r--r--zellij-server/src/pty.rs55
-rw-r--r--zellij-server/src/route.rs6
-rw-r--r--zellij-server/src/screen.rs34
-rw-r--r--zellij-server/src/tab/mod.rs134
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_suppressing_floating_pane.snap26
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__close_suppressing_tiled_pane.snap26
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__resize_whole_tab_while_floting_pane_is_suppressed.snap26
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__resize_whole_tab_while_tiled_pane_is_suppressed.snap26
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__suppress_floating_pane.snap26
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__suppress_floating_pane_embed_it_and_close_it.snap26
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__suppress_tiled_pane.snap26
-rw-r--r--zellij-server/src/tab/unit/snapshots/zellij_server__tab__tab_integration_tests__suppress_tiled_pane_float_it_and_close.snap26
-rw-r--r--zellij-server/src/tab/unit/tab_integration_tests.rs213
-rw-r--r--zellij-server/src/tab/unit/tab_tests.rs3
-rw-r--r--zellij-server/src/unit/screen_tests.rs3
-rw-r--r--zellij-server/src/wasm_vm.rs2
24 files changed, 858 insertions, 45 deletions
diff --git a/zellij-server/Cargo.toml b/zellij-server/Cargo.toml
index d1cf9eb25..74767439d 100644
--- a/zellij-server/Cargo.toml
+++ b/zellij-server/Cargo.toml
@@ -28,6 +28,7 @@ typetag = "0.1.7"
chrono = "0.4.19"
close_fds = "0.3.2"
sysinfo = "0.22.5"
+uuid = { version = "0.8.2", features = ["serde", "v4"] }
[dev-dependencies]
insta = "1.6.0"
diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs
index 81b66801e..0a587d7c6 100644
--- a/zellij-server/src/lib.rs
+++ b/zellij-server/src/lib.rs
@@ -620,6 +620,7 @@ fn init_session(
Some(os_input.clone()),
),
opts.debug,
+ config_options.scrollback_editor.clone(),
);
move || pty_thread_main(pty, layout)
diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs
index 1e43e47fa..b757ad460 100644
--- a/zellij-server/src/os_input_output.rs
+++ b/zellij-server/src/os_input_output.rs
@@ -145,6 +145,7 @@ fn handle_openpty(
///
fn handle_terminal(
cmd: RunCommand,
+ failover_cmd: Option<RunCommand>,
orig_termios: termios::Termios,
quit_cb: Box<dyn Fn(PaneId) + Send>,
) -> (RawFd, RawFd) {
@@ -152,9 +153,12 @@ fn handle_terminal(
// parent.
match openpty(None, Some(&orig_termios)) {
Ok(open_pty_res) => handle_openpty(open_pty_res, cmd, quit_cb),
- Err(e) => {
- panic!("failed to start pty{:?}", e);
- }
+ Err(e) => match failover_cmd {
+ Some(failover_cmd) => handle_terminal(failover_cmd, None, orig_termios, quit_cb),
+ None => {
+ panic!("failed to start pty{:?}", e);
+ }
+ },
}
}
@@ -174,19 +178,40 @@ pub fn spawn_terminal(
terminal_action: TerminalAction,
orig_termios: termios::Termios,
quit_cb: Box<dyn Fn(PaneId) + Send>,
-) -> (RawFd, RawFd) {
+ default_editor: Option<PathBuf>,
+) -> Result<(RawFd, RawFd), &'static str> {
+ let mut failover_cmd_args = None;
let cmd = match terminal_action {
- TerminalAction::OpenFile(file_to_open) => {
- if env::var("EDITOR").is_err() && env::var("VISUAL").is_err() {
- panic!("Can't edit files if an editor is not defined. To fix: define the EDITOR or VISUAL environment variables with the path to your editor (eg. /usr/bin/vim)");
+ TerminalAction::OpenFile(file_to_open, line_number) => {
+ if default_editor.is_none()
+ && env::var("EDITOR").is_err()
+ && env::var("VISUAL").is_err()
+ {
+ return Err(
+ "No Editor found, consider setting a path to one in $EDITOR or $VISUAL",
+ );
}
- let command =
- PathBuf::from(env::var("EDITOR").unwrap_or_else(|_| env::var("VISUAL").unwrap()));
+ let command = default_editor.unwrap_or_else(|| {
+ PathBuf::from(env::var("EDITOR").unwrap_or_else(|_| env::var("VISUAL").unwrap()))
+ });
- let args = vec![file_to_open
+ let mut args = vec![];
+ let file_to_open = file_to_open
.into_os_string()
.into_string()
- .expect("Not valid Utf8 Encoding")];
+ .expect("Not valid Utf8 Encoding");
+ if let Some(line_number) = line_number {
+ if command.ends_with("vim")
+ || command.ends_with("nvim")
+ || command.ends_with("emacs")
+ || command.ends_with("nano")
+ || command.ends_with("kak")
+ {
+ failover_cmd_args = Some(vec![file_to_open.clone()]);
+ args.push(format!("+{}", line_number));
+ }
+ }
+ args.push(file_to_open);
RunCommand {
command,
args,
@@ -195,8 +220,15 @@ pub fn spawn_terminal(
}
TerminalAction::RunCommand(command) => command,
};
+ let failover_cmd = if let Some(failover_cmd_args) = failover_cmd_args {
+ let mut cmd = cmd.clone();
+ cmd.args = failover_cmd_args;
+ Some(cmd)
+ } else {
+ None
+ };
- handle_terminal(cmd, orig_termios, quit_cb)
+ Ok(handle_terminal(cmd, failover_cmd, orig_termios, quit_cb))
}
#[derive(Clone)]
@@ -245,7 +277,8 @@ pub trait ServerOsApi: Send + Sync {
&self,
terminal_action: TerminalAction,
quit_cb: Box<dyn Fn(PaneId) + Send>,
- ) -> (RawFd, RawFd);
+ default_editor: Option<PathBuf>,
+ ) -> Result<(RawFd, RawFd), &'static str>;
/// Read bytes from the standard output of the virtual terminal referred to by `fd`.
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
/// Creates an `AsyncReader` that can be used to read from `fd` in an async context
@@ -284,9 +317,15 @@ impl ServerOsApi for ServerOsInputOutput {
&self,
terminal_action: TerminalAction,
quit_cb: Box<dyn Fn(PaneId) + Send>,
- ) -> (RawFd, RawFd) {
+ default_editor: Option<PathBuf>,
+ ) -> Result<(RawFd, RawFd), &'static str> {
let orig_termios = self.orig_termios.lock().unwrap();
- spawn_terminal(terminal_action, orig_termios.clone(), quit_cb)
+ spawn_terminal(
+ terminal_action,
+ orig_termios.clone(),
+ quit_cb,
+ default_editor,
+ )
}
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> {
unistd::read(fd, buf)
diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs
index e4925c365..f8bd46bc3 100644
--- a/zellij-server/src/panes/floating_panes/mod.rs
+++ b/zellij-server/src/panes/floating_panes/mod.rs
@@ -101,6 +101,49 @@ impl FloatingPanes {
self.panes.insert(pane_id, pane);
self.z_indices.push(pane_id);
}
+ pub fn replace_active_pane(
+ &mut self,
+ pane: Box<dyn Pane>,
+ client_id: ClientId,
+ ) -> Option<Box<dyn Pane>> {
+ self.active_panes
+ .get(&client_id)
+ .copied()
+ .and_then(|active_pane_id| self.replace_pane(active_pane_id, pane))
+ }
+ pub fn replace_pane(
+ &mut self,
+ pane_id: PaneId,
+ mut with_pane: Box<dyn Pane>,
+ ) -> Option<Box<dyn Pane>> {
+ let with_pane_id = with_pane.pid();
+ with_pane.set_content_offset(Offset::frame(1));
+ let removed_pane = self.panes.remove(&pane_id).map(|removed_pane| {
+ let removed_pane_id = removed_pane.pid();
+ let with_pane_id = with_pane.pid();
+ let removed_pane_geom = removed_pane.current_geom();
+ with_pane.set_geom(removed_pane_geom);
+ self.panes.insert(with_pane_id, with_pane);
+ let z_index = self
+ .z_indices
+ .iter()
+ .position(|pane_id| pane_id == &removed_pane_id)
+ .unwrap();
+ self.z_indices.remove(z_index);
+ self.z_indices.insert(z_index, with_pane_id);
+ removed_pane
+ });
+
+ // update the desired_pane_positions to relate to the new pane
+ if let Some(desired_pane_position) = self.desired_pane_positions.remove(&pane_id) {
+ self.desired_pane_positions
+ .insert(with_pane_id, desired_pane_position);
+ }
+
+ // move clients from the previously active pane to the new pane we just inserted
+ self.move_clients_between_panes(pane_id, with_pane_id);
+ removed_pane
+ }
pub fn remove_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
self.z_indices.retain(|p_id| *p_id != pane_id);
self.desired_pane_positions.remove(&pane_id);
@@ -241,6 +284,11 @@ impl FloatingPanes {
floating_pane_grid.resize(new_screen_size);
self.set_force_render();
}
+ pub fn resize_pty_all_panes(&mut self, os_api: &mut Box<dyn ServerOsApi>) {
+ for pane in self.panes.values_mut() {
+ resize_pty!(pane, os_api);
+ }
+ }
pub fn resize_active_pane_left(
&mut self,
client_id: ClientId,
@@ -875,4 +923,16 @@ impl FloatingPanes {
pub fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
self.panes.iter()
}
+ fn move_clients_between_panes(&mut self, from_pane_id: PaneId, to_pane_id: PaneId) {
+ let clients_in_pane: Vec<ClientId> = self
+ .active_panes
+ .iter()
+ .filter(|(_cid, pid)| **pid == from_pane_id)
+ .map(|(cid, _pid)| *cid)
+ .collect();
+ for client_id in clients_in_pane {
+ self.active_panes.remove(&client_id);
+ self.active_panes.insert(client_id, to_pane_id);
+ }
+ }
}
diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs
index e01c1ad72..55b9edab5 100644
--- a/zellij-server/src/panes/grid.rs
+++ b/zellij-server/src/panes/grid.rs
@@ -1460,6 +1460,9 @@ impl Grid {
Some(selection.join("\n"))
}
}
+ pub fn absolute_position_in_scrollback(&self) -> usize {
+ self.lines_above.len() + self.cursor.y
+ }
fn update_selected_lines(&mut self, old_selection: &Selection, new_selection: &Selection) {
for l in old_selection.diff(new_selection, self.height) {
diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs
index 7ada5f024..dd91149bc 100644
--- a/zellij-server/src/panes/terminal_pane.rs
+++ b/zellij-server/src/panes/terminal_pane.rs
@@ -481,6 +481,10 @@ impl Pane for TerminalPane {
fn mouse_mode(&self) -> bool {
self.grid.mouse_mode
}
+ fn get_line_number(&self) -> Option<usize> {
+ // + 1 because the absolute position in the scrollback is 0 indexed and this should be 1 indexed
+ Some(self.grid.absolute_position_in_scrollback() + 1)
+ }
}
impl TerminalPane {
diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs
index 468303b79..1f9582976 100644
--- a/zellij-server/src/panes/tiled_panes/mod.rs
+++ b/zellij-server/src/panes/tiled_panes/mod.rs
@@ -105,13 +105,58 @@ impl TiledPanes {
os_api,
}
}
- pub fn add_pane_with_existing_geom(&mut self, pane_id: PaneId, pane: Box<dyn Pane>) {
+ pub fn add_pane_with_existing_geom(&mut self, pane_id: PaneId, mut pane: Box<dyn Pane>) {
+ if self.draw_pane_frames {
+ pane.set_content_offset(Offset::frame(1));
+ }
self.panes.insert(pane_id, pane);
}
+ pub fn replace_active_pane(
+ &mut self,
+ pane: Box<dyn Pane>,
+ client_id: ClientId,
+ ) -> Option<Box<dyn Pane>> {
+ let pane_id = pane.pid();
+ // remove the currently active pane
+ let previously_active_pane = self
+ .active_panes
+ .get(&client_id)
+ .copied()
+ .and_then(|active_pane_id| self.replace_pane(active_pane_id, pane));
+
+ // move clients from the previously active pane to the new pane we just inserted
+ if let Some(previously_active_pane) = previously_active_pane.as_ref() {
+ let previously_active_pane_id = previously_active_pane.pid();
+ self.move_clients_between_panes(previously_active_pane_id, pane_id);
+ }
+ previously_active_pane
+ }
+ pub fn replace_pane(
+ &mut self,
+ pane_id: PaneId,
+ mut with_pane: Box<dyn Pane>,
+ ) -> Option<Box<dyn Pane>> {
+ let with_pane_id = with_pane.pid();
+ if self.draw_pane_frames {
+ with_pane.set_content_offset(Offset::frame(1));
+ }
+ let removed_pane = self.panes.remove(&pane_id).map(|removed_pane| {
+ let with_pane_id = with_pane.pid();
+ let removed_pane_geom = removed_pane.current_geom();
+ with_pane.set_geom(removed_pane_geom);
+ self.panes.insert(with_pane_id, with_pane);
+ removed_pane
+ });
+
+ // move clients from the previously active pane to the new pane we just inserted
+ self.move_clients_between_panes(pane_id, with_pane_id);
+ removed_pane
+ }
pub fn insert_pane(&mut self, pane_id: PaneId, mut pane: Box<dyn Pane>) {
let cursor_height_width_ratio = self.cursor_height_width_ratio();
let pane_grid = TiledPaneGrid::new(
&mut self.panes,
+ &self.panes_to_hide,
*self.display_area.borrow(),
*self.viewport.borrow(),
);
@@ -133,6 +178,7 @@ impl TiledPanes {
let cursor_height_width_ratio = self.cursor_height_width_ratio();
let pane_grid = TiledPaneGrid::new(
&mut self.panes,
+ &self.panes_to_hide,
*self.display_area.borrow(),
*self.viewport.borrow(),
);
@@ -166,6 +212,7 @@ impl TiledPanes {
pub fn relayout(&mut self, direction: Direction) {
let mut pane_grid = TiledPaneGrid::new(
&mut self.panes,
+ &self.panes_to_hide,
*self.display_area.borrow(),
*self.viewport.borrow(),
);
@@ -399,12 +446,13 @@ impl TiledPanes {
{
let mut display_area = self.display_area.borrow_mut();
let mut viewport = self.viewport.borrow_mut();
- let panes = self
- .panes
- .iter_mut()
- .filter(|(pid, _)| !self.panes_to_hide.contains(pid));
let Size { rows, cols } = new_screen_size;
- let mut pane_grid = TiledPaneGrid::new(panes, *display_area, *viewport);
+ let mut pane_grid = TiledPaneGrid::new(
+ &mut self.panes,
+ &self.panes_to_hide,
+ *display_area,
+ *viewport,
+ );
if pane_grid.layout(Direction::Horizontal, cols).is_ok() {
let column_difference = cols as isize - display_area.cols as isize;
// FIXME: Should the viewport be an Offset?
@@ -427,6 +475,7 @@ impl TiledPanes {
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(),
);
@@ -440,6 +489,7 @@ impl TiledPanes {
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(),
);
@@ -453,6 +503,7 @@ impl TiledPanes {
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(),
);
@@ -466,6 +517,7 @@ impl TiledPanes {
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(),
);
@@ -479,6 +531,7 @@ impl TiledPanes {
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(),
);
@@ -492,6 +545,7 @@ impl TiledPanes {
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(),
);
@@ -507,6 +561,7 @@ impl TiledPanes {
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
let pane_grid = TiledPaneGrid::new(
&mut self.panes,
+ &self.panes_to_hide,
*self.display_area.borrow(),
*self.viewport.borrow(),
);
@@ -522,6 +577,7 @@ impl TiledPanes {
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
let pane_grid = TiledPaneGrid::new(
&mut self.panes,
+ &self.panes_to_hide,
*self.display_area.borrow(),
*self.viewport.borrow(),
);
@@ -547,6 +603,7 @@ impl TiledPanes {
Some(active_pane_id) => {
let pane_grid = TiledPaneGrid::new(
&mut self.panes,
+ &self.panes_to_hide,
*self.display_area.borrow(),
*self.viewport.borrow(),
);
@@ -587,6 +644,7 @@ impl TiledPanes {
Some(active_pane_id) => {
let pane_grid = TiledPaneGrid::new(
&mut self.panes,
+ &self.panes_to_hide,
*self.display_area.borrow(),
*self.viewport.borrow(),
);
@@ -627,6 +685,7 @@ impl TiledPanes {
Some(active_pane_id) => {
let pane_grid = TiledPaneGrid::new(
&mut self.panes,
+ &self.panes_to_hide,
*self.display_area.borrow(),
*self.viewport.borrow(),
);
@@ -667,6 +726,7 @@ impl TiledPanes {
Some(active_pane_id) => {
let pane_grid = TiledPaneGrid::new(
&mut self.panes,
+ &self.panes_to_hide,
*self.display_area.borrow(),
*self.viewport.borrow(),
);
@@ -706,6 +766,7 @@ impl TiledPanes {
let active_pane_id = self.get_active_pane_id(client_id).unwrap();
let pane_grid = TiledPaneGrid::new(
&mut self.panes,
+ &self.panes_to_hide,
*self.display_area.borrow(),
*self.viewport.borrow(),
);
@@ -736,6 +797,7 @@ impl TiledPanes {
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(),
);
@@ -770,6 +832,7 @@ impl TiledPanes {
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(),
);
@@ -804,6 +867,7 @@ impl TiledPanes {
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(),
);
@@ -838,6 +902,7 @@ impl TiledPanes {
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(),
);
@@ -877,6 +942,7 @@ impl TiledPanes {
match self
.panes
.iter()
+ .filter(|(p_id, _)| !self.panes_to_hide.contains(p_id))
.find(|(p_id, p)| **p_id != pane_id && p.selectable())
.map(|(p_id, _p)| p_id)
{
@@ -890,9 +956,13 @@ impl TiledPanes {
None => self.active_panes.clear(),
}
}
+ pub fn extract_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
+ self.panes.remove(&pane_id)
+ }
pub fn remove_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
let mut pane_grid = TiledPaneGrid::new(
&mut self.panes,
+ &self.panes_to_hide,
*self.display_area.borrow(),
*self.viewport.borrow(),
);
@@ -1015,6 +1085,24 @@ impl TiledPanes {
pub fn panes_to_hide_count(&self) -> usize {
self.panes_to_hide.len()
}
+ pub fn add_to_hidden_panels(&mut self, pid: PaneId) {
+ self.panes_to_hide.insert(pid);
+ }
+ pub fn remove_from_hidden_panels(&mut self, pid: PaneId) {
+ self.panes_to_hide.remove(&pid);
+ }
+ fn move_clients_between_panes(&mut self, from_pane_id: PaneId, to_pane_id: PaneId) {
+ let clients_in_pane: Vec<ClientId> = self
+ .active_panes
+ .iter()
+ .filter(|(_cid, pid)| **pid == from_pane_id)
+ .map(|(cid, _pid)| *cid)
+ .collect();
+ for client_id in clients_in_pane {
+ self.active_panes.remove(&client_id);
+ self.active_panes.insert(client_i