← 都来码 / Dolaima← 例程索引c/tty/ansi_colors.c原文

c/tty/ansi_colors.c · 10654 字节 · 下载原文

// ANSI 彩色输出体检 —— 一次把 `AnsiMarkup` 支持的全部子集打出来。

// ANSI color output health check — print the entire subset that `AnsiMarkup` supports in one go.

//

// 这是**命令行页彩色链路**的端到端判据:手机「命令行」页把程序 stdout 里的 ANSI

// This is the end-to-end criterion for the **command-line page color pipeline**: the phone's "command line" page turns the ANSI in a program's stdout

// 转成仓库统一的 «» 中间格式(`UI/Shared/AnsiMarkup.cs`),再由渲染器上色。

// into the repo-wide «» intermediate format (`UI/Shared/AnsiMarkup.cs`), and the renderer then colors it.

// 那条路此前只有**单元测试**(`SelfTest.Chunk10.cs` 的 TestAnsiMarkup 查转换结果),

// That path previously had only a **unit test** (TestAnsiMarkup in `SelfTest.Chunk10.cs` checks the conversion result),

// 没有能一眼看出"到底显示成什么样"的示例 —— 这个就是。

// with no example that shows at a glance "what it actually looks like on screen" — this is that one.

//

// ⚠ 转义写法(**2026-10-04 逐条复验**,结论与实测值见下):

// ⚠ Escape forms (**re-checked item by item on 2026-10-04**; verdict and measured values below):

//   · `\x1b[31m`  ✅ 正确输出 ESC(0x1B)

//   · `\x1b[31m`  ✅ correctly emits ESC(0x1B)

//   · `\033[31m`  ~~❌ C 前端把它解析成 `\0`(NUL)+ 字面量 `33` ⇒ 字符串在 NUL 处终止,整行只剩第一个字符~~

//   · `\033[31m`  ~~❌ the C frontend parses it as `\0` (NUL) + the literal `33` ⇒ the string terminates at the NUL and only the first character survives~~

//     ✅ **已修好(2026-10-04 复验)**:实测 `"A\033[31mB"` 逐字节打出 `41 1B 5B 33 31 6D 42`(ESC = 0x1B,正确)。

//     ✅ **fixed (re-checked 2026-10-04)**: `"A\033[31mB"` measured byte-for-byte as `41 1B 5B 33 31 6D 42` (ESC = 0x1B, correct).

//   · `\e[31m`    ❌ **仍成立(2026-10-04 复验)**:不支持,实测退化成字面量字母 `e`(`"A\e[31mB"` 打出 `Ae[31mB`)。

//   · `\e[31m`    ❌ **still holds (re-checked 2026-10-04)**: unsupported; measured to degrade into the literal letter `e` (`"A\e[31mB"` prints `Ae[31mB`).

//

// ⚠ 两个刻意的取舍(与 AnsiMarkup 一致):**不做反白**(SGR 7,它要交换前景/背景,

// ⚠ Two deliberate trade-offs (consistent with AnsiMarkup): **no reverse video** (SGR 7, since it would swap foreground/background,

//   而本项目取色跟着日/夜主题走,"交换"在主题适配之后语义就变了);

//   while this project picks colors by day/night theme, so "swapping" changes meaning once themes are adapted);

//   光标/擦除/OSC 等**非 SGR 序列会被吃掉**,所以这里也不演示。

//   cursor/erase/OSC and other **non-SGR sequences get eaten**, so they are not demonstrated here either.

//

// 用 `puts` 而不是 `printf`:桌面脚手架里 printf 的格式化路径**曾经**会崩(见 CLAUDE.md

// Use `puts` rather than `printf`: the desktop scaffold's printf formatting path **used to** crash (see CLAUDE.md

// 的「桌面脚手架三条既有事实」);**2026-10-04 复验已正常**(实测 `printf("%s %d %f %x %c", …)`

// "three existing facts about the desktop scaffold"); **re-checked fine on 2026-10-04** (measured `printf("%s %d %f %x %c", …)`

