diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c00816b38..44e1e30fc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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 diff --git a/src/benchmark/TerminalParser.zig b/src/benchmark/TerminalParser.zig index 9107d4555..002af4831 100644 --- a/src/benchmark/TerminalParser.zig +++ b/src/benchmark/TerminalParser.zig @@ -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) { diff --git a/src/benchmark/TerminalStream.zig b/src/benchmark/TerminalStream.zig index 5d235c4ee..28a95226c 100644 --- a/src/benchmark/TerminalStream.zig +++ b/src/benchmark/TerminalStream.zig @@ -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; diff --git a/src/inspector/Inspector.zig b/src/inspector/Inspector.zig index d3e7fcaaa..27abb8657 100644 --- a/src/inspector/Inspector.zig +++ b/src/inspector/Inspector.zig @@ -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; }, }; } diff --git a/src/synthetic/Osc.zig b/src/synthetic/Osc.zig index e0a6b42a0..8d5d7d3a2 100644 --- a/src/synthetic/Osc.zig +++ b/src/synthetic/Osc.zig @@ -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); } diff --git a/src/terminal/Parser.zig b/src/terminal/Parser.zig index ec3f322f6..ed099ee47 100644 --- a/src/terminal/Parser.zig +++ b/src/terminal/Parser.zig @@ -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 { diff --git a/src/terminal/osc.zig b/src/terminal/osc.zig index 7619c82c1..00ede49ce 100644 --- a/src/terminal/osc.zig +++ b/src/terminal/osc.zig @@ -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;"; diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index ec7296490..5af446ebb 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -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[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); diff --git a/src/termio/Termio.zig b/src/termio/Termio.zig index 4b5b93641..e41fe33a9 100644 --- a/src/termio/Termio.zig +++ b/src/termio/Termio.zig @@ -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, };