diff --git a/src/terminal/osc/parsers/semantic_prompt.zig b/src/terminal/osc/parsers/semantic_prompt.zig index 510fe3447..ac7298267 100644 --- a/src/terminal/osc/parsers/semantic_prompt.zig +++ b/src/terminal/osc/parsers/semantic_prompt.zig @@ -186,6 +186,15 @@ const SemanticPromptKVIterator = struct { break :kv kv; }; + // If we have an empty item, we return an empty key and value. + // + // This allows for trailing semicolons, but also lets us parse + // (or rather, ignore) empty fields; for example `a=b;;e=f`. + if (kv.len < 1) return .{ + .key = kv, + .value = kv, + }; + const key = key: { const index = std.mem.indexOfScalar(u8, kv, '=') orelse break :key kv; kv[index] = 0; @@ -348,6 +357,18 @@ test "OSC 133: prompt_start with special_key empty" { try testing.expect(cmd.prompt_start.special_key == false); } +test "OSC 133: prompt_start with trailing ;" { + const testing = std.testing; + + var p: Parser = .init(null); + + const input = "133;A;"; + for (input) |ch| p.next(ch); + + const cmd = p.end(null).?.*; + try testing.expect(cmd == .prompt_start); +} + test "OSC 133: prompt_start with click_events true" { const testing = std.testing; diff --git a/src/termio/stream_handler.zig b/src/termio/stream_handler.zig index c647e3ba2..082a0fa10 100644 --- a/src/termio/stream_handler.zig +++ b/src/termio/stream_handler.zig @@ -398,11 +398,16 @@ pub const StreamHandler = struct { break :tmux; }, - .exit => if (self.tmux_viewer) |viewer| { - // Free our viewer state - viewer.deinit(); - self.alloc.destroy(viewer); - self.tmux_viewer = null; + .exit => { + // Free our viewer state if we have one + if (self.tmux_viewer) |viewer| { + viewer.deinit(); + self.alloc.destroy(viewer); + self.tmux_viewer = null; + } + + // And always break since we assert below + // that we're not handling an exit command. break :tmux; },