mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
terminal: ConEmu OSC9 parsing is more robust and correct
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:
@ -274,6 +274,7 @@ pub const Parser = struct {
|
|||||||
pub const State = enum {
|
pub const State = enum {
|
||||||
empty,
|
empty,
|
||||||
invalid,
|
invalid,
|
||||||
|
swallow,
|
||||||
|
|
||||||
// Command prefixes. We could just accumulate and compare (mem.eql)
|
// 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.
|
// 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,
|
else => self.state = .invalid,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.swallow => {},
|
||||||
|
|
||||||
.@"0" => switch (c) {
|
.@"0" => switch (c) {
|
||||||
';' => {
|
';' => {
|
||||||
self.command = .{ .change_window_title = undefined };
|
self.command = .{ .change_window_title = undefined };
|
||||||
@ -822,7 +825,7 @@ pub const Parser = struct {
|
|||||||
self.buf_start = self.buf_idx;
|
self.buf_start = self.buf_idx;
|
||||||
self.complete = true;
|
self.complete = true;
|
||||||
self.state = .conemu_sleep_value;
|
self.state = .conemu_sleep_value;
|
||||||
},
|
},
|
||||||
else => self.state = .invalid,
|
else => self.state = .invalid,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -871,7 +874,7 @@ pub const Parser = struct {
|
|||||||
.conemu_progress_state => switch (c) {
|
.conemu_progress_state => switch (c) {
|
||||||
'0' => {
|
'0' => {
|
||||||
self.command.progress.state = .remove;
|
self.command.progress.state = .remove;
|
||||||
self.state = .conemu_progress_prevalue;
|
self.state = .swallow;
|
||||||
self.complete = true;
|
self.complete = true;
|
||||||
},
|
},
|
||||||
'1' => {
|
'1' => {
|
||||||
@ -887,7 +890,7 @@ pub const Parser = struct {
|
|||||||
'3' => {
|
'3' => {
|
||||||
self.command.progress.state = .indeterminate;
|
self.command.progress.state = .indeterminate;
|
||||||
self.complete = true;
|
self.complete = true;
|
||||||
self.state = .conemu_progress_prevalue;
|
self.state = .swallow;
|
||||||
},
|
},
|
||||||
'4' => {
|
'4' => {
|
||||||
self.command.progress.state = .pause;
|
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) {
|
.query_fg_color => switch (c) {
|
||||||
@ -1965,18 +1971,18 @@ test "OSC: OSC9 progress set double digit" {
|
|||||||
try testing.expect(cmd.progress.progress == 94);
|
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;
|
const testing = std.testing;
|
||||||
|
|
||||||
var p: Parser = .{};
|
var p: Parser = .{};
|
||||||
|
|
||||||
const input = "9;4;1;100;";
|
const input = "9;4;1;100";
|
||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end('\x1b').?;
|
const cmd = p.end('\x1b').?;
|
||||||
try testing.expect(cmd == .show_desktop_notification);
|
try testing.expect(cmd == .progress);
|
||||||
try testing.expectEqualStrings(cmd.show_desktop_notification.title, "");
|
try testing.expect(cmd.progress.state == .set);
|
||||||
try testing.expectEqualStrings(cmd.show_desktop_notification.body, "4;1;100;");
|
try testing.expect(cmd.progress.progress == 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "OSC: OSC9 progress remove with no progress" {
|
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);
|
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" {
|
test "OSC: OSC9 progress remove ignores progress" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
@ -2016,9 +2036,8 @@ test "OSC: OSC9 progress remove extra semicolon" {
|
|||||||
for (input) |ch| p.next(ch);
|
for (input) |ch| p.next(ch);
|
||||||
|
|
||||||
const cmd = p.end('\x1b').?;
|
const cmd = p.end('\x1b').?;
|
||||||
try testing.expect(cmd == .show_desktop_notification);
|
try testing.expect(cmd == .progress);
|
||||||
try testing.expectEqualStrings(cmd.show_desktop_notification.title, "");
|
try testing.expect(cmd.progress.state == .remove);
|
||||||
try testing.expectEqualStrings(cmd.show_desktop_notification.body, "4;0;100;");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "OSC: OSC9 progress error" {
|
test "OSC: OSC9 progress error" {
|
||||||
|
Reference in New Issue
Block a user