diff --git a/src/terminal/Parser.zig b/src/terminal/Parser.zig index 92f405a23..5fbf214a6 100644 --- a/src/terminal/Parser.zig +++ b/src/terminal/Parser.zig @@ -949,6 +949,31 @@ test "csi: too many params" { } } +test "csi: 17 parameters" { + // Test with exactly 17 parameters (the Kakoune case) + var p = init(); + _ = p.next(0x1B); + _ = p.next('['); + // Build 17 parameters separated by semicolons + for (0..16) |_| { + _ = p.next('1'); + _ = p.next(';'); + } + _ = p.next('2'); // 17th parameter + + { + const a = p.next('H'); + try testing.expect(p.state == .ground); + try testing.expect(a[0] == null); + try testing.expect(a[1].? == .csi_dispatch); + try testing.expect(a[2] == null); + + const csi = a[1].?.csi_dispatch; + try testing.expectEqual(@as(usize, 17), csi.params.len); + try testing.expectEqual(@as(u16, 2), csi.params[16]); + } +} + test "dcs: XTGETTCAP" { var p = init(); _ = p.next(0x1B); diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index c9bb50158..3009935ec 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -2601,3 +2601,22 @@ test "stream CSI ? W reset tab stops" { try s.nextSlice("\x1b[?1;2;3W"); try testing.expect(s.handler.reset); } + +test "stream: SGR with 17+ parameters for underline color" { + const H = struct { + attrs: ?sgr.Attribute = null, + called: bool = false, + + pub fn setAttribute(self: *@This(), attr: sgr.Attribute) !void { + self.attrs = attr; + self.called = true; + } + }; + + var s: Stream(H) = .init(.{}); + + // Kakoune-style SGR with underline color as 17th parameter + // This tests the fix where param 17 was being dropped + try s.nextSlice("\x1b[4:3;38;2;51;51;51;48;2;170;170;170;58;2;255;97;136;0m"); + try testing.expect(s.handler.called); +}