core: parse cmdline and cmdline_url semantic prompt options

This commit is contained in:
Jeffrey C. Ollie
2026-02-26 17:52:09 -06:00
parent ca09c0ef2e
commit 3b4e2bbcbe
2 changed files with 426 additions and 101 deletions

View File

@@ -1,7 +1,9 @@
//! 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 string_encoding = @import("../../../os/string_encoding.zig");
const log = std.log.scoped(.osc_semantic_prompt);
@@ -40,6 +42,20 @@ pub const Command = struct {
) ?option.Type() {
return option.read(self.options_unvalidated);
}
/// Write the decoded command line (if any) to the writer. If an error
/// occurs garbage may have been written to the writer.
pub fn writeCommandLine(self: Command, writer: *std.Io.Writer) (std.Io.Writer.Error || error{DecodeError})!void {
if (self.readOption(.cmdline)) |command_line| {
try string_encoding.printfQDecode(writer, command_line);
return;
}
if (self.readOption(.cmdline_url)) |command_line| {
try string_encoding.urlPercentDecode(writer, command_line);
return;
}
return;
}
};
pub const Option = enum {
@@ -47,6 +63,8 @@ pub const Option = enum {
cl,
prompt_kind,
err,
cmdline,
cmdline_url,
// https://sw.kovidgoyal.net/kitty/shell-integration/#notes-for-shell-developers
// Kitty supports a "redraw" option for prompt_start. This is extended
@@ -83,6 +101,8 @@ pub const Option = enum {
.redraw => Redraw,
.special_key => bool,
.click_events => bool,
.cmdline => []const u8,
.cmdline_url => []const u8,
.exit_code => i32,
};
}
@@ -96,6 +116,8 @@ pub const Option = enum {
.redraw => "redraw",
.special_key => "special_key",
.click_events => "click_events",
.cmdline => "cmdline",
.cmdline_url => "cmdline_url",
// special case, handled before ever calling key
.exit_code => unreachable,
@@ -181,6 +203,8 @@ pub const Option = enum {
'1' => true,
else => null,
} else null,
.cmdline => value,
.cmdline_url => value,
// Handled above
.exit_code => unreachable,
};
@@ -389,6 +413,280 @@ test "OSC 133: end_input_start_output with options" {
try testing.expectEqualStrings("foo", cmd.semantic_prompt.readOption(.aid).?);
}
test "OSC 133: end_input_start_output with cmdline" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try cmd.semantic_prompt.writeCommandLine(&w.writer);
try testing.expectEqualStrings("echo bobr kurwa", w.written());
}
test "OSC 133: end_input_start_output with cmdline 3" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try cmd.semantic_prompt.writeCommandLine(&w.writer);
try testing.expectEqualStrings("echo bobr\nkurwa", w.written());
}
test "OSC 133: end_input_start_output with cmdline 4" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try cmd.semantic_prompt.writeCommandLine(&w.writer);
try testing.expectEqualStrings("echo bobr kurwa", w.written());
}
test "OSC 133: end_input_start_output with cmdline 5" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try cmd.semantic_prompt.writeCommandLine(&w.writer);
try testing.expectEqualStrings("echo bobr kurwa", w.written());
}
test "OSC 133: end_input_start_output with cmdline 6" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try testing.expectError(error.DecodeError, cmd.semantic_prompt.writeCommandLine(&w.writer));
}
test "OSC 133: end_input_start_output with cmdline 7" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try testing.expectError(error.DecodeError, cmd.semantic_prompt.writeCommandLine(&w.writer));
}
test "OSC 133: end_input_start_output with cmdline 8" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try testing.expectError(error.DecodeError, cmd.semantic_prompt.writeCommandLine(&w.writer));
}
test "OSC 133: end_input_start_output with cmdline 9" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try cmd.semantic_prompt.writeCommandLine(&w.writer);
try testing.expectEqualStrings("", w.written());
}
test "OSC 133: end_input_start_output with cmdline_url 1" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try cmd.semantic_prompt.writeCommandLine(&w.writer);
try testing.expectEqualStrings("echo bobr kurwa", w.written());
}
test "OSC 133: end_input_start_output with cmdline_url 2" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try cmd.semantic_prompt.writeCommandLine(&w.writer);
try testing.expectEqualStrings("echo bobr kurwa", w.written());
}
test "OSC 133: end_input_start_output with cmdline_url 3" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try cmd.semantic_prompt.writeCommandLine(&w.writer);
try testing.expectEqualStrings("echo bobr;kurwa", w.written());
}
test "OSC 133: end_input_start_output with cmdline_url 4" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try testing.expectError(error.DecodeError, cmd.semantic_prompt.writeCommandLine(&w.writer));
}
test "OSC 133: end_input_start_output with cmdline_url 5" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try testing.expectError(error.DecodeError, cmd.semantic_prompt.writeCommandLine(&w.writer));
}
test "OSC 133: end_input_start_output with cmdline_url 6" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try cmd.semantic_prompt.writeCommandLine(&w.writer);
try testing.expectEqualStrings("echo bobr kurwa ", w.written());
}
test "OSC 133: end_input_start_output with cmdline_url 7" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try testing.expectError(error.DecodeError, cmd.semantic_prompt.writeCommandLine(&w.writer));
}
test "OSC 133: end_input_start_output with cmdline_url 8" {
const testing = std.testing;
var w: std.Io.Writer.Allocating = .init(testing.allocator);
defer w.deinit();
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 == .semantic_prompt);
try testing.expect(cmd.semantic_prompt.action == .end_input_start_output);
try testing.expectError(error.DecodeError, cmd.semantic_prompt.writeCommandLine(&w.writer));
}
test "OSC 133: fresh_line" {
const testing = std.testing;