feat(ux): allow renaming sessions (#2903)

* change session name through the cli

* change session name from the session-manager

* style(fmt): rustfmt
This commit is contained in:
Aram Drevekenin 2023-11-05 15:32:05 +01:00 committed by GitHub
parent 9eb9734bcc
commit 28a165a969
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 315 additions and 16 deletions

View file

@ -7,8 +7,8 @@ use std::collections::BTreeMap;
use ui::{ use ui::{
components::{ components::{
render_controls_line, render_new_session_line, render_prompt, render_resurrection_toggle, render_controls_line, render_error, render_new_session_line, render_prompt,
Colors, render_renaming_session_screen, render_resurrection_toggle, Colors,
}, },
SessionUiInfo, SessionUiInfo,
}; };
@ -23,6 +23,8 @@ struct State {
resurrectable_sessions: ResurrectableSessions, resurrectable_sessions: ResurrectableSessions,
search_term: String, search_term: String,
new_session_name: Option<String>, new_session_name: Option<String>,
renaming_session_name: Option<String>,
error: Option<String>,
browsing_resurrection_sessions: bool, browsing_resurrection_sessions: bool,
colors: Colors, colors: Colors,
} }
@ -67,6 +69,9 @@ impl ZellijPlugin for State {
if self.browsing_resurrection_sessions { if self.browsing_resurrection_sessions {
self.resurrectable_sessions.render(rows, cols); self.resurrectable_sessions.render(rows, cols);
return; return;
} else if let Some(new_session_name) = self.renaming_session_name.as_ref() {
render_renaming_session_screen(&new_session_name, rows, cols);
return;
} }
render_resurrection_toggle(cols, false); render_resurrection_toggle(cols, false);
render_prompt( render_prompt(
@ -87,7 +92,11 @@ impl ZellijPlugin for State {
self.sessions.is_searching, self.sessions.is_searching,
self.colors, self.colors,
); );
render_controls_line(self.sessions.is_searching, rows, cols, self.colors); if let Some(error) = self.error.as_ref() {
render_error(&error, rows, cols);
} else {
render_controls_line(self.sessions.is_searching, rows, cols, self.colors);
}
} }
} }
@ -96,6 +105,10 @@ impl State {
self.sessions.reset_selected_index(); self.sessions.reset_selected_index();
} }
fn handle_key(&mut self, key: Key) -> bool { fn handle_key(&mut self, key: Key) -> bool {
if self.error.is_some() {
self.error = None;
return true;
}
let mut should_render = false; let mut should_render = false;
if let Key::Right = key { if let Key::Right = key {
if self.new_session_name.is_none() { if self.new_session_name.is_none() {
@ -110,14 +123,14 @@ impl State {
} else if let Key::Down = key { } else if let Key::Down = key {
if self.browsing_resurrection_sessions { if self.browsing_resurrection_sessions {
self.resurrectable_sessions.move_selection_down(); self.resurrectable_sessions.move_selection_down();
} else if self.new_session_name.is_none() { } else if self.new_session_name.is_none() && self.renaming_session_name.is_none() {
self.sessions.move_selection_down(); self.sessions.move_selection_down();
} }
should_render = true; should_render = true;
} else if let Key::Up = key { } else if let Key::Up = key {
if self.browsing_resurrection_sessions { if self.browsing_resurrection_sessions {
self.resurrectable_sessions.move_selection_up(); self.resurrectable_sessions.move_selection_up();
} else if self.new_session_name.is_none() { } else if self.new_session_name.is_none() && self.renaming_session_name.is_none() {
self.sessions.move_selection_up(); self.sessions.move_selection_up();
} }
should_render = true; should_render = true;
@ -126,6 +139,8 @@ impl State {
self.handle_selection(); self.handle_selection();
} else if let Some(new_session_name) = self.new_session_name.as_mut() { } else if let Some(new_session_name) = self.new_session_name.as_mut() {
new_session_name.push(character); new_session_name.push(character);
} else if let Some(renaming_session_name) = self.renaming_session_name.as_mut() {
renaming_session_name.push(character);
} else if self.browsing_resurrection_sessions { } else if self.browsing_resurrection_sessions {
self.resurrectable_sessions.handle_character(character); self.resurrectable_sessions.handle_character(character);
} else { } else {
@ -141,6 +156,12 @@ impl State {
} else { } else {
new_session_name.pop(); new_session_name.pop();
} }
} else if let Some(renaming_session_name) = self.renaming_session_name.as_mut() {
if renaming_session_name.is_empty() {
self.renaming_session_name = None;
} else {
renaming_session_name.pop();
}
} else if self.browsing_resurrection_sessions { } else if self.browsing_resurrection_sessions {
self.resurrectable_sessions.handle_backspace(); self.resurrectable_sessions.handle_backspace();
} else { } else {
@ -150,7 +171,7 @@ impl State {
} }
should_render = true; should_render = true;
} else if let Key::Ctrl('w') = key { } else if let Key::Ctrl('w') = key {
if self.sessions.is_searching { if self.sessions.is_searching || self.browsing_resurrection_sessions {
// no-op // no-op
} else if self.new_session_name.is_some() { } else if self.new_session_name.is_some() {
self.new_session_name = None; self.new_session_name = None;
@ -158,6 +179,15 @@ impl State {
self.new_session_name = Some(String::new()); self.new_session_name = Some(String::new());
} }
should_render = true; should_render = true;
} else if let Key::Ctrl('r') = key {
if self.sessions.is_searching || self.browsing_resurrection_sessions {
// no-op
} else if self.renaming_session_name.is_some() {
self.renaming_session_name = None;
} else {
self.renaming_session_name = Some(String::new());
}
should_render = true;
} else if let Key::Ctrl('c') = key { } else if let Key::Ctrl('c') = key {
if let Some(new_session_name) = self.new_session_name.as_mut() { if let Some(new_session_name) = self.new_session_name.as_mut() {
if new_session_name.is_empty() { if new_session_name.is_empty() {
@ -165,6 +195,12 @@ impl State {
} else { } else {
new_session_name.clear() new_session_name.clear()
} }
} else if let Some(renaming_session_name) = self.renaming_session_name.as_mut() {
if renaming_session_name.is_empty() {
self.renaming_session_name = None;
} else {
renaming_session_name.clear()
}
} else if !self.search_term.is_empty() { } else if !self.search_term.is_empty() {
self.search_term.clear(); self.search_term.clear();
self.sessions self.sessions
@ -190,7 +226,15 @@ impl State {
should_render = true; should_render = true;
} }
} else if let Key::Esc = key { } else if let Key::Esc = key {
hide_self(); if self.renaming_session_name.is_some() {
self.renaming_session_name = None;
should_render = true;
} else if self.new_session_name.is_some() {
self.new_session_name = None;
should_render = true;
} else {
hide_self();
}
} }
should_render should_render
} }
@ -210,6 +254,29 @@ impl State {
} else { } else {
switch_session(Some(new_session_name)); switch_session(Some(new_session_name));
} }
} else if let Some(renaming_session_name) = &self.renaming_session_name.take() {
if renaming_session_name.is_empty() {
// TODO: implement these, then implement the error UI, then implement the renaming
// session screen, then test it
self.show_error("New name must not be empty.");
return; // s that we don't hide self
} else if self.session_name.as_ref() == Some(renaming_session_name) {
// noop - we're already called that!
return; // s that we don't hide self
} else if self.sessions.has_session(&renaming_session_name) {
self.show_error("A session by this name already exists.");
return; // s that we don't hide self
} else if self
.resurrectable_sessions
.has_session(&renaming_session_name)
{
self.show_error("A resurrectable session by this name already exists.");
return; // s that we don't hide self
} else {
self.update_current_session_name_in_ui(&renaming_session_name);
rename_session(&renaming_session_name);
return; // s that we don't hide self
}
} else if let Some(selected_session_name) = self.sessions.get_selected_session_name() { } else if let Some(selected_session_name) = self.sessions.get_selected_session_name() {
let selected_tab = self.sessions.get_selected_tab_position(); let selected_tab = self.sessions.get_selected_tab_position();
let selected_pane = self.sessions.get_selected_pane_id(); let selected_pane = self.sessions.get_selected_pane_id();
@ -235,6 +302,16 @@ impl State {
.update_search_term(&self.search_term, &self.colors); .update_search_term(&self.search_term, &self.colors);
hide_self(); hide_self();
} }
fn show_error(&mut self, error_text: &str) {
self.error = Some(error_text.to_owned());
}
fn update_current_session_name_in_ui(&mut self, new_name: &str) {
if let Some(old_session_name) = self.session_name.as_ref() {
self.sessions
.update_session_name(&old_session_name, new_name);
}
self.session_name = Some(new_name.to_owned());
}
fn update_session_infos(&mut self, session_infos: Vec<SessionInfo>) { fn update_session_infos(&mut self, session_infos: Vec<SessionInfo>) {
let session_infos: Vec<SessionUiInfo> = session_infos let session_infos: Vec<SessionUiInfo> = session_infos
.iter() .iter()

View file

@ -308,6 +308,11 @@ impl ResurrectableSessions {
self.search_term.pop(); self.search_term.pop();
self.update_search_term(); self.update_search_term();
} }
pub fn has_session(&self, session_name: &str) -> bool {
self.all_resurrectable_sessions
.iter()
.any(|s| s.0 == session_name)
}
fn update_search_term(&mut self) { fn update_search_term(&mut self) {
let mut matches = vec![]; let mut matches = vec![];
let matcher = SkimMatcherV2::default().use_cache(true); let matcher = SkimMatcherV2::default().use_cache(true);

View file

@ -314,6 +314,15 @@ impl SessionList {
pub fn reset_selected_index(&mut self) { pub fn reset_selected_index(&mut self) {
self.selected_index.reset(); self.selected_index.reset();
} }
pub fn has_session(&self, session_name: &str) -> bool {
self.session_ui_infos.iter().any(|s| s.name == session_name)
}
pub fn update_session_name(&mut self, old_name: &str, new_name: &str) {
self.session_ui_infos
.iter_mut()
.find(|s| s.name == old_name)
.map(|s| s.name = new_name.to_owned());
}
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]

View file

@ -558,29 +558,68 @@ pub fn render_new_session_line(session_name: &Option<String>, is_searching: bool
} }
} }
pub fn render_error(error_text: &str, rows: usize, columns: usize) {
print_text_with_coordinates(
Text::new(format!("Error: {}", error_text)).color_range(3, ..),
0,
rows,
Some(columns),
None,
);
}
pub fn render_renaming_session_screen(new_session_name: &str, rows: usize, columns: usize) {
if rows == 0 || columns == 0 {
return;
}
let prompt_text = "NEW NAME FOR CURRENT SESSION";
let new_session_name = format!("{}_", new_session_name);
let prompt_y_location = (rows / 2).saturating_sub(1);
let session_name_y_location = (rows / 2) + 1;
let prompt_x_location = columns.saturating_sub(prompt_text.chars().count()) / 2;
let session_name_x_location = columns.saturating_sub(new_session_name.chars().count()) / 2;
print_text_with_coordinates(
Text::new(prompt_text).color_range(0, ..),
prompt_x_location,
prompt_y_location,
None,
None,
);
print_text_with_coordinates(
Text::new(new_session_name).color_range(3, ..),
session_name_x_location,
session_name_y_location,
None,
None,
);
}
pub fn render_controls_line(is_searching: bool, row: usize, max_cols: usize, colors: Colors) { pub fn render_controls_line(is_searching: bool, row: usize, max_cols: usize, colors: Colors) {
let (arrows, navigate) = if is_searching { let (arrows, navigate) = if is_searching {
(colors.magenta("<↓↑>"), colors.bold("Navigate")) (colors.magenta("<↓↑>"), colors.bold("Navigate"))
} else { } else {
(colors.magenta("<←↓↑→>"), colors.bold("Navigate and Expand")) (colors.magenta("<←↓↑→>"), colors.bold("Navigate and Expand"))
}; };
let rename = colors.magenta("<Ctrl r>");
let rename_text = colors.bold("Rename session");
let enter = colors.magenta("<ENTER>"); let enter = colors.magenta("<ENTER>");
let select = colors.bold("Switch to selected"); let select = colors.bold("Switch to selected");
let esc = colors.magenta("<ESC>"); let esc = colors.magenta("<ESC>");
let to_hide = colors.bold("Hide"); let to_hide = colors.bold("Hide");
if max_cols >= 80 { if max_cols >= 104 {
print!( print!(
"\u{1b}[m\u{1b}[{row}HHelp: {arrows} - {navigate}, {enter} - {select}, {esc} - {to_hide}" "\u{1b}[m\u{1b}[{row}HHelp: {arrows} - {navigate}, {enter} - {select}, {rename} - {rename_text}, {esc} - {to_hide}"
); );
} else if max_cols >= 57 { } else if max_cols >= 73 {
let navigate = colors.bold("Navigate"); let navigate = colors.bold("Navigate");
let select = colors.bold("Switch"); let select = colors.bold("Switch");
let rename_text = colors.bold("Rename");
print!( print!(
"\u{1b}[m\u{1b}[{row}HHelp: {arrows} - {navigate}, {enter} - {select}, {esc} - {to_hide}" "\u{1b}[m\u{1b}[{row}HHelp: {arrows} - {navigate}, {enter} - {select}, {rename} - {rename_text}, {esc} - {to_hide}"
); );
} else if max_cols >= 20 { } else if max_cols >= 28 {
print!("\u{1b}[m\u{1b}[{row}H{arrows}/{enter}/{esc}"); print!("\u{1b}[m\u{1b}[{row}H{arrows}/{enter}/{rename}/{esc}");
} }
} }

View file

@ -35,6 +35,10 @@ pub fn start_cli_client(os_input: Box<dyn ClientOsApi>, session_name: &str, acti
log_lines.iter().for_each(|line| println!("{line}")); log_lines.iter().for_each(|line| println!("{line}"));
process::exit(0); process::exit(0);
}, },
Some((ServerToClientMsg::LogError(log_lines), _)) => {
log_lines.iter().for_each(|line| eprintln!("{line}"));
process::exit(2);
},
_ => {}, _ => {},
} }
} }

