Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add RenameSession mode and related actions #3886

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion default-plugins/status-bar/src/first_line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ fn get_key_shortcut_for_mode<'a>(
InputMode::Resize => KeyAction::Resize,
InputMode::Move => KeyAction::Move,
InputMode::Scroll | InputMode::Search | InputMode::EnterSearch => KeyAction::Search,
InputMode::Session => KeyAction::Session,
InputMode::Session | InputMode::RenameSession => KeyAction::Session,
};
for shortcut in shortcuts.iter_mut() {
if shortcut.action == key_action {
Expand Down
3 changes: 2 additions & 1 deletion default-plugins/status-bar/src/one_line_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,7 @@ fn add_keygroup_separator(help: &ModeInfo, max_len: usize) -> Option<LinePart> {
let mode_help_text = match help.mode {
InputMode::RenamePane => Some("RENAMING PANE"),
InputMode::RenameTab => Some("RENAMING TAB"),
InputMode::RenameSession => Some("RENAMING SESSION"),
InputMode::EnterSearch => Some("ENTERING SEARCH TERM"),
InputMode::Search => Some("SEARCHING"),
_ => None,
Expand Down Expand Up @@ -1290,7 +1291,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<KeyWithModifier
(s("Previous Tab"), s("Previous"), action_key(&km, &[A::GoToPreviousTab, TO_NORMAL])),
(s("Next Tab"), s("Next"), action_key(&km, &[A::GoToNextTab, TO_NORMAL])),
(s("Select pane"), s("Select"), to_basemode_key),
]} else if matches!(mi.mode, IM::RenamePane | IM::RenameTab) { vec![
]} else if matches!(mi.mode, IM::RenamePane | IM::RenameTab | IM::RenameSession) { vec![
(s("When done"), s("Done"), to_basemode_key),
]} else { vec![] }
}
Expand Down
4 changes: 3 additions & 1 deletion default-plugins/status-bar/src/second_line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<KeyWithModifier
]} else if mi.mode == IM::Session { vec![
(s("Detach"), s("Detach"), action_key(&km, &[Action::Detach])),
(s("Session Manager"), s("Manager"), action_key(&km, &[A::LaunchOrFocusPlugin(Default::default(), true, true, false, false), TO_NORMAL])), // not entirely accurate
(s("Rename session"), s("Rename"),
action_key(&km, &[A::SwitchToMode(IM::RenameSession), A::SessionNameInput(vec![0])])),
(s("Select pane"), s("Select"), to_normal_key),
]} else if mi.mode == IM::Tmux { vec![
(s("Move focus"), s("Move"), action_key_group(&km, &[
Expand All @@ -265,7 +267,7 @@ fn get_keys_and_hints(mi: &ModeInfo) -> Vec<(String, String, Vec<KeyWithModifier
(s("Previous Tab"), s("Previous"), action_key(&km, &[A::GoToPreviousTab, TO_NORMAL])),
(s("Next Tab"), s("Next"), action_key(&km, &[A::GoToNextTab, TO_NORMAL])),
(s("Select pane"), s("Select"), to_normal_key),
]} else if matches!(mi.mode, IM::RenamePane | IM::RenameTab) { vec![
]} else if matches!(mi.mode, IM::RenamePane | IM::RenameSession | IM::RenameTab) { vec![
(s("When done"), s("Done"), to_normal_key),
(s("Select pane"), s("Select"), action_key_group(&km, &[
&[A::MoveFocus(Dir::Left)], &[A::MoveFocus(Dir::Down)],
Expand Down
9 changes: 8 additions & 1 deletion default-plugins/tab-bar/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,15 @@ impl ZellijPlugin for State {
ThemeHue::Light => self.mode_info.style.colors.white,
};

let mut name = self.mode_info.session_name.as_deref();
if self.mode_info.mode == InputMode::RenameSession
&& name.map(|s| s.len() == 0).unwrap_or_default()
{
name = Some("Enter name...");
}

self.tab_line = tab_line(
self.mode_info.session_name.as_deref(),
name,
all_tabs,
active_tab_index,
cols.saturating_sub(1),
Expand Down
6 changes: 6 additions & 0 deletions zellij-client/src/input_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ impl InputHandler {
None,
);
}
if self.mode == InputMode::RenameSession {
self.dispatch_action(
Action::SessionNameInput(pasted_text.as_bytes().to_vec()),
None,
);
}
},
_ => {},
}
Expand Down
10 changes: 10 additions & 0 deletions zellij-server/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,16 @@ pub(crate) fn route_action(
.send_to_screen(ScreenInstruction::UndoRenameTab(client_id))
.with_context(err_context)?;
},
Action::SessionNameInput(c) => {
senders
.send_to_screen(ScreenInstruction::UpdateSessionName(c, client_id))
.with_context(err_context)?;
},
Action::UndoRenameSession => {
senders
.send_to_screen(ScreenInstruction::UndoRenameSession(client_id))
.with_context(err_context)?;
},
Action::MoveTab(direction) => {
let screen_instr = match direction {
Direction::Left => ScreenInstruction::MoveTabLeft(client_id),
Expand Down
88 changes: 88 additions & 0 deletions zellij-server/src/screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ pub enum ScreenInstruction {
ToggleTab(ClientId),
UpdateTabName(Vec<u8>, ClientId),
UndoRenameTab(ClientId),
UpdateSessionName(Vec<u8>, ClientId),
UndoRenameSession(ClientId),
MoveTabLeft(ClientId),
MoveTabRight(ClientId),
TerminalResize(Size),
Expand Down Expand Up @@ -501,6 +503,8 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::GoToTabName(..) => ScreenContext::GoToTabName,
ScreenInstruction::UpdateTabName(..) => ScreenContext::UpdateTabName,
ScreenInstruction::UndoRenameTab(..) => ScreenContext::UndoRenameTab,
ScreenInstruction::UpdateSessionName(..) => ScreenContext::UpdateSessionName,
ScreenInstruction::UndoRenameSession(..) => ScreenContext::UndoRenameSession,
ScreenInstruction::MoveTabLeft(..) => ScreenContext::MoveTabLeft,
ScreenInstruction::MoveTabRight(..) => ScreenContext::MoveTabRight,
ScreenInstruction::TerminalResize(..) => ScreenContext::TerminalResize,
Expand Down Expand Up @@ -689,6 +693,7 @@ pub(crate) struct Screen {
copy_options: CopyOptions,
debug: bool,
session_name: String,
visual_session_name: Option<String>,
session_infos_on_machine: BTreeMap<String, SessionInfo>, // String is the session name, can
// also be this session
resurrectable_sessions: BTreeMap<String, Duration>, // String is the session name, duration is
Expand Down Expand Up @@ -753,6 +758,7 @@ impl Screen {
copy_options,
debug,
session_name,
visual_session_name: None,
session_infos_on_machine,
default_layout,
default_layout_name,
Expand Down Expand Up @@ -1674,6 +1680,70 @@ impl Screen {
Ok(())
}

pub fn update_visual_session_rename(
&mut self,
buf: Vec<u8>,
client_id: ClientId,
) -> Result<()> {
let err_context = || format!("failed to update session name for client id: {client_id:?}");

let s = str::from_utf8(&buf)
.with_context(|| format!("failed to construct tab name from buf: {buf:?}"))
.with_context(err_context)?;

if self.visual_session_name.is_none() {
self.visual_session_name = Some(String::new());
}
let name = self.visual_session_name.as_mut().unwrap();

match s {
"\0" => {
*name = String::new();
},
"\u{007F}" | "\u{0008}" => {
// delete and backspace keys
name.pop();
},
c => {
// It only allows printable unicode
if buf.iter().all(|u| matches!(u, 0x20..=0x7E | 0xA0..=0xFF)) {
name.push_str(c);
}
},
}

self.generate_and_report_mode_info(client_id, self.visual_session_name.clone())
.with_context(err_context)
}

pub fn undo_visual_session_rename(&mut self, client_id: ClientId) -> Result<()> {
let err_context = || format!("failed to undo session rename for client {}", client_id);
_ = self.visual_session_name.take();
self.generate_and_report_mode_info(client_id, Some(self.session_name.clone()))
.with_context(err_context)
}

pub fn generate_and_report_mode_info(
&mut self,
client_id: ClientId,
name: Option<String>,
) -> Result<()> {
let mut mode_info = self
.mode_info
.get(&client_id)
.unwrap_or(&self.default_mode_info)
.clone();
mode_info.session_name = name;
self.bus
.senders
.send_to_plugin(PluginInstruction::Update(vec![(
None,
Some(client_id),
Event::ModeUpdate(mode_info),
)]))
.context("Failed to report mode info")
}

pub fn update_active_tab_name(&mut self, buf: Vec<u8>, client_id: ClientId) -> Result<()> {
let err_context =
|| format!("failed to update active tabs name for client id: {client_id:?}");
Expand Down Expand Up @@ -1911,6 +1981,14 @@ impl Screen {
}
}

if mode_info.mode == InputMode::RenameSession {
self.visual_session_name = Some(self.session_name.clone());
} else if let Some(name) = self.visual_session_name.take() {
self.bus
.senders
.send_to_screen(ScreenInstruction::RenameSession(name, client_id))?;
}

self.style = mode_info.style;
self.mode_info.insert(client_id, mode_info.clone());
for tab in self.tabs.values_mut() {
Expand Down Expand Up @@ -3651,6 +3729,16 @@ pub(crate) fn screen_thread_main(
screen.unblock_input()?;
screen.render(None)?;
},
ScreenInstruction::UpdateSessionName(c, client_id) => {
screen.update_visual_session_rename(c, client_id)?;
screen.unblock_input()?;
screen.render(None)?;
},
ScreenInstruction::UndoRenameSession(client_id) => {
screen.undo_visual_session_rename(client_id)?;
screen.unblock_input()?;
screen.render(None)?;
},
ScreenInstruction::MoveTabLeft(client_id) => {
if pending_tab_ids.is_empty() {
screen.move_active_tab_to_left(client_id)?;
Expand Down
6 changes: 6 additions & 0 deletions zellij-utils/assets/prost/api.action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,8 @@ pub enum ActionName {
MoveTab = 83,
KeybindPipe = 84,
TogglePanePinned = 85,
SessionNameInput = 86,
UndoRenameSession = 87,
}
impl ActionName {
/// String value of the enum field names used in the ProtoBuf definition.
Expand Down Expand Up @@ -550,6 +552,8 @@ impl ActionName {
ActionName::MoveTab => "MoveTab",
ActionName::KeybindPipe => "KeybindPipe",
ActionName::TogglePanePinned => "TogglePanePinned",
ActionName::SessionNameInput => "SessionNameInput",
ActionName::UndoRenameSession => "UndoRenameSession",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
Expand Down Expand Up @@ -641,6 +645,8 @@ impl ActionName {
"MoveTab" => Some(Self::MoveTab),
"KeybindPipe" => Some(Self::KeybindPipe),
"TogglePanePinned" => Some(Self::TogglePanePinned),
"SessionNameInput" => Some(Self::SessionNameInput),
"UndoRenameSession" => Some(Self::UndoRenameSession),
_ => None,
}
}
Expand Down
4 changes: 4 additions & 0 deletions zellij-utils/assets/prost/api.input_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub enum InputMode {
Prompt = 12,
/// / `Tmux` mode allows for basic tmux keybindings functionality
Tmux = 13,
/// / `RenameSession` mode allows assigning a new name to active session.
RenameSession = 14,
}
impl InputMode {
/// String value of the enum field names used in the ProtoBuf definition.
Expand All @@ -59,6 +61,7 @@ impl InputMode {
InputMode::Move => "Move",
InputMode::Prompt => "Prompt",
InputMode::Tmux => "Tmux",
InputMode::RenameSession => "RenameSession",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
Expand All @@ -78,6 +81,7 @@ impl InputMode {
"Move" => Some(Self::Move),
"Prompt" => Some(Self::Prompt),
"Tmux" => Some(Self::Tmux),
"RenameSession" => Some(Self::RenameSession),
_ => None,
}
}
Expand Down
4 changes: 4 additions & 0 deletions zellij-utils/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,9 @@ pub enum InputMode {
/// `RenamePane` mode allows assigning a new name to a pane.
#[serde(alias = "renamepane")]
RenamePane,
/// `RenameSession` mode allows assigning a new name to a session.
#[serde(alias = "renamesession")]
RenameSession,
/// `Session` mode allows detaching sessions
#[serde(alias = "session")]
Session,
Expand Down Expand Up @@ -1087,6 +1090,7 @@ impl FromStr for InputMode {
"scroll" | "Scroll" => Ok(InputMode::Scroll),
"renametab" | "RenameTab" => Ok(InputMode::RenameTab),
"renamepane" | "RenamePane" => Ok(InputMode::RenamePane),
"renamesession" | "RenameSession" => Ok(InputMode::RenameSession),
"session" | "Session" => Ok(InputMode::Session),
"move" | "Move" => Ok(InputMode::Move),
"prompt" | "Prompt" => Ok(InputMode::Prompt),
Expand Down
2 changes: 2 additions & 0 deletions zellij-utils/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ pub enum ScreenContext {
GoToTabName,
UpdateTabName,
UndoRenameTab,
UpdateSessionName,
UndoRenameSession,
MoveTabLeft,
MoveTabRight,
TerminalResize,
Expand Down
2 changes: 2 additions & 0 deletions zellij-utils/src/input/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ pub enum Action {
CloseFocus,
PaneNameInput(Vec<u8>),
UndoRenamePane,
SessionNameInput(Vec<u8>),
UndoRenameSession,
/// Create a new tab, optionally with a specified tab layout.
NewTab(
Option<TiledPaneLayout>,
Expand Down
1 change: 1 addition & 0 deletions zellij-utils/src/input/keybinds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ impl Keybinds {
},
InputMode::RenameTab => Action::TabNameInput(raw_bytes),
InputMode::RenamePane => Action::PaneNameInput(raw_bytes),
InputMode::RenameSession => Action::SessionNameInput(raw_bytes),
InputMode::EnterSearch => Action::SearchInput(raw_bytes),
_ => Action::NoOp,
}
Expand Down
16 changes: 16 additions & 0 deletions zellij-utils/src/kdl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ macro_rules! parse_kdl_action_arguments {
"CloseTab" => Ok(Action::CloseTab),
"ToggleTab" => Ok(Action::ToggleTab),
"UndoRenameTab" => Ok(Action::UndoRenameTab),
"UndoRenameSession" => Ok(Action::UndoRenameSession),
"Detach" => Ok(Action::Detach),
"Copy" => Ok(Action::Copy),
"Confirm" => Ok(Action::Confirm),
Expand Down Expand Up @@ -414,6 +415,7 @@ impl Action {
"Write" => Ok(Action::Write(None, bytes, false)),
"PaneNameInput" => Ok(Action::PaneNameInput(bytes)),
"TabNameInput" => Ok(Action::TabNameInput(bytes)),
"SessionNameInput" => Ok(Action::SessionNameInput(bytes)),
"SearchInput" => Ok(Action::SearchInput(bytes)),
"GoToTab" => {
let tab_index = *bytes.get(0).ok_or_else(|| {
Expand Down Expand Up @@ -728,6 +730,14 @@ impl Action {
Some(node)
},
Action::UndoRenameTab => Some(KdlNode::new("UndoRenameTab")),
Action::SessionNameInput(bytes) => {
let mut node = KdlNode::new("SessionNameInput");
for byte in bytes {
node.push(KdlValue::Base10(*byte as i64));
}
Some(node)
},
Action::UndoRenameSession => Some(KdlNode::new("UndoRenameSession")),
Action::MoveTab(direction) => {
let mut node = KdlNode::new("MoveTab");
let direction = match direction {
Expand Down Expand Up @@ -1298,6 +1308,9 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
"UndoRenamePane" => {
parse_kdl_action_arguments!(action_name, action_arguments, kdl_action)
},
"UndoRenameSession" => {
parse_kdl_action_arguments!(action_name, action_arguments, kdl_action)
},
"NoOp" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
"GoToNextTab" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action),
"GoToPreviousTab" => {
Expand Down Expand Up @@ -1475,6 +1488,9 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
"TabNameInput" => {
parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action)
},
"SessionNameInput" => {
parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action)
},
"SearchInput" => {
parse_kdl_action_u8_arguments!(action_name, action_arguments, kdl_action)
},
Expand Down
2 changes: 2 additions & 0 deletions zellij-utils/src/plugin_api/action.proto
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ enum ActionName {
MoveTab = 83;
KeybindPipe = 84;
TogglePanePinned = 85;
SessionNameInput = 86;
UndoRenameSession = 87;
}

message Position {
Expand Down
Loading