diff --git a/src/Surface.zig b/src/Surface.zig index 22853908a..97129b0e5 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -2254,6 +2254,39 @@ fn showMouse(self: *Surface) void { 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 /// must be called from the GUI thread. /// @@ -2279,7 +2312,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool const full_data = switch (action) { .csi => try std.fmt.bufPrint(&buf, "\x1b[{s}", .{data}), .esc => try std.fmt.bufPrint(&buf, "\x1b{s}", .{data}), - .text => data, + .text => parseStringLiteral(&buf, data), else => unreachable, }; _ = self.io_thread.mailbox.push(try termio.Message.writeReq( diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 4b4ef03dd..37e93ae3c 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -321,47 +321,6 @@ pub const Action = union(enum) { }; } - // Use Zig syntax to parse a string literal. - // Since the escaping syntax is verbose, we can do the parsing inplace. - pub fn parseStringLiteralInPlace(bytes: []const u8) []const u8 { - var out: [128]u8 = undefined; - var offset: usize = 0; - var index: usize = 0; - while (true) { - assert(offset <= index); - 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; - }, - } - } - // No escaping => no copy needed. - if (offset == index and index == bytes.len) return bytes; - const mut_bytes: []u8 = @as([*]u8, @ptrFromInt(@intFromPtr(bytes.ptr)))[0..offset]; - @memcpy(mut_bytes, out[0..offset]); - return mut_bytes; - } - /// Parse an action in the format of "key=value" where key is the /// action name and value is the action parameter. The parameter /// is optional depending on the action. @@ -387,10 +346,7 @@ pub const Action = union(enum) { []const u8 => { const idx = colonIdx orelse return Error.InvalidFormat; - const param = if (std.mem.eql(u8, field.name, "text")) - parseStringLiteralInPlace(input[idx + 1 ..]) - else - input[idx + 1 ..]; + const param = input[idx + 1 ..]; return @unionInit(Action, field.name, param); }, @@ -896,12 +852,6 @@ test "parse: action with string" { try testing.expect(binding.action == .esc); try testing.expectEqualStrings("A", binding.action.esc); } - // parameter - { - const binding = try parse("a=text:\\x03\\u{26a1}"); - try testing.expect(binding.action == .text); - try testing.expectEqualStrings(binding.action.text, &[_]u8{ 3, 0xe2, 0x9a, 0xa1 }); - } } test "parse: action with enum" {