Skip to content
Merged
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
48 changes: 48 additions & 0 deletions crates/jcode-terminal-launch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ pub fn detected_resume_terminal() -> Option<String> {
{
return Some("handterm".to_string());
}
// tmux: prefer splitting a new pane in the existing session over launching
// a brand-new terminal emulator. Detected via $TMUX which tmux always sets
// for processes inside an attached session. See issue #74 / upstream PR #78.
if std::env::var("TMUX").is_ok() {
return Some("tmux".to_string());
}
if std::env::var("KITTY_PID").is_ok() {
return Some("kitty".to_string());
}
Expand Down Expand Up @@ -176,6 +182,7 @@ pub fn resume_terminal_candidates() -> Vec<String> {
#[cfg(target_os = "macos")]
{
for term in [
"tmux",
"ghostty",
"kitty",
"wezterm",
Expand All @@ -193,6 +200,7 @@ pub fn resume_terminal_candidates() -> Vec<String> {
{
for term in [
"handterm",
"tmux",
"kitty",
"wezterm",
"alacritty",
Expand Down Expand Up @@ -266,6 +274,18 @@ fn build_spawn_command(term: &str, command: &TerminalCommand, cwd: &Path) -> Opt
let shell = shell_command(&command_parts(command));
cmd.args(["--backend", "gpu", "--exec", &shell]);
}
#[cfg(unix)]
"tmux" => {
// Split the current tmux window into a new detached pane and run
// the requested command there. `-d` keeps focus on the current
// pane; `select-layout tiled` re-flows so the new pane is visible.
// See issue #74 / upstream PR #78.
let shell = shell_command(&command_parts(command));
cmd.args(["split-window", "-d", "-c"])
.arg(cwd)
.arg(&shell)
.args([";", "select-layout", "tiled"]);
}
#[cfg(target_os = "macos")]
"ghostty" => {
let shell = shell_command(&command_parts(command));
Expand Down Expand Up @@ -375,6 +395,34 @@ mod tests {
}
}

#[test]
#[cfg(unix)]
fn detected_resume_terminal_recognizes_tmux_via_tmux_env() {
// Regression for issue #74 / upstream PR #78: an attached tmux session
// must be detected so spawn_resume_in_new_terminal opens a new pane in
// the existing session instead of launching a fresh terminal emulator.
let _guard = ENV_LOCK.lock().unwrap();
let prev_tmux = std::env::var_os("TMUX");
unsafe {
// Clear the higher-priority detectors so tmux can win.
std::env::remove_var("HANDTERM_SESSION");
std::env::remove_var("HANDTERM_PID");
std::env::remove_var("KITTY_PID");
std::env::remove_var("WEZTERM_EXECUTABLE");
std::env::remove_var("WEZTERM_PANE");
std::env::remove_var("ALACRITTY_WINDOW_ID");
std::env::remove_var("GHOSTTY_RESOURCES_DIR");
std::env::set_var("TMUX", "/tmp/tmux-1000/default,12345,0");
}
assert_eq!(detected_resume_terminal().as_deref(), Some("tmux"));
unsafe {
std::env::remove_var("TMUX");
if let Some(prev) = prev_tmux {
std::env::set_var("TMUX", prev);
}
}
}

#[test]
fn shell_command_quotes_arguments() {
let shell = shell_command(&["jcode".to_string(), "it's ok".to_string()]);
Expand Down