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; }