diff --git a/src/terminal/osc.zig b/src/terminal/osc.zig index 65a1b1121..aec0b495d 100644 --- a/src/terminal/osc.zig +++ b/src/terminal/osc.zig @@ -42,7 +42,7 @@ pub const Command = union(Key) { 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, + semantic_prompt: SemanticPrompt, /// 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 @@ -221,6 +221,8 @@ pub const Command = union(Key) { /// Kitty text sizing protocol (OSC 66) kitty_text_sizing: parsers.kitty_text_sizing.OSC, + pub const SemanticPrompt = parsers.semantic_prompt2.Command; + pub const Key = LibEnum( if (build_options.c_abi) .c else .zig, // NOTE: Order matters, see LibEnum documentation. diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index d12c59ef3..5d4a37c43 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -130,6 +130,7 @@ pub const Action = union(Key) { set_attribute: sgr.Attribute, kitty_color_report: kitty.color.OSC, color_operation: ColorOperation, + semantic_prompt: SemanticPrompt, pub const Key = lib.Enum( lib_target, @@ -231,6 +232,7 @@ pub const Action = union(Key) { "set_attribute", "kitty_color_report", "color_operation", + "semantic_prompt", }, ); @@ -448,6 +450,8 @@ pub const Action = union(Key) { return {}; } }; + + pub const SemanticPrompt = osc.Command.SemanticPrompt; }; /// Returns a type that can process a stream of tty control characters. @@ -2003,8 +2007,9 @@ pub fn Stream(comptime Handler: type) type { // ref: https://github.com/qwerasd205/asciinema-stats switch (cmd) { - // TODO - .semantic_prompt => {}, + .semantic_prompt => |sp| { + try self.handler.vt(.semantic_prompt, sp); + }, .change_window_title => |title| { @branchHint(.likely); diff --git a/src/terminal/stream_readonly.zig b/src/terminal/stream_readonly.zig index 90fcead93..86879c0d5 100644 --- a/src/terminal/stream_readonly.zig +++ b/src/terminal/stream_readonly.zig @@ -161,6 +161,7 @@ pub const Handler = struct { .prompt_end => self.terminal.markSemanticPrompt(.input), .end_of_input => self.terminal.markSemanticPrompt(.command), .end_of_command => self.terminal.screens.active.cursor.page_row.semantic_prompt = .input, + .semantic_prompt => self.semanticPrompt(value), .mouse_shape => self.terminal.mouse_shape = value, .color_operation => try self.colorOperation(value.op, &value.requests), .kitty_color_report => try self.kittyColorOperation(value), @@ -216,6 +217,39 @@ pub const Handler = struct { } } + fn semanticPrompt( + self: *Handler, + cmd: Action.SemanticPrompt, + ) void { + switch (cmd.action) { + .fresh_line => { + if (self.terminal.screens.active.cursor.x != 0) { + self.terminal.carriageReturn(); + self.terminal.index() catch {}; + } + }, + .fresh_line_new_prompt => { + if (self.terminal.screens.active.cursor.x != 0) { + self.terminal.carriageReturn(); + self.terminal.index() catch {}; + } + self.terminal.screens.active.cursor.page_row.semantic_prompt = .prompt; + }, + .new_command => {}, + .prompt_start => { + const kind = cmd.options.prompt_kind orelse .initial; + switch (kind) { + .initial, .right => self.terminal.screens.active.cursor.page_row.semantic_prompt = .prompt, + .continuation, .secondary => self.terminal.screens.active.cursor.page_row.semantic_prompt = .prompt_continuation, + } + }, + .end_prompt_start_input => self.terminal.markSemanticPrompt(.input), + .end_prompt_start_input_terminate_eol => self.terminal.markSemanticPrompt(.input), + .end_input_start_output => self.terminal.markSemanticPrompt(.command), + .end_command => self.terminal.screens.active.cursor.page_row.semantic_prompt = .input, + } + } + fn setMode(self: *Handler, mode: modes.Mode, enabled: bool) !void { // Set the mode on the terminal self.terminal.modes.set(mode, enabled); diff --git a/src/termio/stream_handler.zig b/src/termio/stream_handler.zig index 2a2b338a4..29ffefbda 100644 --- a/src/termio/stream_handler.zig +++ b/src/termio/stream_handler.zig @@ -325,6 +325,7 @@ pub const StreamHandler = struct { .prompt_start => self.promptStart(value.aid, value.redraw), .prompt_continuation => self.promptContinuation(value.aid), .end_of_command => self.endOfCommand(value.exit_code), + .semantic_prompt => self.semanticPrompt(value), .mouse_shape => try self.setMouseShape(value), .configure_charset => self.configureCharset(value.slot, value.charset), .set_attribute => { @@ -1094,6 +1095,48 @@ pub const StreamHandler = struct { self.surfaceMessageWriter(.{ .stop_command = exit_code }); } + fn semanticPrompt( + self: *StreamHandler, + cmd: Stream.Action.SemanticPrompt, + ) void { + switch (cmd.action) { + .fresh_line => { + if (self.terminal.screens.active.cursor.x != 0) { + self.terminal.carriageReturn(); + self.terminal.index() catch {}; + } + }, + .fresh_line_new_prompt => { + if (self.terminal.screens.active.cursor.x != 0) { + self.terminal.carriageReturn(); + self.terminal.index() catch {}; + } + self.promptStart(cmd.options.aid, false); + }, + .new_command => {}, + .prompt_start => { + const kind = cmd.options.prompt_kind orelse .initial; + switch (kind) { + .initial, .right => self.promptStart(cmd.options.aid, false), + .continuation, .secondary => self.promptContinuation(cmd.options.aid), + } + }, + .end_prompt_start_input => self.terminal.markSemanticPrompt(.input), + .end_prompt_start_input_terminate_eol => self.terminal.markSemanticPrompt(.input), + .end_input_start_output => { + self.terminal.markSemanticPrompt(.command); + self.surfaceMessageWriter(.start_command); + }, + .end_command => { + const exit_code: ?u8 = if (cmd.options.exit_code) |code| + if (code >= 0 and code <= 255) @intCast(code) else null + else + null; + self.surfaceMessageWriter(.{ .stop_command = exit_code }); + }, + } + } + fn reportPwd(self: *StreamHandler, url: []const u8) !void { // Special handling for the empty URL. We treat the empty URL // as resetting the pwd as if we never saw a pwd. I can't find any