mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
config: add string parse, tests
This commit is contained in:
@ -2254,39 +2254,6 @@ fn showMouse(self: *Surface) void {
|
|||||||
self.rt_surface.setMouseVisibility(true);
|
self.rt_surface.setMouseVisibility(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseStringLiteral(out: []u8, bytes: []const u8) []u8 {
|
|
||||||
var offset: usize = 0;
|
|
||||||
var index: usize = 0;
|
|
||||||
while (true) {
|
|
||||||
if (index >= bytes.len or offset >= out.len) break;
|
|
||||||
const b = bytes[index];
|
|
||||||
switch (b) {
|
|
||||||
'\\' => {
|
|
||||||
const escape_char_index = index + 1;
|
|
||||||
const result = std.zig.string_literal.parseEscapeSequence(bytes, &index);
|
|
||||||
switch (result) {
|
|
||||||
.success => |codepoint| {
|
|
||||||
if (bytes[escape_char_index] == 'u') {
|
|
||||||
const len = std.unicode.utf8Encode(codepoint, out[offset..]) catch break;
|
|
||||||
offset += len;
|
|
||||||
} else {
|
|
||||||
out[offset] = @as(u8, @intCast(codepoint));
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.failure => break,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
out[offset] = b;
|
|
||||||
offset += 1;
|
|
||||||
index += 1;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out[0..offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform a binding action. A binding is a keybinding. This function
|
/// Perform a binding action. A binding is a keybinding. This function
|
||||||
/// must be called from the GUI thread.
|
/// must be called from the GUI thread.
|
||||||
///
|
///
|
||||||
@ -2312,7 +2279,10 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
const full_data = switch (action) {
|
const full_data = switch (action) {
|
||||||
.csi => try std.fmt.bufPrint(&buf, "\x1b[{s}", .{data}),
|
.csi => try std.fmt.bufPrint(&buf, "\x1b[{s}", .{data}),
|
||||||
.esc => try std.fmt.bufPrint(&buf, "\x1b{s}", .{data}),
|
.esc => try std.fmt.bufPrint(&buf, "\x1b{s}", .{data}),
|
||||||
.text => parseStringLiteral(&buf, data),
|
.text => configpkg.string.parse(&buf, data) catch |err| {
|
||||||
|
log.warn("error parsing text binding text={s} err={}", .{ data, err });
|
||||||
|
return true;
|
||||||
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
_ = self.io_thread.mailbox.push(try termio.Message.writeReq(
|
_ = self.io_thread.mailbox.push(try termio.Message.writeReq(
|
||||||
|
@ -2,6 +2,7 @@ const builtin = @import("builtin");
|
|||||||
|
|
||||||
pub usingnamespace @import("config/key.zig");
|
pub usingnamespace @import("config/key.zig");
|
||||||
pub const Config = @import("config/Config.zig");
|
pub const Config = @import("config/Config.zig");
|
||||||
|
pub const string = @import("config/string.zig");
|
||||||
|
|
||||||
// Field types
|
// Field types
|
||||||
pub const CopyOnSelect = Config.CopyOnSelect;
|
pub const CopyOnSelect = Config.CopyOnSelect;
|
||||||
|
67
src/config/string.zig
Normal file
67
src/config/string.zig
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
/// Parse a string literal into a byte array. The string can contain
|
||||||
|
/// any valid Zig string literal escape sequences.
|
||||||
|
///
|
||||||
|
/// The output buffer never needs sto be larger than the input buffer.
|
||||||
|
/// The buffers may alias.
|
||||||
|
pub fn parse(out: []u8, bytes: []const u8) ![]u8 {
|
||||||
|
var dst_i: usize = 0;
|
||||||
|
var src_i: usize = 0;
|
||||||
|
while (src_i < bytes.len) {
|
||||||
|
if (dst_i >= out.len) return error.OutOfMemory;
|
||||||
|
|
||||||
|
// If this byte is not beginning an escape sequence we copy.
|
||||||
|
const b = bytes[src_i];
|
||||||
|
if (b != '\\') {
|
||||||
|
out[dst_i] = b;
|
||||||
|
dst_i += 1;
|
||||||
|
src_i += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the escape sequence
|
||||||
|
switch (std.zig.string_literal.parseEscapeSequence(
|
||||||
|
bytes,
|
||||||
|
&src_i,
|
||||||
|
)) {
|
||||||
|
.failure => return error.InvalidString,
|
||||||
|
.success => |cp| dst_i += try std.unicode.utf8Encode(
|
||||||
|
cp,
|
||||||
|
out[dst_i..],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out[0..dst_i];
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse: empty" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
const result = try parse(&buf, "");
|
||||||
|
try testing.expectEqualStrings("", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse: no escapes" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
const result = try parse(&buf, "hello world");
|
||||||
|
try testing.expectEqualStrings("hello world", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse: escapes" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
{
|
||||||
|
const result = try parse(&buf, "hello\\nworld");
|
||||||
|
try testing.expectEqualStrings("hello\nworld", result);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const result = try parse(&buf, "hello\\u{1F601}world");
|
||||||
|
try testing.expectEqualStrings("hello\u{1F601}world", result);
|
||||||
|
}
|
||||||
|
}
|
@ -134,7 +134,10 @@ pub const Action = union(enum) {
|
|||||||
/// Send an ESC sequence.
|
/// Send an ESC sequence.
|
||||||
esc: []const u8,
|
esc: []const u8,
|
||||||
|
|
||||||
// Send the given text. Uses Zig string literal syntax.
|
// Send the given text. Uses Zig string literal syntax. The maximum
|
||||||
|
// length of the string is 128 bytes. This is currently not validated.
|
||||||
|
// If the text is invalid (i.e. contains an invalid escape sequence),
|
||||||
|
// the error will currently only show up in logs.
|
||||||
text: []const u8,
|
text: []const u8,
|
||||||
|
|
||||||
/// Send data to the pty depending on whether cursor key mode is
|
/// Send data to the pty depending on whether cursor key mode is
|
||||||
|
Reference in New Issue
Block a user