View file

@ -46,6 +46,7 @@ pub(crate) enum ClientInstruction {
StartedParsingStdinQuery, StartedParsingStdinQuery,
DoneParsingStdinQuery, DoneParsingStdinQuery,
Log(Vec<String>), Log(Vec<String>),
LogError(Vec<String>),
SwitchSession(ConnectToSession), SwitchSession(ConnectToSession),
SetSynchronizedOutput(Option<SyncOutput>), SetSynchronizedOutput(Option<SyncOutput>),
} }
@ -62,6 +63,7 @@ impl From<ServerToClientMsg> for ClientInstruction {
ServerToClientMsg::Connected => ClientInstruction::Connected, ServerToClientMsg::Connected => ClientInstruction::Connected,
ServerToClientMsg::ActiveClients(clients) => ClientInstruction::ActiveClients(clients), ServerToClientMsg::ActiveClients(clients) => ClientInstruction::ActiveClients(clients),
ServerToClientMsg::Log(log_lines) => ClientInstruction::Log(log_lines), ServerToClientMsg::Log(log_lines) => ClientInstruction::Log(log_lines),
ServerToClientMsg::LogError(log_lines) => ClientInstruction::LogError(log_lines),
ServerToClientMsg::SwitchSession(connect_to_session) => { ServerToClientMsg::SwitchSession(connect_to_session) => {
ClientInstruction::SwitchSession(connect_to_session) ClientInstruction::SwitchSession(connect_to_session)
}, },
@ -80,6 +82,7 @@ impl From<&ClientInstruction> for ClientContext {
ClientInstruction::Connected => ClientContext::Connected, ClientInstruction::Connected => ClientContext::Connected,
ClientInstruction::ActiveClients(_) => ClientContext::ActiveClients, ClientInstruction::ActiveClients(_) => ClientContext::ActiveClients,
ClientInstruction::Log(_) => ClientContext::Log, ClientInstruction::Log(_) => ClientContext::Log,
ClientInstruction::LogError(_) => ClientContext::LogError,
ClientInstruction::StartedParsingStdinQuery => ClientContext::StartedParsingStdinQuery, ClientInstruction::StartedParsingStdinQuery => ClientContext::StartedParsingStdinQuery,
ClientInstruction::DoneParsingStdinQuery => ClientContext::DoneParsingStdinQuery, ClientInstruction::DoneParsingStdinQuery => ClientContext::DoneParsingStdinQuery,
ClientInstruction::SwitchSession(..) => ClientContext::SwitchSession, ClientInstruction::SwitchSession(..) => ClientContext::SwitchSession,
@ -473,6 +476,11 @@ pub fn start_client(
log::info!("{line}"); log::info!("{line}");
} }
}, },
ClientInstruction::LogError(lines_to_log) => {
for line in lines_to_log {
log::error!("{line}");
}
},
ClientInstruction::SwitchSession(connect_to_session) => { ClientInstruction::SwitchSession(connect_to_session) => {
reconnect_to_session = Some(connect_to_session); reconnect_to_session = Some(connect_to_session);
os_input.send_to_server(ClientToServerMsg::ClientExited); os_input.send_to_server(ClientToServerMsg::ClientExited);

View file

@ -237,6 +237,9 @@ fn host_run_plugin_command(env: FunctionEnvMut<ForeignFunctionEnv>) {
PluginCommand::OpenCommandPaneInPlace(command_to_run) => { PluginCommand::OpenCommandPaneInPlace(command_to_run) => {
open_command_pane_in_place(env, command_to_run) open_command_pane_in_place(env, command_to_run)
}, },
PluginCommand::RenameSession(new_session_name) => {
rename_session(env, new_session_name)
},
}, },
(PermissionStatus::Denied, permission) => { (PermissionStatus::Denied, permission) => {
log::error!( log::error!(
@ -1188,6 +1191,17 @@ fn rename_tab(env: &ForeignFunctionEnv, tab_index: u32, new_name: &str) {
apply_action!(rename_tab_action, error_msg, env); apply_action!(rename_tab_action, error_msg, env);
} }
fn rename_session(env: &ForeignFunctionEnv, new_session_name: String) {
let error_msg = || {
format!(
"failed to rename session in plugin {}",
env.plugin_env.name()
)
};
let action = Action::RenameSession(new_session_name);
apply_action!(action, error_msg, env);
}
// Custom panic handler for plugins. // Custom panic handler for plugins.
// //
// This is called when a panic occurs in a plugin. Since most panics will likely originate in the // This is called when a panic occurs in a plugin. Since most panics will likely originate in the
@ -1315,6 +1329,7 @@ fn check_command_permission(
| PluginCommand::SwitchSession(..) | PluginCommand::SwitchSession(..)
| PluginCommand::DeleteDeadSession(..) | PluginCommand::DeleteDeadSession(..)
| PluginCommand::DeleteAllDeadSessions | PluginCommand::DeleteAllDeadSessions
| PluginCommand::RenameSession(..)
| PluginCommand::RenameTab(..) => PermissionType::ChangeApplicationState, | PluginCommand::RenameTab(..) => PermissionType::ChangeApplicationState,
_ => return (PermissionStatus::Granted, None), _ => return (PermissionStatus::Granted, None),
}; };

View file

@ -784,6 +784,11 @@ pub(crate) fn route_action(
.send_to_screen(ScreenInstruction::BreakPaneLeft(client_id)) .send_to_screen(ScreenInstruction::BreakPaneLeft(client_id))
.with_context(err_context)?; .with_context(err_context)?;
}, },
Action::RenameSession(name) => {
senders
.send_to_screen(ScreenInstruction::RenameSession(name, client_id))
.with_context(err_context)?;
},
} }
Ok(should_break) Ok(should_break)
} }

