Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 6 additions & 13 deletions src/Loop.zig
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,14 @@ pub fn Loop(comptime T: type) type {
var parser: Parser = .{};

// initialize the read buffer
var buf: [1024]u8 = undefined;
const buf_size = 1024;
var buf: [buf_size]u8 = undefined;
var read_start: usize = 0;
// read loop
read_loop: while (!self.should_quit) {
const n = try self.tty.read(buf[read_start..]);
if (read_start == buf_size)
return error.EscapeSequenceTooLong;
const n = read_start + try self.tty.read(buf[read_start..]);
var seq_start: usize = 0;
while (seq_start < n) {
const result = try parser.parse(buf[seq_start..n], paste_allocator);
Expand All @@ -145,7 +148,7 @@ pub fn Loop(comptime T: type) type {
while (seq_start < n) : (seq_start += 1) {
buf[seq_start - initial_start] = buf[seq_start];
}
read_start = seq_start - initial_start + 1;
read_start = seq_start - initial_start;
continue :read_loop;
}
read_start = 0;
Expand Down Expand Up @@ -299,16 +302,6 @@ pub fn handleEventGeneric(self: anytype, vx: *Vaxis, cache: *GraphemeCache, Even
return self.postEvent(.focus_out);
}
},
.paste_start => {
if (@hasField(Event, "paste_start")) {
return self.postEvent(.paste_start);
}
},
.paste_end => {
if (@hasField(Event, "paste_end")) {
return self.postEvent(.paste_end);
}
},
.paste => |text| {
if (@hasField(Event, "paste")) {
return self.postEvent(.{ .paste = text });
Expand Down
71 changes: 41 additions & 30 deletions src/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub fn parse(self: *Parser, input: []const u8, paste_allocator: ?std.mem.Allocat
0x4F => return parseSs3(input),
0x50 => return skipUntilST(input), // DCS
0x58 => return skipUntilST(input), // SOS
0x5B => return parseCsi(input, &self.buf), // CSI
0x5B => return parseCsi(input, &self.buf, paste_allocator), // CSI
0x5D => return parseOsc(input, paste_allocator),
0x5E => return skipUntilST(input), // PM
0x5F => return parseApc(input),
Expand Down Expand Up @@ -307,6 +307,8 @@ inline fn parseOsc(input: []const u8, paste_allocator: ?std.mem.Allocator) !Resu
};
},
52 => {
if (paste_allocator == null)
return error.NoPasteAllocator;
if (input[semicolon_idx + 1] != 'c') return null_event;
const payload = if (bel_terminated)
input[semicolon_idx + 3 .. sequence.len - 1]
Expand All @@ -325,7 +327,7 @@ inline fn parseOsc(input: []const u8, paste_allocator: ?std.mem.Allocator) !Resu
}
}

inline fn parseCsi(input: []const u8, text_buf: []u8) Result {
inline fn parseCsi(input: []const u8, text_buf: []u8, paste_allocator: ?std.mem.Allocator) !Result {
if (input.len < 3) {
return .{
.event = null,
Expand Down Expand Up @@ -432,8 +434,22 @@ inline fn parseCsi(input: []const u8, text_buf: []u8) Result {
21 => Key.f10,
23 => Key.f11,
24 => Key.f12,
200 => return .{ .event = .paste_start, .n = sequence.len },
201 => return .{ .event = .paste_end, .n = sequence.len },
200 => { // bracketed paste
if (paste_allocator == null)
return error.NoPasteAllocator;
const end_sequence: []const u8 = "\x1b[201~";
const end_position = std.mem.indexOf(u8, input[sequence.len..], end_sequence);
if (end_position) |index| {
const text = try paste_allocator.?.dupe(u8, input[sequence.len .. sequence.len + index]);
return .{
.event = .{ .paste = text },
.n = sequence.len + index + end_sequence.len,
};
} else {
return .{ .event = null, .n = 0 };
}
},
// 201 "paste end" handled in 200 "paste start"
57427 => Key.kp_begin,
else => return null_event,
},
Expand Down Expand Up @@ -867,26 +883,21 @@ test "parse: xterm insert" {
try testing.expectEqual(expected_event, result.event);
}

test "parse: paste_start" {
test "parse: bracketed paste" {
const alloc = testing.allocator_instance.allocator();
const input = "\x1b[200~";
const input = "\x1b[200~bracketed paste\x1b[201~";
const expected_text = "bracketed paste";
var parser: Parser = .{};
const result = try parser.parse(input, alloc);
const expected_event: Event = .paste_start;

try testing.expectEqual(6, result.n);
try testing.expectEqual(expected_event, result.event);
}

test "parse: paste_end" {
const alloc = testing.allocator_instance.allocator();
const input = "\x1b[201~";
var parser: Parser = .{};
const result = try parser.parse(input, alloc);
const expected_event: Event = .paste_end;

try testing.expectEqual(6, result.n);
try testing.expectEqual(expected_event, result.event);
try testing.expectEqual(27, result.n);
switch (result.event.?) {
.paste => |text| {
defer alloc.free(text);
try testing.expectEqualStrings(expected_text, text);
},
else => try testing.expect(false),
}
}

test "parse: osc52 paste" {
Expand Down Expand Up @@ -1131,7 +1142,7 @@ test "parse(csi): kitty multi cursor" {
var buf: [1]u8 = undefined;
{
const input = "\x1b[>1;2;3;29;30;40;100;101 q";
const result = parseCsi(input, &buf);
const result = try parseCsi(input, &buf, null);
const expected: Result = .{
.event = .cap_multi_cursor,
.n = input.len,
Expand All @@ -1142,7 +1153,7 @@ test "parse(csi): kitty multi cursor" {
}
{
const input = "\x1b[> q";
const result = parseCsi(input, &buf);
const result = try parseCsi(input, &buf, null);
const expected: Result = .{
.event = null,
.n = input.len,
Expand All @@ -1157,7 +1168,7 @@ test "parse(csi): decrpm" {
var buf: [1]u8 = undefined;
{
const input = "\x1b[?1016;1$y";
const result = parseCsi(input, &buf);
const result = try parseCsi(input, &buf, null);
const expected: Result = .{
.event = .cap_sgr_pixels,
.n = input.len,
Expand All @@ -1168,7 +1179,7 @@ test "parse(csi): decrpm" {
}
{
const input = "\x1b[?1016;0$y";
const result = parseCsi(input, &buf);
const result = try parseCsi(input, &buf, null);
const expected: Result = .{
.event = null,
.n = input.len,
Expand All @@ -1182,7 +1193,7 @@ test "parse(csi): decrpm" {
test "parse(csi): primary da" {
var buf: [1]u8 = undefined;
const input = "\x1b[?c";
const result = parseCsi(input, &buf);
const result = try parseCsi(input, &buf, null);
const expected: Result = .{
.event = .cap_da1,
.n = input.len,
Expand All @@ -1196,7 +1207,7 @@ test "parse(csi): dsr" {
var buf: [1]u8 = undefined;
{
const input = "\x1b[?997;1n";
const result = parseCsi(input, &buf);
const result = try parseCsi(input, &buf, null);
const expected: Result = .{
.event = .{ .color_scheme = .dark },
.n = input.len,
Expand All @@ -1207,7 +1218,7 @@ test "parse(csi): dsr" {
}
{
const input = "\x1b[?997;2n";
const result = parseCsi(input, &buf);
const result = try parseCsi(input, &buf, null);
const expected: Result = .{
.event = .{ .color_scheme = .light },
.n = input.len,
Expand All @@ -1218,7 +1229,7 @@ test "parse(csi): dsr" {
}
{
const input = "\x1b[0n";
const result = parseCsi(input, &buf);
const result = try parseCsi(input, &buf, null);
const expected: Result = .{
.event = null,
.n = input.len,
Expand All @@ -1232,7 +1243,7 @@ test "parse(csi): dsr" {
test "parse(csi): mouse" {
var buf: [1]u8 = undefined;
const input = "\x1b[<35;1;1m";
const result = parseCsi(input, &buf);
const result = try parseCsi(input, &buf, null);
const expected: Result = .{
.event = .{ .mouse = .{
.col = 0,
Expand All @@ -1251,7 +1262,7 @@ test "parse(csi): mouse" {
test "parse(csi): xterm mouse" {
var buf: [1]u8 = undefined;
const input = "\x1b[M\x20\x21\x21";
const result = parseCsi(input, &buf);
const result = try parseCsi(input, &buf, null);
const expected: Result = .{
.event = .{ .mouse = .{
.col = 0,
Expand Down
4 changes: 1 addition & 3 deletions src/event.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ pub const Event = union(enum) {
mouse_leave,
focus_in,
focus_out,
paste_start, // bracketed paste start
paste_end, // bracketed paste end
paste: []const u8, // osc 52 paste, caller must free
paste: []const u8, // osc 52, bracketed paste, caller must free
color_report: Color.Report, // osc 4, 10, 11, 12 response
color_scheme: Color.Scheme,
winsize: Winsize,
Expand Down
4 changes: 1 addition & 3 deletions src/vxfw/vxfw.zig
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ pub const Event = union(enum) {
mouse: vaxis.Mouse,
focus_in, // window has gained focus
focus_out, // window has lost focus
paste_start, // bracketed paste start
paste_end, // bracketed paste end
paste: []const u8, // osc 52 paste, caller must free
paste: []const u8, // osc 52, bracketed paste, caller must free
color_report: vaxis.Color.Report, // osc 4, 10, 11, 12 response
color_scheme: vaxis.Color.Scheme, // light / dark OS theme changes
winsize: vaxis.Winsize, // the window size has changed. This event is always sent when the loop is started
Expand Down