// 打出 `str 42 3.500000 ff A`),而 `puts` 一直正常。

// prints `str 42 3.500000 ff A`), and `puts` has always been fine.

//

// ⚠ 但仍有一条没修好:`%f` 对 **float** 变量打 `0.000000`(float 没被提升成 double;

// ⚠ One gap remains: `%f` on a **float** variable prints `0.000000` (the float is not promoted to double;

//   实测 `float f=1.5f; printf("%f", f)` → `0.000000`),**double**(变量或字面量)正常。

//   measured `float f=1.5f; printf("%f", f)` → `0.000000`), while **double** (variable or literal) is fine.

//

// 跑法:命令行页输入  vml run examples/c/ansi_colors.c

// How to run: type this on the command-line page  vml run examples/c/ansi_colors.c


#include <vml_ui.h>
int main()
{
    int lang;

    // 界面语言:开局查一次(ui_scr_get_language 是 syscall,别每帧调)

    // UI language: queried once at start (ui_scr_get_language is a syscall, do not call it every frame)

    lang = ui_scr_get_language();

    // ── 标准 8 色前景(30–37)────────────────────────────────

    // ── Standard 8 foreground colors (30–37)────────────────────────────────

    puts(lang == 0 ? "\x1b[30m30 黑\x1b[0m  \x1b[31m31 红\x1b[0m  \x1b[32m32 绿\x1b[0m  \x1b[33m33 黄\x1b[0m"
                   : "\x1b[30m30 black\x1b[0m  \x1b[31m31 red\x1b[0m  \x1b[32m32 green\x1b[0m  \x1b[33m33 yellow\x1b[0m");
    puts(lang == 0 ? "\x1b[34m34 蓝\x1b[0m  \x1b[35m35 品红\x1b[0m  \x1b[36m36 青\x1b[0m  \x1b[37m37 白\x1b[0m"
                   : "\x1b[34m34 blue\x1b[0m  \x1b[35m35 magenta\x1b[0m  \x1b[36m36 cyan\x1b[0m  \x1b[37m37 white\x1b[0m");

    // ── 亮色前景(90–97)────────────────────────────────────

    // ── Bright foreground colors (90–97)────────────────────────────────────

    puts(lang == 0 ? "\x1b[90m90 亮黑(灰)\x1b[0m  \x1b[91m91 亮红\x1b[0m  \x1b[92m92 亮绿\x1b[0m  \x1b[93m93 亮黄\x1b[0m"
                   : "\x1b[90m90 gray\x1b[0m  \x1b[91m91 bright red\x1b[0m  \x1b[92m92 bright green\x1b[0m  \x1b[93m93 bright yellow\x1b[0m");
    puts(lang == 0 ? "\x1b[94m94 亮蓝\x1b[0m  \x1b[95m95 亮品红\x1b[0m  \x1b[96m96 亮青\x1b[0m  \x1b[97m97 亮白\x1b[0m"
                   : "\x1b[94m94 bright blue\x1b[0m  \x1b[95m95 bright magenta\x1b[0m  \x1b[96m96 bright cyan\x1b[0m  \x1b[97m97 bright white\x1b[0m");

    // ── 背景色(40–47 / 100–107)────────────────────────────

    // ── Background colors (40–47 / 100–107)────────────────────────────

    puts(lang == 0 ? "\x1b[41m 红底 \x1b[0m \x1b[42m 绿底 \x1b[0m \x1b[44m 蓝底 \x1b[0m \x1b[103m 亮黄底 \x1b[0m \x1b[105m 亮品红底 \x1b[0m"
                   : "\x1b[41m red bg \x1b[0m \x1b[42m green bg \x1b[0m \x1b[44m blue bg \x1b[0m \x1b[103m bright yellow bg \x1b[0m \x1b[105m bright magenta bg \x1b[0m");

    // ── 样式(1 粗 / 2 暗 / 3 斜 / 4 下划线 / 9 删除线)─────

    // ── Styles (1 bold / 2 dim / 3 italic / 4 underline / 9 strikethrough)─────

    puts(lang == 0 ? "\x1b[1m粗体 bold\x1b[0m  \x1b[2m暗淡 dim\x1b[0m  \x1b[3m斜体 italic\x1b[0m  \x1b[4m下划线 underline\x1b[0m  \x1b[9m删除线 strike\x1b[0m"
                   : "\x1b[1mbold\x1b[0m  \x1b[2mdim\x1b[0m  \x1b[3mitalic\x1b[0m  \x1b[4munderline\x1b[0m  \x1b[9mstrike\x1b[0m");

    // ── 组合:粗体 + 颜色 ───────────────────────────────────

    // ── Combination: bold + color ───────────────────────────────────

    // 「粗体 + 颜色」在本仓是编码进颜色高位的(AnsiTty.BoldFlag),

    // "Bold + color" is encoded into the color's high bits in this repo (AnsiTty.BoldFlag),

    // 所以这一行同时压住了那条路径。

    // so this line covers that path too.

    puts(lang == 0 ? "\x1b[1;31m粗红\x1b[0m  \x1b[1;92m粗亮绿\x1b[0m  \x1b[4;34m下划线蓝\x1b[0m"
                   : "\x1b[1;31mbold red\x1b[0m  \x1b[1;92mbold bright green\x1b[0m  \x1b[4;34munderline blue\x1b[0m");

    // ── 256 色(38;5;N)─────────────────────────────────────

    // ── 256 colors (38;5;N)─────────────────────────────────────

    puts(lang == 0 ? "\x1b[38;5;208m256-208 橙\x1b[0m  \x1b[38;5;46m256-46 亮绿\x1b[0m  \x1b[38;5;196m256-196 正红\x1b[0m  \x1b[38;5;240m256-240 灰\x1b[0m"
                   : "\x1b[38;5;208m256-208 orange\x1b[0m  \x1b[38;5;46m256-46 bright green\x1b[0m  \x1b[38;5;196m256-196 pure red\x1b[0m  \x1b[38;5;240m256-240 gray\x1b[0m");

    // ── 真彩(38;2;r;g;b)───────────────────────────────────

    // ── Truecolor (38;2;r;g;b)───────────────────────────────────

    // ⚠ 真彩码的数值**大于 255**,解析时必须排在 256 色之前判断,

    // ⚠ The truecolor code's value **exceeds 255**, so it must be tested before the 256-color case,

    //   否则会掉进 256 色分支、全部渲染成同一个颜色。

    //   otherwise it falls into the 256-color branch and everything renders as one and the same color.

    puts(lang == 0 ? "\x1b[38;2;255;128;0m真彩 橙\x1b[0m  \x1b[38;2;0;200;255m真彩 青\x1b[0m  \x1b[38;2;200;0;255m真彩 紫\x1b[0m"
                   : "\x1b[38;2;255;128;0mtruecolor orange\x1b[0m  \x1b[38;2;0;200;255mtruecolor cyan\x1b[0m  \x1b[38;2;200;0;255mtruecolor purple\x1b[0m");

    // ── 真彩背景 ────────────────────────────────────────────

    // ── Truecolor backgrounds ────────────────────────────────────────────

    puts(lang == 0 ? "\x1b[48;2;60;0;90m 真彩深紫底 \x1b[0m  \x1b[48;2;0;90;60m 真彩墨绿底 \x1b[0m"
                   : "\x1b[48;2;60;0;90m truecolor deep purple bg \x1b[0m  \x1b[48;2;0;90;60m truecolor dark green bg \x1b[0m");

    // ── 兜底:纯文本(不该被染色)───────────────────────────

    // ── Fallback: plain text (should not get colored)───────────────────────────

    puts(lang == 0 ? "以上全部结束 —— 这一行是纯文本,应当没有颜色。"
                   : "That is everything -- this line is plain text and should have no color.");
    return 0;
}