View file

@ -15,6 +15,8 @@ use zellij_utils::input::command::RunCommand;
use zellij_utils::input::options::Clipboard; use zellij_utils::input::options::Clipboard;
use zellij_utils::pane_size::{Size, SizeInPixels}; use zellij_utils::pane_size::{Size, SizeInPixels};
use zellij_utils::{ use zellij_utils::{
consts::{session_info_folder_for_session, ZELLIJ_SOCK_DIR},
envs::set_session_name,
input::command::TerminalAction, input::command::TerminalAction,
input::layout::{ input::layout::{
FloatingPaneLayout, Layout, PluginUserConfiguration, Run, RunPlugin, RunPluginLocation, FloatingPaneLayout, Layout, PluginUserConfiguration, Run, RunPlugin, RunPluginLocation,
@ -314,6 +316,7 @@ pub enum ScreenInstruction {
ClientTabIndexOrPaneId, ClientTabIndexOrPaneId,
), ),
DumpLayoutToHd, DumpLayoutToHd,
RenameSession(String, ClientId), // String -> new name
} }
impl From<&ScreenInstruction> for ScreenContext { impl From<&ScreenInstruction> for ScreenContext {
@ -492,6 +495,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::ReplacePane(..) => ScreenContext::ReplacePane, ScreenInstruction::ReplacePane(..) => ScreenContext::ReplacePane,
ScreenInstruction::NewInPlacePluginPane(..) => ScreenContext::NewInPlacePluginPane, ScreenInstruction::NewInPlacePluginPane(..) => ScreenContext::NewInPlacePluginPane,
ScreenInstruction::DumpLayoutToHd => ScreenContext::DumpLayoutToHd, ScreenInstruction::DumpLayoutToHd => ScreenContext::DumpLayoutToHd,
ScreenInstruction::RenameSession(..) => ScreenContext::RenameSession,
} }
} }
} }
@ -1520,7 +1524,10 @@ impl Screen {
} }
} }
pub fn change_mode(&mut self, mode_info: ModeInfo, client_id: ClientId) -> Result<()> { pub fn change_mode(&mut self, mut mode_info: ModeInfo, client_id: ClientId) -> Result<()> {
if mode_info.session_name.as_ref() != Some(&self.session_name) {
mode_info.session_name = Some(self.session_name.clone());
}
let previous_mode = self let previous_mode = self
.mode_info .mode_info
.get(&client_id) .get(&client_id)
@ -3471,6 +3478,69 @@ pub(crate) fn screen_thread_main(
screen.dump_layout_to_hd()?; screen.dump_layout_to_hd()?;
} }
}, },
ScreenInstruction::RenameSession(name, client_id) => {
if screen.session_infos_on_machine.contains_key(&name) {
let error_text = "A session by this name already exists.";
log::error!("{}", error_text);
if let Some(os_input) = &mut screen.bus.os_input {
let _ = os_input.send_to_client(
client_id,
ServerToClientMsg::LogError(vec![error_text.to_owned()]),
);
}
} else if screen.resurrectable_sessions.contains_key(&name) {
let error_text =
"A resurrectable session by this name exists, cannot use this name.";
log::error!("{}", error_text);
if let Some(os_input) = &mut screen.bus.os_input {
let _ = os_input.send_to_client(
client_id,
ServerToClientMsg::LogError(vec![error_text.to_owned()]),
);
}
} else {
let err_context = || format!("Failed to rename session");
let old_session_name = screen.session_name.clone();
// update state
screen.session_name = name.clone();
screen.default_mode_info.session_name = Some(name.clone());
for (_client_id, mut mode_info) in screen.mode_info.iter_mut() {
mode_info.session_name = Some(name.clone());
}
for (_, tab) in screen.tabs.iter_mut() {
tab.rename_session(name.clone()).with_context(err_context)?;
}
// rename socket file
let old_socket_file_path = ZELLIJ_SOCK_DIR.join(&old_session_name);
let new_socket_file_path = ZELLIJ_SOCK_DIR.join(&name);
if let Err(e) = std::fs::rename(old_socket_file_path, new_socket_file_path) {
log::error!("Failed to rename ipc socket: {:?}", e);
}
// rename session_info folder (TODO: make this atomic, right now there is a
// chance background_jobs will re-create this folder before it knows the
// session was renamed)
let old_session_info_folder =
session_info_folder_for_session(&old_session_name);
let new_session_info_folder = session_info_folder_for_session(&name);
if let Err(e) =
std::fs::rename(old_session_info_folder, new_session_info_folder)
{
log::error!("Failed to rename session_info folder: {:?}", e);
}
// report
screen
.log_and_report_session_state()
.with_context(err_context)?;
// set the env variable
set_session_name(name);
}
screen.unblock_input()?;
},
} }
} }
Ok(()) Ok(())

