diff options
author | Kunal Mohan <44079328+kunalmohan@users.noreply.github.com> | 2021-12-09 23:30:40 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-09 23:30:40 +0530 |
commit | c75bcbd9370249f23031edcd69f6724805c1e034 (patch) | |
tree | a45dab48eb4a0eea8f008db2278ea68c8166b044 | |
parent | 368c852e57f3aa0596ef0459d6e9cc41b314cf65 (diff) |
Feature: Add pane names (#928)
* Read pane name from layout
* Update pane name at runtime
* Fix tests
* prefer and render pane name over pane title
* fix clippy errors
* fix after rebase
-rw-r--r-- | default-plugins/status-bar/src/first_line.rs | 2 | ||||
-rw-r--r-- | src/tests/e2e/remote_runner.rs | 3 | ||||
-rw-r--r-- | zellij-client/src/lib.rs | 2 | ||||
-rw-r--r-- | zellij-server/src/lib.rs | 4 | ||||
-rw-r--r-- | zellij-server/src/panes/plugin_pane.rs | 38 | ||||
-rw-r--r-- | zellij-server/src/panes/terminal_pane.rs | 44 | ||||
-rw-r--r-- | zellij-server/src/panes/unit/terminal_pane_tests.rs | 2 | ||||
-rw-r--r-- | zellij-server/src/pty.rs | 2 | ||||
-rw-r--r-- | zellij-server/src/route.rs | 6 | ||||
-rw-r--r-- | zellij-server/src/screen.rs | 10 | ||||
-rw-r--r-- | zellij-server/src/tab.rs | 36 | ||||
-rw-r--r-- | zellij-server/src/ui/pane_contents_and_ui.rs | 2 | ||||
-rw-r--r-- | zellij-tile/src/data.rs | 5 | ||||
-rw-r--r-- | zellij-utils/assets/config/default.yaml | 23 | ||||
-rw-r--r-- | zellij-utils/src/errors.rs | 1 | ||||
-rw-r--r-- | zellij-utils/src/input/actions.rs | 1 | ||||
-rw-r--r-- | zellij-utils/src/input/keybinds.rs | 1 | ||||
-rw-r--r-- | zellij-utils/src/input/layout.rs | 11 | ||||
-rw-r--r-- | zellij-utils/src/input/mod.rs | 2 | ||||
-rw-r--r-- | zellij-utils/src/input/unit/layout_test.rs | 58 | ||||
-rw-r--r-- | zellij-utils/src/ipc.rs | 2 |
21 files changed, 233 insertions, 22 deletions
diff --git a/default-plugins/status-bar/src/first_line.rs b/default-plugins/status-bar/src/first_line.rs index 5bc510868..cdb1decee 100644 --- a/default-plugins/status-bar/src/first_line.rs +++ b/default-plugins/status-bar/src/first_line.rs @@ -279,7 +279,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart { colored_elements, separator, ), - InputMode::Pane => key_indicators( + InputMode::Pane | InputMode::RenamePane => key_indicators( max_len, &[ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock), diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs index e27e9fc21..205710f1d 100644 --- a/src/tests/e2e/remote_runner.rs +++ b/src/tests/e2e/remote_runner.rs @@ -131,7 +131,8 @@ fn read_from_channel( let last_snapshot = last_snapshot.clone(); let cursor_coordinates = cursor_coordinates.clone(); let mut vte_parser = vte::Parser::new(); - let mut terminal_output = TerminalPane::new(0, *pane_geom, Palette::default(), 0); // 0 is the pane index + let mut terminal_output = + TerminalPane::new(0, *pane_geom, Palette::default(), 0, String::new()); // 0 is the pane index let mut retries_left = 3; move || { let mut should_sleep = false; diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs index 38ab30210..9bc8828aa 100644 --- a/zellij-client/src/lib.rs +++ b/zellij-client/src/lib.rs @@ -160,7 +160,7 @@ pub fn start_client( client_attributes, Box::new(opts), Box::new(config_options.clone()), - layout.unwrap(), + Box::new(layout.unwrap()), Some(config.plugins.clone()), ) } diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 6a94742ca..c1f238edc 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -58,7 +58,7 @@ pub enum ServerInstruction { ClientAttributes, Box<CliArgs>, Box<Options>, - LayoutFromYaml, + Box<LayoutFromYaml>, ClientId, Option<PluginsConfig>, ), @@ -531,7 +531,7 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) { pub struct SessionOptions { pub opts: Box<CliArgs>, pub config_options: Box<Options>, - pub layout: LayoutFromYaml, + pub layout: Box<LayoutFromYaml>, pub plugins: Option<PluginsConfig>, } diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs index a9d5d3e5c..46c5ed7a2 100644 --- a/zellij-server/src/panes/plugin_pane.rs +++ b/zellij-server/src/panes/plugin_pane.rs @@ -12,7 +12,7 @@ use crate::ClientId; use zellij_utils::pane_size::Offset; use zellij_utils::position::Position; use zellij_utils::shared::ansi_len; -use zellij_utils::zellij_tile::prelude::{Event, Mouse, PaletteColor}; +use zellij_utils::zellij_tile::prelude::{Event, InputMode, Mouse, PaletteColor}; use zellij_utils::{ channels::SenderWithContext, pane_size::{Dimension, PaneGeom}, @@ -28,6 +28,7 @@ pub(crate) struct PluginPane { pub send_plugin_instructions: SenderWithContext<PluginInstruction>, pub active_at: Instant, pub pane_title: String, + pub pane_name: String, frame: bool, borderless: bool, } @@ -38,6 +39,7 @@ impl PluginPane { position_and_size: PaneGeom, send_plugin_instructions: SenderWithContext<PluginInstruction>, title: String, + pane_name: String, ) -> Self { Self { pid, @@ -51,6 +53,7 @@ impl PluginPane { content_offset: Offset::default(), pane_title: title, borderless: false, + pane_name, } } } @@ -209,14 +212,29 @@ impl Pane for PluginPane { None } } - fn render_frame(&mut self, _client_id: ClientId, frame_params: FrameParams) -> Option<String> { + fn render_frame( + &mut self, + _client_id: ClientId, + frame_params: FrameParams, + input_mode: InputMode, + ) -> Option<String> { // FIXME: This is a hack that assumes all fixed-size panes are borderless. This // will eventually need fixing! if self.frame && !(self.geom.rows.is_fixed() || self.geom.cols.is_fixed()) { + let pane_title = if self.pane_name.is_empty() + && input_mode == InputMode::RenamePane + && frame_params.is_main_client + { + String::from("Enter name...") + } else if self.pane_name.is_empty() { + self.pane_title.clone() + } else { + self.pane_name.clone() + }; let frame = PaneFrame::new( self.current_geom().into(), (0, 0), // scroll position - self.pane_title.clone(), + pane_title, frame_params, ); Some(frame.render()) @@ -231,6 +249,20 @@ impl Pane for PluginPane { ) -> Option<String> { None } + fn update_name(&mut self, name: &str) { + match name { + "\0" => { + self.pane_name = String::new(); + } + "\u{007F}" | "\u{0008}" => { + //delete and backspace keys + self.pane_name.pop(); + } + c => { + self.pane_name.push_str(c); + } + } + } fn pid(&self) -> PaneId { PaneId::Plugin(self.pid) } diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index a6da76709..284d6fe9b 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -17,7 +17,7 @@ use zellij_utils::{ pane_size::{Dimension, PaneGeom}, position::Position, vte, - zellij_tile::data::{Palette, PaletteColor}, + zellij_tile::data::{InputMode, Palette, PaletteColor}, }; pub const SELECTION_SCROLL_INTERVAL_MS: u64 = 10; @@ -44,6 +44,7 @@ pub struct TerminalPane { selection_scrolled_at: time::Instant, content_offset: Offset, pane_title: String, + pane_name: String, frame: HashMap<ClientId, PaneFrame>, borderless: bool, fake_cursor_locations: HashSet<(usize, usize)>, // (x, y) - these hold a record of previous fake cursors which we need to clear on render @@ -278,16 +279,31 @@ impl Pane for TerminalPane { None } } - fn render_frame(&mut self, client_id: ClientId, frame_params: FrameParams) -> Option<String> { + fn render_frame( + &mut self, + client_id: ClientId, + frame_params: FrameParams, + input_mode: InputMode, + ) -> Option<String> { // TODO: remove the cursor stuff from here let mut vte_output = None; - let frame = PaneFrame::new( - self.current_geom().into(), - self.grid.scrollback_position_and_length(), + let pane_title = if self.pane_name.is_empty() + && input_mode == InputMode::RenamePane + && frame_params.is_main_client + { + String::from("Enter name...") + } else if self.pane_name.is_empty() { self.grid .title .clone() - .unwrap_or_else(|| self.pane_title.clone()), + .unwrap_or_else(|| self.pane_title.clone()) + } else { + self.pane_name.clone() + }; + let frame = PaneFrame::new( + self.current_geom().into(), + self.grid.scrollback_position_and_length(), + pane_title, frame_params, ); match self.frame.get(&client_id) { @@ -336,6 +352,20 @@ impl Pane for TerminalPane { } vte_output } + fn update_name(&mut self, name: &str) { + match name { + "\0" => { + self.pane_name = String::new(); + } + "\u{007F}" | "\u{0008}" => { + //delete and backspace keys + self.pane_name.pop(); + } + c => { + self.pane_name.push_str(c); + } + } + } fn pid(&self) -> PaneId { PaneId::Terminal(self.pid) } @@ -475,6 +505,7 @@ impl TerminalPane { position_and_size: PaneGeom, palette: Palette, pane_index: usize, + pane_name: String, ) -> TerminalPane { let initial_pane_title = format!("Pane #{}", pane_index); let grid = Grid::new( @@ -495,6 +526,7 @@ impl TerminalPane { colors: palette, selection_scrolled_at: time::Instant::now(), pane_title: initial_pane_title, + pane_name, borderless: false, fake_cursor_locations: HashSet::new(), } diff --git a/zellij-server/src/panes/unit/terminal_pane_tests.rs b/zellij-server/src/panes/unit/terminal_pane_tests.rs index ef24a77f2..56ec2cf96 100644 --- a/zellij-server/src/panes/unit/terminal_pane_tests.rs +++ b/zellij-server/src/panes/unit/terminal_pane_tests.rs @@ -14,7 +14,7 @@ pub fn scrolling_inside_a_pane() { let pid = 1; let palette = Palette::default(); - let mut terminal_pane = TerminalPane::new(pid, fake_win_size, palette, 0); // 0 is the pane index + let mut terminal_pane = TerminalPane::new(pid, fake_win_size, palette, 0, String::new()); // 0 is the pane index let mut text_to_fill_pane = String::new(); for i in 0..30 { writeln!(&mut text_to_fill_pane, "\rline {}", i + 1).unwrap(); diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs index d7e2ad47b..99f7e3756 100644 --- a/zellij-server/src/pty.rs +++ b/zellij-server/src/pty.rs @@ -74,7 +74,7 @@ pub(crate) struct Pty { use std::convert::TryFrom; -pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) { +pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<LayoutFromYaml>) { loop { let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel"); err_ctx.add_call(ContextType::Pty((&event).into())); diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index e24d2d4f1..4626bc5f1 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -226,6 +226,12 @@ fn route_action( }; session.senders.send_to_pty(pty_instr).unwrap(); } + Action::PaneNameInput(c) => { + session + .senders + .send_to_screen(ScreenInstruction::UpdatePaneName(c, client_id)) + .unwrap(); + } Action::Run(command) => { let run_cmd = Some(TerminalAction::RunCommand(command.clone().into())); let pty_instr = match command.direction { diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 1400235ea..f5beade59 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -68,6 +68,7 @@ pub enum ScreenInstruction { TogglePaneFrames, SetSelectable(PaneId, bool, usize), ClosePane(PaneId, Option<ClientId>), + UpdatePaneName(Vec<u8>, ClientId), NewTab(Layout, Vec<RawFd>, ClientId), SwitchTabNext(ClientId), SwitchTabPrev(ClientId), @@ -140,6 +141,7 @@ impl From<&ScreenInstruction> for ScreenContext { ScreenInstruction::TogglePaneFrames => ScreenContext::TogglePaneFrames, ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable, ScreenInstruction::ClosePane(..) => ScreenContext::ClosePane, + ScreenInstruction::UpdatePaneName(..) => ScreenContext::UpdatePaneName, ScreenInstruction::NewTab(..) => ScreenContext::NewTab, ScreenInstruction::SwitchTabNext(..) => ScreenContext::SwitchTabNext, ScreenInstruction::SwitchTabPrev(..) => ScreenContext::SwitchTabPrev, @@ -975,6 +977,14 @@ pub(crate) fn screen_thread_main( } screen.update_tabs(); } + ScreenInstruction::UpdatePaneName(c, client_id) => { + screen + .get_active_tab_mut(client_id) + .unwrap() + .update_active_pane_name(c, client_id); + + screen.render(); + } ScreenInstruction::ToggleActiveTerminalFullscreen(client_id) => { screen .get_active_tab_mut(client_id) diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index f684b9962..a0386a456 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -23,8 +23,9 @@ use std::time::Instant; use std::{ cmp::Reverse, collections::{BTreeMap, HashMap, HashSet}, + str, }; -use zellij_tile::data::{Event, ModeInfo, Palette, PaletteColor}; +use zellij_tile::data::{Event, InputMode, ModeInfo, Palette, PaletteColor}; use zellij_utils::{ input::{ layout::{Direction, Layout, Run}, @@ -181,12 +182,18 @@ pub trait Pane { fn selectable(&self) -> bool; fn set_selectable(&mut self, selectable: bool); fn render(&mut self, client_id: Option<ClientId>) -> Option<String>; - fn render_frame(&mut self, client_id: ClientId, frame_params: FrameParams) -> Option<String>; + fn render_frame( + &mut self, + client_id: ClientId, + frame_params: FrameParams, + input_mode: InputMode, + ) -> Option<String>; fn render_fake_cursor( &mut self, cursor_color: PaletteColor, text_color: PaletteColor, ) -> Option<String>; + fn update_name(&mut self, name: &str); fn pid(&self) -> PaneId; fn reduce_height(&mut self, percent: f64); fn increase_height(&mut self, percent: f64); @@ -400,6 +407,7 @@ impl Tab { *position_and_size, self.senders.to_plugin.as_ref().unwrap().clone(), pane_title, + layout.pane_name.clone().unwrap_or_default(), ); new_plugin.set_borderless(layout.borderless); self.panes.insert(PaneId::Plugin(pid), Box::new(new_plugin)); @@ -412,6 +420,7 @@ impl Tab { *position_and_size, self.colors, next_terminal_position, + layout.pane_name.clone().unwrap_or_default(), ); new_pane.set_borderless(layout.borderless); self.panes @@ -588,6 +597,7 @@ impl Tab { bottom_winsize, self.colors, next_terminal_position, + String::new(), ); terminal_to_split.set_geom(top_winsize); self.panes.insert(pid, Box::new(new_terminal)); @@ -604,6 +614,7 @@ impl Tab { right_winsize, self.colors, next_terminal_position, + String::new(), ); terminal_to_split.set_geom(left_winsize); self.panes.insert(pid, Box::new(new_terminal)); @@ -647,6 +658,7 @@ impl Tab { bottom_winsize, self.colors, next_terminal_position, + String::new(), ); active_pane.set_geom(top_winsize); self.panes.insert(pid, Box::new(new_terminal)); @@ -684,8 +696,13 @@ impl Tab { } let terminal_ws = active_pane.position_and_size(); if let Some((left_winsize, right_winsize)) = split(Direction::Vertical, &terminal_ws) { - let new_terminal = - TerminalPane::new(term_pid, right_winsize, self.colors, next_terminal_position); + let new_terminal = TerminalPane::new( + term_pid, + right_winsize, + self.colors, + next_terminal_position, + String::new(), + ); active_pane.set_geom(left_winsize); self.panes.insert(pid, Box::new(new_terminal)); } @@ -3451,6 +3468,17 @@ impl Tab { .unwrap(); } } + + pub fn update_active_pane_name(&mut self, buf: Vec<u8>, client_id: ClientId) { + if let Some(active_terminal_id) = self.get_active_terminal_id(client_id) { + let s = str::from_utf8(&buf).unwrap(); + let active_terminal = self + .panes + .get_mut(&PaneId::Terminal(active_terminal_id)) + .unwrap(); + active_terminal.update_name(s); + } + } } #[allow(clippy::borrowed_box)] diff --git a/zellij-server/src/ui/pane_contents_and_ui.rs b/zellij-server/src/ui/pane_contents_and_ui.rs index f2742f17e..25550fe9f 100644 --- a/zellij-server/src/ui/pane_contents_and_ui.rs +++ b/zellij-server/src/ui/pane_contents_and_ui.rs @@ -132,7 +132,7 @@ impl<'a> PaneContentsAndUi<'a> { other_cursors_exist_in_session: self.multiple_users_exist_in_session, } }; - if let Some(vte_output) = self.pane.render_frame(client_id, frame_params) { + if let Some(vte_output) = self.pane.render_frame(client_id, frame_params, client_mode) { // FIXME: Use Termion for cursor and style clearing? self.output.push_to_client( client_id, diff --git a/zellij-tile/src/data.rs b/zellij-tile/src/data.rs index e43049a54..3e269af3b 100644 --- a/zellij-tile/src/data.rs +++ b/zellij-tile/src/data.rs @@ -77,8 +77,12 @@ pub enum InputMode { /// `Scroll` mode allows scrolling up and down within a pane. #[serde(alias = "scroll")] Scroll, + /// `RenameTab` mode allows assigning a new name to a tab. #[serde(alias = "renametab")] RenameTab, + /// `RenamePane` mode allows assigning a new name to a pane. + #[serde(alias = "renamepane")] + RenamePane, /// `Session` mode allows detaching sessions #[serde(alias = "session")] Session, @@ -133,6 +137,7 @@ impl FromStr for InputMode { "session" => Ok(InputMode::Session), "move" => Ok(InputMode::Move), "prompt" => Ok(InputMode::Prompt), + "renamepane" => Ok(InputMode::RenamePane), e => Err(e.to_string().into()), } } diff --git a/zellij-utils/assets/config/default.yaml b/zellij-utils/assets/config/default.yaml index 0c06485a8..4de2a92e4 100644 --- a/zellij-utils/assets/config/default.yaml +++ b/zellij-utils/assets/config/default.yaml @@ -135,6 +135,8 @@ keybinds: key: [ Alt: '[',] - action: [FocusNextPane,] key: [ Alt: ']',] + - action: [SwitchToMode: RenamePane, PaneNameInput: [0],] + key: [Char: 'c'] move: - action: [SwitchToMode: Locked,] key: [Ctrl: 'g'] @@ -304,6 +306,27 @@ keybinds: key: [ Alt: '[',] - action: [FocusNextPane,] key: [ Alt: ']',] + renamepane: + - action: [SwitchToMode: Normal,] + key: [Ctrl: 'c', Ctrl: 's', Char: ' ',] + - action: [SwitchToMode: Pane,] + key: [Char: "\n",] + - action: [PaneNameInput: [27] , SwitchToMode: Pane,] + key: [Esc,] + - action: [NewPane: ,] + key: [ Alt: 'n',] + - action: [MoveFocus: Left,] + key: [ Alt: 'h',] + - action: [MoveFocus: Right,] + key: [ Alt: 'l',] + - action: [MoveFocus: Down,] + key: [ Alt: 'j',] + - action: [MoveFocus: Up,] + key: [ Alt: 'k',] + - action: [FocusPreviousPane,] + key: [ Alt: '[',] + - action: [FocusNextPane,] + key: [ Alt: ']',] session: - action: [SwitchToMode: Locked,] key: [Ctrl: 'g'] diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index f8b8def98..a546e6cc7 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -251,6 +251,7 @@ pub enum ScreenContext { SetFixedHeight, SetFixedWidth, ClosePane, + UpdatePaneName, NewTab, SwitchTabNext, SwitchTabPrev, diff --git a/zellij-utils/src/i |