Skip to content

Commit

Permalink
core: encode text action using Zig escape sequences
Browse files Browse the repository at this point in the history
Fixes ghostty-org#3114 in a slightly different way by encoding any non-printable
ascii that appears in a text keybind action using Zig escape sequences.
  • Loading branch information
jcollie committed Dec 28, 2024
1 parent 6fee203 commit d66dbed
Showing 1 changed file with 137 additions and 0 deletions.
137 changes: 137 additions & 0 deletions src/input/Binding.zig
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,35 @@ pub const Action = union(enum) {
_ = opts;

switch (self) {
.text => |value| {
try writer.print("{s}:", .{@tagName(self)});
const view = std.unicode.Utf8View.init(value) catch |err| switch (err) {
error.InvalidUtf8 => {
for (value) |byte| try writer.print("\\x{x:0>2}", .{byte});
return;
},
};
var it = view.iterator();
while (it.nextCodepoint()) |cp| {
switch (cp) {
0x00...0x08,
0x0b...0x0c,
0x0e...0x1f,
0x7f,
=> try writer.print("\\x{x:0>2}", .{cp}),
0x09 => try writer.writeAll("\\t"),
0x0a => try writer.writeAll("\\n"),
0x0d => try writer.writeAll("\\r"),
0x22 => try writer.writeAll("\\\""),
0x27 => try writer.writeAll("\\'"),
0x20...0x21,
0x23...0x26,
0x28...0x7e,
=> try writer.print("{c}", .{@as(u8, @intCast(cp))}),
else => try writer.print("\\u{{{x:0>2}}}", .{cp}),
}
}
},
inline else => |value| {
// All actions start with the tag.
try writer.print("{s}", .{@tagName(self)});
Expand Down Expand Up @@ -787,6 +816,114 @@ pub const Action = union(enum) {
}
}

test "keybind format text 1" {
var buf: [16]u8 = undefined;
const action: Action = .{
.text = "\\x04",
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:\\x04", text);
}

test "keybind format text 2" {
var buf: [16]u8 = undefined;
const action: Action = .{
.text = "\x04",
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:\\x04", text);
}

test "keybind format text 3" {
var buf: [16]u8 = undefined;
const action: Action = .{
.text = "°",
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:\\u{b0}", text);
}

test "keybind format text 4" {
var buf: [16]u8 = undefined;
const action: Action = .{
.text = "𑫕",
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:\\u{11ad5}", text);
}

test "keybind format text 5" {
var buf: [16]u8 = undefined;
const action: Action = .{
.text = "\n",
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:\\n", text);
}

test "keybind format text 6" {
var buf: [16]u8 = undefined;
const action: Action = .{
.text = "\r",
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:\\r", text);
}

test "keybind format text 7" {
var buf: [16]u8 = undefined;
const action: Action = .{
.text = "\"",
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:\\\"", text);
}

test "keybind format text 8" {
var buf: [16]u8 = undefined;
const action: Action = .{
.text = "'",
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:\\'", text);
}

test "keybind format text 9" {
var buf: [16]u8 = undefined;
const action: Action = .{
.text = "\t",
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:\\t", text);
}

test "keybind format text 10" {
var buf: [16]u8 = undefined;
const action: Action = .{
.text = "\\",
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:\\", text);
}

test "keybind format text 11" {
var buf: [16]u8 = undefined;
const action: Action = .{
.text = "abc",
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:abc", text);
}

test "keybind format text 12" {
var buf: [32]u8 = undefined;
const action: Action = .{
.text = &.{ 0xff, 0xff, 0xff },
};
const text = try std.fmt.bufPrint(&buf, "{}", .{action});
try std.testing.expectEqualStrings("text:\\xff\\xff\\xff", text);
}

/// Clone this action with the given allocator. The allocator
/// should be an arena-style allocator since fine-grained
/// deallocation is not possible.
Expand Down

0 comments on commit d66dbed

Please sign in to comment.