mirror of
https://github.com/openai/codex.git
synced 2026-03-24 00:56:34 +03:00
Compare commits
11 Commits
starr/exec
...
dev/icewea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c81c133070 | ||
|
|
fd31fbca87 | ||
|
|
e33c049c36 | ||
|
|
c5bd2f4cea | ||
|
|
aa9d1cac70 | ||
|
|
d6e722e11e | ||
|
|
d7eb134692 | ||
|
|
7469e4da79 | ||
|
|
09b57f590d | ||
|
|
eb5d0fd48b | ||
|
|
e52f289bf0 |
43
codex-rs/Cargo.lock
generated
43
codex-rs/Cargo.lock
generated
@@ -1480,6 +1480,7 @@ dependencies = [
|
||||
"strum_macros 0.27.2",
|
||||
"supports-color",
|
||||
"tempfile",
|
||||
"terminal-colorsaurus",
|
||||
"textwrap 0.16.2",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
@@ -3748,14 +3749,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.4"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||
checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5750,9 +5751,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
||||
checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
@@ -6135,6 +6136,32 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal-colorsaurus"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8909f33134da34b43f69145e748790de650a6abd84faf1f82e773444dd293ec8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
"terminal-trx",
|
||||
"windows-sys 0.61.1",
|
||||
"xterm-color",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal-trx"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "662a3cd5ca570df622e848ef18b50c151e65c9835257465417242243b0bce783"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"windows-sys 0.61.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.4.2"
|
||||
@@ -7657,6 +7684,12 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xterm-color"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4de5f056fb9dc8b7908754867544e26145767187aaac5a98495e88ad7cb8a80f"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
|
||||
@@ -89,6 +89,9 @@ url = { workspace = true }
|
||||
|
||||
codex-windows-sandbox = { workspace = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
terminal-colorsaurus = "1"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = { workspace = true }
|
||||
|
||||
|
||||
@@ -140,10 +140,7 @@ pub(crate) fn output_lines(
|
||||
|
||||
pub(crate) fn spinner(start_time: Option<Instant>) -> Span<'static> {
|
||||
let elapsed = start_time.map(|st| st.elapsed()).unwrap_or_default();
|
||||
if supports_color::on_cached(supports_color::Stream::Stdout)
|
||||
.map(|level| level.has_16m)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if crate::terminal_palette::stdout_supports_truecolor() {
|
||||
shimmer_spans("•")[0].clone()
|
||||
} else {
|
||||
let blink_on = (elapsed.as_millis() / 600).is_multiple_of(2);
|
||||
|
||||
@@ -30,9 +30,7 @@ pub(crate) fn shimmer_spans(text: &str) -> Vec<Span<'static>> {
|
||||
let pos_f =
|
||||
(elapsed_since_start().as_secs_f32() % sweep_seconds) / sweep_seconds * (period as f32);
|
||||
let pos = pos_f as usize;
|
||||
let has_true_color = supports_color::on_cached(supports_color::Stream::Stdout)
|
||||
.map(|level| level.has_16m)
|
||||
.unwrap_or(false);
|
||||
let has_true_color = crate::terminal_palette::stdout_supports_truecolor();
|
||||
let band_half_width = 5.0;
|
||||
|
||||
let mut spans: Vec<Span<'static>> = Vec::with_capacity(chars.len());
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
use crate::color::perceptual_distance;
|
||||
use ratatui::style::Color;
|
||||
#[cfg(not(test))]
|
||||
use std::sync::LazyLock;
|
||||
#[cfg(not(test))]
|
||||
use std::sync::Mutex;
|
||||
#[cfg(windows)]
|
||||
use std::sync::OnceLock;
|
||||
|
||||
/// Returns the closest color to the target color that the terminal can display.
|
||||
pub fn best_color(target: (u8, u8, u8)) -> Color {
|
||||
let Some(color_level) = supports_color::on_cached(supports_color::Stream::Stdout) else {
|
||||
return Color::default();
|
||||
};
|
||||
if color_level.has_16m {
|
||||
if stdout_supports_truecolor() {
|
||||
let (r, g, b) = target;
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
Color::Rgb(r, g, b)
|
||||
@@ -25,8 +31,37 @@ pub fn best_color(target: (u8, u8, u8)) -> Color {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if stdout supports truecolor (24-bit), using a single shared decision.
|
||||
///
|
||||
/// - On non-Windows, this is simply `supports_color`'s 16m capability.
|
||||
/// - On Windows, we upgrade to truecolor on Windows Terminal if Virtual Terminal
|
||||
/// Processing can be enabled successfully via crossterm. This probing happens once and is cached.
|
||||
pub fn stdout_supports_truecolor() -> bool {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
static TRUECOLOR: OnceLock<bool> = OnceLock::new();
|
||||
*TRUECOLOR.get_or_init(|| {
|
||||
let has = supports_color::on_cached(supports_color::Stream::Stdout)
|
||||
.map(|level| level.has_16m)
|
||||
.unwrap_or(false);
|
||||
if has {
|
||||
return true;
|
||||
}
|
||||
// Upgrade to truecolor on Windows Terminal when VT processing is available.
|
||||
// We only attempt to enable VT once per process to avoid per-frame overhead.
|
||||
std::env::var_os("WT_SESSION").is_some() && crossterm::ansi_support::supports_ansi()
|
||||
})
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
supports_color::on_cached(supports_color::Stream::Stdout)
|
||||
.map(|level| level.has_16m)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn requery_default_colors() {
|
||||
imp::requery_default_colors();
|
||||
imp::refresh_cached_default_colors();
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -36,7 +71,7 @@ pub struct DefaultColors {
|
||||
}
|
||||
|
||||
pub fn default_colors() -> Option<DefaultColors> {
|
||||
imp::default_colors()
|
||||
imp::cached_default_colors()
|
||||
}
|
||||
|
||||
pub fn default_fg() -> Option<(u8, u8, u8)> {
|
||||
@@ -47,67 +82,87 @@ pub fn default_bg() -> Option<(u8, u8, u8)> {
|
||||
default_colors().map(|c| c.bg)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
// Shared cache utility for default terminal colors
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Small helper cache that remembers whether initialization was attempted and stores a value.
|
||||
///
|
||||
/// Used to memoize the terminal's default foreground/background colors. We expose both
|
||||
/// `get_or_init_with` for the first successful query and `refresh_with` for a manual re-query.
|
||||
/// If a first query fails, we remember the failure to avoid repeated failed probes, unless
|
||||
/// a subsequent explicit refresh is performed.
|
||||
#[cfg(not(test))]
|
||||
struct Cache<T> {
|
||||
attempted: bool,
|
||||
value: Option<T>,
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
impl<T> Default for Cache<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
attempted: false,
|
||||
value: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
impl<T: Copy> Cache<T> {
|
||||
/// Returns the cached value if available; otherwise runs `init` once and caches its result.
|
||||
fn get_or_init_with(&mut self, mut init: impl FnMut() -> Option<T>) -> Option<T> {
|
||||
if !self.attempted {
|
||||
self.value = init();
|
||||
self.attempted = true;
|
||||
}
|
||||
self.value
|
||||
}
|
||||
|
||||
/// Re-runs `init` and replaces the cached value. Marks the cache as attempted.
|
||||
fn refresh_with(&mut self, mut init: impl FnMut() -> Option<T>) -> Option<T> {
|
||||
self.value = init();
|
||||
self.attempted = true;
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
fn default_terminal_colors_cache() -> &'static Mutex<Cache<DefaultColors>> {
|
||||
// LazyLock creates the single cache instance; the Mutex guards concurrent refreshes/reads.
|
||||
static CACHE: LazyLock<Mutex<Cache<DefaultColors>>> =
|
||||
LazyLock::new(|| Mutex::new(Cache::default()));
|
||||
&CACHE
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(test)))]
|
||||
mod imp {
|
||||
use super::DefaultColors;
|
||||
use super::default_terminal_colors_cache;
|
||||
use crossterm::style::Color as CrosstermColor;
|
||||
use crossterm::style::query_background_color;
|
||||
use crossterm::style::query_foreground_color;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
struct Cache<T> {
|
||||
attempted: bool,
|
||||
value: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> Default for Cache<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
attempted: false,
|
||||
value: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> Cache<T> {
|
||||
fn get_or_init_with(&mut self, mut init: impl FnMut() -> Option<T>) -> Option<T> {
|
||||
if !self.attempted {
|
||||
self.value = init();
|
||||
self.attempted = true;
|
||||
}
|
||||
self.value
|
||||
}
|
||||
|
||||
fn refresh_with(&mut self, mut init: impl FnMut() -> Option<T>) -> Option<T> {
|
||||
self.value = init();
|
||||
self.attempted = true;
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
fn default_colors_cache() -> &'static Mutex<Cache<DefaultColors>> {
|
||||
static CACHE: OnceLock<Mutex<Cache<DefaultColors>>> = OnceLock::new();
|
||||
CACHE.get_or_init(|| Mutex::new(Cache::default()))
|
||||
}
|
||||
|
||||
pub(super) fn default_colors() -> Option<DefaultColors> {
|
||||
let cache = default_colors_cache();
|
||||
// Returns cached terminal defaults, probing once on first use.
|
||||
pub(super) fn cached_default_colors() -> Option<DefaultColors> {
|
||||
let cache = default_terminal_colors_cache();
|
||||
let mut cache = cache.lock().ok()?;
|
||||
cache.get_or_init_with(|| query_default_colors().unwrap_or_default())
|
||||
cache.get_or_init_with(|| probe_default_terminal_colors().unwrap_or_default())
|
||||
}
|
||||
|
||||
pub(super) fn requery_default_colors() {
|
||||
if let Ok(mut cache) = default_colors_cache().lock() {
|
||||
// Refreshes cached defaults unless we already know probing fails.
|
||||
pub(super) fn refresh_cached_default_colors() {
|
||||
if let Ok(mut cache) = default_terminal_colors_cache().lock() {
|
||||
// Don't try to refresh if the cache is already attempted and failed.
|
||||
if cache.attempted && cache.value.is_none() {
|
||||
return;
|
||||
}
|
||||
cache.refresh_with(|| query_default_colors().unwrap_or_default());
|
||||
cache.refresh_with(|| probe_default_terminal_colors().unwrap_or_default());
|
||||
}
|
||||
}
|
||||
|
||||
fn query_default_colors() -> std::io::Result<Option<DefaultColors>> {
|
||||
// Probes the terminal for default colors; returns None when unsupported/unavailable.
|
||||
fn probe_default_terminal_colors() -> std::io::Result<Option<DefaultColors>> {
|
||||
let fg = query_foreground_color()?.and_then(color_to_tuple);
|
||||
let bg = query_background_color()?.and_then(color_to_tuple);
|
||||
Ok(fg.zip(bg).map(|(fg, bg)| DefaultColors { fg, bg }))
|
||||
@@ -121,15 +176,53 @@ mod imp {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(unix, not(test))))]
|
||||
#[cfg(all(windows, not(test)))]
|
||||
mod imp {
|
||||
use super::DefaultColors;
|
||||
use super::default_terminal_colors_cache;
|
||||
|
||||
pub(super) fn default_colors() -> Option<DefaultColors> {
|
||||
// Returns cached terminal defaults, probing once on first use.
|
||||
pub(super) fn cached_default_colors() -> Option<DefaultColors> {
|
||||
let cache = default_terminal_colors_cache();
|
||||
let mut cache = cache.lock().ok()?;
|
||||
cache.get_or_init_with(|| probe_default_terminal_colors().unwrap_or_default())
|
||||
}
|
||||
|
||||
// Refreshes cached defaults unless we already know probing fails.
|
||||
pub(super) fn refresh_cached_default_colors() {
|
||||
if let Ok(mut cache) = default_terminal_colors_cache().lock() {
|
||||
// Don't try to refresh if the cache is already attempted and failed.
|
||||
if cache.attempted && cache.value.is_none() {
|
||||
return;
|
||||
}
|
||||
cache.refresh_with(|| probe_default_terminal_colors().unwrap_or_default());
|
||||
}
|
||||
}
|
||||
|
||||
// Probes the terminal for default colors; returns None when unsupported/unavailable.
|
||||
fn probe_default_terminal_colors() -> std::io::Result<Option<DefaultColors>> {
|
||||
match terminal_colorsaurus::color_palette(terminal_colorsaurus::QueryOptions::default()) {
|
||||
Ok(p) => {
|
||||
let (fr, fg, fb) = p.foreground.scale_to_8bit();
|
||||
let (br, bg, bb) = p.background.scale_to_8bit();
|
||||
Ok(Some(DefaultColors {
|
||||
fg: (fr, fg, fb),
|
||||
bg: (br, bg, bb),
|
||||
}))
|
||||
}
|
||||
Err(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(all(unix, not(test)), all(windows, not(test)))))]
|
||||
mod imp {
|
||||
use super::DefaultColors;
|
||||
pub(super) fn cached_default_colors() -> Option<DefaultColors> {
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn requery_default_colors() {}
|
||||
pub(super) fn refresh_cached_default_colors() {}
|
||||
}
|
||||
|
||||
/// The subset of Xterm colors that are usually consistent across terminals.
|
||||
|
||||
Reference in New Issue
Block a user