Skip to content

Commit

Permalink
feat(ui): Implement 'attach --create' so attach would never fail
Browse files Browse the repository at this point in the history
Add support for --create in subcommand attach to enable similar use
as in 'tmux new-session -A -s Foo' where attaching a session will always
result in a session, either re-using the old or creating a new.

Closes: #541
  • Loading branch information
ottok committed Jul 18, 2021
1 parent 3df0210 commit 9ade2ce
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 75 deletions.
132 changes: 96 additions & 36 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod sessions;
mod tests;

use crate::install::populate_data_dir;
use sessions::{assert_session, assert_session_ne, get_active_session, list_sessions};
use sessions::{assert_session_ne, get_sessions, print_sessions, print_sessions_and_exit};
use std::convert::TryFrom;
use std::process;
use zellij_client::{os_input_output::get_client_os_input, start_client, ClientInfo};
Expand All @@ -18,11 +18,37 @@ use zellij_utils::{
structopt::StructOpt,
};

fn start_new_session_name(opts: &CliArgs) -> String {
let session_name = opts
.session
.clone()
.unwrap_or_else(|| names::Generator::default().next().unwrap());
assert_session_ne(&session_name);
return session_name;
}

fn start_new_session_layout(opts: &CliArgs, config_options: &Options) -> Option<Layout> {
// Determine and initialize the data directory
let data_dir = opts.data_dir.clone().unwrap_or_else(get_default_data_dir);
#[cfg(not(disable_automatic_asset_installation))]
populate_data_dir(&data_dir);

let layout_dir = config_options
.layout_dir
.clone()
.or_else(|| get_layout_dir(opts.config_dir.clone().or_else(find_default_config_dir)));

let layout =
Layout::from_path_or_default(opts.layout.as_ref(), opts.layout_path.as_ref(), layout_dir);

return layout;
}