View file

@ -838,6 +838,16 @@ impl Tab {
} }
Ok(()) Ok(())
} }
pub fn rename_session(&mut self, new_session_name: String) -> Result<()> {
{
let mode_infos = &mut self.mode_info.borrow_mut();
for (_client_id, mut mode_info) in mode_infos.iter_mut() {
mode_info.session_name = Some(new_session_name.clone());
}
self.default_mode_info.session_name = Some(new_session_name);
}
self.update_input_modes()
}
pub fn update_input_modes(&mut self) -> Result<()> { pub fn update_input_modes(&mut self) -> Result<()> {
// this updates all plugins with the client's input mode // this updates all plugins with the client's input mode
let mode_infos = self.mode_info.borrow(); let mode_infos = self.mode_info.borrow();

View file

@ -682,6 +682,14 @@ pub fn delete_all_dead_sessions() {
unsafe { host_run_plugin_command() }; unsafe { host_run_plugin_command() };
} }
/// Rename the current session
pub fn rename_session(name: &str) {
let plugin_command = PluginCommand::RenameSession(name.to_owned());
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
object_to_stdout(&protobuf_plugin_command.encode_to_vec());
unsafe { host_run_plugin_command() };
}
// Utility Functions // Utility Functions
#[allow(unused)] #[allow(unused)]

View file

@ -5,7 +5,7 @@ pub struct Action {
pub name: i32, pub name: i32,
#[prost( #[prost(
oneof = "action::OptionalPayload", oneof = "action::OptionalPayload",
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44" tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45"
)] )]
pub optional_payload: ::core::option::Option<action::OptionalPayload>, pub optional_payload: ::core::option::Option<action::OptionalPayload>,
} }
@ -100,6 +100,8 @@ pub mod action {
RenamePluginPanePayload(super::IdAndName), RenamePluginPanePayload(super::IdAndName),
#[prost(message, tag = "44")] #[prost(message, tag = "44")]
RenameTabPayload(super::IdAndName), RenameTabPayload(super::IdAndName),
#[prost(string, tag = "45")]
RenameSessionPayload(::prost::alloc::string::String),
} }
} }
#[allow(clippy::derive_partial_eq_without_eq)] #[allow(clippy::derive_partial_eq_without_eq)]
@ -400,6 +402,7 @@ pub enum ActionName {
BreakPane = 77, BreakPane = 77,
BreakPaneRight = 78, BreakPaneRight = 78,
BreakPaneLeft = 79, BreakPaneLeft = 79,
RenameSession = 80,
} }
impl ActionName { impl ActionName {
/// String value of the enum field names used in the ProtoBuf definition. /// String value of the enum field names used in the ProtoBuf definition.
@ -488,6 +491,7 @@ impl ActionName {
ActionName::BreakPane => "BreakPane", ActionName::BreakPane => "BreakPane",
ActionName::BreakPaneRight => "BreakPaneRight", ActionName::BreakPaneRight => "BreakPaneRight",
ActionName::BreakPaneLeft => "BreakPaneLeft", ActionName::BreakPaneLeft => "BreakPaneLeft",
ActionName::RenameSession => "RenameSession",
} }
} }
/// Creates an enum from field names used in the ProtoBuf definition. /// Creates an enum from field names used in the ProtoBuf definition.
@ -573,6 +577,7 @@ impl ActionName {
"BreakPane" => Some(Self::BreakPane), "BreakPane" => Some(Self::BreakPane),
"BreakPaneRight" => Some(Self::BreakPaneRight), "BreakPaneRight" => Some(Self::BreakPaneRight),
"BreakPaneLeft" => Some(Self::BreakPaneLeft), "BreakPaneLeft" => Some(Self::BreakPaneLeft),
"RenameSession" => Some(Self::RenameSession),
_ => None, _ => None,
} }
} }

