summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKunal Mohan <44079328+kunalmohan@users.noreply.github.com>2021-12-09 23:30:40 +0530
committerGitHub <noreply@github.com>2021-12-09 23:30:40 +0530
commitc75bcbd9370249f23031edcd69f6724805c1e034 (patch)
treea45dab48eb4a0eea8f008db2278ea68c8166b044
parent368c852e57f3aa0596ef0459d6e9cc41b314cf65 (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.rs2
-rw-r--r--src/tests/e2e/remote_runner.rs3
-rw-r--r--zellij-client/src/lib.rs2
-rw-r--r--zellij-server/src/lib.rs4
-rw-r--r--zellij-server/src/panes/plugin_pane.rs38
-rw-r--r--zellij-server/src/panes/terminal_pane.rs44
-rw-r--r--zellij-server/src/panes/unit/terminal_pane_tests.rs2
-rw-r--r--zellij-server/src/pty.rs2
-rw-r--r--zellij-server/src/route.rs6
-rw-r--r--zellij-server/src/screen.rs10
-rw-r--r--zellij-server/src/tab.rs36
-rw-r--r--zellij-server/src/ui/pane_contents_and_ui.rs2
-rw-r--r--zellij-tile/src/data.rs5
-rw-r--r--zellij-utils/assets/config/default.yaml23
-rw-r--r--zellij-utils/src/errors.rs1
-rw-r--r--zellij-utils/src/input/actions.rs1
-rw-r--r--zellij-utils/src/input/keybinds.rs1
-rw-r--r--zellij-utils/src/input/layout.rs11
-rw-r--r--zellij-utils/src/input/mod.rs2
-rw-r--r--zellij-utils/src/input/unit/layout_test.rs58
-rw-r--r--zellij-utils/src/ipc.rs2
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