diff options
Diffstat (limited to 'zellij-server')
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 |