Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Profile option to open new terms in last focused term's CWD [rebased 2024-10-06] #257

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 2 additions & 0 deletions i18n/en/cosmic_term.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ make-default = Make default
working-directory = Working directory
hold = Hold
remain-open = Remain open after child process exits.
open-in-cwd = Use parent CWD
open-in-cwd-description = Open new terminals using the focused tab's working directory.

## Settings
settings = Settings
Expand Down
13 changes: 13 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ pub struct Profile {
pub tab_title: String,
#[serde(default)]
pub working_directory: String,
/// Open new terminal with the current working directory of the focused term
#[serde(default = "cwd_default")]
pub open_in_cwd: bool,
#[serde(default)]
pub hold: bool,
}
Expand All @@ -212,10 +215,20 @@ impl Default for Profile {
tab_title: String::new(),
working_directory: String::new(),
hold: false,
open_in_cwd: cwd_default(),
}
}
}

#[cfg(not(windows))]
const fn cwd_default() -> bool {
true
}
#[cfg(windows)]
const fn cwd_default() -> bool {
false
}

#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Config {
pub app_theme: AppTheme,
Expand Down
49 changes: 48 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ pub enum Message {
ProfileName(ProfileId, String),
ProfileNew,
ProfileOpen(ProfileId),
ProfileOpenInCWD(ProfileId, bool),
ProfileRemove(ProfileId),
ProfileSyntaxTheme(ProfileId, ColorSchemeKind, usize),
ProfileTabTitle(ProfileId, String),
Expand Down Expand Up @@ -994,6 +995,13 @@ impl App {
])
.align_items(Alignment::Center)
.padding([0, space_s]),
)
.add(
widget::settings::item::builder(fl!("open-in-cwd"))
.description(fl!("open-in-cwd-description"))
.toggler(profile.open_in_cwd, move |open_in_cwd| {
Message::ProfileOpenInCWD(profile_id, open_in_cwd)
}),
);

let padding = Padding {
Expand Down Expand Up @@ -1253,6 +1261,25 @@ impl App {
shell = Some(tty::Shell::new(command, args));
}
}

#[cfg(not(windows))]
let working_directory = profile
.open_in_cwd
// Evaluate current working working directory based on
// selected tab/terminal
.then(|| {
tab_model.active_data::<Mutex<Terminal>>().and_then(
|terminal| {
terminal
.lock()
.unwrap()
.current_working_directory()
},
)
})
.flatten()
.or_else(|| Some(profile.working_directory.clone().into()));
#[cfg(windows)]
let working_directory = (!profile.working_directory.is_empty())
.then(|| profile.working_directory.clone().into());

Expand All @@ -1269,7 +1296,20 @@ 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();
#[cfg(not(windows))]
{
// Eval CWD since it's the default option
options.working_directory = tab_model
.active_data::<Mutex<Terminal>>()
.and_then(|terminal| {
terminal.lock().unwrap().current_working_directory()
});
}
(options, None)
}
};
let entity = tab_model
.insert()
Expand Down Expand Up @@ -2153,6 +2193,13 @@ impl Application for App {
Message::ProfileOpen(profile_id) => {
return self.create_and_focus_new_terminal(self.pane_model.focus, Some(profile_id));
}
Message::ProfileOpenInCWD(profile_id, open_in_cwd) => {
#[cfg(not(windows))]
if let Some(profile) = self.config.profiles.get_mut(&profile_id) {
profile.open_in_cwd = open_in_cwd;
return self.save_profiles();
}
}
Message::ProfileRemove(profile_id) => {
// Reset matching terminals to default profile
for (_pane, tab_model) in self.pane_model.panes.iter() {
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(windows))]
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 @@ -214,6 +218,10 @@ pub struct Terminal {
size: Size,
use_bright_bold: bool,
zoom_adj: i8,
#[cfg(not(windows))]
master_fd: Option<rustix::fd::OwnedFd>,
#[cfg(not(windows))]
shell_pid: rustix::process::Pid,
}

impl Terminal {
Expand Down Expand Up @@ -283,6 +291,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 @@ -306,6 +319,10 @@ impl Terminal {
term,
use_bright_bold,
zoom_adj: Default::default(),
#[cfg(not(windows))]
master_fd,
#[cfg(not(windows))]
shell_pid,
})
}

Expand Down Expand Up @@ -924,6 +941,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