View file

@ -5,7 +5,7 @@ pub struct PluginCommand {
pub name: i32, pub name: i32,
#[prost( #[prost(
oneof = "plugin_command::Payload", oneof = "plugin_command::Payload",
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45" tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46"
)] )]
pub payload: ::core::option::Option<plugin_command::Payload>, pub payload: ::core::option::Option<plugin_command::Payload>,
} }
@ -102,6 +102,8 @@ pub mod plugin_command {
WebRequestPayload(super::WebRequestPayload), WebRequestPayload(super::WebRequestPayload),
#[prost(string, tag = "45")] #[prost(string, tag = "45")]
DeleteDeadSessionPayload(::prost::alloc::string::String), DeleteDeadSessionPayload(::prost::alloc::string::String),
#[prost(string, tag = "46")]
RenameSessionPayload(::prost::alloc::string::String),
} }
} }
#[allow(clippy::derive_partial_eq_without_eq)] #[allow(clippy::derive_partial_eq_without_eq)]
@ -315,6 +317,7 @@ pub enum CommandName {
WebRequest = 72, WebRequest = 72,
DeleteDeadSession = 73, DeleteDeadSession = 73,
DeleteAllDeadSessions = 74, DeleteAllDeadSessions = 74,
RenameSession = 75,
} }
impl CommandName { impl CommandName {
/// String value of the enum field names used in the ProtoBuf definition. /// String value of the enum field names used in the ProtoBuf definition.
@ -398,6 +401,7 @@ impl CommandName {
CommandName::WebRequest => "WebRequest", CommandName::WebRequest => "WebRequest",
CommandName::DeleteDeadSession => "DeleteDeadSession", CommandName::DeleteDeadSession => "DeleteDeadSession",
CommandName::DeleteAllDeadSessions => "DeleteAllDeadSessions", CommandName::DeleteAllDeadSessions => "DeleteAllDeadSessions",
CommandName::RenameSession => "RenameSession",
} }
} }
/// Creates an enum from field names used in the ProtoBuf definition. /// Creates an enum from field names used in the ProtoBuf definition.
@ -478,6 +482,7 @@ impl CommandName {
"WebRequest" => Some(Self::WebRequest), "WebRequest" => Some(Self::WebRequest),
"DeleteDeadSession" => Some(Self::DeleteDeadSession), "DeleteDeadSession" => Some(Self::DeleteDeadSession),
"DeleteAllDeadSessions" => Some(Self::DeleteAllDeadSessions), "DeleteAllDeadSessions" => Some(Self::DeleteAllDeadSessions),
"RenameSession" => Some(Self::RenameSession),
_ => None, _ => None,
} }
} }