pub fn main() {
let opts = CliArgs::from_args();
let mut opts = CliArgs::from_args();

if let Some(Command::Sessions(Sessions::ListSessions)) = opts.command {
list_sessions();
print_sessions_and_exit();
}

let config = match Config::try_from(&opts) {
Expand Down Expand Up @@ -59,49 +85,83 @@ pub fn main() {
};
if let Some(Command::Sessions(Sessions::Attach {
mut session_name,
create,
force,
})) = opts.command.clone()
{
if let Some(session) = session_name.as_ref() {
assert_session(session);
} else {
session_name = Some(get_active_session());
}

start_client(
Box::new(os_input),
opts,
config,
ClientInfo::Attach(session_name.unwrap(), force, config_options),
None,
);
} else {
let session_name = opts
.session
.clone()
.unwrap_or_else(|| names::Generator::default().next().unwrap());
assert_session_ne(&session_name);
let mut sessions = get_sessions();
let mut start_new_session = false;

// Determine and initialize the data directory
let data_dir = opts.data_dir.clone().unwrap_or_else(get_default_data_dir);
#[cfg(not(disable_automatic_asset_installation))]
populate_data_dir(&data_dir);
if session_name.is_none() {
if sessions.len() == 1 {
// If a session name was omitted but there is only one session, attach it
session_name = sessions.pop();
println!("Attaching session {:?}", session_name);
} else if create {
// If a session name was omitted, but --create was used, attach to new session
// with random name
start_new_session = true;
println!("Creating a new session to attach");
} else if sessions.is_empty() {
// If a session name was omitted and there are no sessions, exit with error
eprintln!("ERROR: No active Zellij sessions found");
process::exit(1);
} else {
// If session name was omitted but there are some sessions, list them before
// exiting with error
eprintln!("ERROR: Please specify the session name to attach to. The following sessions are active:");
print_sessions(sessions);
process::exit(1);
}

let layout_dir = config_options.layout_dir.or_else(|| {
get_layout_dir(opts.config_dir.clone().or_else(find_default_config_dir))
});
let layout = Layout::from_path_or_default(
opts.layout.as_ref(),
opts.layout_path.as_ref(),
layout_dir,
);
} else {
if sessions
.iter()
.any(|s| s.to_string() == session_name.as_deref().unwrap())
{
// If a session name was given, and it exists, attach to it
} else if create {
// If a session name was given, but does not exist while --create was used,
// attach a new session using that name
start_new_session = true;
println!(
"Creating new session {:?} to attach",
session_name.clone().unwrap()
);
opts.session = session_name.clone();
} else {
// If a session name was given, but does not exist and will not be created,
// just list sessions and exit
eprintln!("ERROR: No session named {:?} found", session_name.unwrap());
process::exit(1);
}
}

if start_new_session {
start_client(
Box::new(os_input),
opts.clone(),
config,
ClientInfo::New(start_new_session_name(&opts)),
start_new_session_layout(&opts, &config_options),
);
} else {
start_client(
Box::new(os_input),
opts,
config,
ClientInfo::Attach(session_name.unwrap(), force, config_options),
None,
);
}
} else {
start_client(
Box::new(os_input),
opts,
opts.clone(),
config,
ClientInfo::New(session_name),
layout,
ClientInfo::New(start_new_session_name(&opts)),
start_new_session_layout(&opts, &config_options),
);
}
}
Expand Down
53 changes: 16 additions & 37 deletions src/sessions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use zellij_utils::{
ipc::{ClientToServerMsg, IpcSenderWithContext},
};

fn get_sessions() -> Result<Vec<String>, io::ErrorKind> {
fn read_sessions() -> Result<Vec<String>, io::ErrorKind> {
match fs::read_dir(&*ZELLIJ_SOCK_DIR) {
Ok(files) => {
let mut sessions = Vec::new();
Expand Down Expand Up @@ -47,7 +47,17 @@ fn assert_socket(name: &str) -> bool {
}
}

fn print_sessions(sessions: Vec<String>) {
pub(crate) fn get_sessions() -> Vec<String> {
match read_sessions() {
Ok(sessions) => {
return sessions;
}
Err(e) => eprintln!("Error occured: {:?}", e),
}
process::exit(1);
}

pub(crate) fn print_sessions(sessions: Vec<String>) {
let curr_session = std::env::var("ZELLIJ_SESSION_NAME").unwrap_or_else(|_| "".into());
sessions.iter().for_each(|session| {
let suffix = if curr_session == *session {
Expand All @@ -59,26 +69,8 @@ fn print_sessions(sessions: Vec<String>) {
})
}

pub(crate) fn get_active_session() -> String {
match get_sessions() {
Ok(mut sessions) => {
if sessions.len() == 1 {
return sessions.pop().unwrap();
}
if sessions.is_empty() {
println!("No active zellij sessions found.");
} else {
println!("Please specify the session name to attach to. The following sessions are active:");
print_sessions(sessions);
}
}
Err(e) => eprintln!("Error occured: {:?}", e),
}
process::exit(1);
}

pub(crate) fn list_sessions() {
let exit_code = match get_sessions() {
pub(crate) fn print_sessions_and_exit() {
let exit_code = match read_sessions() {
Ok(sessions) => {
if sessions.is_empty() {
println!("No active zellij sessions found.");
Expand All @@ -95,26 +87,13 @@ pub(crate) fn list_sessions() {
process::exit(exit_code);
}

pub(crate) fn assert_session(name: &str) {
match get_sessions() {
Ok(sessions) => {
if sessions.iter().any(|s| s == name) {
return;
}
println!("No session named {:?} found.", name);
}
Err(e) => eprintln!("Error occured: {:?}", e),
};
process::exit(1);
}

pub(crate) fn assert_session_ne(name: &str) {
match get_sessions() {
match read_sessions() {
Ok(sessions) => {
if sessions.iter().all(|s| s != name) {
return;
}
println!("Session with name {:?} aleady exists. Use attach command to connect to it or specify a different name.", name);
println!("Session with name {:?} already exists. Use attach command to connect to it or specify a different name.", name);
}
Err(e) => eprintln!("Error occured: {:?}", e),
};
Expand Down
8 changes: 6 additions & 2 deletions zellij-utils/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,12 @@ pub enum Sessions {
/// Name of the session to attach to.
session_name: Option<String>,

/// Force attach- session will detach from the other
/// zellij client (if any) and attach to this.
/// If session does not exist, create it to ensure that the
/// attach command will always succeed.
#[structopt(long, short)]
create: bool,

/// Detach session from other Zellij client (if any) and attach to this.
#[structopt(long, short)]
force: bool,
},
Expand Down

0 comments on commit 9ade2ce

Please sign in to comment.