From: Muraoka Taro Date: Sun, 21 Dec 2025 19:42:23 +0000 (+0000) Subject: patch 9.1.2006: MS-Windows: ANSI colors not correct in terminal X-Git-Tag: v9.1.2006^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=86855ea8ee6d81926cfadcebce984afc94fc82dc;p=thirdparty%2Fvim.git patch 9.1.2006: MS-Windows: ANSI colors not correct in terminal Problem: ANSI escape colors are not displayed correctly in non-termguicolors in vim (cli) on Windows. The red and blue channels seem to be swapped. Cause: When converting VTerm ANSI index colors to cterm colors in terminal.c, the Windows case equivalent to NR-16 (:help cterm-colors) is ignored. Solution: Created and used a table to convert ANSI indexed colors to cterm's NR-16 representation (Windows only). This table corresponds to the inverse conversion of cterm_ansi_idx in term.c. The values in both tables are exactly the same, but the meanings are opposite, so they are separate tables (Muraoka Taro). closes: #18931 Signed-off-by: Muraoka Taro Signed-off-by: Christian Brabandt --- diff --git a/src/term.c b/src/term.c index d3939ef4d7..503ec38955 100644 --- a/src/term.c +++ b/src/term.c @@ -7642,7 +7642,7 @@ static const char_u ansi_table[16][3] = { # if defined(MSWIN) // Mapping between cterm indices < 16 and their counterpart in the ANSI palette. -static const char_u cterm_ansi_idx[] = { +const char_u cterm_ansi_idx[] = { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 }; # endif diff --git a/src/termdefs.h b/src/termdefs.h index 2b65abb1ce..d611fcf444 100644 --- a/src/termdefs.h +++ b/src/termdefs.h @@ -233,3 +233,8 @@ typedef enum { TMODE_RAW, // terminal mode for Normal and Insert mode TMODE_UNKNOWN // after executing a shell } tmode_T; + +#if defined(MSWIN) +// Mapping between cterm indices < 16 and their counterpart in the ANSI palette. +extern const char_u cterm_ansi_idx[]; +#endif diff --git a/src/terminal.c b/src/terminal.c index 404cfa125a..48b9e06c40 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -3033,7 +3033,20 @@ color2index(VTermColor *color, int fg, int *boldp) { // Use the color as-is if possible, give up otherwise. if (color->index < t_colors) - return color->index + 1; + { + uint8_t index = color->index; +#ifdef MSWIN + // Convert ANSI palette to cterm color index. + // cterm_ansi_idx is a table for the reverse conversion from cterm + // color index to ANSI palette. However, the current table can + // be used for this conversion as well. + // Note: If the contents of cterm_ansi_idx change in the future, a + // different table may be needed for this purpose. + if (index < 16) + index = cterm_ansi_idx[index]; +#endif + return index + 1; + } // 8-color terminals can actually display twice as many colors by // setting the high-intensity/bold bit. else if (t_colors == 8 && fg && color->index < 16) diff --git a/src/testdir/test_terminal3.vim b/src/testdir/test_terminal3.vim index 218b4e6a04..5049f913c8 100644 --- a/src/testdir/test_terminal3.vim +++ b/src/testdir/test_terminal3.vim @@ -1037,4 +1037,55 @@ func Test_terminal_visual_empty_listchars() call StopVimInTerminal(buf) endfunc +func Test_terminal_ansi_color_windows_cui() + if !has('win32') || has('gui_running') + throw 'Skipped: only for the Windows CUI' + endif + if exists('$APPVEYOR') + throw 'Skipped: this test cannot be performed because AppVeyor does not support ANSI escape sequences' + endif + + call assert_equal('dark', &background) + + " Outputs 16 ANSI colors as background colors + let ansi = '' + for i in range(16) + let ansi ..= printf("\e[%dm%X", i + (i < 8 ? 40 : 92), i) + endfor + let ansi ..= "\e[40m\n" + call writefile([ansi], 'XANSIcolor', 'D') + + let expected_chars = '0123456789ABCDEF' + let expected_colors = [ + \ '#000000', '#e00000', '#00e000', '#e0e000', + \ '#0000e0', '#e000e0', '#00e0e0', '#e0e0e0', + \ '#808080', '#ff4040', '#40ff40', '#ffff40', + \ '#4040ff', '#ff40ff', '#40ffff', '#ffffff', + \ ] + + " Ideally, 16 colors should be checked. However, in the current CI + " environment, the 16th color is something other than white, and we don't + " know the cause nor solution. After discussion, we decided to check only 15 + " colors for the time being. + " FIXME: Check all 16 colors in the future. + let len_to_check = 15 + let expected_colors = expected_colors[:len_to_check-1] + + " First, make sure vim can display correct ANSI color text in terminal. + let buf = term_start("cmd /C type XANSIcolor") + call WaitForAssert({-> assert_equal(expected_chars, term_scrape(buf, 1)[:15]->map({_, v -> v['chars']})->join(''))}) + call assert_equal(expected_colors, term_scrape(buf, 1)[:len_to_check-1]->map({_, v -> v['bg']})) + bwipeout! + + " Next, check if vim can do the same thing in the vim terminal in terminal. + let lines = [ + \ 'call term_start("cmd /C type XANSIcolor")' + \ ] + call writefile(lines, 'XloadANSI', 'D') + let cmd = GetVimCommandCleanTerm() + let buf = term_start(cmd .. '-S XloadANSI') + call WaitForAssert({-> assert_equal(expected_chars, term_scrape(buf, 1)[:15]->map({_, v -> v['chars']})->join(''))}) + call assert_equal(expected_colors, term_scrape(buf, 1)[:len_to_check-1]->map({_, v -> v['bg']})) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 2d4b1df977..12dfb27af9 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2006, /**/ 2005, /**/