feat(cwd-pane): Keeping the cwd when opening new panes (#691)
* feat(cwd-pane): Add a new trait to get the cwd of a given pid Co-authored-by: Quentin Rasmont <qrasmont@gmail.com> * feat(cwd-pane): Allow for setting the cwd when spawning a new terminal Co-authored-by: Quentin Rasmont <qrasmont@gmail.com> * feat(cwd-pane): Add an active_pane field to the Pty struct Co-authored-by: Quentin Rasmont <qrasmont@gmail.com> * feat(cwd-pane): Update Pty with Tab's active pane id Co-authored-by: Quentin Rasmont <qrasmont@gmail.com> * feat(cwd-pane): Refactor spawn_terminal to use cwd by default Co-authored-by: Quentin Rasmont <qrasmont@gmail.com> * feat(cwd-pane): Fix tests and lints Co-authored-by: Quentin Rasmont <qrasmont@gmail.com> * feat(cwd-pane): Fix formatting * feat(cwd-pane): Refactor child pid fetching to handle errors better Instead of panicking when transfering the process id of the forked child command we just return an empty process id. * feat(cwd-pane): Add non Linux/MacOS targets for the get_cwd method. This will allow Zellij to compile on non Linux/MacOS targets without having an inherited cwd. * feat(cwd-pane): Refactor spawn_terminal method to use ChildId struct. The spawn_terminal methods been refactored to use the ChildId struct in order to clarify what the Pid's returned by it are. The documentation for the ChildId struct was improved as well. * feat(cwd-pane): Fix tests/lints Co-authored-by: Jesse Tuchsen <not@disclosing> Co-authored-by: Quentin Rasmont <qrasmont@gmail.com>
This commit is contained in:
parent
26a970a7d8
commit
4f94f95c90
9 changed files with 295 additions and 143 deletions
57
Cargo.lock
generated
57
Cargo.lock
generated
|
|
@ -4,11 +4,11 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.16.0"
|
version = "0.15.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd"
|
checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gimli 0.25.0",
|
"gimli 0.24.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.18"
|
version = "0.7.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
@ -227,16 +227,16 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.61"
|
version = "0.3.59"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01"
|
checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"libc",
|
"libc",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
"object 0.26.0",
|
"object 0.24.0",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -621,6 +621,26 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darwin-libproc"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc629b7cf42586fee31dae31f9ab73fa5ff5f0170016aa61be5fcbc12a90c516"
|
||||||
|
dependencies = [
|
||||||
|
"darwin-libproc-sys",
|
||||||
|
"libc",
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darwin-libproc-sys"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef0aa083b94c54aa4cfd9bbfd37856714c139d1dc511af80270558c7ba3b4816"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derivative"
|
name = "derivative"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
|
@ -897,9 +917,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.25.0"
|
version = "0.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7"
|
checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gloo-timers"
|
name = "gloo-timers"
|
||||||
|
|
@ -1188,9 +1208,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.4.0"
|
version = "2.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memmap2"
|
name = "memmap2"
|
||||||
|
|
@ -1325,12 +1345,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.26.0"
|
version = "0.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c55827317fb4c08822499848a14237d2874d6f139828893017237e7ab93eb386"
|
checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170"
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
|
|
@ -1675,9 +1692,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.5.4"
|
version = "1.4.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
@ -2652,9 +2669,11 @@ dependencies = [
|
||||||
"ansi_term 0.12.1",
|
"ansi_term 0.12.1",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64",
|
"base64",
|
||||||
|
"byteorder",
|
||||||
"cassowary",
|
"cassowary",
|
||||||
"chrono",
|
"chrono",
|
||||||
"daemonize",
|
"daemonize",
|
||||||
|
"darwin-libproc",
|
||||||
"insta",
|
"insta",
|
||||||
"log",
|
"log",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ license = "MIT"
|
||||||
ansi_term = "0.12.1"
|
ansi_term = "0.12.1"
|
||||||
async-trait = "0.1.50"
|
async-trait = "0.1.50"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
|
byteorder = "1.4.3"
|
||||||
daemonize = "0.4.1"
|
daemonize = "0.4.1"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
unicode-width = "0.1.8"
|
unicode-width = "0.1.8"
|
||||||
|
|
@ -23,6 +24,9 @@ log = "0.4.14"
|
||||||
typetag = "0.1.7"
|
typetag = "0.1.7"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
|
darwin-libproc = "0.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
insta = "1.6.0"
|
insta = "1.6.0"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use darwin_libproc;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::os::unix::process::CommandExt;
|
use std::os::unix::process::CommandExt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
@ -10,7 +14,7 @@ use zellij_utils::{async_std, interprocess, libc, nix, signal_hook, zellij_tile}
|
||||||
use async_std::fs::File as AsyncFile;
|
use async_std::fs::File as AsyncFile;
|
||||||
use async_std::os::unix::io::FromRawFd;
|
use async_std::os::unix::io::FromRawFd;
|
||||||
use interprocess::local_socket::LocalSocketStream;
|
use interprocess::local_socket::LocalSocketStream;
|
||||||
use nix::pty::{forkpty, Winsize};
|
use nix::pty::{forkpty, ForkptyResult, Winsize};
|
||||||
use nix::sys::signal::{kill, Signal};
|
use nix::sys::signal::{kill, Signal};
|
||||||
use nix::sys::termios;
|
use nix::sys::termios;
|
||||||
use nix::sys::wait::waitpid;
|
use nix::sys::wait::waitpid;
|
||||||
|
|
@ -29,6 +33,7 @@ use zellij_utils::{
|
||||||
|
|
||||||
use async_std::io::ReadExt;
|
use async_std::io::ReadExt;
|
||||||
pub use async_trait::async_trait;
|
pub use async_trait::async_trait;
|
||||||
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
|
||||||
pub use nix::unistd::Pid;
|
pub use nix::unistd::Pid;
|
||||||
|
|
||||||
|
|
@ -92,44 +97,94 @@ fn handle_command_exit(mut child: Child) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_fork_pty(
|
||||||
|
fork_pty_res: ForkptyResult,
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
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(|| -> 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
|
||||||
|
unistd::setpgid(Pid::from_raw(0), Pid::from_raw(0))
|
||||||
|
.expect("failed to create a new process group");
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to spawn")
|
||||||
|
};
|
||||||
|
unistd::tcsetpgrp(0, Pid::from_raw(child.id() as i32))
|
||||||
|
.expect("faled to set child's forceground process group");
|
||||||
|
write_to_pipe(child.id(), parent_fd, child_fd);
|
||||||
|
handle_command_exit(child);
|
||||||
|
::std::process::exit(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
pid_primary,
|
||||||
|
ChildId {
|
||||||
|
primary: pid_secondary,
|
||||||
|
shell: pid_shell.map(|pid| Pid::from_raw(pid as i32)),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Spawns a new terminal from the parent terminal with [`termios`](termios::Termios)
|
/// Spawns a new terminal from the parent terminal with [`termios`](termios::Termios)
|
||||||
/// `orig_termios`.
|
/// `orig_termios`.
|
||||||
///
|
///
|
||||||
fn handle_terminal(cmd: RunCommand, orig_termios: termios::Termios) -> (RawFd, Pid) {
|
fn handle_terminal(cmd: RunCommand, orig_termios: termios::Termios) -> (RawFd, ChildId) {
|
||||||
let (pid_primary, pid_secondary): (RawFd, Pid) = {
|
// Create a pipe to allow the child the communicate the shell's pid to it's
|
||||||
match forkpty(None, Some(&orig_termios)) {
|
// parent.
|
||||||
Ok(fork_pty_res) => {
|
let (parent_fd, child_fd) = unistd::pipe().expect("failed to create pipe");
|
||||||
let pid_primary = fork_pty_res.master;
|
match forkpty(None, Some(&orig_termios)) {
|
||||||
let pid_secondary = match fork_pty_res.fork_result {
|
Ok(fork_pty_res) => handle_fork_pty(fork_pty_res, cmd, parent_fd, child_fd),
|
||||||
ForkResult::Parent { child } => child,
|
Err(e) => {
|
||||||
ForkResult::Child => {
|
panic!("failed to fork {:?}", e);
|
||||||
let child = unsafe {
|
|
||||||
Command::new(cmd.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
|
|
||||||
unistd::setpgid(Pid::from_raw(0), Pid::from_raw(0))
|
|
||||||
.expect("failed to create a new process group");
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.spawn()
|
|
||||||
.expect("failed to spawn")
|
|
||||||
};
|
|
||||||
unistd::tcsetpgrp(0, Pid::from_raw(child.id() as i32))
|
|
||||||
.expect("faled to set child's forceground process group");
|
|
||||||
handle_command_exit(child);
|
|
||||||
::std::process::exit(0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(pid_primary, pid_secondary)
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
panic!("failed to fork {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
(pid_primary, pid_secondary)
|
}
|
||||||
|
|
||||||
|
/// Write to a pipe given both file descriptors
|
||||||
|
fn write_to_pipe(data: u32, parent_fd: RawFd, child_fd: RawFd) {
|
||||||
|
let mut buff = [0; 4];
|
||||||
|
BigEndian::write_u32(&mut buff, data);
|
||||||
|
if unistd::close(parent_fd).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if unistd::write(child_fd, &buff).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unistd::close(child_fd).unwrap_or_default();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read from a pipe given both file descriptors
|
||||||
|
fn read_from_pipe(parent_fd: RawFd, child_fd: RawFd) -> Option<u32> {
|
||||||
|
let mut buffer = [0; 4];
|
||||||
|
if unistd::close(child_fd).is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if unistd::read(parent_fd, &mut buffer).is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if unistd::close(parent_fd).is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(u32::from_be_bytes(buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If a [`TerminalAction::OpenFile(file)`] is given, the text editor specified by environment variable `EDITOR`
|
/// If a [`TerminalAction::OpenFile(file)`] is given, the text editor specified by environment variable `EDITOR`
|
||||||
|
|
@ -145,11 +200,11 @@ fn handle_terminal(cmd: RunCommand, orig_termios: termios::Termios) -> (RawFd, P
|
||||||
/// This function will panic if both the `EDITOR` and `VISUAL` environment variables are not
|
/// This function will panic if both the `EDITOR` and `VISUAL` environment variables are not
|
||||||
/// set.
|
/// set.
|
||||||
pub fn spawn_terminal(
|
pub fn spawn_terminal(
|
||||||
terminal_action: Option<TerminalAction>,
|
terminal_action: TerminalAction,
|
||||||
orig_termios: termios::Termios,
|
orig_termios: termios::Termios,
|
||||||
) -> (RawFd, Pid) {
|
) -> (RawFd, ChildId) {
|
||||||
let cmd = match terminal_action {
|
let cmd = match terminal_action {
|
||||||
Some(TerminalAction::OpenFile(file_to_open)) => {
|
TerminalAction::OpenFile(file_to_open) => {
|
||||||
if env::var("EDITOR").is_err() && env::var("VISUAL").is_err() {
|
if env::var("EDITOR").is_err() && env::var("VISUAL").is_err() {
|
||||||
panic!("Can't edit files if an editor is not defined. To fix: define the EDITOR or VISUAL environment variables with the path to your editor (eg. /usr/bin/vim)");
|
panic!("Can't edit files if an editor is not defined. To fix: define the EDITOR or VISUAL environment variables with the path to your editor (eg. /usr/bin/vim)");
|
||||||
}
|
}
|
||||||
|
|
@ -160,15 +215,13 @@ pub fn spawn_terminal(
|
||||||
.into_os_string()
|
.into_os_string()
|
||||||
.into_string()
|
.into_string()
|
||||||
.expect("Not valid Utf8 Encoding")];
|
.expect("Not valid Utf8 Encoding")];
|
||||||
RunCommand { command, args }
|
RunCommand {
|
||||||
}
|
command,
|
||||||
Some(TerminalAction::RunCommand(command)) => command,
|
args,
|
||||||
None => {
|
cwd: None,
|
||||||
let command =
|
}
|
||||||
PathBuf::from(env::var("SHELL").expect("Could not find the SHELL variable"));
|
|
||||||
let args = vec![];
|
|
||||||
RunCommand { command, args }
|
|
||||||
}
|
}
|
||||||
|
TerminalAction::RunCommand(command) => command,
|
||||||
};
|
};
|
||||||
|
|
||||||
handle_terminal(cmd, orig_termios)
|
handle_terminal(cmd, orig_termios)
|
||||||
|
|
@ -214,8 +267,10 @@ impl AsyncReader for RawFdAsyncReader {
|
||||||
pub trait ServerOsApi: Send + Sync {
|
pub trait ServerOsApi: Send + Sync {
|
||||||
/// Sets the size of the terminal associated to file descriptor `fd`.
|
/// Sets the size of the terminal associated to file descriptor `fd`.
|
||||||
fn set_terminal_size_using_fd(&self, fd: RawFd, cols: u16, rows: u16);
|
fn set_terminal_size_using_fd(&self, fd: RawFd, cols: u16, rows: u16);
|
||||||
/// Spawn a new terminal, with a terminal action.
|
/// Spawn a new terminal, with a terminal action. The returned tuple contains the master file
|
||||||
fn spawn_terminal(&self, terminal_action: Option<TerminalAction>) -> (RawFd, Pid);
|
/// descriptor of the forked psuedo terminal and a [ChildId] struct containing process id's for
|
||||||
|
/// the forked child process.
|
||||||
|
fn spawn_terminal(&self, terminal_action: TerminalAction) -> (RawFd, ChildId);
|
||||||
/// Read bytes from the standard output of the virtual terminal referred to by `fd`.
|
/// Read bytes from the standard output of the virtual terminal referred to by `fd`.
|
||||||
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
|
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
|
||||||
/// Creates an `AsyncReader` that can be used to read from `fd` in an async context
|
/// Creates an `AsyncReader` that can be used to read from `fd` in an async context
|
||||||
|
|
@ -247,6 +302,8 @@ pub trait ServerOsApi: Send + Sync {
|
||||||
/// Update the receiver socket for the client
|
/// Update the receiver socket for the client
|
||||||
fn update_receiver(&mut self, stream: LocalSocketStream);
|
fn update_receiver(&mut self, stream: LocalSocketStream);
|
||||||
fn load_palette(&self) -> Palette;
|
fn load_palette(&self) -> Palette;
|
||||||
|
/// Returns the current working directory for a given pid
|
||||||
|
fn get_cwd(&self, pid: Pid) -> Option<PathBuf>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerOsApi for ServerOsInputOutput {
|
impl ServerOsApi for ServerOsInputOutput {
|
||||||
|
|
@ -255,7 +312,7 @@ impl ServerOsApi for ServerOsInputOutput {
|
||||||
set_terminal_size_using_fd(fd, cols, rows);
|
set_terminal_size_using_fd(fd, cols, rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn spawn_terminal(&self, terminal_action: Option<TerminalAction>) -> (RawFd, Pid) {
|
fn spawn_terminal(&self, terminal_action: TerminalAction) -> (RawFd, ChildId) {
|
||||||
let orig_termios = self.orig_termios.lock().unwrap();
|
let orig_termios = self.orig_termios.lock().unwrap();
|
||||||
spawn_terminal(terminal_action, orig_termios.clone())
|
spawn_terminal(terminal_action, orig_termios.clone())
|
||||||
}
|
}
|
||||||
|
|
@ -336,6 +393,18 @@ impl ServerOsApi for ServerOsInputOutput {
|
||||||
fn load_palette(&self) -> Palette {
|
fn load_palette(&self) -> Palette {
|
||||||
default_palette()
|
default_palette()
|
||||||
}
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fn get_cwd(&self, pid: Pid) -> Option<PathBuf> {
|
||||||
|
darwin_libproc::pid_cwd(pid.as_raw()).ok()
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn get_cwd(&self, pid: Pid) -> Option<PathBuf> {
|
||||||
|
fs::read_link(format!("/proc/{}/cwd", pid)).ok()
|
||||||
|
}
|
||||||
|
#[cfg(all(not(target_os = "linux"), not(target_os = "macos")))]
|
||||||
|
fn get_cwd(&self, _pid: Pid) -> Option<PathBuf> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Box<dyn ServerOsApi> {
|
impl Clone for Box<dyn ServerOsApi> {
|
||||||
|
|
@ -353,3 +422,13 @@ pub fn get_server_os_input() -> Result<ServerOsInputOutput, nix::Error> {
|
||||||
send_instructions_to_client: Arc::new(Mutex::new(None)),
|
send_instructions_to_client: Arc::new(Mutex::new(None)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Process id's for forked terminals
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ChildId {
|
||||||
|
/// Primary process id of a forked terminal
|
||||||
|
pub primary: Pid,
|
||||||
|
/// Process id of the command running inside the forked terminal, usually a shell. The primary
|
||||||
|
/// field is it's parent process id.
|
||||||
|
pub shell: Option<Pid>,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
os_input_output::{AsyncReader, Pid, ServerOsApi},
|
os_input_output::{AsyncReader, ChildId, ServerOsApi},
|
||||||
panes::PaneId,
|
panes::PaneId,
|
||||||
screen::ScreenInstruction,
|
screen::ScreenInstruction,
|
||||||
thread_bus::{Bus, ThreadSenders},
|
thread_bus::{Bus, ThreadSenders},
|
||||||
|
|
@ -12,14 +12,16 @@ use async_std::{
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
env,
|
||||||
os::unix::io::RawFd,
|
os::unix::io::RawFd,
|
||||||
|
path::PathBuf,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use zellij_utils::{
|
use zellij_utils::{
|
||||||
async_std,
|
async_std,
|
||||||
errors::{get_current_ctx, ContextType, PtyContext},
|
errors::{get_current_ctx, ContextType, PtyContext},
|
||||||
input::{
|
input::{
|
||||||
command::TerminalAction,
|
command::{RunCommand, TerminalAction},
|
||||||
layout::{Layout, LayoutFromYaml, Run, TabLayout},
|
layout::{Layout, LayoutFromYaml, Run, TabLayout},
|
||||||
},
|
},
|
||||||
logging::debug_to_file,
|
logging::debug_to_file,
|
||||||
|
|
@ -33,6 +35,7 @@ pub(crate) enum PtyInstruction {
|
||||||
SpawnTerminal(Option<TerminalAction>),
|
SpawnTerminal(Option<TerminalAction>),
|
||||||
SpawnTerminalVertically(Option<TerminalAction>),
|
SpawnTerminalVertically(Option<TerminalAction>),
|
||||||
SpawnTerminalHorizontally(Option<TerminalAction>),
|
SpawnTerminalHorizontally(Option<TerminalAction>),
|
||||||
|
UpdateActivePane(Option<PaneId>),
|
||||||
NewTab(Option<TerminalAction>, Option<TabLayout>),
|
NewTab(Option<TerminalAction>, Option<TabLayout>),
|
||||||
ClosePane(PaneId),
|
ClosePane(PaneId),
|
||||||
CloseTab(Vec<PaneId>),
|
CloseTab(Vec<PaneId>),
|
||||||
|
|
@ -45,6 +48,7 @@ impl From<&PtyInstruction> for PtyContext {
|
||||||
PtyInstruction::SpawnTerminal(_) => PtyContext::SpawnTerminal,
|
PtyInstruction::SpawnTerminal(_) => PtyContext::SpawnTerminal,
|
||||||
PtyInstruction::SpawnTerminalVertically(_) => PtyContext::SpawnTerminalVertically,
|
PtyInstruction::SpawnTerminalVertically(_) => PtyContext::SpawnTerminalVertically,
|
||||||
PtyInstruction::SpawnTerminalHorizontally(_) => PtyContext::SpawnTerminalHorizontally,
|
PtyInstruction::SpawnTerminalHorizontally(_) => PtyContext::SpawnTerminalHorizontally,
|
||||||
|
PtyInstruction::UpdateActivePane(_) => PtyContext::UpdateActivePane,
|
||||||
PtyInstruction::ClosePane(_) => PtyContext::ClosePane,
|
PtyInstruction::ClosePane(_) => PtyContext::ClosePane,
|
||||||
PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
|
PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
|
||||||
PtyInstruction::NewTab(..) => PtyContext::NewTab,
|
PtyInstruction::NewTab(..) => PtyContext::NewTab,
|
||||||
|
|
@ -54,8 +58,9 @@ impl From<&PtyInstruction> for PtyContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Pty {
|
pub(crate) struct Pty {
|
||||||
|
pub active_pane: Option<PaneId>,
|
||||||
pub bus: Bus<PtyInstruction>,
|
pub bus: Bus<PtyInstruction>,
|
||||||
pub id_to_child_pid: HashMap<RawFd, Pid>,
|
pub id_to_child_pid: HashMap<RawFd, ChildId>,
|
||||||
debug_to_file: bool,
|
debug_to_file: bool,
|
||||||
task_handles: HashMap<RawFd, JoinHandle<()>>,
|
task_handles: HashMap<RawFd, JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
@ -86,6 +91,9 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) {
|
||||||
.send_to_screen(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)))
|
.send_to_screen(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
PtyInstruction::UpdateActivePane(pane_id) => {
|
||||||
|
pty.set_active_pane(pane_id);
|
||||||
|
}
|
||||||
PtyInstruction::NewTab(terminal_action, tab_layout) => {
|
PtyInstruction::NewTab(terminal_action, tab_layout) => {
|
||||||
let merged_layout = layout.template.clone().insert_tab_layout(tab_layout);
|
let merged_layout = layout.template.clone().insert_tab_layout(tab_layout);
|
||||||
pty.spawn_terminals_for_layout(merged_layout.into(), terminal_action.clone());
|
pty.spawn_terminals_for_layout(merged_layout.into(), terminal_action.clone());
|
||||||
|
|
@ -208,14 +216,30 @@ fn stream_terminal_bytes(
|
||||||
impl Pty {
|
impl Pty {
|
||||||
pub fn new(bus: Bus<PtyInstruction>, debug_to_file: bool) -> Self {
|
pub fn new(bus: Bus<PtyInstruction>, debug_to_file: bool) -> Self {
|
||||||
Pty {
|
Pty {
|
||||||
|
active_pane: None,
|
||||||
bus,
|
bus,
|
||||||
id_to_child_pid: HashMap::new(),
|
id_to_child_pid: HashMap::new(),
|
||||||
debug_to_file,
|
debug_to_file,
|
||||||
task_handles: HashMap::new(),
|
task_handles: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn get_default_terminal(&self) -> TerminalAction {
|
||||||
|
TerminalAction::RunCommand(RunCommand {
|
||||||
|
args: vec![],
|
||||||
|
command: PathBuf::from(env::var("SHELL").expect("Could not find the SHELL variable")),
|
||||||
|
cwd: self
|
||||||
|
.active_pane
|
||||||
|
.and_then(|pane| match pane {
|
||||||
|
PaneId::Plugin(..) => None,
|
||||||
|
PaneId::Terminal(id) => self.id_to_child_pid.get(&id).and_then(|id| id.shell),
|
||||||
|
})
|
||||||
|
.and_then(|id| self.bus.os_input.as_ref().map(|input| input.get_cwd(id)))
|
||||||
|
.flatten(),
|
||||||
|
})
|
||||||
|
}
|
||||||
pub fn spawn_terminal(&mut self, terminal_action: Option<TerminalAction>) -> RawFd {
|
pub fn spawn_terminal(&mut self, terminal_action: Option<TerminalAction>) -> RawFd {
|
||||||
let (pid_primary, pid_secondary): (RawFd, Pid) = self
|
let terminal_action = terminal_action.unwrap_or_else(|| self.get_default_terminal());
|
||||||
|
let (pid_primary, child_id): (RawFd, ChildId) = self
|
||||||
.bus
|
.bus
|
||||||
.os_input
|
.os_input
|
||||||
.as_mut()
|
.as_mut()
|
||||||
|
|
@ -228,7 +252,7 @@ impl Pty {
|
||||||
self.debug_to_file,
|
self.debug_to_file,
|
||||||
);
|
);
|
||||||
self.task_handles.insert(pid_primary, task_handle);
|
self.task_handles.insert(pid_primary, task_handle);
|
||||||
self.id_to_child_pid.insert(pid_primary, pid_secondary);
|
self.id_to_child_pid.insert(pid_primary, child_id);
|
||||||
pid_primary
|
pid_primary
|
||||||
}
|
}
|
||||||
pub fn spawn_terminals_for_layout(
|
pub fn spawn_terminals_for_layout(
|
||||||
|
|
@ -236,29 +260,26 @@ impl Pty {
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
default_shell: Option<TerminalAction>,
|
default_shell: Option<TerminalAction>,
|
||||||
) {
|
) {
|
||||||
|
let default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal());
|
||||||
let extracted_run_instructions = layout.extract_run_instructions();
|
let extracted_run_instructions = layout.extract_run_instructions();
|
||||||
let mut new_pane_pids = vec![];
|
let mut new_pane_pids = vec![];
|
||||||
for run_instruction in extracted_run_instructions {
|
for run_instruction in extracted_run_instructions {
|
||||||
match run_instruction {
|
match run_instruction {
|
||||||
Some(Run::Command(command)) => {
|
Some(Run::Command(command)) => {
|
||||||
let cmd = TerminalAction::RunCommand(command);
|
let cmd = TerminalAction::RunCommand(command);
|
||||||
let (pid_primary, pid_secondary): (RawFd, Pid) = self
|
let (pid_primary, child_id): (RawFd, ChildId) =
|
||||||
.bus
|
self.bus.os_input.as_mut().unwrap().spawn_terminal(cmd);
|
||||||
.os_input
|
self.id_to_child_pid.insert(pid_primary, child_id);
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.spawn_terminal(Some(cmd));
|
|
||||||
self.id_to_child_pid.insert(pid_primary, pid_secondary);
|
|
||||||
new_pane_pids.push(pid_primary);
|
new_pane_pids.push(pid_primary);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let (pid_primary, pid_secondary): (RawFd, Pid) = self
|
let (pid_primary, child_id): (RawFd, ChildId) = self
|
||||||
.bus
|
.bus
|
||||||
.os_input
|
.os_input
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.spawn_terminal(default_shell.clone());
|
.spawn_terminal(default_shell.clone());
|
||||||
self.id_to_child_pid.insert(pid_primary, pid_secondary);
|
self.id_to_child_pid.insert(pid_primary, child_id);
|
||||||
new_pane_pids.push(pid_primary);
|
new_pane_pids.push(pid_primary);
|
||||||
}
|
}
|
||||||
// Investigate moving plugin loading to here.
|
// Investigate moving plugin loading to here.
|
||||||
|
|
@ -285,10 +306,15 @@ impl Pty {
|
||||||
pub fn close_pane(&mut self, id: PaneId) {
|
pub fn close_pane(&mut self, id: PaneId) {
|
||||||
match id {
|
match id {
|
||||||
PaneId::Terminal(id) => {
|
PaneId::Terminal(id) => {
|
||||||
let child_pid = self.id_to_child_pid.remove(&id).unwrap();
|
let pids = self.id_to_child_pid.remove(&id).unwrap();
|
||||||
let handle = self.task_handles.remove(&id).unwrap();
|
let handle = self.task_handles.remove(&id).unwrap();
|
||||||
task::block_on(async {
|
task::block_on(async {
|
||||||
self.bus.os_input.as_mut().unwrap().kill(child_pid).unwrap();
|
self.bus
|
||||||
|
.os_input
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.kill(pids.primary)
|
||||||
|
.unwrap();
|
||||||
let timeout = Duration::from_millis(100);
|
let timeout = Duration::from_millis(100);
|
||||||
match async_timeout(timeout, handle.cancel()).await {
|
match async_timeout(timeout, handle.cancel()).await {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
|
|
@ -297,7 +323,7 @@ impl Pty {
|
||||||
.os_input
|
.os_input
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.force_kill(child_pid)
|
.force_kill(pids.primary)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -315,6 +341,9 @@ impl Pty {
|
||||||
self.close_pane(id);
|
self.close_pane(id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
pub fn set_active_pane(&mut self, pane_id: Option<PaneId>) {
|
||||||
|
self.active_pane = pane_id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Pty {
|
impl Drop for Pty {
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,13 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_active_terminal(&mut self, pane_id: Option<PaneId>) {
|
||||||
|
self.active_terminal = pane_id;
|
||||||
|
self.senders
|
||||||
|
.send_to_pty(PtyInstruction::UpdateActivePane(self.active_terminal))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>, tab_index: usize) {
|
pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>, tab_index: usize) {
|
||||||
// TODO: this should be an attribute on Screen instead of full_screen_ws
|
// TODO: this should be an attribute on Screen instead of full_screen_ws
|
||||||
let free_space = PaneGeom::default();
|
let free_space = PaneGeom::default();
|
||||||
|
|
@ -393,7 +400,7 @@ impl Tab {
|
||||||
self.set_pane_frames(self.draw_pane_frames);
|
self.set_pane_frames(self.draw_pane_frames);
|
||||||
// This is the end of the nasty viewport hack...
|
// This is the end of the nasty viewport hack...
|
||||||
// FIXME: Active / new / current terminal, should be pane
|
// FIXME: Active / new / current terminal, should be pane
|
||||||
self.active_terminal = self.panes.iter().map(|(id, _)| id.to_owned()).next();
|
self.set_active_terminal(self.panes.iter().map(|(id, _)| id.to_owned()).next());
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
pub fn new_pane(&mut self, pid: PaneId) {
|
pub fn new_pane(&mut self, pid: PaneId) {
|
||||||
|
|
@ -466,7 +473,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.active_terminal = Some(pid);
|
self.set_active_terminal(Some(pid));
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
pub fn horizontal_split(&mut self, pid: PaneId) {
|
pub fn horizontal_split(&mut self, pid: PaneId) {
|
||||||
|
|
@ -495,7 +502,7 @@ impl Tab {
|
||||||
);
|
);
|
||||||
active_pane.set_geom(top_winsize);
|
active_pane.set_geom(top_winsize);
|
||||||
self.panes.insert(pid, Box::new(new_terminal));
|
self.panes.insert(pid, Box::new(new_terminal));
|
||||||
self.active_terminal = Some(pid);
|
self.set_active_terminal(Some(pid));
|
||||||
self.relayout_tab(Direction::Vertical);
|
self.relayout_tab(Direction::Vertical);
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
|
|
@ -524,7 +531,7 @@ impl Tab {
|
||||||
active_pane.set_geom(left_winsize);
|
active_pane.set_geom(left_winsize);
|
||||||
self.panes.insert(pid, Box::new(new_terminal));
|
self.panes.insert(pid, Box::new(new_terminal));
|
||||||
}
|
}
|
||||||
self.active_terminal = Some(pid);
|
self.set_active_terminal(Some(pid));
|
||||||
self.relayout_tab(Direction::Horizontal);
|
self.relayout_tab(Direction::Horizontal);
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
|
|
@ -1757,16 +1764,16 @@ impl Tab {
|
||||||
}
|
}
|
||||||
let active_terminal_id = self.get_active_pane_id().unwrap();
|
let active_terminal_id = self.get_active_pane_id().unwrap();
|
||||||
let terminal_ids: Vec<PaneId> = self.get_selectable_panes().map(|(&pid, _)| pid).collect(); // TODO: better, no allocations
|
let terminal_ids: Vec<PaneId> = self.get_selectable_panes().map(|(&pid, _)| pid).collect(); // TODO: better, no allocations
|
||||||
let first_terminal = terminal_ids.get(0).unwrap();
|
|
||||||
let active_terminal_id_position = terminal_ids
|
let active_terminal_id_position = terminal_ids
|
||||||
.iter()
|
.iter()
|
||||||
.position(|id| id == &active_terminal_id)
|
.position(|id| id == &active_terminal_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if let Some(next_terminal) = terminal_ids.get(active_terminal_id_position + 1) {
|
let active_terminal = terminal_ids
|
||||||
self.active_terminal = Some(*next_terminal);
|
.get(active_terminal_id_position + 1)
|
||||||
} else {
|
.or_else(|| terminal_ids.get(0))
|
||||||
self.active_terminal = Some(*first_terminal);
|
.copied();
|
||||||
}
|
|
||||||
|
self.set_active_terminal(active_terminal);
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
pub fn focus_next_pane(&mut self) {
|
pub fn focus_next_pane(&mut self) {
|
||||||
|
|
@ -1785,16 +1792,17 @@ impl Tab {
|
||||||
a_pane.y().cmp(&b_pane.y())
|
a_pane.y().cmp(&b_pane.y())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let first_pane = panes.get(0).unwrap();
|
|
||||||
let active_pane_position = panes
|
let active_pane_position = panes
|
||||||
.iter()
|
.iter()
|
||||||
.position(|(id, _)| *id == &active_pane_id) // TODO: better
|
.position(|(id, _)| *id == &active_pane_id) // TODO: better
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if let Some(next_pane) = panes.get(active_pane_position + 1) {
|
|
||||||
self.active_terminal = Some(*next_pane.0);
|
let active_terminal = panes
|
||||||
} else {
|
.get(active_pane_position + 1)
|
||||||
self.active_terminal = Some(*first_pane.0);
|
.or_else(|| panes.get(0))
|
||||||
}
|
.map(|p| *p.0);
|
||||||
|
|
||||||
|
self.set_active_terminal(active_terminal);
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
pub fn focus_previous_pane(&mut self) {
|
pub fn focus_previous_pane(&mut self) {
|
||||||
|
|
@ -1818,11 +1826,13 @@ impl Tab {
|
||||||
.iter()
|
.iter()
|
||||||
.position(|(id, _)| *id == &active_pane_id) // TODO: better
|
.position(|(id, _)| *id == &active_pane_id) // TODO: better
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if active_pane_position == 0 {
|
|
||||||
self.active_terminal = Some(*last_pane.0);
|
let active_terminal = if active_pane_position == 0 {
|
||||||
|
Some(*last_pane.0)
|
||||||
} else {
|
} else {
|
||||||
self.active_terminal = Some(*panes.get(active_pane_position - 1).unwrap().0);
|
Some(*panes.get(active_pane_position - 1).unwrap().0)
|
||||||
}
|
};
|
||||||
|
self.set_active_terminal(active_terminal);
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
// returns a boolean that indicates whether the focus moved
|
// returns a boolean that indicates whether the focus moved
|
||||||
|
|
@ -1834,7 +1844,7 @@ impl Tab {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let active_terminal = self.get_active_pane();
|
let active_terminal = self.get_active_pane();
|
||||||
if let Some(active) = active_terminal {
|
let updated_active_terminal = if let Some(active) = active_terminal {
|
||||||
let terminals = self.get_selectable_panes();
|
let terminals = self.get_selectable_panes();
|
||||||
let next_index = terminals
|
let next_index = terminals
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
@ -1853,17 +1863,16 @@ impl Tab {
|
||||||
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
||||||
next_active_pane.set_should_render(true);
|
next_active_pane.set_should_render(true);
|
||||||
|
|
||||||
self.active_terminal = Some(p);
|
self.set_active_terminal(Some(p));
|
||||||
self.render();
|
self.render();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
None => {
|
None => Some(active.pid()),
|
||||||
self.active_terminal = Some(active.pid());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.active_terminal = Some(active_terminal.unwrap().pid());
|
Some(active_terminal.unwrap().pid())
|
||||||
}
|
};
|
||||||
|
self.set_active_terminal(updated_active_terminal);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
pub fn move_focus_down(&mut self) {
|
pub fn move_focus_down(&mut self) {
|
||||||
|
|
@ -1874,7 +1883,7 @@ impl Tab {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let active_terminal = self.get_active_pane();
|
let active_terminal = self.get_active_pane();
|
||||||
if let Some(active) = active_terminal {
|
let updated_active_terminal = if let Some(active) = active_terminal {
|
||||||
let terminals = self.get_selectable_panes();
|
let terminals = self.get_selectable_panes();
|
||||||
let next_index = terminals
|
let next_index = terminals
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
@ -1893,15 +1902,14 @@ impl Tab {
|
||||||
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
||||||
next_active_pane.set_should_render(true);
|
next_active_pane.set_should_render(true);
|
||||||
|
|
||||||
self.active_terminal = Some(p);
|
Some(p)
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.active_terminal = Some(active.pid());
|
|
||||||
}
|
}
|
||||||
|
None => Some(active.pid()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.active_terminal = Some(active_terminal.unwrap().pid());
|
Some(active_terminal.unwrap().pid())
|
||||||
}
|
};
|
||||||
|
self.set_active_terminal(updated_active_terminal);
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
pub fn move_focus_up(&mut self) {
|
pub fn move_focus_up(&mut self) {
|
||||||
|
|
@ -1912,7 +1920,7 @@ impl Tab {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let active_terminal = self.get_active_pane();
|
let active_terminal = self.get_active_pane();
|
||||||
if let Some(active) = active_terminal {
|
let updated_active_terminal = if let Some(active) = active_terminal {
|
||||||
let terminals = self.get_selectable_panes();
|
let terminals = self.get_selectable_panes();
|
||||||
let next_index = terminals
|
let next_index = terminals
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
@ -1931,15 +1939,14 @@ impl Tab {
|
||||||
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
||||||
next_active_pane.set_should_render(true);
|
next_active_pane.set_should_render(true);
|
||||||
|
|
||||||
self.active_terminal = Some(p);
|
Some(p)
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.active_terminal = Some(active.pid());
|
|
||||||
}
|
}
|
||||||
|
None => Some(active.pid()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.active_terminal = Some(active_terminal.unwrap().pid());
|
Some(active_terminal.unwrap().pid())
|
||||||
}
|
};
|
||||||
|
self.set_active_terminal(updated_active_terminal);
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
// returns a boolean that indicates whether the focus moved
|
// returns a boolean that indicates whether the focus moved
|
||||||
|
|
@ -1951,7 +1958,7 @@ impl Tab {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let active_terminal = self.get_active_pane();
|
let active_terminal = self.get_active_pane();
|
||||||
if let Some(active) = active_terminal {
|
let updated_active_terminal = if let Some(active) = active_terminal {
|
||||||
let terminals = self.get_selectable_panes();
|
let terminals = self.get_selectable_panes();
|
||||||
let next_index = terminals
|
let next_index = terminals
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
@ -1970,17 +1977,16 @@ impl Tab {
|
||||||
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
let next_active_pane = self.panes.get_mut(&p).unwrap();
|
||||||
next_active_pane.set_should_render(true);
|
next_active_pane.set_should_render(true);
|
||||||
|
|
||||||
self.active_terminal = Some(p);
|
self.set_active_terminal(Some(p));
|
||||||
self.render();
|
self.render();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
None => {
|
None => Some(active.pid()),
|
||||||
self.active_terminal = Some(active.pid());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.active_terminal = Some(active_terminal.unwrap().pid());
|
Some(active_terminal.unwrap().pid())
|
||||||
}
|
};
|
||||||
|
self.set_active_terminal(updated_active_terminal);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
fn horizontal_borders(&self, terminals: &[PaneId]) -> HashSet<usize> {
|
fn horizontal_borders(&self, terminals: &[PaneId]) -> HashSet<usize> {
|
||||||
|
|
@ -1999,6 +2005,7 @@ impl Tab {
|
||||||
borders
|
borders
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn panes_to_the_left_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
|
fn panes_to_the_left_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
|
||||||
if let Some(terminal) = self.panes.get(&id) {
|
if let Some(terminal) = self.panes.get(&id) {
|
||||||
let upper_close_border = terminal.y();
|
let upper_close_border = terminal.y();
|
||||||
|
|
@ -2125,7 +2132,7 @@ impl Tab {
|
||||||
if let Some(pane) = self.panes.get_mut(&id) {
|
if let Some(pane) = self.panes.get_mut(&id) {
|
||||||
pane.set_selectable(selectable);
|
pane.set_selectable(selectable);
|
||||||
if self.get_active_pane_id() == Some(id) && !selectable {
|
if self.get_active_pane_id() == Some(id) && !selectable {
|
||||||
self.active_terminal = self.next_active_pane(&self.get_pane_ids())
|
self.set_active_terminal(self.next_active_pane(&self.get_pane_ids()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.render();
|
self.render();
|
||||||
|
|
@ -2146,7 +2153,7 @@ impl Tab {
|
||||||
self.panes.remove(&id);
|
self.panes.remove(&id);
|
||||||
if self.active_terminal == Some(id) {
|
if self.active_terminal == Some(id) {
|
||||||
let next_active_pane = self.next_active_pane(&panes);
|
let next_active_pane = self.next_active_pane(&panes);
|
||||||
self.active_terminal = next_active_pane;
|
self.set_active_terminal(next_active_pane);
|
||||||
}
|
}
|
||||||
self.relayout_tab(Direction::Horizontal);
|
self.relayout_tab(Direction::Horizontal);
|
||||||
return;
|
return;
|
||||||
|
|
@ -2158,7 +2165,7 @@ impl Tab {
|
||||||
self.panes.remove(&id);
|
self.panes.remove(&id);
|
||||||
if self.active_terminal == Some(id) {
|
if self.active_terminal == Some(id) {
|
||||||
let next_active_pane = self.next_active_pane(&panes);
|
let next_active_pane = self.next_active_pane(&panes);
|
||||||
self.active_terminal = next_active_pane;
|
self.set_active_terminal(next_active_pane);
|
||||||
}
|
}
|
||||||
self.relayout_tab(Direction::Horizontal);
|
self.relayout_tab(Direction::Horizontal);
|
||||||
return;
|
return;
|
||||||
|
|
@ -2170,7 +2177,7 @@ impl Tab {
|
||||||
self.panes.remove(&id);
|
self.panes.remove(&id);
|
||||||
if self.active_terminal == Some(id) {
|
if self.active_terminal == Some(id) {
|
||||||
let next_active_pane = self.next_active_pane(&panes);
|
let next_active_pane = self.next_active_pane(&panes);
|
||||||
self.active_terminal = next_active_pane;
|
self.set_active_terminal(next_active_pane);
|
||||||
}
|
}
|
||||||
self.relayout_tab(Direction::Vertical);
|
self.relayout_tab(Direction::Vertical);
|
||||||
return;
|
return;
|
||||||
|
|
@ -2182,7 +2189,7 @@ impl Tab {
|
||||||
self.panes.remove(&id);
|
self.panes.remove(&id);
|
||||||
if self.active_terminal == Some(id) {
|
if self.active_terminal == Some(id) {
|
||||||
let next_active_pane = self.next_active_pane(&panes);
|
let next_active_pane = self.next_active_pane(&panes);
|
||||||
self.active_terminal = next_active_pane;
|
self.set_active_terminal(next_active_pane);
|
||||||
}
|
}
|
||||||
self.relayout_tab(Direction::Vertical);
|
self.relayout_tab(Direction::Vertical);
|
||||||
return;
|
return;
|
||||||
|
|
@ -2304,7 +2311,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
fn focus_pane_at(&mut self, point: &Position) {
|
fn focus_pane_at(&mut self, point: &Position) {
|
||||||
if let Some(clicked_pane) = self.get_pane_id_at(point) {
|
if let Some(clicked_pane) = self.get_pane_id_at(point) {
|
||||||
self.active_terminal = Some(clicked_pane);
|
self.set_active_terminal(Some(clicked_pane));
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use super::{Screen, ScreenInstruction};
|
use super::{Screen, ScreenInstruction};
|
||||||
use crate::zellij_tile::data::{ModeInfo, Palette};
|
use crate::zellij_tile::data::{ModeInfo, Palette};
|
||||||
use crate::{
|
use crate::{
|
||||||
os_input_output::{AsyncReader, Pid, ServerOsApi},
|
os_input_output::{AsyncReader, ChildId, Pid, ServerOsApi},
|
||||||
thread_bus::Bus,
|
thread_bus::Bus,
|
||||||
SessionState,
|
SessionState,
|
||||||
};
|
};
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use zellij_utils::input::command::TerminalAction;
|
use zellij_utils::input::command::TerminalAction;
|
||||||
use zellij_utils::input::layout::LayoutTemplate;
|
use zellij_utils::input::layout::LayoutTemplate;
|
||||||
|
|
@ -28,7 +29,7 @@ impl ServerOsApi for FakeInputOutput {
|
||||||
fn set_terminal_size_using_fd(&self, _fd: RawFd, _cols: u16, _rows: u16) {
|
fn set_terminal_size_using_fd(&self, _fd: RawFd, _cols: u16, _rows: u16) {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
fn spawn_terminal(&self, _file_to_open: Option<TerminalAction>) -> (RawFd, Pid) {
|
fn spawn_terminal(&self, _file_to_open: TerminalAction) -> (RawFd, ChildId) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
|
fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
|
||||||
|
|
@ -73,6 +74,9 @@ impl ServerOsApi for FakeInputOutput {
|
||||||
fn load_palette(&self) -> Palette {
|
fn load_palette(&self) -> Palette {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
fn get_cwd(&self, _pid: Pid) -> Option<PathBuf> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_new_screen(size: Size) -> Screen {
|
fn create_new_screen(size: Size) -> Screen {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
use super::Tab;
|
use super::Tab;
|
||||||
use crate::zellij_tile::data::{ModeInfo, Palette};
|
use crate::zellij_tile::data::{ModeInfo, Palette};
|
||||||
use crate::{
|
use crate::{
|
||||||
os_input_output::{AsyncReader, Pid, ServerOsApi},
|
os_input_output::{AsyncReader, ChildId, Pid, ServerOsApi},
|
||||||
panes::PaneId,
|
panes::PaneId,
|
||||||
thread_bus::ThreadSenders,
|
thread_bus::ThreadSenders,
|
||||||
SessionState,
|
SessionState,
|
||||||
};
|
};
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use zellij_utils::input::layout::LayoutTemplate;
|
use zellij_utils::input::layout::LayoutTemplate;
|
||||||
use zellij_utils::pane_size::Size;
|
use zellij_utils::pane_size::Size;
|
||||||
|
|
@ -27,7 +28,7 @@ impl ServerOsApi for FakeInputOutput {
|
||||||
fn set_terminal_size_using_fd(&self, _fd: RawFd, _cols: u16, _rows: u16) {
|
fn set_terminal_size_using_fd(&self, _fd: RawFd, _cols: u16, _rows: u16) {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
fn spawn_terminal(&self, _file_to_open: Option<TerminalAction>) -> (RawFd, Pid) {
|
fn spawn_terminal(&self, _file_to_open: TerminalAction) -> (RawFd, ChildId) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
|
fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
|
||||||
|
|
@ -72,6 +73,9 @@ impl ServerOsApi for FakeInputOutput {
|
||||||
fn load_palette(&self) -> Palette {
|
fn load_palette(&self) -> Palette {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
fn get_cwd(&self, _pid: Pid) -> Option<PathBuf> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_new_tab(size: Size) -> Tab {
|
fn create_new_tab(size: Size) -> Tab {
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,7 @@ pub enum PtyContext {
|
||||||
SpawnTerminal,
|
SpawnTerminal,
|
||||||
SpawnTerminalVertically,
|
SpawnTerminalVertically,
|
||||||
SpawnTerminalHorizontally,
|
SpawnTerminalHorizontally,
|
||||||
|
UpdateActivePane,
|
||||||
NewTab,
|
NewTab,
|
||||||
ClosePane,
|
ClosePane,
|
||||||
CloseTab,
|
CloseTab,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ pub struct RunCommand {
|
||||||
pub command: PathBuf,
|
pub command: PathBuf,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub args: Vec<String>,
|
pub args: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub cwd: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Intermediate representation
|
/// Intermediate representation
|
||||||
|
|
@ -25,6 +27,8 @@ pub struct RunCommandAction {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub args: Vec<String>,
|
pub args: Vec<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub cwd: Option<PathBuf>,
|
||||||
|
#[serde(default)]
|
||||||
pub direction: Option<Direction>,
|
pub direction: Option<Direction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,6 +37,7 @@ impl From<RunCommandAction> for RunCommand {
|
||||||
RunCommand {
|
RunCommand {
|
||||||
command: action.command,
|
command: action.command,
|
||||||
args: action.args,
|
args: action.args,
|
||||||
|
cwd: action.cwd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue