From adca3a748e725486226faae475ea3c31a0f18365 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 12 Mar 2026 14:14:49 -0700 Subject: [PATCH 1/2] Use `std::sync::OnceLock` instead of `once_cell` `OnceLock` was stabilized in Rust 1.70, so this is usable in `console` now that its MSRV is 1.71, and thus `once_cell` can be dropped. This would be closer to the original code if we could use 1.80's `LazyLock`, but function wrappers here can still achieve a similar kind of abstraction around the lazy initialization. --- Cargo.toml | 3 +-- Makefile | 2 +- src/ansi.rs | 19 ++++++++++--------- src/unix_term.rs | 15 +++++++-------- src/utils.rs | 42 +++++++++++++++++++++++++----------------- 5 files changed, 44 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6649f616..d6f7bbd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,14 +14,13 @@ include = ["CHANGELOG.md", "Cargo.toml", "LICENSE", "README.md", "src/**/*.rs"] [features] default = ["unicode-width", "ansi-parsing", "std"] -std = ["dep:libc", "dep:once_cell", "alloc"] +std = ["dep:libc", "alloc"] alloc = [] windows-console-colors = ["ansi-parsing"] ansi-parsing = [] [dependencies] libc = { version = "0.2.99", optional = true } -once_cell = { version = "1.8", optional = true } unicode-width = { version = "0.2", optional = true } [target.'cfg(windows)'.dependencies] diff --git a/Makefile b/Makefile index cb534e7c..f27a9018 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,6 @@ lint: @cargo clippy --examples --tests --all-features -- --deny warnings msrv-lock: - @cargo update -p once_cell --precise 1.20.3 + @# cargo update -p once_cell --precise 1.20.3 .PHONY: all doc build check test format format-check lint check-minver msrv-lock diff --git a/src/ansi.rs b/src/ansi.rs index 09239be4..e3a39aad 100644 --- a/src/ansi.rs +++ b/src/ansi.rs @@ -292,18 +292,19 @@ mod tests { use super::*; use core::fmt::Write; - use once_cell::sync::Lazy; use proptest::prelude::*; use regex::Regex; + use std::sync::OnceLock; // The manual dfa `State` is a handwritten translation from the previously used regex. That // regex is kept here and used to ensure that the new matches are the same as the old - static STRIP_ANSI_RE: Lazy = Lazy::new(|| { - Regex::new( + fn strip_ansi_re() -> &'static Regex { + static RE: OnceLock = OnceLock::new(); + + RE.get_or_init(|| Regex::new( r"[\x1b\x9b]([()][012AB]|[\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><])", - ) - .unwrap() - }); + ).unwrap()) + } impl<'a> PartialEq> for regex::Match<'_> { fn eq(&self, other: &Match<'a>) -> bool { @@ -314,7 +315,7 @@ mod tests { proptest! { #[test] fn dfa_matches_old_regex(s in r"([\x1b\x9b]?.*){0,5}") { - let old_matches: Vec<_> = STRIP_ANSI_RE.find_iter(&s).collect(); + let old_matches: Vec<_> = strip_ansi_re().find_iter(&s).collect(); let new_matches: Vec<_> = Matches::new(&s).collect(); assert_eq!(old_matches, new_matches); } @@ -334,7 +335,7 @@ mod tests { fn _check_all_strings_of_len(len: usize, chunk: &mut Vec) { if len == 0 { if let Ok(s) = core::str::from_utf8(chunk) { - let old_matches: Vec<_> = STRIP_ANSI_RE.find_iter(s).collect(); + let old_matches: Vec<_> = strip_ansi_re().find_iter(s).collect(); let new_matches: Vec<_> = Matches::new(s).collect(); assert_eq!(old_matches, new_matches); } @@ -363,7 +364,7 @@ mod tests { ) .unwrap(); - let old_matches: Vec<_> = STRIP_ANSI_RE.find_iter(&s).collect(); + let old_matches: Vec<_> = strip_ansi_re().find_iter(&s).collect(); let new_matches: Vec<_> = Matches::new(&s).collect(); assert_eq!(old_matches, new_matches); } diff --git a/src/unix_term.rs b/src/unix_term.rs index 565b1985..ec445d4b 100644 --- a/src/unix_term.rs +++ b/src/unix_term.rs @@ -7,7 +7,7 @@ use std::io::{self, BufRead, BufReader}; use std::os::fd::{AsRawFd, RawFd}; #[cfg(not(target_os = "macos"))] -use once_cell::sync::Lazy; +use std::sync::OnceLock; use crate::kb::Key; use crate::term::Term; @@ -380,12 +380,6 @@ fn key_from_utf8(buf: &[u8]) -> Key { Key::Unknown } -#[cfg(not(target_os = "macos"))] -static IS_LANG_UTF8: Lazy = Lazy::new(|| match std::env::var("LANG") { - Ok(lang) => lang.to_uppercase().ends_with("UTF-8"), - _ => false, -}); - #[cfg(target_os = "macos")] pub(crate) fn wants_emoji() -> bool { true @@ -393,7 +387,12 @@ pub(crate) fn wants_emoji() -> bool { #[cfg(not(target_os = "macos"))] pub(crate) fn wants_emoji() -> bool { - *IS_LANG_UTF8 + static IS_LANG_UTF8: OnceLock = OnceLock::new(); + + *IS_LANG_UTF8.get_or_init(|| match std::env::var("LANG") { + Ok(lang) => lang.to_uppercase().ends_with("UTF-8"), + _ => false, + }) } pub(crate) fn set_title(title: T) { diff --git a/src/utils.rs b/src/utils.rs index f0a55830..db182a99 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,7 +5,7 @@ use core::{ }; use std::env; -use once_cell::sync::Lazy; +use std::sync::OnceLock; use crate::term::{wants_emoji, Term}; @@ -22,14 +22,22 @@ fn default_true_colors_enabled(out: &Term) -> bool { out.features().true_colors_supported() } -static STDOUT_COLORS: Lazy = - Lazy::new(|| AtomicBool::new(default_colors_enabled(&Term::stdout()))); -static STDOUT_TRUE_COLORS: Lazy = - Lazy::new(|| AtomicBool::new(default_true_colors_enabled(&Term::stdout()))); -static STDERR_COLORS: Lazy = - Lazy::new(|| AtomicBool::new(default_colors_enabled(&Term::stderr()))); -static STDERR_TRUE_COLORS: Lazy = - Lazy::new(|| AtomicBool::new(default_true_colors_enabled(&Term::stderr()))); +fn stdout_colors() -> &'static AtomicBool { + static ENABLED: OnceLock = OnceLock::new(); + ENABLED.get_or_init(|| AtomicBool::new(default_colors_enabled(&Term::stdout()))) +} +fn stdout_true_colors() -> &'static AtomicBool { + static ENABLED: OnceLock = OnceLock::new(); + ENABLED.get_or_init(|| AtomicBool::new(default_true_colors_enabled(&Term::stdout()))) +} +fn stderr_colors() -> &'static AtomicBool { + static ENABLED: OnceLock = OnceLock::new(); + ENABLED.get_or_init(|| AtomicBool::new(default_colors_enabled(&Term::stderr()))) +} +fn stderr_true_colors() -> &'static AtomicBool { + static ENABLED: OnceLock = OnceLock::new(); + ENABLED.get_or_init(|| AtomicBool::new(default_true_colors_enabled(&Term::stderr()))) +} /// Returns `true` if colors should be enabled for stdout. /// @@ -40,13 +48,13 @@ static STDERR_TRUE_COLORS: Lazy = /// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what. #[inline] pub fn colors_enabled() -> bool { - STDOUT_COLORS.load(Ordering::Relaxed) + stdout_colors().load(Ordering::Relaxed) } /// Returns `true` if true colors should be enabled for stdout. #[inline] pub fn true_colors_enabled() -> bool { - STDOUT_TRUE_COLORS.load(Ordering::Relaxed) + stdout_true_colors().load(Ordering::Relaxed) } /// Forces colorization on or off for stdout. @@ -55,7 +63,7 @@ pub fn true_colors_enabled() -> bool { /// `colors_enabled` function. #[inline] pub fn set_colors_enabled(val: bool) { - STDOUT_COLORS.store(val, Ordering::Relaxed) + stdout_colors().store(val, Ordering::Relaxed) } /// Forces true colorization on or off for stdout. @@ -64,7 +72,7 @@ pub fn set_colors_enabled(val: bool) { /// `true_colors_enabled` function. #[inline] pub fn set_true_colors_enabled(val: bool) { - STDOUT_TRUE_COLORS.store(val, Ordering::Relaxed) + stdout_true_colors().store(val, Ordering::Relaxed) } /// Returns `true` if colors should be enabled for stderr. @@ -76,13 +84,13 @@ pub fn set_true_colors_enabled(val: bool) { /// * `CLICOLOR_FORCE != 0`: ANSI colors should be enabled no matter what. #[inline] pub fn colors_enabled_stderr() -> bool { - STDERR_COLORS.load(Ordering::Relaxed) + stderr_colors().load(Ordering::Relaxed) } /// Returns `true` if true colors should be enabled for stderr. #[inline] pub fn true_colors_enabled_stderr() -> bool { - STDERR_TRUE_COLORS.load(Ordering::Relaxed) + stderr_true_colors().load(Ordering::Relaxed) } /// Forces colorization on or off for stderr. @@ -91,7 +99,7 @@ pub fn true_colors_enabled_stderr() -> bool { /// `colors_enabled_stderr` function. #[inline] pub fn set_colors_enabled_stderr(val: bool) { - STDERR_COLORS.store(val, Ordering::Relaxed) + stderr_colors().store(val, Ordering::Relaxed) } /// Forces true colorization on or off for stderr. @@ -100,7 +108,7 @@ pub fn set_colors_enabled_stderr(val: bool) { /// `true_colors_enabled_stderr` function. #[inline] pub fn set_true_colors_enabled_stderr(val: bool) { - STDERR_TRUE_COLORS.store(val, Ordering::Relaxed) + stderr_true_colors().store(val, Ordering::Relaxed) } /// Measure the width of a string in terminal characters. From cb26fd3e14368ef9b1cd6f1d22d0ea62a962a0d0 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 12 Mar 2026 14:35:34 -0700 Subject: [PATCH 2/2] Remove `make msrv-lock` --- .github/workflows/ci.yml | 6 ------ Makefile | 5 +---- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7dfa80c4..f671847b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,9 +28,6 @@ jobs: override: true - name: Checkout uses: actions/checkout@v3 - - name: MSRV dependencies - if: matrix.rust == '1.71.0' - run: make msrv-lock - name: Check env: CARGO_BUILD_TARGET: ${{ matrix.target }} @@ -79,9 +76,6 @@ jobs: run: | sudo apt-get update sudo apt-get install gcc-multilib - - name: MSRV dependencies - if: matrix.rust == '1.71.0' - run: make msrv-lock - name: Test env: CARGO_BUILD_TARGET: ${{ matrix.target }} diff --git a/Makefile b/Makefile index f27a9018..4c08d898 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,4 @@ lint: @rustup component add clippy 2> /dev/null @cargo clippy --examples --tests --all-features -- --deny warnings -msrv-lock: - @# cargo update -p once_cell --precise 1.20.3 - -.PHONY: all doc build check test format format-check lint check-minver msrv-lock +.PHONY: all doc build check test format format-check lint check-minver