fix(tui): promote windows terminal diff ansi16 to truecolor (#13016)

## Summary

- Promote ANSI-16 to truecolor for diff rendering when running inside
Windows Terminal
- Respect explicit `FORCE_COLOR` override, skipping promotion when set
- Extract a pure `diff_color_level_for_terminal` function for
testability
- Strip background tints from ANSI-16 diff output, rendering add/delete
lines with foreground color only
- Introduce `RichDiffColorLevel` to type-safely restrict background
fills to truecolor and ansi256

## Problem

Windows Terminal fully supports 24-bit (truecolor) rendering but often
does not provide the usual TERM metadata (`TERM`, `TERM_PROGRAM`,
`COLORTERM`) in `cmd.exe`/PowerShell sessions. In those environments,
`supports-color` can report only ANSI-16 support. The diff renderer
therefore falls back to a 16-color palette, producing washed-out,
hard-to-read diffs.

The screenshots below demonstrate that both PowerShell and cmd.exe don't
set any `*TERM*` environment variables.

| PowerShell | cmd.exe |
|---|---|
| <img width="2032" height="1162" alt="SCR-20260226-nfvy"
src="https://github.com/user-attachments/assets/59e968cc-4add-4c7b-a415-07163297e86a"
/> | <img width="2032" height="1162" alt="SCR-20260226-nfyc"
src="https://github.com/user-attachments/assets/d06b3e39-bf91-4ce3-9705-82bf9563a01b"
/> |


## Mental model

`StdoutColorLevel` (from `supports-color`) is the _detected_ capability.
`DiffColorLevel` is the _intended_ capability for diff rendering. A new
intermediary — `diff_color_level_for_terminal` — maps one to the other
and is the single place where terminal-specific overrides live.

Windows Terminal is detected two independent ways: the `TerminalName`
parsed by `terminal_info()` and the raw presence of `WT_SESSION`. When
`WT_SESSION` is present and `FORCE_COLOR` is not set, we promote
unconditionally to truecolor. When `WT_SESSION` is absent but
`TerminalName::WindowsTerminal` is detected, we promote only the ANSI-16
level (not `Unknown`).

A single override helper — `has_force_color_override()` — checks whether
`FORCE_COLOR` is set. When it is, both the `WT_SESSION` fast-path and
the `TerminalName`-based promotion are suppressed, preserving explicit
user intent.

| PowerShell | cmd.exe | WSL | Bash for Windows |
|---|---|---|---|
|
![SCR-20260226-msrh](https://github.com/user-attachments/assets/0f6297a6-4241-4dbf-b7ff-cf02da8941b0)
|
![SCR-20260226-nbao](https://github.com/user-attachments/assets/bb5ff8a9-903c-4677-a2de-1f6e1f34b18e)
|
![SCR-20260226-nbej](https://github.com/user-attachments/assets/26ecec2c-a7e9-410a-8702-f73995b490a6)
|
![SCR-20260226-nbkz](https://github.com/user-attachments/assets/80c4bf9a-3b41-40e1-bc87-f5c565f96075)
|

## Non-goals

- This does not change color detection for anything outside the diff
renderer (e.g. the chat widget, markdown rendering).
- This does not add a user-facing config knob; `FORCE_COLOR` already
serves that role.

## Tradeoffs

- The `has_wt_session` signal is intentionally kept separate from
`TerminalName::WindowsTerminal`. `terminal_info()` is derived with
`TERM_PROGRAM` precedence, so it can differ from raw `WT_SESSION`.
- Real-world validation in this issue: in both `cmd.exe` and PowerShell,
`TERM`/`TERM_PROGRAM`/`COLORTERM` were absent, so TERM-based capability
hints were unavailable in those sessions.
- Checking `FORCE_COLOR` for presence rather than parsing its value is a
simplification. In practice `supports-color` has already parsed it, so
our check is a coarse "did the user set _anything_?" gate. The effective
color level still comes from `supports-color`.
- When `WT_SESSION` is present without `FORCE_COLOR`, we promote to
truecolor regardless of `stdout_level` (including `Unknown`). This is
aggressive but correct: `WT_SESSION` is a strong signal that we're in
Windows Terminal.
- ANSI-16 add/delete backgrounds (bright green/red) overpower
syntax-highlighted token colors, making diffs harder to read.
Foreground-only cues (colored text, gutter signs) preserve readability
on low-color terminals.

## Architecture

```
stdout_color_level()  ──┐
terminal_info().name  ──┤
WT_SESSION presence   ──┼──▶ diff_color_level_for_terminal() ──▶ DiffColorLevel
FORCE_COLOR presence  ──┘                                            │
                                                                     ▼
                                                          RichDiffColorLevel::from_diff_color_level()
                                                                     │
                                                          ┌──────────┴──────────┐
                                                          │ Some(TrueColor|256) │ → bg tints
                                                          │ None (Ansi16)       │ → fg only
                                                          └─────────────────────┘
```

`diff_color_level()` is the environment-reading entry point; it gathers
the four runtime signals and delegates to the pure, testable
`diff_color_level_for_terminal()`.

## Observability

No new logs or metrics. Incorrect color selection is immediately visible
as broken diff rendering; the test suite covers the decision matrix
exhaustively.

## Tests

Six new unit tests exercise every branch of
`diff_color_level_for_terminal`:

| Test | Inputs | Expected |
|------|--------|----------|
| `windows_terminal_promotes_ansi16_to_truecolor_for_diffs` | Ansi16 +
WindowsTerminal name | TrueColor |
| `wt_session_promotes_ansi16_to_truecolor_for_diffs` | Ansi16 +
WT_SESSION only | TrueColor |
| `non_windows_terminal_keeps_ansi16_diff_palette` | Ansi16 + WezTerm |
Ansi16 |
| `wt_session_promotes_unknown_color_level_to_truecolor` | Unknown +
WT_SESSION | TrueColor |
| `explicit_force_override_keeps_ansi16_on_windows_terminal` | Ansi16 +
WindowsTerminal + FORCE_COLOR | Ansi16 |
| `explicit_force_override_keeps_ansi256_on_windows_terminal` | Ansi256
+ WT_SESSION + FORCE_COLOR | Ansi256 |
| `ansi16_add_style_uses_foreground_only` | Dark + Ansi16 | fg=Green,
bg=None |
| (and any other new snapshot/assertion tests from commits d757fee and
d7c78b3) | | |

## Test plan

- [x] Verify all new unit tests pass (`cargo test -p codex-tui --lib`)
- [x] On Windows Terminal: confirm diffs render with truecolor
backgrounds
- [x] On Windows Terminal with `FORCE_COLOR` set: confirm promotion is
disabled and output follows the forced `supports-color` level
- [x] On macOS/Linux terminals: confirm no behavior change

Fixes https://github.com/openai/codex/issues/12904 
Fixes https://github.com/openai/codex/issues/12890
Fixes https://github.com/openai/codex/issues/12912
Fixes https://github.com/openai/codex/issues/12840
This commit is contained in:
Felipe Coury
2026-02-27 14:45:59 -03:00
committed by GitHub
parent d09a7535ed
commit 3b5996f988
3 changed files with 324 additions and 59 deletions

View File

@@ -249,6 +249,7 @@ pub fn terminal_info() -> TerminalInfo {
/// type is split on whitespace to extract a program name plus optional version (for example,
/// `ghostty 1.2.3`), while the client term name becomes the `TERM` capability string.
/// - Otherwise, `TERM_PROGRAM` (plus `TERM_PROGRAM_VERSION`) drives the detected terminal name.
/// This means `TERM_PROGRAM` can mask later probes (for example `WT_SESSION`).
/// - Next, terminal-specific variables (WEZTERM, iTerm2, Apple Terminal, kitty, etc.) are checked.
/// - Finally, `TERM` is used as the capability fallback with `TerminalName::Unknown`.
///

View File

@@ -83,6 +83,8 @@ use crate::terminal_palette::indexed_color;
use crate::terminal_palette::rgb_color;
use crate::terminal_palette::stdout_color_level;
use codex_core::git_info::get_git_repo_root;
use codex_core::terminal::TerminalName;
use codex_core::terminal::terminal_info;
use codex_protocol::protocol::FileChange;
/// Classifies a diff line for gutter sign rendering and style selection.
@@ -110,6 +112,14 @@ enum DiffTheme {
Light,
}
/// Palette depth the diff renderer will target.
///
/// This is the *renderer's own* notion of color depth, derived from — but not
/// identical to — the raw [`StdoutColorLevel`] reported by `supports-color`.
/// The indirection exists because some terminals (notably Windows Terminal)
/// advertise only ANSI-16 support while actually rendering truecolor sequences
/// correctly; [`diff_color_level_for_terminal`] promotes those cases so the
/// diff output uses the richer palette.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum DiffColorLevel {
TrueColor,
@@ -117,6 +127,22 @@ enum DiffColorLevel {
Ansi16,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum RichDiffColorLevel {
TrueColor,
Ansi256,
}
impl RichDiffColorLevel {
fn from_diff_color_level(level: DiffColorLevel) -> Option<Self> {
match level {
DiffColorLevel::TrueColor => Some(Self::TrueColor),
DiffColorLevel::Ansi256 => Some(Self::Ansi256),
DiffColorLevel::Ansi16 => None,
}
}
}
pub struct DiffSummary {
changes: HashMap<PathBuf, FileChange>,
cwd: PathBuf,
@@ -876,11 +902,75 @@ fn diff_theme() -> DiffTheme {
diff_theme_for_bg(default_bg())
}
/// Return the [`DiffColorLevel`] for the current terminal session.
///
/// This is the environment-reading adapter: it samples runtime signals
/// (`supports-color` level, terminal name, `WT_SESSION`, and `FORCE_COLOR`)
/// and forwards them to [`diff_color_level_for_terminal`].
///
/// Keeping env reads in this thin wrapper lets
/// [`diff_color_level_for_terminal`] stay pure and easy to unit test.
fn diff_color_level() -> DiffColorLevel {
match stdout_color_level() {
diff_color_level_for_terminal(
stdout_color_level(),
terminal_info().name,
std::env::var_os("WT_SESSION").is_some(),
has_force_color_override(),
)
}
/// Returns whether `FORCE_COLOR` is explicitly set.
fn has_force_color_override() -> bool {
std::env::var_os("FORCE_COLOR").is_some()
}
/// Map a raw [`StdoutColorLevel`] to a [`DiffColorLevel`] using
/// Windows Terminal-specific truecolor promotion rules.
///
/// This helper is intentionally pure (no env access) so tests can validate
/// the policy table by passing explicit inputs.
///
/// Windows Terminal fully supports 24-bit color but the `supports-color`
/// crate often reports only ANSI-16 there because no `COLORTERM` variable
/// is set. We detect Windows Terminal two ways — via `terminal_name`
/// (parsed from `WT_SESSION` / `TERM_PROGRAM` by `terminal_info()`) and
/// via the raw `has_wt_session` flag.
///
/// These signals are intentionally not equivalent: `terminal_name` is a
/// derived classification with `TERM_PROGRAM` precedence, so `WT_SESSION`
/// can be present while `terminal_name` is not `WindowsTerminal`.
///
/// When `WT_SESSION` is present, we promote to truecolor unconditionally
/// unless `FORCE_COLOR` is set. This keeps Windows Terminal rendering rich
/// by default while preserving explicit `FORCE_COLOR` user intent.
///
/// Outside `WT_SESSION`, only ANSI-16 is promoted for identified
/// `WindowsTerminal` sessions; `Unknown` stays conservative.
fn diff_color_level_for_terminal(
stdout_level: StdoutColorLevel,
terminal_name: TerminalName,
has_wt_session: bool,
has_force_color_override: bool,
) -> DiffColorLevel {
if has_wt_session && !has_force_color_override {
return DiffColorLevel::TrueColor;
}
let base = match stdout_level {
StdoutColorLevel::TrueColor => DiffColorLevel::TrueColor,
StdoutColorLevel::Ansi256 => DiffColorLevel::Ansi256,
StdoutColorLevel::Ansi16 | StdoutColorLevel::Unknown => DiffColorLevel::Ansi16,
};
// Outside `WT_SESSION`, keep the existing Windows Terminal promotion for
// ANSI-16 sessions that likely support truecolor.
if stdout_level == StdoutColorLevel::Ansi16
&& terminal_name == TerminalName::WindowsTerminal
&& !has_force_color_override
{
DiffColorLevel::TrueColor
} else {
base
}
}
@@ -908,10 +998,11 @@ fn diff_color_level() -> DiffColorLevel {
/// Context lines intentionally leave the background unset so the terminal
/// default shows through.
fn style_line_bg_for(kind: DiffLineType, theme: DiffTheme, color_level: DiffColorLevel) -> Style {
match kind {
DiffLineType::Insert => Style::default().bg(add_line_bg(theme, color_level)),
DiffLineType::Delete => Style::default().bg(del_line_bg(theme, color_level)),
DiffLineType::Context => Style::default(),
match (kind, RichDiffColorLevel::from_diff_color_level(color_level)) {
(_, None) => Style::default(),
(DiffLineType::Insert, Some(level)) => Style::default().bg(add_line_bg(theme, level)),
(DiffLineType::Delete, Some(level)) => Style::default().bg(del_line_bg(theme, level)),
(DiffLineType::Context, _) => Style::default(),
}
}
@@ -919,25 +1010,21 @@ fn style_context() -> Style {
Style::default()
}
fn add_line_bg(theme: DiffTheme, color_level: DiffColorLevel) -> Color {
fn add_line_bg(theme: DiffTheme, color_level: RichDiffColorLevel) -> Color {
match (theme, color_level) {
(DiffTheme::Dark, DiffColorLevel::TrueColor) => rgb_color(DARK_TC_ADD_LINE_BG_RGB),
(DiffTheme::Dark, DiffColorLevel::Ansi256) => indexed_color(DARK_256_ADD_LINE_BG_IDX),
(DiffTheme::Dark, DiffColorLevel::Ansi16) => Color::Green,
(DiffTheme::Light, DiffColorLevel::TrueColor) => rgb_color(LIGHT_TC_ADD_LINE_BG_RGB),
(DiffTheme::Light, DiffColorLevel::Ansi256) => indexed_color(LIGHT_256_ADD_LINE_BG_IDX),
(DiffTheme::Light, DiffColorLevel::Ansi16) => Color::LightGreen,
(DiffTheme::Dark, RichDiffColorLevel::TrueColor) => rgb_color(DARK_TC_ADD_LINE_BG_RGB),
(DiffTheme::Dark, RichDiffColorLevel::Ansi256) => indexed_color(DARK_256_ADD_LINE_BG_IDX),
(DiffTheme::Light, RichDiffColorLevel::TrueColor) => rgb_color(LIGHT_TC_ADD_LINE_BG_RGB),
(DiffTheme::Light, RichDiffColorLevel::Ansi256) => indexed_color(LIGHT_256_ADD_LINE_BG_IDX),
}
}
fn del_line_bg(theme: DiffTheme, color_level: DiffColorLevel) -> Color {
fn del_line_bg(theme: DiffTheme, color_level: RichDiffColorLevel) -> Color {
match (theme, color_level) {
(DiffTheme::Dark, DiffColorLevel::TrueColor) => rgb_color(DARK_TC_DEL_LINE_BG_RGB),
(DiffTheme::Dark, DiffColorLevel::Ansi256) => indexed_color(DARK_256_DEL_LINE_BG_IDX),
(DiffTheme::Dark, DiffColorLevel::Ansi16) => Color::Red,
(DiffTheme::Light, DiffColorLevel::TrueColor) => rgb_color(LIGHT_TC_DEL_LINE_BG_RGB),
(DiffTheme::Light, DiffColorLevel::Ansi256) => indexed_color(LIGHT_256_DEL_LINE_BG_IDX),
(DiffTheme::Light, DiffColorLevel::Ansi16) => Color::LightRed,
(DiffTheme::Dark, RichDiffColorLevel::TrueColor) => rgb_color(DARK_TC_DEL_LINE_BG_RGB),
(DiffTheme::Dark, RichDiffColorLevel::Ansi256) => indexed_color(DARK_256_DEL_LINE_BG_IDX),
(DiffTheme::Light, RichDiffColorLevel::TrueColor) => rgb_color(LIGHT_TC_DEL_LINE_BG_RGB),
(DiffTheme::Light, RichDiffColorLevel::Ansi256) => indexed_color(LIGHT_256_DEL_LINE_BG_IDX),
}
}
@@ -949,19 +1036,17 @@ fn light_gutter_fg(color_level: DiffColorLevel) -> Color {
}
}
fn light_add_num_bg(color_level: DiffColorLevel) -> Color {
fn light_add_num_bg(color_level: RichDiffColorLevel) -> Color {
match color_level {
DiffColorLevel::TrueColor => rgb_color(LIGHT_TC_ADD_NUM_BG_RGB),
DiffColorLevel::Ansi256 => indexed_color(LIGHT_256_ADD_NUM_BG_IDX),
DiffColorLevel::Ansi16 => Color::Green,
RichDiffColorLevel::TrueColor => rgb_color(LIGHT_TC_ADD_NUM_BG_RGB),
RichDiffColorLevel::Ansi256 => indexed_color(LIGHT_256_ADD_NUM_BG_IDX),
}
}
fn light_del_num_bg(color_level: DiffColorLevel) -> Color {
fn light_del_num_bg(color_level: RichDiffColorLevel) -> Color {
match color_level {
DiffColorLevel::TrueColor => rgb_color(LIGHT_TC_DEL_NUM_BG_RGB),
DiffColorLevel::Ansi256 => indexed_color(LIGHT_256_DEL_NUM_BG_IDX),
DiffColorLevel::Ansi16 => Color::Red,
RichDiffColorLevel::TrueColor => rgb_color(LIGHT_TC_DEL_NUM_BG_RGB),
RichDiffColorLevel::Ansi256 => indexed_color(LIGHT_256_DEL_NUM_BG_IDX),
}
}
@@ -969,13 +1054,23 @@ fn light_del_num_bg(color_level: DiffColorLevel) -> Color {
/// tinted background so numbers contrast against the pastel line fill. On
/// dark backgrounds a simple `DIM` modifier is sufficient.
fn style_gutter_for(kind: DiffLineType, theme: DiffTheme, color_level: DiffColorLevel) -> Style {
match (theme, kind) {
(DiffTheme::Light, DiffLineType::Insert) => Style::default()
match (
theme,
kind,
RichDiffColorLevel::from_diff_color_level(color_level),
) {
(DiffTheme::Light, DiffLineType::Insert, None) => {
Style::default().fg(light_gutter_fg(color_level))
}
(DiffTheme::Light, DiffLineType::Delete, None) => {
Style::default().fg(light_gutter_fg(color_level))
}
(DiffTheme::Light, DiffLineType::Insert, Some(level)) => Style::default()
.fg(light_gutter_fg(color_level))
.bg(light_add_num_bg(color_level)),
(DiffTheme::Light, DiffLineType::Delete) => Style::default()
.bg(light_add_num_bg(level)),
(DiffTheme::Light, DiffLineType::Delete, Some(level)) => Style::default()
.fg(light_gutter_fg(color_level))
.bg(light_del_num_bg(color_level)),
.bg(light_del_num_bg(level)),
_ => style_gutter_dim(),
}
}
@@ -1001,26 +1096,38 @@ fn style_sign_del(theme: DiffTheme, color_level: DiffColorLevel) -> Style {
/// Content style for insert lines (plain, non-syntax-highlighted text).
fn style_add(theme: DiffTheme, color_level: DiffColorLevel) -> Style {
match (theme, color_level) {
(DiffTheme::Dark, DiffColorLevel::Ansi16) => Style::default()
.fg(Color::Black)
.bg(add_line_bg(theme, color_level)),
(DiffTheme::Light, _) => Style::default().bg(add_line_bg(theme, color_level)),
(DiffTheme::Dark, _) => Style::default()
(_, DiffColorLevel::Ansi16) => Style::default().fg(Color::Green),
(DiffTheme::Light, DiffColorLevel::TrueColor) => {
Style::default().bg(add_line_bg(theme, RichDiffColorLevel::TrueColor))
}
(DiffTheme::Light, DiffColorLevel::Ansi256) => {
Style::default().bg(add_line_bg(theme, RichDiffColorLevel::Ansi256))
}
(DiffTheme::Dark, DiffColorLevel::TrueColor) => Style::default()
.fg(Color::Green)
.bg(add_line_bg(theme, color_level)),
.bg(add_line_bg(theme, RichDiffColorLevel::TrueColor)),
(DiffTheme::Dark, DiffColorLevel::Ansi256) => Style::default()
.fg(Color::Green)
.bg(add_line_bg(theme, RichDiffColorLevel::Ansi256)),
}
}
/// Content style for delete lines (plain, non-syntax-highlighted text).
fn style_del(theme: DiffTheme, color_level: DiffColorLevel) -> Style {
match (theme, color_level) {
(DiffTheme::Dark, DiffColorLevel::Ansi16) => Style::default()
.fg(Color::Black)
.bg(del_line_bg(theme, color_level)),
(DiffTheme::Light, _) => Style::default().bg(del_line_bg(theme, color_level)),
(DiffTheme::Dark, _) => Style::default()
(_, DiffColorLevel::Ansi16) => Style::default().fg(Color::Red),
(DiffTheme::Light, DiffColorLevel::TrueColor) => {
Style::default().bg(del_line_bg(theme, RichDiffColorLevel::TrueColor))
}
(DiffTheme::Light, DiffColorLevel::Ansi256) => {
Style::default().bg(del_line_bg(theme, RichDiffColorLevel::Ansi256))
}
(DiffTheme::Dark, DiffColorLevel::TrueColor) => Style::default()
.fg(Color::Red)
.bg(del_line_bg(theme, color_level)),
.bg(del_line_bg(theme, RichDiffColorLevel::TrueColor)),
(DiffTheme::Dark, DiffColorLevel::Ansi256) => Style::default()
.fg(Color::Red)
.bg(del_line_bg(theme, RichDiffColorLevel::Ansi256)),
}
}
@@ -1039,32 +1146,28 @@ mod tests {
use ratatui::widgets::Paragraph;
#[test]
fn dark_ansi16_add_style_has_contrast() {
fn ansi16_add_style_uses_foreground_only() {
let style = style_add(DiffTheme::Dark, DiffColorLevel::Ansi16);
assert_eq!(style.fg, Some(Color::Black));
assert_eq!(style.bg, Some(Color::Green));
assert_ne!(style.fg, style.bg);
assert_eq!(style.fg, Some(Color::Green));
assert_eq!(style.bg, None);
}
#[test]
fn dark_ansi16_del_style_has_contrast() {
fn ansi16_del_style_uses_foreground_only() {
let style = style_del(DiffTheme::Dark, DiffColorLevel::Ansi16);
assert_eq!(style.fg, Some(Color::Black));
assert_eq!(style.bg, Some(Color::Red));
assert_ne!(style.fg, style.bg);
assert_eq!(style.fg, Some(Color::Red));
assert_eq!(style.bg, None);
}
#[test]
fn dark_ansi16_sign_styles_have_contrast() {
fn ansi16_sign_styles_use_foreground_only() {
let add_sign = style_sign_add(DiffTheme::Dark, DiffColorLevel::Ansi16);
assert_eq!(add_sign.fg, Some(Color::Black));
assert_eq!(add_sign.bg, Some(Color::Green));
assert_ne!(add_sign.fg, add_sign.bg);
assert_eq!(add_sign.fg, Some(Color::Green));
assert_eq!(add_sign.bg, None);
let del_sign = style_sign_del(DiffTheme::Dark, DiffColorLevel::Ansi16);
assert_eq!(del_sign.fg, Some(Color::Black));
assert_eq!(del_sign.bg, Some(Color::Red));
assert_ne!(del_sign.fg, del_sign.bg);
assert_eq!(del_sign.fg, Some(Color::Red));
assert_eq!(del_sign.bg, None);
}
use ratatui::widgets::WidgetRef;
use ratatui::widgets::Wrap;
@@ -1466,6 +1569,32 @@ mod tests {
snapshot_diff_gallery("diff_gallery_120x40", 120, 40);
}
#[test]
fn ui_snapshot_ansi16_insert_delete_no_background() {
let mut lines = push_wrapped_diff_line_inner_with_theme_and_color_level(
1,
DiffLineType::Insert,
"added in ansi16 mode",
80,
line_number_width(2),
None,
DiffTheme::Dark,
DiffColorLevel::Ansi16,
);
lines.extend(push_wrapped_diff_line_inner_with_theme_and_color_level(
2,
DiffLineType::Delete,
"deleted in ansi16 mode",
80,
line_number_width(2),
None,
DiffTheme::Dark,
DiffColorLevel::Ansi16,
));
snapshot_lines("ansi16_insert_delete_no_background", lines, 40, 4);
}
#[test]
fn truecolor_dark_theme_uses_configured_backgrounds() {
assert_eq!(
@@ -1535,6 +1664,42 @@ mod tests {
);
}
#[test]
fn ansi16_disables_line_and_gutter_backgrounds() {
assert_eq!(
style_line_bg_for(
DiffLineType::Insert,
DiffTheme::Dark,
DiffColorLevel::Ansi16
),
Style::default()
);
assert_eq!(
style_line_bg_for(
DiffLineType::Delete,
DiffTheme::Light,
DiffColorLevel::Ansi16
),
Style::default()
);
assert_eq!(
style_gutter_for(
DiffLineType::Insert,
DiffTheme::Light,
DiffColorLevel::Ansi16
),
Style::default().fg(Color::Black)
);
assert_eq!(
style_gutter_for(
DiffLineType::Delete,
DiffTheme::Light,
DiffColorLevel::Ansi16
),
Style::default().fg(Color::Black)
);
}
#[test]
fn light_truecolor_theme_uses_readable_gutter_and_line_backgrounds() {
assert_eq!(
@@ -1608,6 +1773,97 @@ mod tests {
assert_eq!(lines[1].style.bg, Some(rgb_color(LIGHT_TC_ADD_LINE_BG_RGB)));
}
#[test]
fn windows_terminal_promotes_ansi16_to_truecolor_for_diffs() {
assert_eq!(
diff_color_level_for_terminal(
StdoutColorLevel::Ansi16,
TerminalName::WindowsTerminal,
false,
false,
),
DiffColorLevel::TrueColor
);
}
#[test]
fn wt_session_promotes_ansi16_to_truecolor_for_diffs() {
assert_eq!(
diff_color_level_for_terminal(
StdoutColorLevel::Ansi16,
TerminalName::Unknown,
true,
false,
),
DiffColorLevel::TrueColor
);
}
#[test]
fn non_windows_terminal_keeps_ansi16_diff_palette() {
assert_eq!(
diff_color_level_for_terminal(
StdoutColorLevel::Ansi16,
TerminalName::WezTerm,
false,
false,
),
DiffColorLevel::Ansi16
);
}
#[test]
fn wt_session_promotes_unknown_color_level_to_truecolor() {
assert_eq!(
diff_color_level_for_terminal(
StdoutColorLevel::Unknown,
TerminalName::WindowsTerminal,
true,
false,
),
DiffColorLevel::TrueColor
);
}
#[test]
fn non_wt_windows_terminal_keeps_unknown_color_level_conservative() {
assert_eq!(
diff_color_level_for_terminal(
StdoutColorLevel::Unknown,
TerminalName::WindowsTerminal,
false,
false,
),
DiffColorLevel::Ansi16
);
}
#[test]
fn explicit_force_override_keeps_ansi16_on_windows_terminal() {
assert_eq!(
diff_color_level_for_terminal(
StdoutColorLevel::Ansi16,
TerminalName::WindowsTerminal,
false,
true,
),
DiffColorLevel::Ansi16
);
}
#[test]
fn explicit_force_override_keeps_ansi256_on_windows_terminal() {
assert_eq!(
diff_color_level_for_terminal(
StdoutColorLevel::Ansi256,
TerminalName::WindowsTerminal,
true,
true,
),
DiffColorLevel::Ansi256
);
}
#[test]
fn add_diff_uses_path_extension_for_highlighting() {
let mut changes: HashMap<PathBuf, FileChange> = HashMap::new();

View File

@@ -0,0 +1,8 @@
---
source: tui/src/diff_render.rs
expression: terminal.backend()
---
"1 +added in ansi16 mode "
"2 -deleted in ansi16 mode "
" "
" "