View file

@ -527,4 +527,7 @@ pub enum CliAction {
#[clap(short, long, value_parser)] #[clap(short, long, value_parser)]
configuration: Option<PluginUserConfiguration>, configuration: Option<PluginUserConfiguration>,
}, },
RenameSession {
name: String,
},
} }

View file

@ -1104,4 +1104,5 @@ pub enum PluginCommand {
Vec<u8>, // body Vec<u8>, // body
BTreeMap<String, String>, // context BTreeMap<String, String>, // context
), ),
RenameSession(String), // String -> new session name
} }

View file

@ -347,6 +347,7 @@ pub enum ScreenContext {
ReplacePane, ReplacePane,
NewInPlacePluginPane, NewInPlacePluginPane,
DumpLayoutToHd, DumpLayoutToHd,
RenameSession,
} }
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s. /// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
@ -404,6 +405,7 @@ pub enum ClientContext {
Connected, Connected,
ActiveClients, ActiveClients,
Log, Log,
LogError,
OwnClientId, OwnClientId,
StartedParsingStdinQuery, StartedParsingStdinQuery,
DoneParsingStdinQuery, DoneParsingStdinQuery,

View file

@ -250,6 +250,7 @@ pub enum Action {
BreakPane, BreakPane,
BreakPaneRight, BreakPaneRight,
BreakPaneLeft, BreakPaneLeft,
RenameSession(String),
} }
impl Action { impl Action {
@ -537,6 +538,7 @@ impl Action {
in_place, in_place,
)]) )])
}, },
CliAction::RenameSession { name } => Ok(vec![Action::RenameSession(name)]),
} }
} }
} }

