From 2793c92ed4e80369db9d4d9ba473595c01ee4adc Mon Sep 17 00:00:00 2001 From: Eric Case Date: Wed, 14 Jan 2026 10:18:47 -0800 Subject: [PATCH 1/3] iTerm now supports OSC9;4 --- src/cargo/core/shell.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cargo/core/shell.rs b/src/cargo/core/shell.rs index b729256eb26..96361835766 100644 --- a/src/cargo/core/shell.rs +++ b/src/cargo/core/shell.rs @@ -640,8 +640,9 @@ fn supports_term_integration(stream: &dyn IsTerminal) -> bool { let conemu = std::env::var("ConEmuANSI").ok() == Some("ON".into()); let wezterm = std::env::var("TERM_PROGRAM").ok() == Some("WezTerm".into()); let ghostty = std::env::var("TERM_PROGRAM").ok() == Some("ghostty".into()); + let iterm = std::env::var("TERM_PROGRAM").ok() == Some("iTerm.app".into()); - (windows_terminal || conemu || wezterm || ghostty) && stream.is_terminal() + (windows_terminal || conemu || wezterm || ghostty || iterm) && stream.is_terminal() } pub struct Hyperlink { From 85c74855e8b85f9ae059a91c1d04563964490ac2 Mon Sep 17 00:00:00 2001 From: Eric Case Date: Tue, 20 Jan 2026 21:50:27 -0800 Subject: [PATCH 2/3] Support iTerm's TERM_FEATURES --- src/cargo/core/shell.rs | 52 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/cargo/core/shell.rs b/src/cargo/core/shell.rs index 96361835766..a34d1a7a5fe 100644 --- a/src/cargo/core/shell.rs +++ b/src/cargo/core/shell.rs @@ -640,11 +640,61 @@ fn supports_term_integration(stream: &dyn IsTerminal) -> bool { let conemu = std::env::var("ConEmuANSI").ok() == Some("ON".into()); let wezterm = std::env::var("TERM_PROGRAM").ok() == Some("WezTerm".into()); let ghostty = std::env::var("TERM_PROGRAM").ok() == Some("ghostty".into()); - let iterm = std::env::var("TERM_PROGRAM").ok() == Some("iTerm.app".into()); + // iTerm added OSC 9;4 support in v3.6.6, which we can check for. + // See https://github.com/rust-lang/cargo/pull/16506#discussion_r2706584034 for reference. + let iterm = term_features_has_progress(); (windows_terminal || conemu || wezterm || ghostty || iterm) && stream.is_terminal() } +#[expect( + clippy::disallowed_methods, + reason = "reading the state of the system, not config" +)] +// For iTerm, the TERM_FEATURES value "P" indicates OSC 9;4 support. +// See https://iterm2.com/feature-reporting/ for reference. +fn term_features_has_progress() -> bool { + let Ok(value) = std::env::var("TERM_FEATURES") else { + return false; + }; + + let mut current = String::new(); + + for ch in value.chars() { + if !ch.is_ascii_alphanumeric() { + break; + } + if ch.is_ascii_uppercase() { + if current == "P" { + return true; + } + current.clear(); + current.push(ch); + } else { + current.push(ch); + } + } + current == "P" +} + +#[cfg(test)] +mod tests { + use super::term_features_has_progress; + + #[test] + fn term_features_progress_detection() { + // With PROGRESS feature ("P") + unsafe { std::env::set_var("TERM_FEATURES", "MBT2ScP") }; + assert!(term_features_has_progress()); + + // Without PROGRESS feature + unsafe { std::env::set_var("TERM_FEATURES", "MBT2Sc") }; + assert!(!term_features_has_progress()); + + unsafe { std::env::remove_var("TERM_FEATURES") }; + } +} + pub struct Hyperlink { url: Option, } From ab7d3dd694f804067977bdf0442a21ed60af6a86 Mon Sep 17 00:00:00 2001 From: Eric Case Date: Wed, 21 Jan 2026 17:46:59 -0800 Subject: [PATCH 3/3] Address PR feedback - Check for TERM_PROGRAM=iTerm.app before checking TERM_FEATURES - Safer env var testing per multi-threaded testing --- src/cargo/core/shell.rs | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/cargo/core/shell.rs b/src/cargo/core/shell.rs index a34d1a7a5fe..4adb5894865 100644 --- a/src/cargo/core/shell.rs +++ b/src/cargo/core/shell.rs @@ -641,23 +641,18 @@ fn supports_term_integration(stream: &dyn IsTerminal) -> bool { let wezterm = std::env::var("TERM_PROGRAM").ok() == Some("WezTerm".into()); let ghostty = std::env::var("TERM_PROGRAM").ok() == Some("ghostty".into()); // iTerm added OSC 9;4 support in v3.6.6, which we can check for. - // See https://github.com/rust-lang/cargo/pull/16506#discussion_r2706584034 for reference. - let iterm = term_features_has_progress(); + // For context: https://github.com/rust-lang/cargo/pull/16506#discussion_r2706584034 + let iterm = std::env::var("TERM_PROGRAM").ok() == Some("iTerm.app".into()) + && std::env::var("TERM_FEATURES") + .ok() + .is_some_and(|v| term_features_has_progress(&v)); (windows_terminal || conemu || wezterm || ghostty || iterm) && stream.is_terminal() } -#[expect( - clippy::disallowed_methods, - reason = "reading the state of the system, not config" -)] // For iTerm, the TERM_FEATURES value "P" indicates OSC 9;4 support. -// See https://iterm2.com/feature-reporting/ for reference. -fn term_features_has_progress() -> bool { - let Ok(value) = std::env::var("TERM_FEATURES") else { - return false; - }; - +// Context: https://iterm2.com/feature-reporting/ +fn term_features_has_progress(value: &str) -> bool { let mut current = String::new(); for ch in value.chars() { @@ -684,14 +679,10 @@ mod tests { #[test] fn term_features_progress_detection() { // With PROGRESS feature ("P") - unsafe { std::env::set_var("TERM_FEATURES", "MBT2ScP") }; - assert!(term_features_has_progress()); + assert!(term_features_has_progress("MBT2ScP")); // Without PROGRESS feature - unsafe { std::env::set_var("TERM_FEATURES", "MBT2Sc") }; - assert!(!term_features_has_progress()); - - unsafe { std::env::remove_var("TERM_FEATURES") }; + assert!(!term_features_has_progress("MBT2Sc")); } }