mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-18 21:40:29 +00:00
terminal: remove old semantic prompt handling
This commit is contained in:
@@ -44,75 +44,6 @@ pub const Command = union(Key) {
|
||||
/// Semantic prompt command: https://gitlab.freedesktop.org/Per_Bothner/specifications/blob/master/proposals/semantic-prompts.md
|
||||
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
|
||||
/// prompt string (as if followed by OSC 133;P;k=i\007). Note: I've noticed
|
||||
/// not all shells will send the prompt end code.
|
||||
prompt_start: struct {
|
||||
/// "aid" is an optional "application identifier" that helps disambiguate
|
||||
/// nested shell sessions. It can be anything but is usually a process ID.
|
||||
aid: ?[:0]const u8 = null,
|
||||
/// "kind" tells us which kind of semantic prompt sequence this is:
|
||||
/// - primary: normal, left-aligned first-line prompt (initial, default)
|
||||
/// - continuation: an editable continuation line
|
||||
/// - secondary: a non-editable continuation line
|
||||
/// - right: a right-aligned prompt that may need adjustment during reflow
|
||||
kind: enum { primary, continuation, secondary, right } = .primary,
|
||||
/// If true, the shell will not redraw the prompt on resize so don't erase it.
|
||||
/// See: https://sw.kovidgoyal.net/kitty/shell-integration/#notes-for-shell-developers
|
||||
redraw: bool = true,
|
||||
/// Use a special key instead of arrow keys to move the cursor on
|
||||
/// mouse click. Useful if arrow keys have side-effets like triggering
|
||||
/// auto-complete. The shell integration script should bind the special
|
||||
/// key as needed.
|
||||
/// See: https://sw.kovidgoyal.net/kitty/shell-integration/#notes-for-shell-developers
|
||||
special_key: bool = false,
|
||||
/// If true, the shell is capable of handling mouse click events.
|
||||
/// Ghostty will then send a click event to the shell when the user
|
||||
/// clicks somewhere in the prompt. The shell can then move the cursor
|
||||
/// to that position or perform some other appropriate action. If false,
|
||||
/// Ghostty may generate a number of fake key events to move the cursor
|
||||
/// which is not very robust.
|
||||
/// See: https://sw.kovidgoyal.net/kitty/shell-integration/#notes-for-shell-developers
|
||||
click_events: bool = false,
|
||||
},
|
||||
|
||||
/// End of prompt and start of user input, terminated by a OSC "133;C"
|
||||
/// or another prompt (OSC "133;P").
|
||||
prompt_end: void,
|
||||
|
||||
/// The OSC "133;C" command can be used to explicitly end
|
||||
/// the input area and begin the output area. However, some applications
|
||||
/// don't provide a convenient way to emit that command.
|
||||
/// That is why we also specify an implicit way to end the input area
|
||||
/// at the end of the line. In the case of multiple input lines: If the
|
||||
/// cursor is on a fresh (empty) line and we see either OSC "133;P" or
|
||||
/// OSC "133;I" then this is the start of a continuation input line.
|
||||
/// If we see anything else, it is the start of the output area (or end
|
||||
/// of command).
|
||||
end_of_input: struct {
|
||||
/// The command line that the user entered.
|
||||
/// See: https://sw.kovidgoyal.net/kitty/shell-integration/#notes-for-shell-developers
|
||||
cmdline: ?[:0]const u8 = null,
|
||||
},
|
||||
|
||||
/// End of current command.
|
||||
///
|
||||
/// The exit-code need not be specified if there are no options,
|
||||
/// or if the command was cancelled (no OSC "133;C"), such as by typing
|
||||
/// an interrupt/cancel character (typically ctrl-C) during line-editing.
|
||||
/// Otherwise, it must be an integer code, where 0 means the command
|
||||
/// succeeded, and other values indicate failure. In additing to the
|
||||
/// exit-code there may be an err= option, which non-legacy terminals
|
||||
/// should give precedence to. The err=_value_ option is more general:
|
||||
/// an empty string is success, and any non-empty value (which need not
|
||||
/// be an integer) is an error code. So to indicate success both ways you
|
||||
/// could send OSC "133;D;0;err=\007", though `OSC "133;D;0\007" is shorter.
|
||||
end_of_command: struct {
|
||||
exit_code: ?u8 = null,
|
||||
// TODO: err option
|
||||
},
|
||||
|
||||
/// Set or get clipboard contents. If data is null, then the current
|
||||
/// clipboard contents are sent to the pty. If data is set, this
|
||||
/// contents is set on the clipboard.
|
||||
@@ -221,7 +152,7 @@ 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 SemanticPrompt = parsers.semantic_prompt.Command;
|
||||
|
||||
pub const Key = LibEnum(
|
||||
if (build_options.c_abi) .c else .zig,
|
||||
@@ -231,10 +162,6 @@ pub const Command = union(Key) {
|
||||
"change_window_title",
|
||||
"change_window_icon",
|
||||
"semantic_prompt",
|
||||
"prompt_start",
|
||||
"prompt_end",
|
||||
"end_of_input",
|
||||
"end_of_command",
|
||||
"clipboard_contents",
|
||||
"report_pwd",
|
||||
"mouse_shape",
|
||||
@@ -466,14 +393,10 @@ pub const Parser = struct {
|
||||
.conemu_sleep,
|
||||
.conemu_wait_input,
|
||||
.conemu_xterm_emulation,
|
||||
.end_of_command,
|
||||
.end_of_input,
|
||||
.hyperlink_end,
|
||||
.hyperlink_start,
|
||||
.invalid,
|
||||
.mouse_shape,
|
||||
.prompt_end,
|
||||
.prompt_start,
|
||||
.report_pwd,
|
||||
.semantic_prompt,
|
||||
.show_desktop_notification,
|
||||
@@ -758,7 +681,7 @@ pub const Parser = struct {
|
||||
|
||||
.@"77" => null,
|
||||
|
||||
.@"133" => parsers.semantic_prompt2.parse(self, terminator_ch),
|
||||
.@"133" => parsers.semantic_prompt.parse(self, terminator_ch),
|
||||
|
||||
.@"777" => parsers.rxvt_extension.parse(self, terminator_ch),
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@ pub const mouse_shape = @import("parsers/mouse_shape.zig");
|
||||
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");
|
||||
pub const semantic_prompt = @import("parsers/semantic_prompt2.zig");
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
|
||||
@@ -98,9 +98,7 @@ pub fn parse(parser: *Parser, _: ?u8) ?*Command {
|
||||
},
|
||||
// OSC 9;12 mark prompt start
|
||||
'2' => {
|
||||
parser.command = .{
|
||||
.prompt_start = .{},
|
||||
};
|
||||
parser.command = .{ .semantic_prompt = .init(.fresh_line_new_prompt) };
|
||||
return &parser.command;
|
||||
},
|
||||
else => break :conemu,
|
||||
@@ -1125,7 +1123,7 @@ test "OSC: 9;12: ConEmu mark prompt start 1" {
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd == .semantic_prompt);
|
||||
}
|
||||
|
||||
test "OSC: 9;12: ConEmu mark prompt start 2" {
|
||||
@@ -1138,5 +1136,5 @@ test "OSC: 9;12: ConEmu mark prompt start 2" {
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end('\x1b').?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd == .semantic_prompt);
|
||||
}
|
||||
|
||||
@@ -1,770 +0,0 @@
|
||||
//! 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;
|
||||
|
||||
const log = std.log.scoped(.osc_semantic_prompt);
|
||||
|
||||
/// Parse OSC 133, semantic prompts
|
||||
pub fn parse(parser: *Parser, _: ?u8) ?*Command {
|
||||
const writer = parser.writer orelse {
|
||||
parser.state = .invalid;
|
||||
return null;
|
||||
};
|
||||
const data = writer.buffered();
|
||||
if (data.len == 0) {
|
||||
parser.state = .invalid;
|
||||
return null;
|
||||
}
|
||||
switch (data[0]) {
|
||||
'A' => prompt_start: {
|
||||
parser.command = .{
|
||||
.prompt_start = .{},
|
||||
};
|
||||
if (data.len == 1) break :prompt_start;
|
||||
if (data[1] != ';') {
|
||||
parser.state = .invalid;
|
||||
return null;
|
||||
}
|
||||
var it = SemanticPromptKVIterator.init(writer) catch {
|
||||
parser.state = .invalid;
|
||||
return null;
|
||||
};
|
||||
while (it.next()) |kv| {
|
||||
const key = kv.key orelse continue;
|
||||
if (std.mem.eql(u8, key, "aid")) {
|
||||
parser.command.prompt_start.aid = kv.value;
|
||||
} else if (std.mem.eql(u8, key, "redraw")) redraw: {
|
||||
// https://sw.kovidgoyal.net/kitty/shell-integration/#notes-for-shell-developers
|
||||
// Kitty supports a "redraw" option for prompt_start. I can't find
|
||||
// this documented anywhere but can see in the code that this is used
|
||||
// by shell environments to tell the terminal that the shell will NOT
|
||||
// redraw the prompt so we should attempt to resize it.
|
||||
parser.command.prompt_start.redraw = (value: {
|
||||
const value = kv.value orelse break :value null;
|
||||
if (value.len != 1) break :value null;
|
||||
switch (value[0]) {
|
||||
'0' => break :value false,
|
||||
'1' => break :value true,
|
||||
else => break :value null,
|
||||
}
|
||||
}) orelse {
|
||||
log.info("OSC 133 A: invalid redraw value: {?s}", .{kv.value});
|
||||
break :redraw;
|
||||
};
|
||||
} else if (std.mem.eql(u8, key, "special_key")) redraw: {
|
||||
// https://sw.kovidgoyal.net/kitty/shell-integration/#notes-for-shell-developers
|
||||
parser.command.prompt_start.special_key = (value: {
|
||||
const value = kv.value orelse break :value null;
|
||||
if (value.len != 1) break :value null;
|
||||
switch (value[0]) {
|
||||
'0' => break :value false,
|
||||
'1' => break :value true,
|
||||
else => break :value null,
|
||||
}
|
||||
}) orelse {
|
||||
log.info("OSC 133 A invalid special_key value: {?s}", .{kv.value});
|
||||
break :redraw;
|
||||
};
|
||||
} else if (std.mem.eql(u8, key, "click_events")) redraw: {
|
||||
// https://sw.kovidgoyal.net/kitty/shell-integration/#notes-for-shell-developers
|
||||
parser.command.prompt_start.click_events = (value: {
|
||||
const value = kv.value orelse break :value null;
|
||||
if (value.len != 1) break :value null;
|
||||
switch (value[0]) {
|
||||
'0' => break :value false,
|
||||
'1' => break :value true,
|
||||
else => break :value null,
|
||||
}
|
||||
}) orelse {
|
||||
log.info("OSC 133 A invalid click_events value: {?s}", .{kv.value});
|
||||
break :redraw;
|
||||
};
|
||||
} else if (std.mem.eql(u8, key, "k")) k: {
|
||||
// The "k" marks the kind of prompt, or "primary" if we don't know.
|
||||
// This can be used to distinguish between the first (initial) prompt,
|
||||
// a continuation, etc.
|
||||
const value = kv.value orelse break :k;
|
||||
if (value.len != 1) break :k;
|
||||
parser.command.prompt_start.kind = switch (value[0]) {
|
||||
'c' => .continuation,
|
||||
's' => .secondary,
|
||||
'r' => .right,
|
||||
'i' => .primary,
|
||||
else => .primary,
|
||||
};
|
||||
} else log.info("OSC 133 A: unknown semantic prompt option: {?s}", .{kv.key});
|
||||
}
|
||||
},
|
||||
'B' => prompt_end: {
|
||||
parser.command = .prompt_end;
|
||||
if (data.len == 1) break :prompt_end;
|
||||
if (data[1] != ';') {
|
||||
parser.state = .invalid;
|
||||
return null;
|
||||
}
|
||||
var it = SemanticPromptKVIterator.init(writer) catch {
|
||||
parser.state = .invalid;
|
||||
return null;
|
||||
};
|
||||
while (it.next()) |kv| {
|
||||
log.info("OSC 133 B: unknown semantic prompt option: {?s}", .{kv.key});
|
||||
}
|
||||
},
|
||||
'C' => end_of_input: {
|
||||
parser.command = .{
|
||||
.end_of_input = .{},
|
||||
};
|
||||
if (data.len == 1) break :end_of_input;
|
||||
if (data[1] != ';') {
|
||||
parser.state = .invalid;
|
||||
return null;
|
||||
}
|
||||
var it = SemanticPromptKVIterator.init(writer) catch {
|
||||
parser.state = .invalid;
|
||||
return null;
|
||||
};
|
||||
while (it.next()) |kv| {
|
||||
const key = kv.key orelse continue;
|
||||
if (std.mem.eql(u8, key, "cmdline")) {
|
||||
parser.command.end_of_input.cmdline = if (kv.value) |value| string_encoding.printfQDecode(value) catch null else null;
|
||||
} else if (std.mem.eql(u8, key, "cmdline_url")) {
|
||||
parser.command.end_of_input.cmdline = if (kv.value) |value| string_encoding.urlPercentDecode(value) catch null else null;
|
||||
} else {
|
||||
log.info("OSC 133 C: unknown semantic prompt option: {s}", .{key});
|
||||
}
|
||||
}
|
||||
},
|
||||
'D' => {
|
||||
const exit_code: ?u8 = exit_code: {
|
||||
if (data.len == 1) break :exit_code null;
|
||||
if (data[1] != ';') {
|
||||
parser.state = .invalid;
|
||||
return null;
|
||||
}
|
||||
break :exit_code std.fmt.parseUnsigned(u8, data[2..], 10) catch null;
|
||||
};
|
||||
parser.command = .{
|
||||
.end_of_command = .{
|
||||
.exit_code = exit_code,
|
||||
},
|
||||
};
|
||||
},
|
||||
else => {
|
||||
parser.state = .invalid;
|
||||
return null;
|
||||
},
|
||||
}
|
||||
return &parser.command;
|
||||
}
|
||||
|
||||
const SemanticPromptKVIterator = struct {
|
||||
index: usize,
|
||||
string: []u8,
|
||||
|
||||
pub const SemanticPromptKV = struct {
|
||||
key: ?[:0]u8,
|
||||
value: ?[:0]u8,
|
||||
};
|
||||
|
||||
pub fn init(writer: *std.Io.Writer) std.Io.Writer.Error!SemanticPromptKVIterator {
|
||||
// add a semicolon to make it easier to find and sentinel terminate the values
|
||||
try writer.writeByte(';');
|
||||
return .{
|
||||
.index = 0,
|
||||
.string = writer.buffered()[2..],
|
||||
};
|
||||
}
|
||||
|
||||
pub fn next(self: *SemanticPromptKVIterator) ?SemanticPromptKV {
|
||||
if (self.index >= self.string.len) return null;
|
||||
|
||||
const kv = kv: {
|
||||
const index = std.mem.indexOfScalarPos(u8, self.string, self.index, ';') orelse {
|
||||
self.index = self.string.len;
|
||||
return null;
|
||||
};
|
||||
self.string[index] = 0;
|
||||
const kv = self.string[self.index..index :0];
|
||||
self.index = index + 1;
|
||||
break :kv kv;
|
||||
};
|
||||
|
||||
// If we have an empty item, we return a null 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 = null,
|
||||
.value = null,
|
||||
};
|
||||
|
||||
const key = key: {
|
||||
const index = std.mem.indexOfScalar(u8, kv, '=') orelse {
|
||||
// If there is no '=' return entire `kv` string as the key and
|
||||
// a null value.
|
||||
return .{
|
||||
.key = kv,
|
||||
.value = null,
|
||||
};
|
||||
};
|
||||
kv[index] = 0;
|
||||
const key = kv[0..index :0];
|
||||
break :key key;
|
||||
};
|
||||
|
||||
const value = kv[key.len + 1 .. :0];
|
||||
|
||||
return .{
|
||||
.key = key,
|
||||
.value = value,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test "OSC 133: prompt_start" {
|
||||
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);
|
||||
try testing.expect(cmd.prompt_start.aid == null);
|
||||
try testing.expect(cmd.prompt_start.redraw);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with single option" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;aid=14";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expectEqualStrings("14", cmd.prompt_start.aid.?);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with '=' in aid" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;aid=a=b;redraw=0";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expectEqualStrings("a=b", cmd.prompt_start.aid.?);
|
||||
try testing.expect(!cmd.prompt_start.redraw);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with redraw disabled" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;redraw=0";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(!cmd.prompt_start.redraw);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with redraw invalid value" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;redraw=42";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd.prompt_start.redraw);
|
||||
try testing.expect(cmd.prompt_start.kind == .primary);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with continuation" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;k=c";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd.prompt_start.kind == .continuation);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with secondary" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;k=s";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd.prompt_start.kind == .secondary);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with special_key" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;special_key=1";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd.prompt_start.special_key == true);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with special_key invalid" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;special_key=bobr";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd.prompt_start.special_key == false);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with special_key 0" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;special_key=0";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd.prompt_start.special_key == false);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with special_key empty" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;special_key=";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
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;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;click_events=1";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd.prompt_start.click_events == true);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with click_events false" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;click_events=0";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd.prompt_start.click_events == false);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with click_events empty" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;click_events=";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd.prompt_start.click_events == false);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with click_events bare key" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;click_events";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd.prompt_start.click_events == false);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_start with invalid bare key" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;A;barekey";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_start);
|
||||
try testing.expect(cmd.prompt_start.aid == null);
|
||||
try testing.expectEqual(.primary, cmd.prompt_start.kind);
|
||||
try testing.expect(cmd.prompt_start.redraw == true);
|
||||
try testing.expect(cmd.prompt_start.special_key == false);
|
||||
try testing.expect(cmd.prompt_start.click_events == false);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_command no exit code" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;D";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_command);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_command with exit code" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;D;25";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_command);
|
||||
try testing.expectEqual(@as(u8, 25), cmd.end_of_command.exit_code.?);
|
||||
}
|
||||
|
||||
test "OSC 133: prompt_end" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;B";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .prompt_end);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline 1" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline=echo bobr kurwa";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline != null);
|
||||
try testing.expectEqualStrings("echo bobr kurwa", cmd.end_of_input.cmdline.?);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline 2" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline=echo bobr\\ kurwa";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline != null);
|
||||
try testing.expectEqualStrings("echo bobr kurwa", cmd.end_of_input.cmdline.?);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline 3" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline=echo bobr\\nkurwa";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline != null);
|
||||
try testing.expectEqualStrings("echo bobr\nkurwa", cmd.end_of_input.cmdline.?);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline 4" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline=$'echo bobr kurwa'";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline != null);
|
||||
try testing.expectEqualStrings("echo bobr kurwa", cmd.end_of_input.cmdline.?);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline 5" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline='echo bobr kurwa'";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline != null);
|
||||
try testing.expectEqualStrings("echo bobr kurwa", cmd.end_of_input.cmdline.?);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline 6" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline='echo bobr kurwa";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline == null);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline 7" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline=$'echo bobr kurwa";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline == null);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline 8" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline=$'";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline == null);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline 9" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline=$'";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline == null);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline 10" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline=";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline != null);
|
||||
try testing.expectEqualStrings("", cmd.end_of_input.cmdline.?);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline_url 1" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline_url=echo bobr kurwa";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline != null);
|
||||
try testing.expectEqualStrings("echo bobr kurwa", cmd.end_of_input.cmdline.?);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline_url 2" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline_url=echo bobr%20kurwa";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline != null);
|
||||
try testing.expectEqualStrings("echo bobr kurwa", cmd.end_of_input.cmdline.?);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline_url 3" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline_url=echo bobr%3bkurwa";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline != null);
|
||||
try testing.expectEqualStrings("echo bobr;kurwa", cmd.end_of_input.cmdline.?);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline_url 4" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline_url=echo bobr%3kurwa";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline == null);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline_url 5" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline_url=echo bobr%kurwa";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline == null);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline_url 6" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline_url=echo bobr%kurwa";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline == null);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline_url 7" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline_url=echo bobr kurwa%20";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline != null);
|
||||
try testing.expectEqualStrings("echo bobr kurwa ", cmd.end_of_input.cmdline.?);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline_url 8" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline_url=echo bobr kurwa%2";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline == null);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with cmdline_url 9" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline_url=echo bobr kurwa%2";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline == null);
|
||||
}
|
||||
|
||||
test "OSC 133: end_of_input with bare key" {
|
||||
const testing = std.testing;
|
||||
|
||||
var p: Parser = .init(null);
|
||||
|
||||
const input = "133;C;cmdline_url";
|
||||
for (input) |ch| p.next(ch);
|
||||
|
||||
const cmd = p.end(null).?.*;
|
||||
try testing.expect(cmd == .end_of_input);
|
||||
try testing.expect(cmd.end_of_input.cmdline == null);
|
||||
}
|
||||
@@ -111,8 +111,6 @@ pub const Action = union(Key) {
|
||||
apc_start,
|
||||
apc_end,
|
||||
apc_put: u8,
|
||||
prompt_end,
|
||||
end_of_input,
|
||||
end_hyperlink,
|
||||
active_status_display: ansi.StatusDisplay,
|
||||
decaln,
|
||||
@@ -122,9 +120,6 @@ pub const Action = union(Key) {
|
||||
progress_report: osc.Command.ProgressReport,
|
||||
start_hyperlink: StartHyperlink,
|
||||
clipboard_contents: ClipboardContents,
|
||||
prompt_start: PromptStart,
|
||||
prompt_continuation: PromptContinuation,
|
||||
end_of_command: EndOfCommand,
|
||||
mouse_shape: MouseShape,
|
||||
configure_charset: ConfigureCharset,
|
||||
set_attribute: sgr.Attribute,
|
||||
@@ -213,8 +208,6 @@ pub const Action = union(Key) {
|
||||
"apc_start",
|
||||
"apc_end",
|
||||
"apc_put",
|
||||
"prompt_end",
|
||||
"end_of_input",
|
||||
"end_hyperlink",
|
||||
"active_status_display",
|
||||
"decaln",
|
||||
@@ -224,9 +217,6 @@ pub const Action = union(Key) {
|
||||
"progress_report",
|
||||
"start_hyperlink",
|
||||
"clipboard_contents",
|
||||
"prompt_start",
|
||||
"prompt_continuation",
|
||||
"end_of_command",
|
||||
"mouse_shape",
|
||||
"configure_charset",
|
||||
"set_attribute",
|
||||
@@ -393,47 +383,6 @@ pub const Action = union(Key) {
|
||||
}
|
||||
};
|
||||
|
||||
pub const PromptStart = struct {
|
||||
aid: ?[]const u8,
|
||||
redraw: bool,
|
||||
|
||||
pub const C = extern struct {
|
||||
aid: lib.String,
|
||||
redraw: bool,
|
||||
};
|
||||
|
||||
pub fn cval(self: PromptStart) PromptStart.C {
|
||||
return .{
|
||||
.aid = .init(self.aid orelse ""),
|
||||
.redraw = self.redraw,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const PromptContinuation = struct {
|
||||
aid: ?[]const u8,
|
||||
|
||||
pub const C = lib.String;
|
||||
|
||||
pub fn cval(self: PromptContinuation) PromptContinuation.C {
|
||||
return .init(self.aid orelse "");
|
||||
}
|
||||
};
|
||||
|
||||
pub const EndOfCommand = struct {
|
||||
exit_code: ?u8,
|
||||
|
||||
pub const C = extern struct {
|
||||
exit_code: i16,
|
||||
};
|
||||
|
||||
pub fn cval(self: EndOfCommand) EndOfCommand.C {
|
||||
return .{
|
||||
.exit_code = if (self.exit_code) |code| @intCast(code) else -1,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const ConfigureCharset = lib.Struct(lib_target, struct {
|
||||
slot: charsets.Slots,
|
||||
charset: charsets.Charset,
|
||||
@@ -1992,10 +1941,9 @@ pub fn Stream(comptime Handler: type) type {
|
||||
// 4. hyperlink_start
|
||||
// 5. report_pwd
|
||||
// 6. color_operation
|
||||
// 7. prompt_start
|
||||
// 8. prompt_end
|
||||
// 7. semantic_prompt
|
||||
//
|
||||
// Together, these 8 commands make up about 96% of all
|
||||
// Together, these 7 commands make up about 96% of all
|
||||
// OSC commands encountered in real world scenarios.
|
||||
//
|
||||
// Additionally, within the prongs, unlikely branch
|
||||
@@ -2008,6 +1956,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||
|
||||
switch (cmd) {
|
||||
.semantic_prompt => |sp| {
|
||||
@branchHint(.likely);
|
||||
try self.handler.vt(.semantic_prompt, sp);
|
||||
},
|
||||
|
||||
@@ -2034,30 +1983,6 @@ pub fn Stream(comptime Handler: type) type {
|
||||
});
|
||||
},
|
||||
|
||||
.prompt_start => |v| {
|
||||
@branchHint(.likely);
|
||||
switch (v.kind) {
|
||||
.primary, .right => try self.handler.vt(.prompt_start, .{
|
||||
.aid = v.aid,
|
||||
.redraw = v.redraw,
|
||||
}),
|
||||
.continuation, .secondary => try self.handler.vt(.prompt_continuation, .{
|
||||
.aid = v.aid,
|
||||
}),
|
||||
}
|
||||
},
|
||||
|
||||
.prompt_end => {
|
||||
@branchHint(.likely);
|
||||
try self.handler.vt(.prompt_end, {});
|
||||
},
|
||||
|
||||
.end_of_input => try self.handler.vt(.end_of_input, {}),
|
||||
|
||||
.end_of_command => |end| {
|
||||
try self.handler.vt(.end_of_command, .{ .exit_code = end.exit_code });
|
||||
},
|
||||
|
||||
.report_pwd => |v| {
|
||||
@branchHint(.likely);
|
||||
try self.handler.vt(.report_pwd, .{ .url = v.value });
|
||||
|
||||
@@ -153,14 +153,6 @@ pub const Handler = struct {
|
||||
.full_reset => self.terminal.fullReset(),
|
||||
.start_hyperlink => try self.terminal.screens.active.startHyperlink(value.uri, value.id),
|
||||
.end_hyperlink => self.terminal.screens.active.endHyperlink(),
|
||||
.prompt_start => {
|
||||
self.terminal.screens.active.cursor.page_row.semantic_prompt = .prompt;
|
||||
self.terminal.flags.shell_redraws_prompt = value.redraw;
|
||||
},
|
||||
.prompt_continuation => self.terminal.screens.active.cursor.page_row.semantic_prompt = .prompt_continuation,
|
||||
.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),
|
||||
|
||||
@@ -311,8 +311,6 @@ pub const StreamHandler = struct {
|
||||
},
|
||||
.kitty_color_report => try self.kittyColorReport(value),
|
||||
.color_operation => try self.colorOperation(value.op, &value.requests, value.terminator),
|
||||
.prompt_end => try self.promptEnd(),
|
||||
.end_of_input => try self.endOfInput(),
|
||||
.end_hyperlink => try self.endHyperlink(),
|
||||
.active_status_display => self.terminal.status_display = value,
|
||||
.decaln => try self.decaln(),
|
||||
@@ -322,9 +320,6 @@ pub const StreamHandler = struct {
|
||||
.progress_report => self.progressReport(value),
|
||||
.start_hyperlink => try self.startHyperlink(value.uri, value.id),
|
||||
.clipboard_contents => try self.clipboardContents(value.kind, value.data),
|
||||
.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),
|
||||
@@ -1071,30 +1066,6 @@ pub const StreamHandler = struct {
|
||||
});
|
||||
}
|
||||
|
||||
inline fn promptStart(self: *StreamHandler, aid: ?[]const u8, redraw: bool) void {
|
||||
_ = aid;
|
||||
self.terminal.markSemanticPrompt(.prompt);
|
||||
self.terminal.flags.shell_redraws_prompt = redraw;
|
||||
}
|
||||
|
||||
inline fn promptContinuation(self: *StreamHandler, aid: ?[]const u8) void {
|
||||
_ = aid;
|
||||
self.terminal.markSemanticPrompt(.prompt_continuation);
|
||||
}
|
||||
|
||||
pub inline fn promptEnd(self: *StreamHandler) !void {
|
||||
self.terminal.markSemanticPrompt(.input);
|
||||
}
|
||||
|
||||
pub inline fn endOfInput(self: *StreamHandler) !void {
|
||||
self.terminal.markSemanticPrompt(.command);
|
||||
self.surfaceMessageWriter(.start_command);
|
||||
}
|
||||
|
||||
inline fn endOfCommand(self: *StreamHandler, exit_code: ?u8) void {
|
||||
self.surfaceMessageWriter(.{ .stop_command = exit_code });
|
||||
}
|
||||
|
||||
fn semanticPrompt(
|
||||
self: *StreamHandler,
|
||||
cmd: Stream.Action.SemanticPrompt,
|
||||
|
||||
Reference in New Issue
Block a user