terminal: change OSC parser to explicit init to set undefined

This works around: https://github.com/ziglang/zig/issues/19148
This lets our `test-valgrind` command catch some issues. We'll have to
follow this pattern in more places but I want to do it incrementally so
things keep passing.

I **do not** want to blindly follow this pattern everywhere. I want to
start by focusing in only on the structs that set `undefined` as default
fields that we're also about to test in isolation with Valgrind. Its
just too much noise otherwise and not a general style I'm sure of; it's
worth it for Valgrind though.
This commit is contained in:
Mitchell Hashimoto
2025-08-20 12:06:09 -07:00
parent b3a80f2e47
commit 131f170f89
9 changed files with 222 additions and 186 deletions

View File

@@ -22,7 +22,6 @@ jobs:
- build-macos-matrix
- build-windows
- flatpak-check-zig-cache
- flatpak
- test
- test-gtk
- test-gtk-ng
@@ -1013,28 +1012,29 @@ jobs:
- name: Check Flatpak Zig Dependencies
run: nix develop -c ./flatpak/build-support/check-zig-cache.sh
flatpak:
if: github.repository == 'ghostty-org/ghostty'
name: "Flatpak"
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-47
options: --privileged
strategy:
fail-fast: false
matrix:
variant:
- arch: x86_64
runner: namespace-profile-ghostty-md
- arch: aarch64
runner: namespace-profile-ghostty-md-arm64
runs-on: ${{ matrix.variant.runner }}
needs: [flatpak-check-zig-cache, test]
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: flatpak/flatpak-github-actions/flatpak-builder@10a3c29f0162516f0f68006be14c92f34bd4fa6c # v6.5
with:
bundle: com.mitchellh.ghostty
manifest-path: flatpak/com.mitchellh.ghostty.yml
cache-key: flatpak-builder-${{ github.sha }}
arch: ${{ matrix.variant.arch }}
verbose: true
# Disabled until we update to Zig 0.15 or if we can pin this to Zig 0.14
# flatpak:
# if: github.repository == 'ghostty-org/ghostty'
# name: "Flatpak"
# container:
# image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-47
# options: --privileged
# strategy:
# fail-fast: false
# matrix:
# variant:
# - arch: x86_64
# runner: namespace-profile-ghostty-md
# - arch: aarch64
# runner: namespace-profile-ghostty-md-arm64
# runs-on: ${{ matrix.variant.runner }}
# needs: [flatpak-check-zig-cache, test]
# steps:
# - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
# - uses: flatpak/flatpak-github-actions/flatpak-builder@10a3c29f0162516f0f68006be14c92f34bd4fa6c # v6.5
# with:
# bundle: com.mitchellh.ghostty
# manifest-path: flatpak/com.mitchellh.ghostty.yml
# cache-key: flatpak-builder-${{ github.sha }}
# arch: ${{ matrix.variant.arch }}
# verbose: true

View File

@@ -77,7 +77,7 @@ fn step(ptr: *anyopaque) Benchmark.Error!void {
const f = self.data_f orelse return;
var r = std.io.bufferedReader(f.reader());
var p: terminalpkg.Parser = .{};
var p: terminalpkg.Parser = .init();
var buf: [4096]u8 = undefined;
while (true) {

View File

@@ -62,7 +62,7 @@ pub fn create(
.cols = opts.@"terminal-cols",
}),
.handler = .{ .t = &ptr.terminal },
.stream = .{ .handler = &ptr.handler },
.stream = .init(&ptr.handler),
};
return ptr;

View File

@@ -172,13 +172,10 @@ pub fn init(surface: *Surface) !Inspector {
.surface = surface,
.key_events = key_buf,
.vt_events = vt_events,
.vt_stream = .{
.handler = vt_handler,
.parser = .{
.osc_parser = .{
.alloc = surface.alloc,
},
},
.vt_stream = stream: {
var s: inspector.termio.Stream = .init(vt_handler);
s.parser.osc_parser.alloc = surface.alloc;
break :stream s;
},
};
}

View File

