summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKen Matsui <26405363+ken-matsui@users.noreply.github.com>2021-11-01 03:14:12 +0900
committerKen Matsui <26405363+ken-matsui@users.noreply.github.com>2021-11-06 05:15:09 +0900
commit4acb2458d2e0da1d0459114f45123841e2fca741 (patch)
tree50a81760d29440580eabdb5e4f691db038e4bb8d
parent03e62eb91c4f53a5be1548fecacc1fd5173adf47 (diff)
feat(attach): Support `--index` option to choose specific session by provided number in active sessions ordered by creation date, resolve #823
feat(attach): Support `--first` option for `attach` sub-command to let zellij choose the alphabetically first session; resolve #823 fix(attach-first): Fix `--first` option to choose the first created session in the existent sessions feat(attach): Support `--index` option to choose the session indexed by provided number like -t option of tmux feat(attach): Support listing active sessions with index when a provided number is not found in the active sessions feat(attach): Support listing active sessions with index when a provided number is not found in the active sessions feat: Add anyhow to uniformly treat error types and avoid panics
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md4
-rw-r--r--CHANGELOG.md10
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--Cargo.lock20
-rw-r--r--Cargo.toml2
-rw-r--r--src/main.rs119
-rw-r--r--src/sessions.rs53
-rw-r--r--zellij-client/src/input_handler.rs3
-rw-r--r--zellij-server/Cargo.toml1
-rw-r--r--zellij-server/src/os_input_output.rs149
-rw-r--r--zellij-server/src/panes/plugin_pane.rs8
-rw-r--r--zellij-server/src/pty.rs112
-rw-r--r--zellij-server/src/route.rs18
-rw-r--r--zellij-server/src/screen.rs10
-rw-r--r--zellij-server/src/tab.rs9
-rw-r--r--zellij-server/src/unit/screen_tests.rs9
-rw-r--r--zellij-server/src/unit/tab_tests.rs8
-rw-r--r--zellij-tile/src/data.rs1
-rw-r--r--zellij-utils/Cargo.toml2
-rw-r--r--zellij-utils/src/cli.rs4
-rw-r--r--zellij-utils/src/errors.rs1
-rw-r--r--zellij-utils/src/input/actions.rs3
-rw-r--r--zellij-utils/src/input/config.rs41
-rw-r--r--zellij-utils/src/ipc.rs2
-rw-r--r--zellij-utils/src/shared.rs7
25 files changed, 391 insertions, 207 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index fd34b4f95..9e45c461e 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -3,7 +3,7 @@ name: "\U0001F41B Bug Report"
about: "If something isn't working as expected."
labels: bug
---
-Thank you for taking the time to file this issue! Please follow the instructions and fill the missing parts below the instructions, if it is meaningful. Try to be brief and concise.
+Thank you for taking the time to file this issue! Please follow the instructions and fill in the missing parts below the instructions, if it is meaningful. Try to be brief and concise.
**In Case of Graphical or Performance Issues**
@@ -26,4 +26,4 @@ List of programs you interact with as, `PROGRAM --version`: output cropped meani
`alacritty --version`: alacritty 0.7.2 (5ac8060b)
**Further information**
-Reproduction steps, noticable behavior, related issues etc
+Reproduction steps, noticeable behavior, related issues, etc
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0e91ca637..ecd9d79c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* Fix: pasted text performs much faster and doesn't kill Termion (https://github.com/zellij-org/zellij/pull/810)
* Fix: resizing/scrolling through heavily wrapped panes no longer hangs (https://github.com/zellij-org/zellij/pull/814)
* Terminal compatibility: properly handle HOME/END keys in eg. vim/zsh (https://github.com/zellij-org/zellij/pull/815)
+* Fix: Typo (https://github.com/zellij-org/zellij/pull/821)
+* Fix: Update `cargo-make` instructions post `v0.35.3` (https://github.com/zellij-org/zellij/pull/819)
+* Fix: Unused import for darwin systems (https://github.com/zellij-org/zellij/pull/820)
+* Add: `WriteChars` action (https://github.com/zellij-org/zellij/pull/825)
+* Fix: typo and grammar (https://github.com/zellij-org/zellij/pull/826)
+* Add: `rust-version' - msrv field to `Cargo.toml` (https://github.com/zellij-org/zellij/pull/828)
+* Fix: improve memory utilization, reap both sides of pty properly and do not expose open FDs to child processes (https://github.com/zellij-org/zellij/pull/830)
+* Fix: move from the deprecated `colors_transform` to `colorsys` (https://github.com/zellij-org/zellij/pull/832)
+* Feature: plugins can now detect right mouse clicks (https://github.com/zellij-org/zellij/pull/801)
+* Fix: open pane in cwd even when explicitly specifying shell (https://github.com/zellij-org/zellij/pull/834)
## [0.19.0] - 2021-10-20
* Fix: Prevent text overwrite when scrolled up (https://github.com/zellij-org/zellij/pull/655)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2a7039c50..11c6d1f33 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -28,7 +28,7 @@ cargo make build
cargo make test
# Run Zellij (optionally with additional arguments)
cargo make run
-cargo make run -- -l strider
+cargo make run -l strider
# Run Clippy (potentially with additional options)
cargo make clippy
cargo make clippy -W clippy::pedantic
diff --git a/Cargo.lock b/Cargo.lock
index 39d449fe1..a3ffff357 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -352,6 +352,16 @@ dependencies = [
]
[[package]]
+name = "close_fds"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bc416f33de9d59e79e57560f450d21ff8393adcf1cdfc3e6d8fb93d5f88a2ed"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+]
+
+[[package]]
name = "colored"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -363,10 +373,10 @@ dependencies = [
]
[[package]]
-name = "colors-transform"
-version = "0.2.11"
+name = "colorsys"
+version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9226dbc05df4fb986f48d730b001532580883c4c06c5d1c213f4b34c1c157178"
+checksum = "4dfdf9179d546b55ff3f88c9d93ecfaa3e9760163da5a1080af5243230dbbb70"
[[package]]
name = "concurrent-queue"
@@ -2824,6 +2834,7 @@ dependencies = [
name = "zellij"
version = "0.20.0"
dependencies = [
+ "anyhow",
"insta",
"log",
"names",
@@ -2857,6 +2868,7 @@ dependencies = [
"byteorder",
"cassowary",
"chrono",
+ "close_fds",
"daemonize",
"darwin-libproc",
"highway",
@@ -2895,7 +2907,7 @@ dependencies = [
"async-std",
"backtrace",
"bincode",
- "colors-transform",
+ "colorsys",
"crossbeam",
"directories-next",
"interprocess",
diff --git a/Cargo.toml b/Cargo.toml
index f205b5f0a..1c33bae74 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,10 +9,12 @@ repository = "https://github.com/zellij-org/zellij"
homepage = "https://zellij.dev"
include = ["src/**/*", "assets/plugins/*", "assets/layouts/*", "assets/config/*", "LICENSE.md", "README.md", "!**/*_test.*", "!**/tests/**/*"]
resolver = "2"
+rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+anyhow = "1.0"
names = "0.11.0"
zellij-client = { path = "zellij-client/", version = "0.20.0" }
zellij-server = { path = "zellij-server/", version = "0.20.0" }
diff --git a/src/main.rs b/src/main.rs
index 0cacc3759..f12eb010b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,8 +5,9 @@ mod tests;
use crate::install::populate_data_dir;
use sessions::{
- assert_session, assert_session_ne, get_active_session, get_sessions, kill_session,
- list_sessions, print_sessions, session_exists, ActiveSession,
+ assert_session, assert_session_ne, get_active_session, get_sessions,
+ get_sessions_sorted_by_creation_date, kill_session, list_sessions, print_sessions,
+ print_sessions_with_index, session_exists, ActiveSession,
};
use std::process;
use zellij_client::{os_input_output::get_client_os_input, start_client, ClientInfo};
@@ -62,7 +63,7 @@ pub fn main() {
}
}
Err(e) => {
- eprintln!("Error occured: {:?}", e);
+ eprintln!("Error occurred: {:?}", e);
process::exit(1);
}
}
@@ -111,6 +112,7 @@ pub fn main() {
if let Some(Command::Sessions(Sessions::Attach {
session_name,
create,
+ index,
options,
})) = opts.command.clone()
{
@@ -119,47 +121,94 @@ pub fn main() {
None => config_options,
};
- let (client, attach_layout) = match session_name.as_ref() {
- Some(session) => {
- if create {
- if !session_exists(session).unwrap() {
- (ClientInfo::New(session_name.unwrap()), layout)
+ let (client, attach_layout) = if let Some(idx) = index {
+ // Ignore session_name when `--index` is provided
+ match get_sessions_sorted_by_creation_date() {
+ Ok(sessions) => {
+ if sessions.is_empty() {
+ if create {
+ (
+ ClientInfo::New(names::Generator::default().next().unwrap()),
+ layout,
+ )
+ } else {
+ println!("No active zellij sessions found.");
+ process::exit(1);
+ }
} else {
- (
- ClientInfo::Attach(session_name.unwrap(), config_options.clone()),
- None,
- )
+ match sessions.get(idx) {
+ Some(session) => (
+ ClientInfo::Attach(session.clone(), config_options.clone()),
+ None,
+ ),
+ None => {
+ if create {
+ (
+ ClientInfo::New(
+ names::Generator::default().next().unwrap(),
+ ),
+ layout,
+ )
+ } else {
+ println!("No session indexed by {} found. The following sessions are active:", idx);
+ print_sessions_with_index(sessions);
+ process::exit(1);
+ }
+ }
+ }
}
- } else {
- assert_session(session);
- (
- ClientInfo::Attach(session_name.unwrap(), config_options.clone()),
- None,
- )
+ }
+ Err(e) => {
+ eprintln!("Error occurred: {:?}", e);
+ process::exit(1);
}
}
- None => match get_active_session() {
- ActiveSession::None => {
+ } else {
+ match session_name.as_ref() {
+ Some(session) => {
if create {
+ if !session_exists(session).unwrap() {
+ (ClientInfo::New(session_name.unwrap()), layout)
+ } else {
+ (
+ ClientInfo::Attach(
+ session_name.unwrap(),
+ config_options.clone(),
+ ),
+ None,
+ )
+ }
+ } else {
+ assert_session(session);
(
- ClientInfo::New(names::Generator::default().next().unwrap()),
- layout,
+ ClientInfo::Attach(session_name.unwrap(), config_options.clone()),
+ None,
)
- } else {
- println!("No active zellij sessions found.");
- process::exit(1);
}
}
- ActiveSession::One(session_name) => (
- ClientInfo::Attach(session_name, config_options.clone()),
- None,
- ),
- ActiveSession::Many => {
- println!("Please specify the session name to attach to. The following sessions are active:");
- print_sessions(get_sessions().unwrap());
- process::exit(1);
- }
- },
+ None => match get_active_session() {
+ ActiveSession::None => {
+ if create {
+ (
+ ClientInfo::New(names::Generator::default().next().unwrap()),
+ layout,
+ )
+ } else {
+ println!("No active zellij sessions found.");
+ process::exit(1);
+ }
+ }
+ ActiveSession::One(session_name) => (
+ ClientInfo::Attach(session_name, config_options.clone()),
+ None,
+ ),
+ ActiveSession::Many => {
+ println!("Please specify the session name to attach to. The following sessions are active:");
+ print_sessions(get_sessions().unwrap());
+ process::exit(1);
+ }
+ },
+ }
};
start_client(
diff --git a/src/sessions.rs b/src/sessions.rs
index c2d8afea4..bf44d0885 100644
--- a/src/sessions.rs
+++ b/src/sessions.rs
@@ -1,4 +1,5 @@
use std::os::unix::fs::FileTypeExt;
+use std::time::SystemTime;
use std::{fs, io, process};
use zellij_utils::{
consts::ZELLIJ_SOCK_DIR,
@@ -29,6 +30,36 @@ pub(crate) fn get_sessions() -> Result<Vec<String>, io::ErrorKind> {
}
}
+pub(crate) fn get_sessions_sorted_by_creation_date() -> anyhow::Result<Vec<String>> {
+ match fs::read_dir(&*ZELLIJ_SOCK_DIR) {
+ Ok(files) => {
+ let mut sessions_with_creation_date: Vec<(String, SystemTime)> = Vec::new();
+ for file in files {
+ let file = file?;
+ let file_name = file.file_name().into_string().unwrap();
+ let file_created_at = file.metadata()?.created()?;
+ if file.file_type()?.is_socket() && assert_socket(&file_name) {
+ sessions_with_creation_date.push((file_name, file_created_at));
+ }
+ }
+ sessions_with_creation_date.sort_by_key(|x| x.1); // the oldest one will be the first
+
+ let sessions = sessions_with_creation_date
+ .iter()
+ .map(|x| x.0.clone())
+ .collect();
+ Ok(sessions)
+ }
+ Err(err) => {
+ if let io::ErrorKind::NotFound = err.kind() {
+ Ok(Vec::with_capacity(0))
+ } else {
+ Err(err.into())
+ }
+ }
+ }
+}
+
fn assert_socket(name: &str) -> bool {
let path = &*ZELLIJ_SOCK_DIR.join(name);
match LocalSocketStream::connect(path) {
@@ -59,6 +90,18 @@ pub(crate) fn print_sessions(sessions: Vec<String>) {
})
}
+pub(crate) fn print_sessions_with_index(sessions: Vec<String>) {
+ let curr_session = std::env::var("ZELLIJ_SESSION_NAME").unwrap_or_else(|_| "".into());
+ for (i, session) in sessions.iter().enumerate() {
+ let suffix = if curr_session == *session {
+ " (current)"
+ } else {
+ ""
+ };
+ println!("{}: {}{}", i, session, suffix);
+ }
+}
+
pub(crate) enum ActiveSession {
None,
One(String),
@@ -78,7 +121,7 @@ pub(crate) fn get_active_session() -> ActiveSession {
}
}
Err(e) => {
- eprintln!("Error occured: {:?}", e);
+ eprintln!("Error occurred: {:?}", e);
process::exit(1);
}
}
@@ -91,7 +134,7 @@ pub(crate) fn kill_session(name: &str) {
IpcSenderWithContext::new(stream).send(ClientToServerMsg::KillSession);
}
Err(e) => {
- eprintln!("Error occured: {:?}", e);
+ eprintln!("Error occurred: {:?}", e);
process::exit(1);
}
};
@@ -108,7 +151,7 @@ pub(crate) fn list_sessions() {
0
}
Err(e) => {
- eprintln!("Error occured: {:?}", e);
+ eprintln!("Error occurred: {:?}", e);
1
}
};
@@ -137,7 +180,7 @@ pub(crate) fn assert_session(name: &str) {
}
}
Err(e) => {
- eprintln!("Error occured: {:?}", e);
+ eprintln!("Error occurred: {:?}", e);
}
};
process::exit(1);
@@ -151,7 +194,7 @@ pub(crate) fn assert_session_ne(name: &str) {
}
println!("Session with name {:?} aleady exists. Use attach command to connect to it or specify a different name.", name);
}
- Err(e) => eprintln!("Error occured: {:?}", e),
+ Err(e) => eprintln!("Error occurred: {:?}", e),
};
process::exit(1);
}
diff --git a/zellij-client/src/input_handler.rs b/zellij-client/src/input_handler.rs
index 2b89741e0..d07c2caa8 100644
--- a/zellij-client/src/input_handler.rs
+++ b/zellij-client/src/input_handler.rs
@@ -135,6 +135,9 @@ impl InputHandler {
MouseButton::Left => {
self.dispatch_action(Action::LeftClick(point));
}
+ MouseButton::Right => {
+ self.dispatch_action(Action::RightClick(point));
+ }
_ => {}
},
MouseEvent::Release(point) => {
diff --git a/zellij-server/Cargo.toml b/zellij-server/Cargo.toml
index 9d7c075e1..ea72f2d01 100644
--- a/zellij-server/Cargo.toml
+++ b/zellij-server/Cargo.toml
@@ -25,6 +25,7 @@ zellij-utils = { path = "../zellij-utils/", version = "0.20.0" }
log = "0.4.14"
typetag = "0.1.7"
chrono = "0.4.19"
+close_fds = "0.3.2"
[target.'cfg(target_os = "macos")'.dependencies]
darwin-libproc = "0.2.0"
diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs
index 09c5e9d70..f8fe751a6 100644
--- a/zellij-server/src/os_input_output.rs
+++ b/zellij-server/src/os_input_output.rs
@@ -1,10 +1,14 @@
use std::collections::HashMap;
+use crate::panes::PaneId;
+
#[cfg(target_os = "macos")]
use darwin_libproc;
-use std::env;
+#[cfg(target_os = "linux")]
use std::fs;
+
+use std::env;
use std::os::unix::io::RawFd;
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
@@ -16,11 +20,12 @@ use zellij_utils::{async_std, interprocess, libc, nix, signal_hook, zellij_tile}
use async_std::fs::File as AsyncFile;
use async_std::os::unix::io::FromRawFd;
use interprocess::local_socket::LocalSocketStream;
-use nix::pty::{forkpty, ForkptyResult, Winsize};
+
+use nix::pty::{openpty, OpenptyResult, Winsize};
use nix::sys::signal::{kill, Signal};
use nix::sys::termios;
-use nix::sys::wait::waitpid;
-use nix::unistd::{self, ForkResult};
+
+use nix::unistd;
use signal_hook::consts::*;
use zellij_tile::data::Palette;
use zellij_utils::{
@@ -31,7 +36,6 @@ use zellij_utils::{
use async_std::io::ReadExt;
pub use async_trait::async_trait;
-use byteorder::{BigEndian, ByteOrder};
pub use nix::unistd::Pid;
@@ -97,96 +101,63 @@ fn handle_command_exit(mut child: Child) {
}
}
-fn handle_fork_pty(
- fork_pty_res: ForkptyResult,
+fn handle_openpty(
+ open_pty_res: OpenptyResult,
cmd: RunCommand,
- parent_fd: RawFd,
- child_fd: RawFd,
-) -> (RawFd, ChildId) {
- let pid_primary = fork_pty_res.master;
- let (pid_secondary, pid_shell) = match fork_pty_res.fork_result {
- ForkResult::Parent { child } => {
- let pid_shell = read_from_pipe(parent_fd, child_fd);
- (child, pid_shell)
+ quit_cb: Box<dyn Fn(PaneId) + Send>,
+) -> (RawFd, RawFd) {
+ // primary side of pty and child fd
+ let pid_primary = open_pty_res.master;
+ let pid_secondary = open_pty_res.slave;
+
+ let mut child = unsafe {
+ let command = &mut Command::new(cmd.command);
+ if let Some(current_dir) = cmd.cwd {
+ command.current_dir(current_dir);
}
- ForkResult::Child => {
- let child = unsafe {
- let command = &mut Command::new(cmd.command);
- if let Some(current_dir) = cmd.cwd {
- command.current_dir(current_dir);
+ command
+ .args(&cmd.args)
+ .pre_exec(move || -> std::io::Result<()> {
+ if libc::login_tty(pid_secondary) != 0 {
+ panic!("failed to set controlling terminal");
}
- command
- .args(&cmd.args)
- .pre_exec(|| -> std::io::Result<()> {
- // this is the "unsafe" part, for more details please see:
- // https://doc.rust-lang.org/std/os/unix/process/trait.CommandExt.html#notes-and-safety
-