terminal: ConEmu OSC9 parsing is more robust and correct (#4727)

Related to #4485

This commit matches ConEmu's parsing logic[^1] more faithfully. For any
substate that requires a progress, ConEmu parses so long as there is a
number and then just ignores the rest.

For substates that don't require a progress, ConEmu literally ignores
everything after the state.

Tests cover both.

[^1]:
740b09c363/src/ConEmuCD/ConAnsiImpl.cpp (L2264)
This commit is contained in:
Mitchell Hashimoto
2025-01-06 15:56:32 -08:00
committed by GitHub

View File

@ -274,6 +274,7 @@ pub const Parser = struct {
pub const State = enum {
empty,
invalid,
swallow,
// Command prefixes. We could just accumulate and compare (mem.eql)
// but the state space is small enough that we just build it up this way.
@ -451,6 +452,8 @@ pub const Parser = struct {
else => self.state = .invalid,
},
.swallow => {},
.@"0" => switch (c) {
';' => {
self.command = .{ .change_window_title = undefined };
@ -871,7 +874,7 @@ pub const Parser = struct {
.conemu_progress_state => switch (c) {
'0' => {
self.command.progress.state = .remove;
self.state = .conemu_progress_prevalue;
self.state = .swallow;
self.complete = true;
},
'1' => {
@ -887,7 +890,7 @@ pub const Parser = struct {
'3' => {
self.command.progress.state = .indeterminate;
self.complete = true;
self.state = .conemu_progress_prevalue;
self.state = .swallow;
},
'4' => {
self.command.progress.state = .pause;
@ -934,7 +937,10 @@ pub const Parser = struct {
}
},
else => self.showDesktopNotification(),
else => {
self.state = .swallow;
self.complete = true;
},
},
.query_fg_color => switch (c) {
@ -1965,18 +1971,18 @@ test "OSC: OSC9 progress set double digit" {
try testing.expect(cmd.progress.progress == 94);
}
test "OSC: OSC9 progress set extra semicolon triggers desktop notification" {
test "OSC: OSC9 progress set extra semicolon ignored" {
const testing = std.testing;
var p: Parser = .{};
const input = "9;4;1;100;";
const input = "9;4;1;100";
for (input) |ch| p.next(ch);
const cmd = p.end('\x1b').?;
try testing.expect(cmd == .show_desktop_notification);
try testing.expectEqualStrings(cmd.show_desktop_notification.title, "");
try testing.expectEqualStrings(cmd.show_desktop_notification.body, "4;1;100;");
try testing.expect(cmd == .progress);
try testing.expect(cmd.progress.state == .set);
try testing.expect(cmd.progress.progress == 100);
}
test "OSC: OSC9 progress remove with no progress" {
@ -1993,6 +1999,20 @@ test "OSC: OSC9 progress remove with no progress" {
try testing.expect(cmd.progress.progress == null);
}
test "OSC: OSC9 progress remove with double semicolon" {
const testing = std.testing;
var p: Parser = .{};
const input = "9;4;0;;";
for (input) |ch| p.next(ch);
const cmd = p.end('\x1b').?;
try testing.expect(cmd == .progress);
try testing.expect(cmd.progress.state == .remove);
try testing.expect(cmd.progress.progress == null);
}
test "OSC: OSC9 progress remove ignores progress" {
const testing = std.testing;
@ -2016,9 +2036,8 @@ test "OSC: OSC9 progress remove extra semicolon" {
for (input) |ch| p.next(ch);
const cmd = p.end('\x1b').?;
try testing.expect(cmd == .show_desktop_notification);
try testing.expectEqualStrings(cmd.show_desktop_notification.title, "");
try testing.expectEqualStrings(cmd.show_desktop_notification.body, "4;0;100;");
try testing.expect(cmd == .progress);
try testing.expect(cmd.progress.state == .remove);
}
test "OSC: OSC9 progress error" {