@@ -196,7 +196,7 @@ test "OSC generator valid" {
};
for (0..50) |_| {
const seq = try gen.next(&buf);
var parser: terminal.osc.Parser = .{};
var parser: terminal.osc.Parser = .init();
for (seq[2 .. seq.len - 1]) |c| parser.next(c);
try testing.expect(parser.end(null) != null);
}
@@ -214,7 +214,7 @@ test "OSC generator invalid" {
};
for (0..50) |_| {
const seq = try gen.next(&buf);
var parser: terminal.osc.Parser = .{};
var parser: terminal.osc.Parser = .init();
for (seq[2 .. seq.len - 1]) |c| parser.next(c);
try testing.expect(parser.end(null) == null);
}

View File

@@ -223,10 +223,12 @@ param_acc: u16 = 0,
param_acc_idx: u8 = 0,
/// Parser for OSC sequences
osc_parser: osc.Parser = .{},
osc_parser: osc.Parser,
pub fn init() Parser {
return .{};
return .{
.osc_parser = .init(),
};
}
pub fn deinit(self: *Parser) void {

View File

@@ -282,23 +282,23 @@ pub const Parser = struct {
/// Optional allocator used to accept data longer than MAX_BUF.
/// This only applies to some commands (e.g. OSC 52) that can
/// reasonably exceed MAX_BUF.
alloc: ?Allocator = null,
alloc: ?Allocator,
/// Current state of the parser.
state: State = .empty,
state: State,
/// Current command of the parser, this accumulates.
command: Command = undefined,
command: Command,
/// Buffer that stores the input we see for a single OSC command.
/// Slices in Command are offsets into this buffer.
buf: [MAX_BUF]u8 = undefined,
buf_start: usize = 0,
buf_idx: usize = 0,
buf_dynamic: ?*std.ArrayListUnmanaged(u8) = null,
buf: [MAX_BUF]u8,
buf_start: usize,
buf_idx: usize,
buf_dynamic: ?*std.ArrayListUnmanaged(u8),
/// True when a command is complete/valid to return.
complete: bool = false,
complete: bool,
/// Temporary state that is dependent on the current state.
temp_state: union {
@@ -310,7 +310,7 @@ pub const Parser = struct {
/// Temporary state for key/value pairs
key: []const u8,
} = undefined,
},
// Maximum length of a single OSC command. This is the full OSC command
// sequence length (excluding ESC ]). This is arbitrary, I couldn't find
@@ -429,6 +429,38 @@ pub const Parser = struct {
conemu_progress_value,
};
pub fn init() Parser {
var result: Parser = .{
.alloc = null,
.state = .empty,
.buf_start = 0,
.buf_idx = 0,
.buf_dynamic = null,
.complete = false,
// Keeping all our undefined values together so we can
// visually easily duplicate them in the Valgrind check below.
.command = undefined,
.buf = undefined,
.temp_state = undefined,
};
if (std.valgrind.runningOnValgrind() > 0) {
// Initialize our undefined fields so Valgrind can catch it.
// https://github.com/ziglang/zig/issues/19148
result.command = undefined;
result.buf = undefined;
result.temp_state = undefined;
}
return result;
}
pub fn initAlloc(alloc: Allocator) Parser {
var result: Parser = .init();
result.alloc = alloc;
return result;
}
/// This must be called to clean up any allocated memory.
pub fn deinit(self: *Parser) void {
self.reset();
@@ -1590,7 +1622,7 @@ pub const Parser = struct {
test "OSC: change_window_title" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
p.next('0');
p.next(';');
p.next('a');
@@ -1603,7 +1635,7 @@ test "OSC: change_window_title" {
test "OSC: change_window_title with 2" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
p.next('2');
p.next(';');
p.next('a');
@@ -1616,7 +1648,7 @@ test "OSC: change_window_title with 2" {
test "OSC: change_window_title with utf8" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
p.next('2');
p.next(';');
// '—' EM DASH U+2014 (E2 80 94)
@@ -1638,7 +1670,7 @@ test "OSC: change_window_title with utf8" {
test "OSC: change_window_title empty" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
p.next('2');
p.next(';');
const cmd = p.end(null).?;
@@ -1649,7 +1681,7 @@ test "OSC: change_window_title empty" {
test "OSC: change_window_icon" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
p.next('1');
p.next(';');
p.next('a');
@@ -1662,7 +1694,7 @@ test "OSC: change_window_icon" {
test "OSC: prompt_start" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "133;A";
for (input) |ch| p.next(ch);
@@ -1676,7 +1708,7 @@ test "OSC: prompt_start" {
test "OSC: prompt_start with single option" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "133;A;aid=14";
for (input) |ch| p.next(ch);
@@ -1689,7 +1721,7 @@ test "OSC: prompt_start with single option" {
test "OSC: prompt_start with redraw disabled" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "133;A;redraw=0";
for (input) |ch| p.next(ch);
@@ -1702,7 +1734,7 @@ test "OSC: prompt_start with redraw disabled" {
test "OSC: prompt_start with redraw invalid value" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "133;A;redraw=42";
for (input) |ch| p.next(ch);
@@ -1716,7 +1748,7 @@ test "OSC: prompt_start with redraw invalid value" {
test "OSC: prompt_start with continuation" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "133;A;k=c";
for (input) |ch| p.next(ch);
@@ -1729,7 +1761,7 @@ test "OSC: prompt_start with continuation" {
test "OSC: prompt_start with secondary" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "133;A;k=s";
for (input) |ch| p.next(ch);
@@ -1742,7 +1774,7 @@ test "OSC: prompt_start with secondary" {
test "OSC: end_of_command no exit code" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "133;D";
for (input) |ch| p.next(ch);
@@ -1754,7 +1786,7 @@ test "OSC: end_of_command no exit code" {
test "OSC: end_of_command with exit code" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "133;D;25";
for (input) |ch| p.next(ch);
@@ -1767,7 +1799,7 @@ test "OSC: end_of_command with exit code" {
test "OSC: prompt_end" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "133;B";
for (input) |ch| p.next(ch);
@@ -1779,7 +1811,7 @@ test "OSC: prompt_end" {
test "OSC: end_of_input" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "133;C";
for (input) |ch| p.next(ch);
@@ -1791,7 +1823,7 @@ test "OSC: end_of_input" {
test "OSC: OSC110: reset foreground color" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "110";
@@ -1817,7 +1849,7 @@ test "OSC: OSC110: reset foreground color" {
test "OSC: OSC111: reset background color" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "111";
@@ -1843,7 +1875,7 @@ test "OSC: OSC111: reset background color" {
test "OSC: OSC112: reset cursor color" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "112";
@@ -1869,7 +1901,7 @@ test "OSC: OSC112: reset cursor color" {
test "OSC: OSC112: reset cursor color with semicolon" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "112;";
@@ -1896,7 +1928,7 @@ test "OSC: OSC112: reset cursor color with semicolon" {
test "OSC: get/set clipboard" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "52;s;?";
for (input) |ch| p.next(ch);
@@ -1910,7 +1942,7 @@ test "OSC: get/set clipboard" {
test "OSC: get/set clipboard (optional parameter)" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "52;;?";
for (input) |ch| p.next(ch);
@@ -1924,7 +1956,7 @@ test "OSC: get/set clipboard (optional parameter)" {
test "OSC: get/set clipboard with allocator" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "52;s;?";
@@ -1939,7 +1971,7 @@ test "OSC: get/set clipboard with allocator" {
test "OSC: clear clipboard" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .init();
defer p.deinit();
const input = "52;;";
@@ -1954,7 +1986,7 @@ test "OSC: clear clipboard" {
test "OSC: report pwd" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "7;file:///tmp/example";
for (input) |ch| p.next(ch);
@@ -1967,7 +1999,7 @@ test "OSC: report pwd" {
test "OSC: report pwd empty" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "7;";
for (input) |ch| p.next(ch);
@@ -1979,7 +2011,7 @@ test "OSC: report pwd empty" {
test "OSC: pointer cursor" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "22;pointer";
for (input) |ch| p.next(ch);
@@ -1992,7 +2024,7 @@ test "OSC: pointer cursor" {
test "OSC: longer than buffer" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "0;" ++ "a" ** (Parser.MAX_BUF + 2);
for (input) |ch| p.next(ch);
@@ -2004,7 +2036,7 @@ test "OSC: longer than buffer" {
test "OSC: OSC10: report foreground color" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "10;?";
@@ -2032,7 +2064,7 @@ test "OSC: OSC10: report foreground color" {
test "OSC: OSC10: set foreground color" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "10;rgbi:0.0/0.5/1.0";
@@ -2062,7 +2094,7 @@ test "OSC: OSC10: set foreground color" {
test "OSC: OSC11: report background color" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "11;?";
@@ -2090,7 +2122,7 @@ test "OSC: OSC11: report background color" {
test "OSC: OSC11: set background color" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "11;rgb:f/ff/ffff";
@@ -2120,7 +2152,7 @@ test "OSC: OSC11: set background color" {
test "OSC: OSC12: report cursor color" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "12;?";
@@ -2148,7 +2180,7 @@ test "OSC: OSC12: report cursor color" {
test "OSC: OSC12: set cursor color" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "12;rgb:f/ff/ffff";
@@ -2178,7 +2210,7 @@ test "OSC: OSC12: set cursor color" {
test "OSC: OSC4: get palette color 1" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;1;?";
@@ -2204,7 +2236,7 @@ test "OSC: OSC4: get palette color 1" {
test "OSC: OSC4: get palette color 2" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;1;?;2;?";
@@ -2238,7 +2270,7 @@ test "OSC: OSC4: get palette color 2" {
test "OSC: OSC4: set palette color 1" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;17;rgb:aa/bb/cc";
@@ -2267,7 +2299,7 @@ test "OSC: OSC4: set palette color 1" {
test "OSC: OSC4: set palette color 2" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;17;rgb:aa/bb/cc;1;rgb:00/11/22";
@@ -2308,7 +2340,7 @@ test "OSC: OSC4: set palette color 2" {
test "OSC: OSC4: get with invalid index 1" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;1111;?;1;?";
@@ -2333,7 +2365,7 @@ test "OSC: OSC4: get with invalid index 1" {
test "OSC: OSC4: get with invalid index 2" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;5;?;1111;?;1;?";
@@ -2367,7 +2399,7 @@ test "OSC: OSC4: get with invalid index 2" {
test "OSC: OSC4: multiple get 8a" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;0;?;1;?;2;?;3;?;4;?;5;?;6;?;7;?";
@@ -2449,7 +2481,7 @@ test "OSC: OSC4: multiple get 8a" {
test "OSC: OSC4: multiple get 8b" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;8;?;9;?;10;?;11;?;12;?;13;?;14;?;15;?";
@@ -2530,7 +2562,7 @@ test "OSC: OSC4: multiple get 8b" {
test "OSC: OSC4: set with invalid index" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;256;#ffffff;1;#aabbcc";
@@ -2559,7 +2591,7 @@ test "OSC: OSC4: set with invalid index" {
test "OSC: OSC4: mix get/set palette color" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;17;rgb:aa/bb/cc;254;?";
@@ -2596,7 +2628,7 @@ test "OSC: OSC4: mix get/set palette color" {
test "OSC: OSC4: incomplete color/spec 1" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;17";
@@ -2613,7 +2645,7 @@ test "OSC: OSC4: incomplete color/spec 1" {
test "OSC: OSC4: incomplete color/spec 2" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "4;17;?;42";
@@ -2638,7 +2670,7 @@ test "OSC: OSC4: incomplete color/spec 2" {
test "OSC: OSC104: reset palette color 1" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "104;17";
@@ -2663,7 +2695,7 @@ test "OSC: OSC104: reset palette color 1" {
test "OSC: OSC104: reset palette color 2" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "104;17;111";
@@ -2696,7 +2728,7 @@ test "OSC: OSC104: reset palette color 2" {
test "OSC: OSC104: invalid palette index" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "104;ffff;111";
@@ -2721,7 +2753,7 @@ test "OSC: OSC104: invalid palette index" {
test "OSC: OSC104: empty palette index" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "104;;111";
@@ -2746,7 +2778,7 @@ test "OSC: OSC104: empty palette index" {
test "OSC: conemu sleep" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;1;420";
for (input) |ch| p.next(ch);
@@ -2760,7 +2792,7 @@ test "OSC: conemu sleep" {
test "OSC: conemu sleep with no value default to 100ms" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;1;";
for (input) |ch| p.next(ch);
@@ -2774,7 +2806,7 @@ test "OSC: conemu sleep with no value default to 100ms" {
test "OSC: conemu sleep cannot exceed 10000ms" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;1;12345";
for (input) |ch| p.next(ch);
@@ -2788,7 +2820,7 @@ test "OSC: conemu sleep cannot exceed 10000ms" {
test "OSC: conemu sleep invalid input" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;1;foo";
for (input) |ch| p.next(ch);
@@ -2802,7 +2834,7 @@ test "OSC: conemu sleep invalid input" {
test "OSC: show desktop notification" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;Hello world";
for (input) |ch| p.next(ch);
@@ -2816,7 +2848,7 @@ test "OSC: show desktop notification" {
test "OSC: show desktop notification with title" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "777;notify;Title;Body";
for (input) |ch| p.next(ch);
@@ -2830,7 +2862,7 @@ test "OSC: show desktop notification with title" {
test "OSC: conemu message box" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;2;hello world";
for (input) |ch| p.next(ch);
@@ -2843,7 +2875,7 @@ test "OSC: conemu message box" {
test "OSC: conemu message box invalid input" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;2";
for (input) |ch| p.next(ch);
@@ -2855,7 +2887,7 @@ test "OSC: conemu message box invalid input" {
test "OSC: conemu message box empty message" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;2;";
for (input) |ch| p.next(ch);
@@ -2868,7 +2900,7 @@ test "OSC: conemu message box empty message" {
test "OSC: conemu message box spaces only message" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;2; ";
for (input) |ch| p.next(ch);
@@ -2881,7 +2913,7 @@ test "OSC: conemu message box spaces only message" {
test "OSC: conemu change tab title" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;3;foo bar";
for (input) |ch| p.next(ch);
@@ -2894,7 +2926,7 @@ test "OSC: conemu change tab title" {
test "OSC: conemu change tab reset title" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;3;";
for (input) |ch| p.next(ch);
@@ -2908,7 +2940,7 @@ test "OSC: conemu change tab reset title" {
test "OSC: conemu change tab spaces only title" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;3; ";
for (input) |ch| p.next(ch);
@@ -2922,7 +2954,7 @@ test "OSC: conemu change tab spaces only title" {
test "OSC: conemu change tab invalid input" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;3";
for (input) |ch| p.next(ch);
@@ -2934,7 +2966,7 @@ test "OSC: conemu change tab invalid input" {
test "OSC: OSC9 progress set" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;1;100";
for (input) |ch| p.next(ch);
@@ -2948,7 +2980,7 @@ test "OSC: OSC9 progress set" {
test "OSC: OSC9 progress set overflow" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;1;900";
for (input) |ch| p.next(ch);
@@ -2962,7 +2994,7 @@ test "OSC: OSC9 progress set overflow" {
test "OSC: OSC9 progress set single digit" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;1;9";
for (input) |ch| p.next(ch);
@@ -2976,7 +3008,7 @@ test "OSC: OSC9 progress set single digit" {
test "OSC: OSC9 progress set double digit" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;1;94";
for (input) |ch| p.next(ch);
@@ -2990,7 +3022,7 @@ test "OSC: OSC9 progress set double digit" {
test "OSC: OSC9 progress set extra semicolon ignored" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;1;100";
for (input) |ch| p.next(ch);
@@ -3004,7 +3036,7 @@ test "OSC: OSC9 progress set extra semicolon ignored" {
test "OSC: OSC9 progress remove with no progress" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;0;";
for (input) |ch| p.next(ch);
@@ -3018,7 +3050,7 @@ test "OSC: OSC9 progress remove with no progress" {
test "OSC: OSC9 progress remove with double semicolon" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;0;;";
for (input) |ch| p.next(ch);
@@ -3032,7 +3064,7 @@ test "OSC: OSC9 progress remove with double semicolon" {
test "OSC: OSC9 progress remove ignores progress" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;0;100";
for (input) |ch| p.next(ch);
@@ -3046,7 +3078,7 @@ test "OSC: OSC9 progress remove ignores progress" {
test "OSC: OSC9 progress remove extra semicolon" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;0;100;";
for (input) |ch| p.next(ch);
@@ -3059,7 +3091,7 @@ test "OSC: OSC9 progress remove extra semicolon" {
test "OSC: OSC9 progress error" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;2";
for (input) |ch| p.next(ch);
@@ -3073,7 +3105,7 @@ test "OSC: OSC9 progress error" {
test "OSC: OSC9 progress error with progress" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;2;100";
for (input) |ch| p.next(ch);
@@ -3087,7 +3119,7 @@ test "OSC: OSC9 progress error with progress" {
test "OSC: OSC9 progress pause" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;4";
for (input) |ch| p.next(ch);
@@ -3101,7 +3133,7 @@ test "OSC: OSC9 progress pause" {
test "OSC: OSC9 progress pause with progress" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;4;4;100";
for (input) |ch| p.next(ch);
@@ -3115,7 +3147,7 @@ test "OSC: OSC9 progress pause with progress" {
test "OSC: OSC9 conemu wait input" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;5";
for (input) |ch| p.next(ch);
@@ -3127,7 +3159,7 @@ test "OSC: OSC9 conemu wait input" {
test "OSC: OSC9 conemu wait ignores trailing characters" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "9;5;foo";
for (input) |ch| p.next(ch);
@@ -3139,7 +3171,7 @@ test "OSC: OSC9 conemu wait ignores trailing characters" {
test "OSC: empty param" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "4;;";
for (input) |ch| p.next(ch);
@@ -3151,7 +3183,7 @@ test "OSC: empty param" {
test "OSC: hyperlink" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "8;;http://example.com";
for (input) |ch| p.next(ch);
@@ -3164,7 +3196,7 @@ test "OSC: hyperlink" {
test "OSC: hyperlink with id set" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "8;id=foo;http://example.com";
for (input) |ch| p.next(ch);
@@ -3178,7 +3210,7 @@ test "OSC: hyperlink with id set" {
test "OSC: hyperlink with empty id" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "8;id=;http://example.com";
for (input) |ch| p.next(ch);
@@ -3192,7 +3224,7 @@ test "OSC: hyperlink with empty id" {
test "OSC: hyperlink with incomplete key" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "8;id;http://example.com";
for (input) |ch| p.next(ch);
@@ -3206,7 +3238,7 @@ test "OSC: hyperlink with incomplete key" {
test "OSC: hyperlink with empty key" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "8;=value;http://example.com";
for (input) |ch| p.next(ch);
@@ -3220,7 +3252,7 @@ test "OSC: hyperlink with empty key" {
test "OSC: hyperlink with empty key and id" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "8;=value:id=foo;http://example.com";
for (input) |ch| p.next(ch);
@@ -3234,7 +3266,7 @@ test "OSC: hyperlink with empty key and id" {
test "OSC: hyperlink with empty uri" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "8;id=foo;";
for (input) |ch| p.next(ch);
@@ -3246,7 +3278,7 @@ test "OSC: hyperlink with empty uri" {
test "OSC: hyperlink end" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
const input = "8;;";
for (input) |ch| p.next(ch);
@@ -3259,7 +3291,7 @@ test "OSC: kitty color protocol" {
const testing = std.testing;
const Kind = kitty.color.Kind;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "21;foreground=?;background=rgb:f0/f8/ff;cursor=aliceblue;cursor_text;visual_bell=;selection_foreground=#xxxyyzz;selection_background=?;selection_background=#aabbcc;2=?;3=rgbi:1.0/1.0/1.0";
@@ -3330,7 +3362,7 @@ test "OSC: kitty color protocol" {
test "OSC: kitty color protocol without allocator" {
const testing = std.testing;
var p: Parser = .{};
var p: Parser = .init();
defer p.deinit();
const input = "21;foreground=?";
@@ -3341,7 +3373,7 @@ test "OSC: kitty color protocol without allocator" {
test "OSC: kitty color protocol double reset" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "21;foreground=?;background=rgb:f0/f8/ff;cursor=aliceblue;cursor_text;visual_bell=;selection_foreground=#xxxyyzz;selection_background=?;selection_background=#aabbcc;2=?;3=rgbi:1.0/1.0/1.0";
@@ -3357,7 +3389,7 @@ test "OSC: kitty color protocol double reset" {
test "OSC: kitty color protocol reset after invalid" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "21;foreground=?;background=rgb:f0/f8/ff;cursor=aliceblue;cursor_text;visual_bell=;selection_foreground=#xxxyyzz;selection_background=?;selection_background=#aabbcc;2=?;3=rgbi:1.0/1.0/1.0";
@@ -3378,7 +3410,7 @@ test "OSC: kitty color protocol reset after invalid" {
test "OSC: kitty color protocol no key" {
const testing = std.testing;
var p: Parser = .{ .alloc = testing.allocator };
var p: Parser = .initAlloc(testing.allocator);
defer p.deinit();
const input = "21;";

View File

@@ -47,8 +47,16 @@ pub fn Stream(comptime Handler: type) type {
};
handler: Handler,
parser: Parser = .{},
utf8decoder: UTF8Decoder = .{},
parser: Parser,
utf8decoder: UTF8Decoder,
pub fn init(h: Handler) Self {
return .{
.handler = h,
.parser = .init(),
.utf8decoder = .{},
};
}
pub fn deinit(self: *Self) void {
self.parser.deinit();
@@ -1842,7 +1850,7 @@ test "stream: print" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.next('x');
try testing.expectEqual(@as(u21, 'x'), s.handler.c.?);
}
@@ -1856,7 +1864,7 @@ test "simd: print invalid utf-8" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice(&.{0xFF});
try testing.expectEqual(@as(u21, 0xFFFD), s.handler.c.?);
}
@@ -1870,7 +1878,7 @@ test "simd: complete incomplete utf-8" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice(&.{0xE0}); // 3 byte
try testing.expect(s.handler.c == null);
try s.nextSlice(&.{0xA0}); // still incomplete
@@ -1888,7 +1896,7 @@ test "stream: cursor right (CUF)" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1B[C");
try testing.expectEqual(@as(u16, 1), s.handler.amount);
@@ -1913,7 +1921,7 @@ test "stream: dec set mode (SM) and reset mode (RM)" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1B[?6h");
try testing.expectEqual(@as(modes.Mode, .origin), s.handler.mode);
@@ -1935,7 +1943,7 @@ test "stream: ansi set mode (SM) and reset mode (RM)" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1B[4h");
try testing.expectEqual(@as(modes.Mode, .insert), s.handler.mode.?);
@@ -1957,7 +1965,7 @@ test "stream: ansi set mode (SM) and reset mode (RM) with unknown value" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1B[6h");
try testing.expect(s.handler.mode == null);
@@ -1977,7 +1985,7 @@ test "stream: restore mode" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
for ("\x1B[?42r") |c| try s.next(c);
try testing.expect(!s.handler.called);
}
@@ -1992,7 +2000,7 @@ test "stream: pop kitty keyboard with no params defaults to 1" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
for ("\x1B[<u") |c| try s.next(c);
try testing.expectEqual(@as(u16, 1), s.handler.n);
}
@@ -2007,7 +2015,7 @@ test "stream: DECSCA" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
{
for ("\x1B[\"q") |c| try s.next(c);
try testing.expectEqual(ansi.ProtectedMode.off, s.handler.v.?);
@@ -2042,7 +2050,7 @@ test "stream: DECED, DECSED" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
{
for ("\x1B[?J") |c| try s.next(c);
try testing.expectEqual(csi.EraseDisplay.below, s.handler.mode.?);
@@ -2118,7 +2126,7 @@ test "stream: DECEL, DECSEL" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
{
for ("\x1B[?K") |c| try s.next(c);
try testing.expectEqual(csi.EraseLine.right, s.handler.mode.?);
@@ -2177,7 +2185,7 @@ test "stream: DECSCUSR" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1B[ q");
try testing.expect(s.handler.style.? == .default);
@@ -2198,7 +2206,7 @@ test "stream: DECSCUSR without space" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1B[q");
try testing.expect(s.handler.style == null);
@@ -2215,7 +2223,7 @@ test "stream: XTSHIFTESCAPE" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1B[>2s");
try testing.expect(s.handler.escape == null);
@@ -2245,13 +2253,13 @@ test "stream: change window title with invalid utf-8" {
};
{
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b]2;abc\x1b\\");
try testing.expect(s.handler.seen);
}
{
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b]2;abc\xc0\x1b\\");
try testing.expect(!s.handler.seen);
}
@@ -2268,7 +2276,7 @@ test "stream: insert characters" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
for ("\x1B[42@") |c| try s.next(c);
try testing.expect(s.handler.called);
@@ -2294,7 +2302,7 @@ test "stream: SCOSC" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
for ("\x1B[s") |c| try s.next(c);
try testing.expect(s.handler.called);
}
@@ -2309,7 +2317,7 @@ test "stream: SCORC" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
for ("\x1B[u") |c| try s.next(c);
try testing.expect(s.handler.called);
}
@@ -2323,7 +2331,7 @@ test "stream: too many csi params" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1B[1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1C");
}
@@ -2335,7 +2343,7 @@ test "stream: csi param too long" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1B[1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111C");
}
@@ -2348,7 +2356,7 @@ test "stream: send report with CSI t" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[14t");
try testing.expectEqual(csi.SizeReportStyle.csi_14_t, s.handler.style);
@@ -2372,7 +2380,7 @@ test "stream: invalid CSI t" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[19t");
try testing.expectEqual(null, s.handler.style);
@@ -2387,7 +2395,7 @@ test "stream: CSI t push title" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[22;0t");
try testing.expectEqual(csi.TitlePushPop{
@@ -2405,7 +2413,7 @@ test "stream: CSI t push title with explicit window" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[22;2t");
try testing.expectEqual(csi.TitlePushPop{
@@ -2423,7 +2431,7 @@ test "stream: CSI t push title with explicit icon" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[22;1t");
try testing.expectEqual(null, s.handler.op);
@@ -2438,7 +2446,7 @@ test "stream: CSI t push title with index" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[22;0;5t");
try testing.expectEqual(csi.TitlePushPop{
@@ -2456,7 +2464,7 @@ test "stream: CSI t pop title" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[23;0t");
try testing.expectEqual(csi.TitlePushPop{
@@ -2474,7 +2482,7 @@ test "stream: CSI t pop title with explicit window" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[23;2t");
try testing.expectEqual(csi.TitlePushPop{
@@ -2492,7 +2500,7 @@ test "stream: CSI t pop title with explicit icon" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[23;1t");
try testing.expectEqual(null, s.handler.op);
@@ -2507,7 +2515,7 @@ test "stream: CSI t pop title with index" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[23;0;5t");
try testing.expectEqual(csi.TitlePushPop{
@@ -2525,7 +2533,7 @@ test "stream CSI W clear tab stops" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[2W");
try testing.expectEqual(csi.TabClear.current, s.handler.op.?);
@@ -2543,7 +2551,7 @@ test "stream CSI W tab set" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[W");
try testing.expect(s.handler.called);
@@ -2570,7 +2578,7 @@ test "stream CSI ? W reset tab stops" {
}
};
var s: Stream(H) = .{ .handler = .{} };
var s: Stream(H) = .init(.{});
try s.nextSlice("\x1b[?2W");
try testing.expect(!s.handler.reset);

View File

@@ -313,15 +313,12 @@ pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
.size = opts.size,
.backend = backend,
.mailbox = opts.mailbox,
.terminal_stream = .{
.handler = handler,
.parser = .{
.osc_parser = .{
// Populate the OSC parser allocator (optional) because
// we want to support large OSC payloads such as OSC 52.
.alloc = alloc,
},
},
.terminal_stream = stream: {
var s: terminalpkg.Stream(StreamHandler) = .init(handler);
// Populate the OSC parser allocator (optional) because
// we want to support large OSC payloads such as OSC 52.
s.parser.osc_parser.alloc = alloc;
break :stream s;
},
.thread_enter_state = thread_enter_state,
};