View file

@ -101,6 +101,7 @@ pub enum ServerToClientMsg {
Connected, Connected,
ActiveClients(Vec<ClientId>), ActiveClients(Vec<ClientId>),
Log(Vec<String>), Log(Vec<String>),
LogError(Vec<String>),
SwitchSession(ConnectToSession), SwitchSession(ConnectToSession),
} }

View file

@ -505,6 +505,7 @@ impl Action {
})?; })?;
Ok(Action::Search(search_direction)) Ok(Action::Search(search_direction))
}, },
"RenameSession" => Ok(Action::RenameSession(string)),
_ => Err(ConfigError::new_kdl_error( _ => Err(ConfigError::new_kdl_error(
format!("Unsupported action: {}", action_name), format!("Unsupported action: {}", action_name),
action_node.span().offset(), action_node.span().offset(),
@ -957,6 +958,11 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
"BreakPane" => Ok(Action::BreakPane), "BreakPane" => Ok(Action::BreakPane),
"BreakPaneRight" => Ok(Action::BreakPaneRight), "BreakPaneRight" => Ok(Action::BreakPaneRight),
"BreakPaneLeft" => Ok(Action::BreakPaneLeft), "BreakPaneLeft" => Ok(Action::BreakPaneLeft),
"RenameSession" => parse_kdl_action_char_or_string_arguments!(
action_name,
action_arguments,
kdl_action
),
_ => Err(ConfigError::new_kdl_error( _ => Err(ConfigError::new_kdl_error(
format!("Unsupported action: {}", action_name).into(), format!("Unsupported action: {}", action_name).into(),
kdl_action.span().offset(), kdl_action.span().offset(),

View file

@ -51,6 +51,7 @@ message Action {
IdAndName rename_terminal_pane_payload = 42; IdAndName rename_terminal_pane_payload = 42;
IdAndName rename_plugin_pane_payload = 43; IdAndName rename_plugin_pane_payload = 43;
IdAndName rename_tab_payload = 44; IdAndName rename_tab_payload = 44;
string rename_session_payload = 45;
} }
} }
@ -221,6 +222,7 @@ enum ActionName {
BreakPane = 77; BreakPane = 77;
BreakPaneRight = 78; BreakPaneRight = 78;
BreakPaneLeft = 79; BreakPaneLeft = 79;
RenameSession = 80;
} }
message Position { message Position {

View file

@ -626,6 +626,12 @@ impl TryFrom<ProtobufAction> for Action {
Some(_) => Err("BreakPaneLeft should not have a payload"), Some(_) => Err("BreakPaneLeft should not have a payload"),
None => Ok(Action::BreakPaneLeft), None => Ok(Action::BreakPaneLeft),
}, },
Some(ProtobufActionName::RenameSession) => match protobuf_action.optional_payload {
Some(OptionalPayload::RenameSessionPayload(name)) => {
Ok(Action::RenameSession(name))
},
_ => Err("Wrong payload for Action::RenameSession"),
},
_ => Err("Unknown Action"), _ => Err("Unknown Action"),
} }
} }
@ -1164,6 +1170,10 @@ impl TryFrom<Action> for ProtobufAction {
name: ProtobufActionName::BreakPaneLeft as i32, name: ProtobufActionName::BreakPaneLeft as i32,
optional_payload: None, optional_payload: None,
}), }),
Action::RenameSession(session_name) => Ok(ProtobufAction {
name: ProtobufActionName::RenameSession as i32,
optional_payload: Some(OptionalPayload::RenameSessionPayload(session_name)),
}),
Action::NoOp Action::NoOp
| Action::Confirm | Action::Confirm
| Action::NewInPlacePane(..) | Action::NewInPlacePane(..)

