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
This commit is contained in:
raphCode 2022-05-03 18:55:31 +02:00 committed by GitHub
parent e487537472
commit 7b0a46f812
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 11 deletions

View file

@ -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)

View file

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

View file

@ -144,14 +144,43 @@ pub(crate) fn list_sessions() {
process::exit(exit_code);
}
pub(crate) fn session_exists(name: &str) -> Result<bool, io::ErrorKind> {
#[derive(Debug, Clone)]
pub enum SessionNameMatch {
AmbiguousPrefix(Vec<String>),
UniquePrefix(String),
Exact(String),
None,
}
pub(crate) fn match_session_name(prefix: &str) -> Result<SessionNameMatch, io::ErrorKind> {
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<String> = 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<bool, io::ErrorKind> {
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) => {

View file

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