From 7b0a46f812f0c0f60068742ea2984b88b6ad8ed5 Mon Sep 17 00:00:00 2001 From: raphCode <15750438+raphCode@users.noreply.github.com> Date: Tue, 3 May 2022 18:55:31 +0200 Subject: [PATCH] feat: Attach sessions by unique prefix name (#1169) (#1360) * feat: Attach sessions by unique name prefix (#1169) This makes attaching to sessions more convenient since only the first character(s) of the session name must be typed. If this prefix matches multiple sessions and is therefore ambiguous, zellij will complain and show all sessions names starting with these characters. If any session name matches the given string exact, it is attached immediately, therefore it is always possible to attach to every session, even if the set of session names is not prefix-free. * Add feature to changelog * Try to fix flaky e2e test --- CHANGELOG.md | 2 ++ src/commands.rs | 27 ++++++++++++++++++++------- src/sessions.rs | 35 ++++++++++++++++++++++++++++++++--- src/tests/e2e/cases.rs | 3 ++- 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ed3fc5b..7a6c9d60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] * fix: right and middle clicks creating selection (https://github.com/zellij-org/zellij/pull/1372) +* feat: Attach to sessions more conveniently by only typing their name's first character(s) (https://github.com/zellij-org/zellij/pull/1360) + ## [0.29.1] - 2022-05-02 * fix: forward mouse events to plugin panes (https://github.com/zellij-org/zellij/pull/1369) diff --git a/src/commands.rs b/src/commands.rs index e66a1b20..a2311dc7 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -2,8 +2,8 @@ use crate::install::populate_data_dir; use crate::sessions::kill_session as kill_session_impl; use crate::sessions::{ assert_session, assert_session_ne, get_active_session, get_sessions, - get_sessions_sorted_by_mtime, print_sessions, print_sessions_with_index, session_exists, - ActiveSession, + get_sessions_sorted_by_mtime, match_session_name, print_sessions, print_sessions_with_index, + session_exists, ActiveSession, SessionNameMatch, }; use dialoguer::Confirm; use std::path::PathBuf; @@ -144,10 +144,23 @@ fn attach_with_session_name( ClientInfo::Attach(session_name.unwrap(), config_options) } } - Some(session) => { - assert_session(session); - ClientInfo::Attach(session_name.unwrap(), config_options) - } + Some(prefix) => match match_session_name(prefix).unwrap() { + SessionNameMatch::UniquePrefix(s) | SessionNameMatch::Exact(s) => { + ClientInfo::Attach(s, config_options) + } + SessionNameMatch::AmbiguousPrefix(sessions) => { + println!( + "Ambiguous selection: multiple sessions names start with '{}':", + prefix + ); + print_sessions(sessions); + process::exit(1); + } + SessionNameMatch::None => { + eprintln!("No session with the name '{}' found!", prefix); + process::exit(1); + } + }, None => match get_active_session() { ActiveSession::None if create => create_new_client(), ActiveSession::None => { @@ -156,7 +169,7 @@ fn attach_with_session_name( } ActiveSession::One(session_name) => ClientInfo::Attach(session_name, config_options), ActiveSession::Many => { - println!("Please specify the session name to attach to. The following sessions are active:"); + println!("Please specify the session to attach to, either by using the full name or a unique prefix.\nThe following sessions are active:"); print_sessions(get_sessions().unwrap()); process::exit(1); } diff --git a/src/sessions.rs b/src/sessions.rs index b39818d7..13d33adf 100644 --- a/src/sessions.rs +++ b/src/sessions.rs @@ -144,14 +144,43 @@ pub(crate) fn list_sessions() { process::exit(exit_code); } -pub(crate) fn session_exists(name: &str) -> Result { +#[derive(Debug, Clone)] +pub enum SessionNameMatch { + AmbiguousPrefix(Vec), + UniquePrefix(String), + Exact(String), + None, +} + +pub(crate) fn match_session_name(prefix: &str) -> Result { return match get_sessions() { - Ok(sessions) if sessions.iter().any(|s| s == name) => Ok(true), - Ok(_) => Ok(false), + Ok(sessions) => Ok({ + let filtered_sessions: Vec = sessions + .iter() + .filter(|s| s.starts_with(prefix)) + .cloned() + .collect(); + if filtered_sessions.iter().any(|s| s == prefix) { + return Ok(SessionNameMatch::Exact(prefix.to_string())); + } + match &filtered_sessions[..] { + [] => SessionNameMatch::None, + [s] => SessionNameMatch::UniquePrefix(s.to_string()), + _ => SessionNameMatch::AmbiguousPrefix(filtered_sessions), + } + }), Err(e) => Err(e), }; } +pub(crate) fn session_exists(name: &str) -> Result { + match match_session_name(name) { + Ok(SessionNameMatch::Exact(_)) => Ok(true), + Ok(_) => Ok(false), + Err(e) => Err(e), + } +} + pub(crate) fn assert_session(name: &str) { match session_exists(name) { Ok(result) => { diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index 19579f55..92438525 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -950,7 +950,8 @@ pub fn detach_and_attach_session() { name: "Wait for session to be attached", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.cursor_position_is(3, 2) { + if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(3, 2) + { // we're back inside the session step_is_complete = true; }