View file

@ -86,6 +86,7 @@ enum CommandName {
WebRequest = 72; WebRequest = 72;
DeleteDeadSession = 73; DeleteDeadSession = 73;
DeleteAllDeadSessions = 74; DeleteAllDeadSessions = 74;
RenameSession = 75;
} }
message PluginCommand { message PluginCommand {
@ -135,6 +136,7 @@ message PluginCommand {
RunCommandPayload run_command_payload = 43; RunCommandPayload run_command_payload = 43;
WebRequestPayload web_request_payload = 44; WebRequestPayload web_request_payload = 44;
string delete_dead_session_payload = 45; string delete_dead_session_payload = 45;
string rename_session_payload = 46;
} }
} }

View file

@ -635,6 +635,12 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
_ => Err("Mismatched payload for DeleteDeadSession"), _ => Err("Mismatched payload for DeleteDeadSession"),
}, },
Some(CommandName::DeleteAllDeadSessions) => Ok(PluginCommand::DeleteAllDeadSessions), Some(CommandName::DeleteAllDeadSessions) => Ok(PluginCommand::DeleteAllDeadSessions),
Some(CommandName::RenameSession) => match protobuf_plugin_command.payload {
Some(Payload::RenameSessionPayload(new_session_name)) => {
Ok(PluginCommand::RenameSession(new_session_name))
},
_ => Err("Mismatched payload for RenameSession"),
},
None => Err("Unrecognized plugin command"), None => Err("Unrecognized plugin command"),
} }
} }
@ -1059,6 +1065,10 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
name: CommandName::DeleteAllDeadSessions as i32, name: CommandName::DeleteAllDeadSessions as i32,
payload: None, payload: None,
}), }),
PluginCommand::RenameSession(new_session_name) => Ok(ProtobufPluginCommand {
name: CommandName::RenameSession as i32,
payload: Some(Payload::RenameSessionPayload(new_session_name)),
}),
} }
} }
} }