summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKunal Mohan <44079328+kunalmohan@users.noreply.github.com>2021-05-25 17:30:40 +0530
committerGitHub <noreply@github.com>2021-05-25 17:30:40 +0530
commit2bca7e007aad9a74e6c48191b8a7a3edf85808d4 (patch)
tree61366b1e29601ad003fc53a715f4b925d8ef5871
parent6755b6a88defe30c63fa3bc6d25aa89e545180c9 (diff)
parentdf6d6cb3a70cbea9d088c7af47f3540896b98089 (diff)
Merge pull request #531 from zellij-org/detach-sessions
Feature: Detachable/Persistent sessions
-rw-r--r--Cargo.lock3
-rw-r--r--Cargo.toml1
-rw-r--r--assets/config/default.yaml27
-rw-r--r--default-plugins/status-bar/src/first_line.rs68
-rw-r--r--src/main.rs45
-rw-r--r--src/sessions.rs109
-rw-r--r--src/tests/fakes.rs2
-rw-r--r--src/tests/mod.rs4
-rw-r--r--zellij-client/src/input_handler.rs8
-rw-r--r--zellij-client/src/lib.rs111
-rw-r--r--zellij-server/src/lib.rs162
-rw-r--r--zellij-server/src/os_input_output.rs36
-rw-r--r--zellij-server/src/route.rs55
-rw-r--r--zellij-server/src/screen.rs24
-rw-r--r--zellij-server/src/tab.rs12
-rw-r--r--zellij-tile/src/data.rs3
-rw-r--r--zellij-utils/Cargo.toml2
-rw-r--r--zellij-utils/src/cli.rs33
-rw-r--r--zellij-utils/src/consts.rs15
-rw-r--r--zellij-utils/src/errors.rs2
-rw-r--r--zellij-utils/src/input/actions.rs2
-rw-r--r--zellij-utils/src/input/config.rs6
-rw-r--r--zellij-utils/src/input/mod.rs3
-rw-r--r--zellij-utils/src/input/options.rs6
-rw-r--r--zellij-utils/src/ipc.rs36
25 files changed, 593 insertions, 182 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0dda83734..596062e17 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2301,6 +2301,7 @@ name = "zellij"
version = "0.12.0"
dependencies = [
"insta",
+ "names",
"zellij-client",
"zellij-server",
"zellij-utils",
@@ -2358,8 +2359,8 @@ dependencies = [
"interprocess",
"lazy_static",
"libc",
- "names",
"nix",
+ "once_cell",
"serde",
"serde_yaml",
"signal-hook 0.3.8",
diff --git a/Cargo.toml b/Cargo.toml
index 9e7e31ab4..5f7afaaa6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@ resolver = "2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+names = "0.11.0"
zellij-client = { path = "zellij-client/", version = "0.12.0" }
zellij-server = { path = "zellij-server/", version = "0.12.0" }
zellij-utils = { path = "zellij-utils/", version = "0.12.0" }
diff --git a/assets/config/default.yaml b/assets/config/default.yaml
index 4582e79f6..42bc16886 100644
--- a/assets/config/default.yaml
+++ b/assets/config/default.yaml
@@ -12,6 +12,8 @@ keybinds:
key: [Ctrl: 't',]
- action: [SwitchToMode: Scroll,]
key: [Ctrl: 's',]
+ - action: [SwitchToMode: Session,]
+ key: [Ctrl: 'o',]
- action: [Quit,]
key: [Ctrl: 'q',]
- action: [NewPane: ]
@@ -42,6 +44,8 @@ keybinds:
key: [Ctrl: 'r', Char: "\n", Char: ' ',]
- action: [SwitchToMode: Scroll,]
key: [Ctrl: 's']
+ - action: [SwitchToMode: Session,]
+ key: [Ctrl: 'o',]
- action: [Quit]
key: [Ctrl: 'q']
- action: [Resize: Left,]
@@ -77,6 +81,8 @@ keybinds:
key: [Ctrl: 'p', Char: "\n", Char: ' ',]
- action: [SwitchToMode: Scroll,]
key: [Ctrl: 's']
+ - action: [SwitchToMode: Session,]
+ key: [Ctrl: 'o',]
- action: [Quit,]
key: [Ctrl: 'q',]
- action: [MoveFocus: Left,]
@@ -114,6 +120,8 @@ keybinds:
key: [Ctrl: 't', Char: "\n", Char: ' ',]
- action: [SwitchToMode: Scroll,]
key: [Ctrl: 's']
+ - action: [SwitchToMode: Session,]
+ key: [Ctrl: 'o',]
- action: [SwitchToMode: RenameTab, TabNameInput: [0],]
key: [Char: 'r']
- action: [Quit,]
@@ -168,6 +176,8 @@ keybinds:
key: [Ctrl: 'g',]
- action: [SwitchToMode: Pane,]
key: [Ctrl: 'p',]
+ - action: [SwitchToMode: Session,]
+ key: [Ctrl: 'o',]
- action: [Quit,]
key: [Ctrl: 'q',]
- action: [ScrollDown,]
@@ -213,3 +223,20 @@ keybinds:
key: [ Alt: '[',]
- action: [FocusNextPane,]
key: [ Alt: ']',]
+ session:
+ - action: [SwitchToMode: Locked,]
+ key: [Ctrl: 'g']
+ - action: [SwitchToMode: Resize,]
+ key: [Ctrl: 'r',]
+ - action: [SwitchToMode: Pane,]
+ key: [Ctrl: 'p',]
+ - action: [SwitchToMode: Tab,]
+ key: [Ctrl: 't',]
+ - action: [SwitchToMode: Normal,]
+ key: [Ctrl: 'o', Char: "\n", Char: ' ',]
+ - action: [SwitchToMode: Scroll,]
+ key: [Ctrl: 's']
+ - action: [Quit,]
+ key: [Ctrl: 'q',]
+ - action: [Detach,]
+ key: [Char: 'd',]
diff --git a/default-plugins/status-bar/src/first_line.rs b/default-plugins/status-bar/src/first_line.rs
index 37175e3fa..7a678cdc4 100644
--- a/default-plugins/status-bar/src/first_line.rs
+++ b/default-plugins/status-bar/src/first_line.rs
@@ -22,6 +22,7 @@ enum CtrlKeyAction {
Resize,
Scroll,
Quit,
+ Session,
}
enum CtrlKeyMode {
@@ -39,16 +40,7 @@ impl CtrlKeyShortcut {
CtrlKeyAction::Resize => String::from("RESIZE"),
CtrlKeyAction::Scroll => String::from("SCROLL"),
CtrlKeyAction::Quit => String::from("QUIT"),
- }
- }
- pub fn shortened_text(&self) -> String {
- match self.action {
- CtrlKeyAction::Lock => String::from("LOCK"),
- CtrlKeyAction::Pane => String::from("ane"),
- CtrlKeyAction::Tab => String::from("ab"),
- CtrlKeyAction::Resize => String::from("esize"),
- CtrlKeyAction::Scroll => String::from("croll"),
- CtrlKeyAction::Quit => String::from("uit"),
+ CtrlKeyAction::Session => String::from("SESSION"),
}
}
pub fn letter_shortcut(&self) -> char {
@@ -59,6 +51,7 @@ impl CtrlKeyShortcut {
CtrlKeyAction::Resize => 'r',
CtrlKeyAction::Scroll => 's',
CtrlKeyAction::Quit => 'q',
+ CtrlKeyAction::Session => 'o',
}
}
}
@@ -193,32 +186,6 @@ fn full_ctrl_key(key: &CtrlKeyShortcut, palette: ColoredElements, separator: &st
}
}
-fn shortened_ctrl_key(
- key: &CtrlKeyShortcut,
- palette: ColoredElements,
- separator: &str,
-) -> LinePart {
- let shortened_text = key.shortened_text();
- let letter_shortcut = key.letter_shortcut();
- let shortened_text = match key.action {
- CtrlKeyAction::Lock => format!(" {}", shortened_text),
- _ => shortened_text,
- };
- match key.mode {
- CtrlKeyMode::Unselected => {
- unselected_mode_shortcut(letter_shortcut, &shortened_text, palette, separator)
- }
- CtrlKeyMode::Selected => {
- selected_mode_shortcut(letter_shortcut, &shortened_text, palette, separator)
- }
- CtrlKeyMode::Disabled => disabled_mode_shortcut(
- &format!(" <{}>{}", letter_shortcut, shortened_text),
- palette,
- separator,
- ),
- }
-}
-
fn single_letter_ctrl_key(
key: &CtrlKeyShortcut,
palette: ColoredElements,
@@ -255,15 +222,6 @@ fn key_indicators(
}
line_part = LinePart::default();
for ctrl_key in keys {
- let key = shortened_ctrl_key(ctrl_key, palette, separator);
- line_part.part = format!("{}{}", line_part.part, key.part);
- line_part.len += key.len;
- }
- if line_part.len < max_len {
- return line_part;
- }
- line_part = LinePart::default();
- for ctrl_key in keys {
let key = single_letter_ctrl_key(ctrl_key, palette, separator);
line_part.part = format!("{}{}", line_part.part, key.part);
line_part.len += key.len;
@@ -296,6 +254,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Tab),
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Resize),
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Scroll),
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Quit),
],
colored_elements,
@@ -309,6 +268,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Resize),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll),
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
],
colored_elements,
@@ -322,6 +282,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Resize),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll),
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
],
colored_elements,
@@ -335,6 +296,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Tab),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Resize),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll),
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
],
colored_elements,
@@ -348,6 +310,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Resize),
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Scroll),
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
],
colored_elements,
@@ -361,6 +324,21 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Resize),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll),
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
+ ],
+ colored_elements,
+ separator,
+ ),
+ InputMode::Session => key_indicators(
+ max_len,
+ &[
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock),
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane),
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Resize),
+ CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll),
+ CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Session),
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
],
colored_elements,
diff --git a/src/main.rs b/src/main.rs
index a970ce446..67b833988 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,11 +1,14 @@
+mod sessions;
#[cfg(test)]
mod tests;
+use sessions::{assert_session, assert_session_ne, list_sessions};
use std::convert::TryFrom;
-use zellij_client::{os_input_output::get_client_os_input, start_client};
+use std::process;
+use zellij_client::{os_input_output::get_client_os_input, start_client, ClientInfo};
use zellij_server::{os_input_output::get_server_os_input, start_server};
use zellij_utils::{
- cli::{CliArgs, ConfigCli},
+ cli::{CliArgs, Command, Sessions},
consts::{ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR},
input::config::Config,
logging::*,
@@ -16,15 +19,17 @@ use zellij_utils::{
pub fn main() {
let opts = CliArgs::from_args();
- if let Some(ConfigCli::Setup(setup)) = opts.option.clone() {
- Setup::from_cli(&setup, &opts).expect("Failed to print to stdout");
+ if let Some(Command::Sessions(Sessions::ListSessions)) = opts.command {
+ list_sessions();
+ } else if let Some(Command::Setup(ref setup)) = opts.command {
+ Setup::from_cli(setup, &opts).expect("Failed to print to stdout");
}
let config = match Config::try_from(&opts) {
Ok(config) => config,
Err(e) => {
eprintln!("There was an error in the config file:\n{}", e);
- std::process::exit(1);
+ process::exit(1);
}
};
atomic_create_dir(&*ZELLIJ_TMP_DIR).unwrap();
@@ -34,7 +39,7 @@ pub fn main() {
Ok(server_os_input) => server_os_input,
Err(e) => {
eprintln!("failed to open terminal:\n{}", e);
- std::process::exit(1);
+ process::exit(1);
}
};
start_server(Box::new(os_input), path);
@@ -43,9 +48,33 @@ pub fn main() {
Ok(os_input) => os_input,
Err(e) => {
eprintln!("failed to open terminal:\n{}", e);
- std::process::exit(1);
+ process::exit(1);
}
};
- start_client(Box::new(os_input), opts, config);
+ if let Some(Command::Sessions(Sessions::Attach {
+ session_name,
+ force,
+ })) = opts.command.clone()
+ {
+ assert_session(&session_name);
+ start_client(
+ Box::new(os_input),
+ opts,
+ config,
+ ClientInfo::Attach(session_name, force),
+ );
+ } else {
+ let session_name = opts
+ .session
+ .clone()
+ .unwrap_or_else(|| names::Generator::default().next().unwrap());
+ assert_session_ne(&session_name);
+ start_client(
+ Box::new(os_input),
+ opts,
+ config,
+ ClientInfo::New(session_name),
+ );
+ }
}
}
diff --git a/src/sessions.rs b/src/sessions.rs
new file mode 100644
index 000000000..fd834e408
--- /dev/null
+++ b/src/sessions.rs
@@ -0,0 +1,109 @@
+use std::os::unix::fs::FileTypeExt;
+use std::{fs, io, process};
+use zellij_utils::{
+ consts::ZELLIJ_SOCK_DIR,
+ interprocess::local_socket::LocalSocketStream,
+ ipc::{ClientToServerMsg, IpcSenderWithContext},
+};
+
+fn get_sessions() -> Result<Vec<String>, io::ErrorKind> {
+ match fs::read_dir(&*ZELLIJ_SOCK_DIR) {
+ Ok(files) => {
+ let mut sessions = Vec::new();
+ files.for_each(|file| {
+ let file = file.unwrap();
+ let file_name = file.file_name().into_string().unwrap();
+ if file.file_type().unwrap().is_socket() && assert_socket(&file_name) {
+ sessions.push(file_name);
+ }
+ });
+ Ok(sessions)
+ }
+ Err(err) => {
+ if let io::ErrorKind::NotFound = err.kind() {
+ Ok(Vec::with_capacity(0))
+ } else {
+ Err(err.kind())
+ }
+ }
+ }
+}
+
+pub(crate) fn list_sessions() {
+ let exit_code = match get_sessions() {
+ Ok(sessions) => {
+ if sessions.is_empty() {
+ println!("No active zellij sessions found.");
+ } else {
+ let curr_session =
+ std::env::var("ZELLIJ_SESSION_NAME").unwrap_or_else(|_| "".into());
+ sessions.iter().for_each(|session| {
+ let suffix = if curr_session == *session {
+ " (current)"
+ } else {
+ ""
+ };
+ println!("{}{}", session, suffix);
+ })
+ }
+ 0
+ }
+ Err(e) => {
+ eprintln!("Error occured: {:?}", e);
+ 1
+ }
+ };
+ process::exit(exit_code);
+}
+
+pub(crate) fn assert_session(name: &str) {
+ let exit_code = match get_sessions() {
+ Ok(sessions) => {
+ if sessions.iter().any(|s| s == name) {
+ return;
+ }
+ println!("No session named {:?} found.", name);
+ 0
+ }
+ Err(e) => {
+ eprintln!("Error occured: {:?}", e);
+ 1
+ }
+ };
+ process::exit(exit_code);
+}
+
+pub(crate) fn assert_session_ne(name: &str) {
+ let exit_code = match get_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);
+ 0
+ }
+ Err(e) => {
+ eprintln!("Error occured: {:?}", e);
+ 1
+ }
+ };
+ process::exit(exit_code);
+}
+
+fn assert_socket(name: &str) -> bool {
+ let path = &*ZELLIJ_SOCK_DIR.join(name);
+ match LocalSocketStream::connect(path) {
+ Ok(stream) => {
+ IpcSenderWithContext::new(stream).send(ClientToServerMsg::ClientExited);
+ true
+ }
+ Err(e) => {
+ if e.kind() == io::ErrorKind::ConnectionRefused {
+ drop(fs::remove_file(path));
+ false
+ } else {
+ true
+ }
+ }
+ }
+}
diff --git a/src/tests/fakes.rs b/src/tests/fakes.rs
index a2f338ba6..23f9f1824 100644
--- a/src/tests/fakes.rs
+++ b/src/tests/fakes.rs
@@ -333,6 +333,8 @@ impl ServerOsApi for FakeInputOutput {
self.send_instructions_to_client.send(msg).unwrap();
}
fn add_client_sender(&self) {}
+ fn remove_client_sender(&self) {}
+ fn send_to_temp_client(&self, _msg: ServerToClientMsg) {}
fn update_receiver(&mut self, _stream: LocalSocketStream) {}
fn load_palette(&self) -> Palette {
default_palette()
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 3e548db7c..e075ed49a 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -5,7 +5,7 @@ pub mod tty_inputs;
pub mod utils;
use std::path::PathBuf;
-use zellij_client::{os_input_output::ClientOsApi, start_client};
+use zellij_client::{os_input_output::ClientOsApi, start_client, ClientInfo};
use zellij_server::{os_input_output::ServerOsApi, start_server};
use zellij_utils::{cli::CliArgs, input::config::Config};
@@ -21,6 +21,6 @@ pub fn start(
start_server(server_os_input, PathBuf::from(""));
})
.unwrap();
- start_client(client_os_input, opts, config);
+ start_client(client_os_input, opts, config, ClientInfo::New("".into()));
let _ = server_thread.join();
}
diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs
index 4e2cff94c..70f6e9824 100644
--- a/zellij-client/src/input_handler.rs
+++ b/zellij-client/src/input_handler.rs
@@ -7,7 +7,7 @@ use zellij_utils::{
channels::{SenderWithContext, OPENCALLS},
errors::ContextType,
input::{actions::Action, cast_termion_key, config::Config, keybinds::Keybinds},
- ipc::ClientToServerMsg,
+ ipc::{ClientToServerMsg, ExitReason},
};
use termion::input::TermReadEventsAndRaw;
@@ -132,7 +132,9 @@ impl InputHandler {
let mut should_break = false;
match action {
- Action::Quit => {
+ Action::Quit | Action::Detach => {
+ self.os_input
+ .send_to_server(ClientToServerMsg::Action(action));
self.exit();
should_break = true;
}
@@ -167,7 +169,7 @@ impl InputHandler {
/// same as quitting Zellij).
fn exit(&mut self) {
self.send_client_instructions
- .send(ClientInstruction::Exit)
+ .send(ClientInstruction::Exit(ExitReason::Normal))
.unwrap();
}
}
diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs
index 9deb415e6..f3b0f11d3 100644
--- a/zellij-client/src/lib.rs
+++ b/zellij-client/src/lib.rs
@@ -17,30 +17,27 @@ use crate::{
use zellij_utils::cli::CliArgs;
use zellij_utils::{
channels::{SenderType, SenderWithContext, SyncChannelWithContext},
- consts::ZELLIJ_IPC_PIPE,
+ consts::{SESSION_NAME, ZELLIJ_IPC_PIPE},
errors::{ClientContext, ContextType, ErrorInstruction},
- input::config::Config,
- input::options::Options,
- ipc::{ClientAttributes, ClientToServerMsg, ServerToClient