From 0346958153bb0d991ad684bd8ba30ec2c056f35c Mon Sep 17 00:00:00 2001 From: KBS Date: Wed, 26 Aug 2026 23:10:57 +0900 Subject: [PATCH] fix(terminal): truecolor SGR with colour space id #41491 libvterm reads the first three sub-parameters of an SGR 38:2 or 48:2 sequence as R:G:B, so a colour space id shifts the channels: the empty slot becomes red via CSI_ARG_MISSING truncating to 255, and green and blue move over one. Skip the colour space id when the colon-separated group holds more than three arguments. The group length comes from CSI_ARG_HAS_MORE rather than the raw argument count, so a following semicolon-separated parameter is not consumed. --- src/nvim/vterm/pen.c | 20 +++++++++++++++----- test/functional/terminal/highlight_spec.lua | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/nvim/vterm/pen.c b/src/nvim/vterm/pen.c index f2054633d5..4b7221be14 100644 --- a/src/nvim/vterm/pen.c +++ b/src/nvim/vterm/pen.c @@ -90,15 +90,25 @@ static int lookup_colour(const VTermState *state, int palette, const long args[] VTermColor *col) { switch (palette) { - case 2: // RGB mode - 3 args contain colour values directly - if (argcount < 3) { + case 2: { // RGB mode - 3 args contain colour values directly + // ITU-T T.416 places a colour space id before R:G:B, as in "38:2::R:G:B". + // Skip it when this colon-separated group holds more than three arguments. + int grouplen = 0; + while (grouplen < argcount && CSI_ARG_HAS_MORE(args[grouplen])) { + grouplen++; + } + grouplen = grouplen < argcount ? grouplen + 1 : argcount; + const int skip = grouplen > 3 ? 1 : 0; + + if (argcount - skip < 3) { return argcount; } - vterm_color_rgb(col, (uint8_t)CSI_ARG(args[0]), (uint8_t)CSI_ARG(args[1]), - (uint8_t)CSI_ARG(args[2])); + vterm_color_rgb(col, (uint8_t)CSI_ARG(args[skip]), (uint8_t)CSI_ARG(args[skip + 1]), + (uint8_t)CSI_ARG(args[skip + 2])); - return 3; + return skip + 3; + } case 5: // XTerm 256-colour mode if (!argcount || CSI_ARG_IS_MISSING(args[0])) { diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua index 9df2777d1f..acab5331a4 100644 --- a/test/functional/terminal/highlight_spec.lua +++ b/test/functional/terminal/highlight_spec.lua @@ -332,6 +332,20 @@ describe(':terminal highlight forwarding', function() {1:-- TERMINAL --} | ]]) end) + + it('handles truecolor SGR with a colour space id', function() + skip(is_os('win')) + tt.feed_termcode('[38:2::255:128:0m') + tt.feed_data('color') + tt.clear_attrs() + tt.feed_data('text') + screen:expect([[ + tty ready | + {3:color}text^ | + |*4 + {1:-- TERMINAL --} | + ]]) + end) end) --- @param buflocal boolean