Skip to content

Commit

Permalink
feat: Open new terms in focused term's CWD
Browse files Browse the repository at this point in the history
Closes: #251

This patch implements an optional (but enabled by default) feature for
opening new terminals using the focused terminal's working directory.
The code to retrieve the CWD is largely based on Alacritty's
implementation of the same feature.

I added Rustix as a new direct dependency for the working directory
logic. Both libc and Rustix are transitive dependencies of COSMIC Term.
I opted for Rustix over libc to avoid an `unsafe` block as well as for
its stronger type guarantees.

References:
* https://github.com/alacritty/alacritty/blob/6bd1674bd80e73df0d41e4342ad4e34bb7d04f84/alacritty/src/daemon.rs#L85-L108
  • Loading branch information
joshuamegnauth54 committed Jul 25, 2024
1 parent 0652001 commit b62121c
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ron = "0.8"
serde = { version = "1", features = ["serde_derive"] }
shlex = "1"
tokio = { version = "1", features = ["sync"] }
rustix = { version = "0.38", features = ["termios"] }
# Internationalization
i18n-embed = { version = "0.14", features = [
"fluent-system",
Expand Down
3 changes: 3 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ pub struct Config {
pub syntax_theme_light: String,
pub focus_follow_mouse: bool,
pub default_profile: Option<ProfileId>,
/// Open new terminal with the current working directory of the focused term
pub current_working_directory: bool,
}

impl Default for Config {
Expand All @@ -259,6 +261,7 @@ impl Default for Config {
syntax_theme_light: COSMIC_THEME_LIGHT.to_string(),
use_bright_bold: false,
default_profile: None,
current_working_directory: true,
}
}
}
Expand Down
23 changes: 20 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,18 @@ impl App {
Some(colors) => {
let current_pane = self.pane_model.focus;
if let Some(tab_model) = self.pane_model.active_mut() {
let cwd = self
.config
.current_working_directory
.then(|| {
tab_model.active_data::<Mutex<Terminal>>().and_then(
|terminal| {
terminal.lock().unwrap().current_working_directory()
},
)
})
.flatten();

// Use the profile options, startup options, or defaults
let (options, tab_title_override) = match profile_id_opt
.and_then(|profile_id| self.config.profiles.get(&profile_id))
Expand All @@ -1214,8 +1226,8 @@ impl App {
shell = Some(tty::Shell::new(command, args));
}
}
let working_directory = (!profile.working_directory.is_empty())
.then(|| profile.working_directory.clone().into());
let working_directory = cwd
.or_else(|| Some(profile.working_directory.clone().into()));

let options = tty::Options {
shell,
Expand All @@ -1230,7 +1242,12 @@ impl App {
};
(options, tab_title_override)
}
None => (self.startup_options.take().unwrap_or_default(), None),
None => {
let mut options =
self.startup_options.take().unwrap_or_default();
options.working_directory = cwd;
(options, None)
}
};
let entity = tab_model
.insert()
Expand Down
43 changes: 43 additions & 0 deletions src/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use cosmic_text::{
Weight, Wrap,
};
use indexmap::IndexSet;
#[cfg(not(any(windows, target_os = "macos")))]
use rustix::fd::AsFd;
use std::{
borrow::Cow,
collections::HashMap,
Expand All @@ -35,6 +37,8 @@ use std::{
},
time::Instant,
};
#[cfg(not(windows))]
use std::{fs, path::PathBuf};
use tokio::sync::mpsc;

pub use alacritty_terminal::grid::Scroll as TerminalScroll;
Expand Down Expand Up @@ -212,6 +216,10 @@ pub struct Terminal {
search_value: String,
size: Size,
use_bright_bold: bool,
#[cfg(not(windows))]
master_fd: Option<rustix::fd::OwnedFd>,
#[cfg(not(windows))]
shell_pid: rustix::process::Pid,
}

impl Terminal {
Expand Down Expand Up @@ -281,6 +289,11 @@ impl Terminal {
let window_id = 0;
let pty = tty::new(&options, size.into(), window_id)?;

#[cfg(not(windows))]
let master_fd = pty.file().as_fd().try_clone_to_owned().ok();
#[cfg(not(windows))]
let shell_pid = rustix::process::Pid::from_child(pty.child());

let pty_event_loop = EventLoop::new(term.clone(), event_proxy, pty, options.hold, false)?;
let notifier = Notifier(pty_event_loop.channel());
let _pty_join_handle = pty_event_loop.spawn();
Expand All @@ -303,6 +316,10 @@ impl Terminal {
tab_title_override,
term,
use_bright_bold,
#[cfg(not(any(windows, target_os = "macos")))]
master_fd,
#[cfg(not(any(windows, target_os = "macos")))]
shell_pid,
})
}

Expand Down Expand Up @@ -915,6 +932,32 @@ impl Terminal {
);
}
}

/// Current working directory
#[cfg(not(windows))]
pub fn current_working_directory(&self) -> Option<PathBuf> {
// Largely based off of Alacritty
// https://github.com/alacritty/alacritty/blob/6bd1674bd80e73df0d41e4342ad4e34bb7d04f84/alacritty/src/daemon.rs#L85-L108
let pid = self
.master_fd
.as_ref()
.and_then(|pid| rustix::termios::tcgetpgrp(pid).ok())
.or(Some(self.shell_pid))?;

#[cfg(not(any(target_os = "freebsd", target_os = "macos")))]
let link_path = format!("/proc/{}/cwd", pid.as_raw_nonzero());
#[cfg(target_os = "freebsd")]
let link_path = format!("/compat/linux/proc/{}/cwd", pid.as_raw_nonzero());

#[cfg(not(target_os = "macos"))]
let cwd = fs::read_link(link_path).ok();

// TODO: macOS support
#[cfg(target_os = "macos")]
let cwd = None;

cwd
}
}

impl Drop for Terminal {
Expand Down

0 comments on commit b62121c

Please sign in to comment.