diff --git a/src/terminal/osc.zig b/src/terminal/osc.zig index 368da4afc..65a1b1121 100644 --- a/src/terminal/osc.zig +++ b/src/terminal/osc.zig @@ -41,6 +41,9 @@ pub const Command = union(Key) { /// in the log. change_window_icon: [:0]const u8, + /// Semantic prompt command: https://gitlab.freedesktop.org/Per_Bothner/specifications/blob/master/proposals/semantic-prompts.md + semantic_prompt: parsers.semantic_prompt2.Command, + /// First do a fresh-line. Then start a new command, and enter prompt mode: /// Subsequent text (until a OSC "133;B" or OSC "133;I" command) is a /// prompt string (as if followed by OSC 133;P;k=i\007). Note: I've noticed @@ -225,6 +228,7 @@ pub const Command = union(Key) { "invalid", "change_window_title", "change_window_icon", + "semantic_prompt", "prompt_start", "prompt_end", "end_of_input", @@ -469,6 +473,7 @@ pub const Parser = struct { .prompt_end, .prompt_start, .report_pwd, + .semantic_prompt, .show_desktop_notification, .kitty_text_sizing, => {}, @@ -751,7 +756,7 @@ pub const Parser = struct { .@"77" => null, - .@"133" => parsers.semantic_prompt.parse(self, terminator_ch), + .@"133" => parsers.semantic_prompt2.parse(self, terminator_ch), .@"777" => parsers.rxvt_extension.parse(self, terminator_ch), diff --git a/src/terminal/osc/parsers.zig b/src/terminal/osc/parsers.zig index f3028ec79..d005bd4c0 100644 --- a/src/terminal/osc/parsers.zig +++ b/src/terminal/osc/parsers.zig @@ -13,19 +13,8 @@ pub const osc9 = @import("parsers/osc9.zig"); pub const report_pwd = @import("parsers/report_pwd.zig"); pub const rxvt_extension = @import("parsers/rxvt_extension.zig"); pub const semantic_prompt = @import("parsers/semantic_prompt.zig"); +pub const semantic_prompt2 = @import("parsers/semantic_prompt2.zig"); test { - _ = change_window_icon; - _ = change_window_title; - _ = clipboard_operation; - _ = color; - _ = hyperlink; - _ = iterm2; - _ = kitty_color; - _ = kitty_text_sizing; - _ = mouse_shape; - _ = osc9; - _ = report_pwd; - _ = rxvt_extension; - _ = semantic_prompt; + std.testing.refAllDecls(@This()); } diff --git a/src/terminal/osc/parsers/semantic_prompt.zig b/src/terminal/osc/parsers/semantic_prompt.zig index 652fe34da..d7cfe7c35 100644 --- a/src/terminal/osc/parsers/semantic_prompt.zig +++ b/src/terminal/osc/parsers/semantic_prompt.zig @@ -1,7 +1,6 @@ +//! https://gitlab.freedesktop.org/Per_Bothner/specifications/blob/master/proposals/semantic-prompts.md const std = @import("std"); - const string_encoding = @import("../../../os/string_encoding.zig"); - const Parser = @import("../../osc.zig").Parser; const Command = @import("../../osc.zig").Command; diff --git a/src/terminal/osc/parsers/semantic_prompt2.zig b/src/terminal/osc/parsers/semantic_prompt2.zig new file mode 100644 index 000000000..954c101a1 --- /dev/null +++ b/src/terminal/osc/parsers/semantic_prompt2.zig @@ -0,0 +1,92 @@ +//! https://gitlab.freedesktop.org/Per_Bothner/specifications/blob/master/proposals/semantic-prompts.md +const std = @import("std"); +const Parser = @import("../../osc.zig").Parser; +const OSCCommand = @import("../../osc.zig").Command; + +const log = std.log.scoped(.osc_semantic_prompt); + +pub const Command = union(enum) { + fresh_line, + fresh_line_new_prompt: Options, +}; + +pub const Options = struct { + aid: ?[:0]const u8, + cl: ?Click, + // TODO: more + + pub const init: Options = .{ + .aid = null, + .click = null, + }; +}; + +pub const Click = enum { + line, + multiple, + conservative_vertical, + smart_vertical, +}; + +/// Parse OSC 133, semantic prompts +pub fn parse(parser: *Parser, _: ?u8) ?*OSCCommand { + const writer = parser.writer orelse { + parser.state = .invalid; + return null; + }; + const data = writer.buffered(); + if (data.len == 0) { + parser.state = .invalid; + return null; + } + + parser.command = command: { + parse: switch (data[0]) { + 'L' => { + if (data.len > 1) break :parse; + break :command .{ .semantic_prompt = .fresh_line }; + }, + + else => {}, + } + + // Any fallthroughs are invalid + parser.state = .invalid; + return null; + }; + + return &parser.command; +} + +test "OSC 133: fresh_line" { + const testing = std.testing; + + var p: Parser = .init(null); + + const input = "133;L"; + for (input) |ch| p.next(ch); + + const cmd = p.end(null).?.*; + try testing.expect(cmd == .semantic_prompt); + try testing.expect(cmd.semantic_prompt == .fresh_line); +} + +test "OSC 133: fresh_line extra contents" { + const testing = std.testing; + + // Random + { + var p: Parser = .init(null); + const input = "133;Lol"; + for (input) |ch| p.next(ch); + try testing.expect(p.end(null) == null); + } + + // Options + { + var p: Parser = .init(null); + const input = "133;L;aid=foo"; + for (input) |ch| p.next(ch); + try testing.expect(p.end(null) == null); + } +} diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index 4e1398d8d..d12c59ef3 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -2003,6 +2003,9 @@ pub fn Stream(comptime Handler: type) type { // ref: https://github.com/qwerasd205/asciinema-stats switch (cmd) { + // TODO + .semantic_prompt => {}, + .change_window_title => |title| { @branchHint(.likely); if (!std.unicode.utf8ValidateSlice(title)) {