Deduplicate TUI modifier handling and terminal color caching (#53556)

## What changed

- Reuse `custom_terminal::ModifierDiff` when writing history spans.
- Share the terminal default-color probe and cache implementation between Unix and Windows.

Both changes remove duplicate implementations while preserving existing behavior.

GitOrigin-RevId: 35f8d77ade972b791c6beac0678d2fe2be3aa763
This commit is contained in:
Benjamin Carlsson
2026-10-13 04:42:04 +00:00
committed by copyberry
parent 12cca49c81
commit f82e365922
3 changed files with 4 additions and 134 deletions
+2 -2
View File
@@ -730,13 +730,13 @@ where
/// The `ModifierDiff` struct is used to calculate the difference between two `Modifier`
/// values. This is useful when updating the terminal display, as it allows for more
/// efficient updates by only sending the necessary changes.
struct ModifierDiff {
pub(crate) struct ModifierDiff {
pub from: Modifier,
pub to: Modifier,
}
impl ModifierDiff {
fn queue<W: io::Write>(self, w: &mut W) -> io::Result<()> {
pub(crate) fn queue<W: io::Write>(self, w: &mut W) -> io::Result<()> {
use crossterm::style::Attribute as CAttribute;
let removed = self.from - self.to;
if removed.contains(Modifier::REVERSED) {
+1 -67
View File
@@ -8,6 +8,7 @@ use std::fmt;
use std::io;
use std::io::Write;
use crate::custom_terminal::ModifierDiff;
use crate::render::line_utils::line_to_static;
use crate::terminal_hyperlinks::HyperlinkLine;
use crate::terminal_hyperlinks::decorate_spans;
@@ -381,73 +382,6 @@ impl Command for ResetScrollRegion {
}
}
struct ModifierDiff {
pub from: Modifier,
pub to: Modifier,
}
impl ModifierDiff {
fn queue<W>(self, mut w: W) -> io::Result<()>
where
W: io::Write,
{
use crossterm::style::Attribute as CAttribute;
let removed = self.from - self.to;
if removed.contains(Modifier::REVERSED) {
queue!(w, SetAttribute(CAttribute::NoReverse))?;
}
if removed.contains(Modifier::BOLD) {
queue!(w, SetAttribute(CAttribute::NormalIntensity))?;
if self.to.contains(Modifier::DIM) {
queue!(w, SetAttribute(CAttribute::Dim))?;
}
}
if removed.contains(Modifier::ITALIC) {
queue!(w, SetAttribute(CAttribute::NoItalic))?;
}
if removed.contains(Modifier::UNDERLINED) {
queue!(w, SetAttribute(CAttribute::NoUnderline))?;
}
if removed.contains(Modifier::DIM) {
queue!(w, SetAttribute(CAttribute::NormalIntensity))?;
}
if removed.contains(Modifier::CROSSED_OUT) {
queue!(w, SetAttribute(CAttribute::NotCrossedOut))?;
}
if removed.contains(Modifier::SLOW_BLINK) || removed.contains(Modifier::RAPID_BLINK) {
queue!(w, SetAttribute(CAttribute::NoBlink))?;
}
let added = self.to - self.from;
if added.contains(Modifier::REVERSED) {
queue!(w, SetAttribute(CAttribute::Reverse))?;
}
if added.contains(Modifier::BOLD) {
queue!(w, SetAttribute(CAttribute::Bold))?;
}
if added.contains(Modifier::ITALIC) {
queue!(w, SetAttribute(CAttribute::Italic))?;
}
if added.contains(Modifier::UNDERLINED) {
queue!(w, SetAttribute(CAttribute::Underlined))?;
}
if added.contains(Modifier::DIM) {
queue!(w, SetAttribute(CAttribute::Dim))?;
}
if added.contains(Modifier::CROSSED_OUT) {
queue!(w, SetAttribute(CAttribute::CrossedOut))?;
}
if added.contains(Modifier::SLOW_BLINK) {
queue!(w, SetAttribute(CAttribute::SlowBlink))?;
}
if added.contains(Modifier::RAPID_BLINK) {
queue!(w, SetAttribute(CAttribute::RapidBlink))?;
}
Ok(())
}
}
fn write_spans<'a, I>(mut writer: &mut impl Write, content: I) -> io::Result<()>
where
I: IntoIterator<Item = &'a Span<'a>>,
+1 -65
View File
@@ -142,7 +142,7 @@ pub(crate) fn set_default_colors_from_startup_probe(
imp::set_default_colors_from_startup_probe(colors);
}
#[cfg(all(unix, not(test)))]
#[cfg(any(all(unix, not(test)), windows))]
mod imp {
use super::DefaultColors;
use std::sync::Mutex;
@@ -211,70 +211,6 @@ mod imp {
}
}
#[cfg(windows)]
mod imp {
use super::DefaultColors;
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 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();
let mut cache = cache.lock().ok()?;
cache.get_or_init_with(query_default_colors)
}
pub(super) fn set_default_colors_from_startup_probe(
colors: Option<crate::terminal_probe::DefaultColors>,
) {
if let Ok(mut cache) = default_colors_cache().lock() {
cache.value = colors.map(|colors| DefaultColors {
fg: colors.fg,
bg: colors.bg,
});
cache.attempted = true;
}
}
fn query_default_colors() -> Option<DefaultColors> {
crate::terminal_probe::default_colors(crate::terminal_probe::DEFAULT_TIMEOUT)
.ok()
.flatten()
.map(|colors| DefaultColors {
fg: colors.fg,
bg: colors.bg,
})
}
}
#[cfg(not(any(all(unix, not(test)), windows)))]
mod imp {
use super::DefaultColors;