refactor: lots and lots of writergate changes

This commit is contained in:
CJ van den Berg 2025-09-24 22:30:18 +02:00
parent 96e8100373
commit e6b39c274c
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
19 changed files with 437 additions and 382 deletions

View file

@ -30,8 +30,8 @@
.hash = "fuzzig-0.1.1-Ji0xivxIAQBD0g8O_NV_0foqoPf3elsg9Sc3pNfdVH4D", .hash = "fuzzig-0.1.1-Ji0xivxIAQBD0g8O_NV_0foqoPf3elsg9Sc3pNfdVH4D",
}, },
.vaxis = .{ .vaxis = .{
.url = "git+https://github.com/neurocyte/libvaxis?ref=zig-0.15#db66bacf4799945c9d99704bf150112df2d688cf", .url = "git+https://github.com/neurocyte/libvaxis?ref=main#35c384757b713c206d11efd76448eaea429f587e",
.hash = "vaxis-0.5.1-BWNV_J0eCQD_x1nEGocK1PgaTZU9L81qc9Vf3IIGx7W2", .hash = "vaxis-0.5.1-BWNV_GQeCQBDaH91XVg4ql_F5RBv__F_Y-_eQ2nCuyNk",
}, },
.zeit = .{ .zeit = .{
.url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#ed2ca60db118414bda2b12df2039e33bad3b0b88", .url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#ed2ca60db118414bda2b12df2039e33bad3b0b88",

View file

@ -48,18 +48,18 @@ pub fn send_request(
method: []const u8, method: []const u8,
m: anytype, m: anytype,
ctx: anytype, ctx: anytype,
) (OutOfMemoryError || SpawnError)!void { ) (OutOfMemoryError || SpawnError || std.Io.Writer.Error)!void {
var cb = std.ArrayList(u8).init(self.allocator); var cb: std.Io.Writer.Allocating = .init(self.allocator);
defer cb.deinit(); defer cb.deinit();
try cbor.writeValue(cb.writer(), m); try cbor.writeValue(&cb.writer, m);
return RequestContext(@TypeOf(ctx)).send(allocator, self.pid.ref(), ctx, tp.message.fmt(.{ "REQ", method, cb.items })); return RequestContext(@TypeOf(ctx)).send(allocator, self.pid.ref(), ctx, tp.message.fmt(.{ "REQ", method, cb.written() }));
} }
pub fn send_notification(self: *const Self, method: []const u8, m: anytype) (OutOfMemoryError || SendError)!void { pub fn send_notification(self: *const Self, method: []const u8, m: anytype) (OutOfMemoryError || SendError || std.Io.Writer.Error)!void {
var cb = std.ArrayList(u8).init(self.allocator); var cb: std.Io.Writer.Allocating = .init(self.allocator);
defer cb.deinit(); defer cb.deinit();
try cbor.writeValue(cb.writer(), m); try cbor.writeValue(&cb.writer, m);
return self.send_notification_raw(method, cb.items); return self.send_notification_raw(method, cb.written());
} }
pub fn send_notification_raw(self: *const Self, method: []const u8, cb: []const u8) SendError!void { pub fn send_notification_raw(self: *const Self, method: []const u8, cb: []const u8) SendError!void {
@ -82,27 +82,27 @@ pub const ErrorCode = enum(i32) {
RequestCancelled = -32800, RequestCancelled = -32800,
}; };
pub fn send_response(allocator: std.mem.Allocator, to: tp.pid_ref, cbor_id: []const u8, result: anytype) (SendError || OutOfMemoryError)!void { pub fn send_response(allocator: std.mem.Allocator, to: tp.pid_ref, cbor_id: []const u8, result: anytype) (SendError || OutOfMemoryError || std.Io.Writer.Error)!void {
var cb = std.ArrayList(u8).init(allocator); var cb: std.Io.Writer.Allocating = .init(allocator);
defer cb.deinit(); defer cb.deinit();
const writer = cb.writer(); const writer = &cb.writer;
try cbor.writeArrayHeader(writer, 3); try cbor.writeArrayHeader(writer, 3);
try cbor.writeValue(writer, "RSP"); try cbor.writeValue(writer, "RSP");
try writer.writeAll(cbor_id); try writer.writeAll(cbor_id);
try cbor.writeValue(cb.writer(), result); try cbor.writeValue(writer, result);
to.send_raw(.{ .buf = cb.items }) catch return error.SendFailed; to.send_raw(.{ .buf = cb.written() }) catch return error.SendFailed;
} }
pub fn send_error_response(allocator: std.mem.Allocator, to: tp.pid_ref, cbor_id: []const u8, code: ErrorCode, message: []const u8) (SendError || OutOfMemoryError)!void { pub fn send_error_response(allocator: std.mem.Allocator, to: tp.pid_ref, cbor_id: []const u8, code: ErrorCode, message: []const u8) (SendError || OutOfMemoryError || std.Io.Writer.Error)!void {
var cb = std.ArrayList(u8).init(allocator); var cb: std.Io.Writer.Allocating = .init(allocator);
defer cb.deinit(); defer cb.deinit();
const writer = cb.writer(); const writer = &cb.writer;
try cbor.writeArrayHeader(writer, 4); try cbor.writeArrayHeader(writer, 4);
try cbor.writeValue(writer, "ERR"); try cbor.writeValue(writer, "ERR");
try writer.writeAll(cbor_id); try writer.writeAll(cbor_id);
try cbor.writeValue(cb.writer(), code); try cbor.writeValue(writer, code);
try cbor.writeValue(cb.writer(), message); try cbor.writeValue(writer, message);
to.send_raw(.{ .buf = cb.items }) catch return error.SendFailed; to.send_raw(.{ .buf = cb.written() }) catch return error.SendFailed;
} }
pub fn close(self: *Self) void { pub fn close(self: *Self) void {
@ -172,6 +172,8 @@ const Process = struct {
sp_tag: [:0]const u8, sp_tag: [:0]const u8,
log_file: ?std.fs.File = null, log_file: ?std.fs.File = null,
log_file_path: ?[]const u8 = null, log_file_path: ?[]const u8 = null,
log_file_writer: ?std.fs.File.Writer = null,
log_file_writer_buf: [1024]u8 = undefined,
next_id: i32 = 0, next_id: i32 = 0,
requests: std.StringHashMap(tp.pid), requests: std.StringHashMap(tp.pid),
state: enum { init, running } = .init, state: enum { init, running } = .init,
@ -181,7 +183,7 @@ const Process = struct {
const Receiver = tp.Receiver(*Process); const Receiver = tp.Receiver(*Process);
pub fn create(allocator: std.mem.Allocator, project: []const u8, cmd: tp.message) (error{ ThespianSpawnFailed, InvalidLspCommand } || OutOfMemoryError || cbor.Error)!tp.pid { pub fn create(allocator: std.mem.Allocator, project: []const u8, cmd: tp.message) (error{ ThespianSpawnFailed, InvalidLspCommand } || OutOfMemoryError || cbor.Error || std.Io.Writer.Error)!tp.pid {
var tag: []const u8 = undefined; var tag: []const u8 = undefined;
if (try cbor.match(cmd.buf, .{tp.extract(&tag)})) { if (try cbor.match(cmd.buf, .{tp.extract(&tag)})) {
// //
@ -194,15 +196,15 @@ const Process = struct {
} }
const self = try allocator.create(Process); const self = try allocator.create(Process);
errdefer allocator.destroy(self); errdefer allocator.destroy(self);
var sp_tag_ = std.ArrayList(u8).init(allocator); var sp_tag_: std.Io.Writer.Allocating = .init(allocator);
defer sp_tag_.deinit(); defer sp_tag_.deinit();
try sp_tag_.appendSlice(tag); try sp_tag_.writer.writeAll(tag);
try sp_tag_.appendSlice("-" ++ sp_tag); try sp_tag_.writer.writeAll("-" ++ sp_tag);
self.* = .{ self.* = .{
.allocator = allocator, .allocator = allocator,
.cmd = try cmd.clone(allocator), .cmd = try cmd.clone(allocator),
.receiver = Receiver.init(receive, self), .receiver = Receiver.init(receive, self),
.recv_buf = std.ArrayList(u8).init(allocator), .recv_buf = .empty,
.parent = tp.self_pid().clone(), .parent = tp.self_pid().clone(),
.tag = try allocator.dupeZ(u8, tag), .tag = try allocator.dupeZ(u8, tag),
.project = try allocator.dupeZ(u8, project), .project = try allocator.dupeZ(u8, project),
@ -220,11 +222,14 @@ const Process = struct {
req.value_ptr.deinit(); req.value_ptr.deinit();
} }
self.allocator.free(self.sp_tag); self.allocator.free(self.sp_tag);
self.recv_buf.deinit(); self.recv_buf.deinit(self.allocator);
self.allocator.free(self.cmd.buf); self.allocator.free(self.cmd.buf);
self.close() catch {}; self.close() catch {};
self.write_log("### terminated LSP process ###\n", .{}); self.write_log("### terminated LSP process ###\n", .{});
if (self.log_file) |file| file.close(); if (self.log_file) |file| {
if (self.log_file_writer) |*writer| writer.interface.flush() catch {};
file.close();
}
if (self.log_file_path) |file_path| self.allocator.free(file_path); if (self.log_file_path) |file_path| self.allocator.free(file_path);
} }
@ -265,12 +270,13 @@ const Process = struct {
self.sp = tp.subprocess.init(self.allocator, self.cmd, self.sp_tag, .Pipe) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.sp = tp.subprocess.init(self.allocator, self.cmd, self.sp_tag, .Pipe) catch |e| return tp.exit_error(e, @errorReturnTrace());
tp.receive(&self.receiver); tp.receive(&self.receiver);
var log_file_path = std.ArrayList(u8).init(self.allocator); var log_file_path: std.Io.Writer.Allocating = .init(self.allocator);
defer log_file_path.deinit(); defer log_file_path.deinit();
const state_dir = root.get_state_dir() catch |e| return tp.exit_error(e, @errorReturnTrace()); const state_dir = root.get_state_dir() catch |e| return tp.exit_error(e, @errorReturnTrace());
log_file_path.writer().print("{s}{c}lsp-{s}.log", .{ state_dir, std.fs.path.sep, self.tag }) catch |e| return tp.exit_error(e, @errorReturnTrace()); log_file_path.writer.print("{s}{c}lsp-{s}.log", .{ state_dir, std.fs.path.sep, self.tag }) catch |e| return tp.exit_error(e, @errorReturnTrace());
self.log_file = std.fs.createFileAbsolute(log_file_path.items, .{ .truncate = true }) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.log_file = std.fs.createFileAbsolute(log_file_path.written(), .{ .truncate = true }) catch |e| return tp.exit_error(e, @errorReturnTrace());
self.log_file_path = log_file_path.toOwnedSlice() catch null; self.log_file_path = log_file_path.toOwnedSlice() catch null;
if (self.log_file) |log_file| self.log_file_writer = log_file.writer(&self.log_file_writer_buf);
} }
fn receive(self: *Process, from: tp.pid_ref, m: tp.message) tp.result { fn receive(self: *Process, from: tp.pid_ref, m: tp.message) tp.result {
@ -441,7 +447,7 @@ const Process = struct {
} }
fn handle_output(self: *Process, bytes: []const u8) Error!void { fn handle_output(self: *Process, bytes: []const u8) Error!void {
try self.recv_buf.appendSlice(bytes); try self.recv_buf.appendSlice(self.allocator, bytes);
self.write_log("### RECV:\n{s}\n###\n", .{bytes}); self.write_log("### RECV:\n{s}\n###\n", .{bytes});
self.frame_message_recv() catch |e| { self.frame_message_recv() catch |e| {
self.write_log("### RECV error: {any}\n", .{e}); self.write_log("### RECV error: {any}\n", .{e});
@ -473,9 +479,9 @@ const Process = struct {
const id = self.next_id; const id = self.next_id;
self.next_id += 1; self.next_id += 1;
var request = std.ArrayList(u8).init(self.allocator); var request: std.Io.Writer.Allocating = .init(self.allocator);
defer request.deinit(); defer request.deinit();
const msg_writer = request.writer(); const msg_writer = &request.writer;
try cbor.writeMapHeader(msg_writer, 4); try cbor.writeMapHeader(msg_writer, 4);
try cbor.writeValue(msg_writer, "jsonrpc"); try cbor.writeValue(msg_writer, "jsonrpc");
try cbor.writeValue(msg_writer, "2.0"); try cbor.writeValue(msg_writer, "2.0");
@ -486,32 +492,32 @@ const Process = struct {
try cbor.writeValue(msg_writer, "params"); try cbor.writeValue(msg_writer, "params");
_ = try msg_writer.write(params_cb); _ = try msg_writer.write(params_cb);
const json = try cbor.toJsonAlloc(self.allocator, request.items); const json = try cbor.toJsonAlloc(self.allocator, request.written());
defer self.allocator.free(json); defer self.allocator.free(json);
var output = std.ArrayList(u8).init(self.allocator); var output: std.Io.Writer.Allocating = .init(self.allocator);
defer output.deinit(); defer output.deinit();
const writer = output.writer(); const writer = &output.writer;
const terminator = "\r\n"; const terminator = "\r\n";
const content_length = json.len + terminator.len; const content_length = json.len + terminator.len;
try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length}); try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length});
_ = try writer.write(json); _ = try writer.write(json);
_ = try writer.write(terminator); _ = try writer.write(terminator);
sp.send(output.items) catch return error.SendFailed; sp.send(output.written()) catch return error.SendFailed;
self.write_log("### SEND request:\n{s}\n###\n", .{output.items}); self.write_log("### SEND request:\n{s}\n###\n", .{output.written()});
var cbor_id = std.ArrayList(u8).init(self.allocator); var cbor_id: std.Io.Writer.Allocating = .init(self.allocator);
defer cbor_id.deinit(); defer cbor_id.deinit();
try cbor.writeValue(cbor_id.writer(), id); try cbor.writeValue(&cbor_id.writer, id);
try self.requests.put(try cbor_id.toOwnedSlice(), from.clone()); try self.requests.put(try cbor_id.toOwnedSlice(), from.clone());
} }
fn send_response(self: *Process, cbor_id: []const u8, result_cb: []const u8) (error{Closed} || SendError || cbor.Error || cbor.JsonEncodeError)!void { fn send_response(self: *Process, cbor_id: []const u8, result_cb: []const u8) (error{Closed} || SendError || cbor.Error || cbor.JsonEncodeError)!void {
const sp = if (self.sp) |*sp| sp else return error.Closed; const sp = if (self.sp) |*sp| sp else return error.Closed;
var response = std.ArrayList(u8).init(self.allocator); var response: std.Io.Writer.Allocating = .init(self.allocator);
defer response.deinit(); defer response.deinit();
const msg_writer = response.writer(); const msg_writer = &response.writer;
try cbor.writeMapHeader(msg_writer, 3); try cbor.writeMapHeader(msg_writer, 3);
try cbor.writeValue(msg_writer, "jsonrpc"); try cbor.writeValue(msg_writer, "jsonrpc");
try cbor.writeValue(msg_writer, "2.0"); try cbor.writeValue(msg_writer, "2.0");
@ -520,27 +526,28 @@ const Process = struct {
try cbor.writeValue(msg_writer, "result"); try cbor.writeValue(msg_writer, "result");
_ = try msg_writer.write(result_cb); _ = try msg_writer.write(result_cb);
const json = try cbor.toJsonAlloc(self.allocator, response.items); const json = try cbor.toJsonAlloc(self.allocator, response.written());
defer self.allocator.free(json); defer self.allocator.free(json);
var output = std.ArrayList(u8).init(self.allocator);
var output: std.Io.Writer.Allocating = .init(self.allocator);
defer output.deinit(); defer output.deinit();
const writer = output.writer(); const writer = &output.writer;
const terminator = "\r\n"; const terminator = "\r\n";
const content_length = json.len + terminator.len; const content_length = json.len + terminator.len;
try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length}); try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length});
_ = try writer.write(json); _ = try writer.write(json);
_ = try writer.write(terminator); _ = try writer.write(terminator);
sp.send(output.items) catch return error.SendFailed; sp.send(output.written()) catch return error.SendFailed;
self.write_log("### SEND response:\n{s}\n###\n", .{output.items}); self.write_log("### SEND response:\n{s}\n###\n", .{output.written()});
} }
fn send_error_response(self: *Process, cbor_id: []const u8, error_code: ErrorCode, message: []const u8) (error{Closed} || SendError || cbor.Error || cbor.JsonEncodeError)!void { fn send_error_response(self: *Process, cbor_id: []const u8, error_code: ErrorCode, message: []const u8) (error{Closed} || SendError || cbor.Error || cbor.JsonEncodeError)!void {
const sp = if (self.sp) |*sp| sp else return error.Closed; const sp = if (self.sp) |*sp| sp else return error.Closed;
var response = std.ArrayList(u8).init(self.allocator); var response: std.Io.Writer.Allocating = .init(self.allocator);
defer response.deinit(); defer response.deinit();
const msg_writer = response.writer(); const msg_writer = &response.writer;
try cbor.writeMapHeader(msg_writer, 3); try cbor.writeMapHeader(msg_writer, 3);
try cbor.writeValue(msg_writer, "jsonrpc"); try cbor.writeValue(msg_writer, "jsonrpc");
try cbor.writeValue(msg_writer, "2.0"); try cbor.writeValue(msg_writer, "2.0");
@ -553,19 +560,20 @@ const Process = struct {
try cbor.writeValue(msg_writer, "message"); try cbor.writeValue(msg_writer, "message");
try cbor.writeValue(msg_writer, message); try cbor.writeValue(msg_writer, message);
const json = try cbor.toJsonAlloc(self.allocator, response.items); const json = try cbor.toJsonAlloc(self.allocator, response.written());
defer self.allocator.free(json); defer self.allocator.free(json);
var output = std.ArrayList(u8).init(self.allocator);
var output: std.Io.Writer.Allocating = .init(self.allocator);
defer output.deinit(); defer output.deinit();
const writer = output.writer(); const writer = &output.writer;
const terminator = "\r\n"; const terminator = "\r\n";
const content_length = json.len + terminator.len; const content_length = json.len + terminator.len;
try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length}); try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length});
_ = try writer.write(json); _ = try writer.write(json);
_ = try writer.write(terminator); _ = try writer.write(terminator);
sp.send(output.items) catch return error.SendFailed; sp.send(output.written()) catch return error.SendFailed;
self.write_log("### SEND error response:\n{s}\n###\n", .{output.items}); self.write_log("### SEND error response:\n{s}\n###\n", .{output.written()});
} }
fn send_notification(self: *Process, method: []const u8, params_cb: []const u8) Error!void { fn send_notification(self: *Process, method: []const u8, params_cb: []const u8) Error!void {
@ -573,9 +581,9 @@ const Process = struct {
const have_params = !(cbor.match(params_cb, cbor.null_) catch false); const have_params = !(cbor.match(params_cb, cbor.null_) catch false);
var notification = std.ArrayList(u8).init(self.allocator); var notification: std.Io.Writer.Allocating = .init(self.allocator);
defer notification.deinit(); defer notification.deinit();
const msg_writer = notification.writer(); const msg_writer = &notification.writer;
try cbor.writeMapHeader(msg_writer, 3); try cbor.writeMapHeader(msg_writer, 3);
try cbor.writeValue(msg_writer, "jsonrpc"); try cbor.writeValue(msg_writer, "jsonrpc");
try cbor.writeValue(msg_writer, "2.0"); try cbor.writeValue(msg_writer, "2.0");
@ -588,19 +596,20 @@ const Process = struct {
try cbor.writeMapHeader(msg_writer, 0); try cbor.writeMapHeader(msg_writer, 0);
} }
const json = try cbor.toJsonAlloc(self.allocator, notification.items); const json = try cbor.toJsonAlloc(self.allocator, notification.written());
defer self.allocator.free(json); defer self.allocator.free(json);
var output = std.ArrayList(u8).init(self.allocator);
var output: std.Io.Writer.Allocating = .init(self.allocator);
defer output.deinit(); defer output.deinit();
const writer = output.writer(); const writer = &output.writer;
const terminator = "\r\n"; const terminator = "\r\n";
const content_length = json.len + terminator.len; const content_length = json.len + terminator.len;
try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length}); try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length});
_ = try writer.write(json); _ = try writer.write(json);
_ = try writer.write(terminator); _ = try writer.write(terminator);
sp.send(output.items) catch return error.SendFailed; sp.send(output.written()) catch return error.SendFailed;
self.write_log("### SEND notification:\n{s}\n###\n", .{output.items}); self.write_log("### SEND notification:\n{s}\n###\n", .{output.written()});
} }
fn frame_message_recv(self: *Process) Error!void { fn frame_message_recv(self: *Process) Error!void {
@ -609,11 +618,11 @@ const Process = struct {
const headers_data = self.recv_buf.items[0..headers_end]; const headers_data = self.recv_buf.items[0..headers_end];
const headers = try Headers.parse(headers_data); const headers = try Headers.parse(headers_data);
if (self.recv_buf.items.len - (headers_end + sep.len) < headers.content_length) return; if (self.recv_buf.items.len - (headers_end + sep.len) < headers.content_length) return;
const buf = try self.recv_buf.toOwnedSlice(); const buf = try self.recv_buf.toOwnedSlice(self.allocator);
const data = buf[headers_end + sep.len .. headers_end + sep.len + headers.content_length]; const data = buf[headers_end + sep.len .. headers_end + sep.len + headers.content_length];
const rest = buf[headers_end + sep.len + headers.content_length ..]; const rest = buf[headers_end + sep.len + headers.content_length ..];
defer self.allocator.free(buf); defer self.allocator.free(buf);
if (rest.len > 0) try self.recv_buf.appendSlice(rest); if (rest.len > 0) try self.recv_buf.appendSlice(self.allocator, rest);
const message = .{ .body = data[0..headers.content_length] }; const message = .{ .body = data[0..headers.content_length] };
const cb = try cbor.fromJsonAlloc(self.allocator, message.body); const cb = try cbor.fromJsonAlloc(self.allocator, message.body);
defer self.allocator.free(cb); defer self.allocator.free(cb);
@ -627,9 +636,9 @@ const Process = struct {
const json = if (params) |p| try cbor.toJsonPrettyAlloc(self.allocator, p) else null; const json = if (params) |p| try cbor.toJsonPrettyAlloc(self.allocator, p) else null;
defer if (json) |p| self.allocator.free(p); defer if (json) |p| self.allocator.free(p);
self.write_log("### RECV req: {s}\nmethod: {s}\n{s}\n###\n", .{ json_id, method, json orelse "no params" }); self.write_log("### RECV req: {s}\nmethod: {s}\n{s}\n###\n", .{ json_id, method, json orelse "no params" });
var request = std.ArrayList(u8).init(self.allocator); var request: std.Io.Writer.Allocating = .init(self.allocator);
defer request.deinit(); defer request.deinit();
const writer = request.writer(); const writer = &request.writer;
try cbor.writeArrayHeader(writer, 7); try cbor.writeArrayHeader(writer, 7);
try cbor.writeValue(writer, sp_tag); try cbor.writeValue(writer, sp_tag);
try cbor.writeValue(writer, self.project); try cbor.writeValue(writer, self.project);
@ -638,7 +647,7 @@ const Process = struct {
try cbor.writeValue(writer, method); try cbor.writeValue(writer, method);
try writer.writeAll(cbor_id); try writer.writeAll(cbor_id);
if (params) |p| _ = try writer.write(p) else try cbor.writeValue(writer, null); if (params) |p| _ = try writer.write(p) else try cbor.writeValue(writer, null);
self.parent.send_raw(.{ .buf = request.items }) catch return error.SendFailed; self.parent.send_raw(.{ .buf = request.written() }) catch return error.SendFailed;
} }
fn receive_lsp_response(self: *Process, cbor_id: []const u8, result: ?[]const u8, err: ?[]const u8) Error!void { fn receive_lsp_response(self: *Process, cbor_id: []const u8, result: ?[]const u8, err: ?[]const u8) Error!void {
@ -650,9 +659,9 @@ const Process = struct {
defer if (json_err) |p| self.allocator.free(p); defer if (json_err) |p| self.allocator.free(p);
self.write_log("### RECV rsp: {s} {s}\n{s}\n###\n", .{ json_id, if (json_err) |_| "error" else "response", json_err orelse json orelse "no result" }); self.write_log("### RECV rsp: {s} {s}\n{s}\n###\n", .{ json_id, if (json_err) |_| "error" else "response", json_err orelse json orelse "no result" });
const from = self.requests.get(cbor_id) orelse return; const from = self.requests.get(cbor_id) orelse return;
var response = std.ArrayList(u8).init(self.allocator); var response: std.Io.Writer.Allocating = .init(self.allocator);
defer response.deinit(); defer response.deinit();
const writer = response.writer(); const writer = &response.writer;
try cbor.writeArrayHeader(writer, 4); try cbor.writeArrayHeader(writer, 4);
try cbor.writeValue(writer, sp_tag); try cbor.writeValue(writer, sp_tag);
try cbor.writeValue(writer, self.tag); try cbor.writeValue(writer, self.tag);
@ -663,16 +672,16 @@ const Process = struct {
try cbor.writeValue(writer, "result"); try cbor.writeValue(writer, "result");
_ = try writer.write(result_); _ = try writer.write(result_);
} }
from.send_raw(.{ .buf = response.items }) catch return error.SendFailed; from.send_raw(.{ .buf = response.written() }) catch return error.SendFailed;
} }
fn receive_lsp_notification(self: *Process, method: []const u8, params: ?[]const u8) Error!void { fn receive_lsp_notification(self: *Process, method: []const u8, params: ?[]const u8) Error!void {
const json = if (params) |p| try cbor.toJsonPrettyAlloc(self.allocator, p) else null; const json = if (params) |p| try cbor.toJsonPrettyAlloc(self.allocator, p) else null;
defer if (json) |p| self.allocator.free(p); defer if (json) |p| self.allocator.free(p);
self.write_log("### RECV notify:\nmethod: {s}\n{s}\n###\n", .{ method, json orelse "no params" }); self.write_log("### RECV notify:\nmethod: {s}\n{s}\n###\n", .{ method, json orelse "no params" });
var notification = std.ArrayList(u8).init(self.allocator); var notification: std.Io.Writer.Allocating = .init(self.allocator);
defer notification.deinit(); defer notification.deinit();
const writer = notification.writer(); const writer = &notification.writer;
try cbor.writeArrayHeader(writer, 6); try cbor.writeArrayHeader(writer, 6);
try cbor.writeValue(writer, sp_tag); try cbor.writeValue(writer, sp_tag);
try cbor.writeValue(writer, self.project); try cbor.writeValue(writer, self.project);
@ -680,13 +689,13 @@ const Process = struct {
try cbor.writeValue(writer, "notify"); try cbor.writeValue(writer, "notify");
try cbor.writeValue(writer, method); try cbor.writeValue(writer, method);
if (params) |p| _ = try writer.write(p) else try cbor.writeValue(writer, null); if (params) |p| _ = try writer.write(p) else try cbor.writeValue(writer, null);
self.parent.send_raw(.{ .buf = notification.items }) catch return error.SendFailed; self.parent.send_raw(.{ .buf = notification.written() }) catch return error.SendFailed;
} }
fn write_log(self: *Process, comptime format: []const u8, args: anytype) void { fn write_log(self: *Process, comptime format: []const u8, args: anytype) void {
if (!debug_lsp) return; if (!debug_lsp) return;
const file = self.log_file orelse return; const file_writer = if (self.log_file_writer) |*writer| writer else return;
file.writer().print(format, args) catch {}; file_writer.interface.print(format, args) catch {};
} }
}; };

View file

@ -47,8 +47,8 @@ const OutOfMemoryError = error{OutOfMemory};
const SpawnError = (OutOfMemoryError || error{ThespianSpawnFailed}); const SpawnError = (OutOfMemoryError || error{ThespianSpawnFailed});
pub const InvalidMessageError = error{ InvalidMessage, InvalidMessageField, InvalidTargetURI, InvalidMapType }; pub const InvalidMessageError = error{ InvalidMessage, InvalidMessageField, InvalidTargetURI, InvalidMapType };
pub const StartLspError = (error{ ThespianSpawnFailed, Timeout, InvalidLspCommand } || LspError || OutOfMemoryError || cbor.Error); pub const StartLspError = (error{ ThespianSpawnFailed, Timeout, InvalidLspCommand } || LspError || OutOfMemoryError || cbor.Error);
pub const LspError = (error{ NoLsp, LspFailed } || OutOfMemoryError); pub const LspError = (error{ NoLsp, LspFailed } || OutOfMemoryError || std.Io.Writer.Error);
pub const ClientError = (error{ClientFailed} || OutOfMemoryError); pub const ClientError = (error{ClientFailed} || OutOfMemoryError || std.Io.Writer.Error);
pub const LspOrClientError = (LspError || ClientError); pub const LspOrClientError = (LspError || ClientError);
const File = struct { const File = struct {
@ -80,7 +80,7 @@ pub fn init(allocator: std.mem.Allocator, name: []const u8) OutOfMemoryError!Sel
.open_time = std.time.milliTimestamp(), .open_time = std.time.milliTimestamp(),
.language_servers = std.StringHashMap(*const LSP).init(allocator), .language_servers = std.StringHashMap(*const LSP).init(allocator),
.file_language_server = std.StringHashMap(*const LSP).init(allocator), .file_language_server = std.StringHashMap(*const LSP).init(allocator),
.tasks = std.ArrayList(Task).init(allocator), .tasks = .empty,
.logger = log.logger("project"), .logger = log.logger("project"),
.logger_lsp = log.logger("lsp"), .logger_lsp = log.logger("lsp"),
.logger_git = log.logger("git"), .logger_git = log.logger("git"),
@ -104,7 +104,7 @@ pub fn deinit(self: *Self) void {
self.files.deinit(self.allocator); self.files.deinit(self.allocator);
self.pending.deinit(self.allocator); self.pending.deinit(self.allocator);
for (self.tasks.items) |task| self.allocator.free(task.command); for (self.tasks.items) |task| self.allocator.free(task.command);
self.tasks.deinit(); self.tasks.deinit(self.allocator);
self.logger_lsp.deinit(); self.logger_lsp.deinit();
self.logger_git.deinit(); self.logger_git.deinit();
self.logger.deinit(); self.logger.deinit();
@ -216,7 +216,7 @@ pub fn restore_state_v1(self: *Self, data: []const u8) !void {
continue; continue;
} }
tp.trace(tp.channel.debug, .{ "restore_state_v1", "task", command, mtime }); tp.trace(tp.channel.debug, .{ "restore_state_v1", "task", command, mtime });
(try self.tasks.addOne()).* = .{ (try self.tasks.addOne(self.allocator)).* = .{
.command = try self.allocator.dupe(u8, command), .command = try self.allocator.dupe(u8, command),
.mtime = mtime, .mtime = mtime,
}; };
@ -237,6 +237,7 @@ pub fn restore_state_v0(self: *Self, data: []const u8) error{
BadArrayAllocExtract, BadArrayAllocExtract,
InvalidMapType, InvalidMapType,
InvalidUnion, InvalidUnion,
WriteFailed,
}!void { }!void {
tp.trace(tp.channel.debug, .{"restore_state_v0"}); tp.trace(tp.channel.debug, .{"restore_state_v0"});
defer self.sort_files_by_mtime(); defer self.sort_files_by_mtime();
@ -309,14 +310,16 @@ fn get_language_server(self: *Self, file_path: []const u8) LspError!*const LSP {
} }
fn make_URI(self: *Self, file_path: ?[]const u8) LspError![]const u8 { fn make_URI(self: *Self, file_path: ?[]const u8) LspError![]const u8 {
var buf = std.ArrayList(u8).init(self.allocator); var buf: std.Io.Writer.Allocating = .init(self.allocator);
defer buf.deinit();
const writer = &buf.writer;
if (file_path) |path| { if (file_path) |path| {
if (std.fs.path.isAbsolute(path)) { if (std.fs.path.isAbsolute(path)) {
try buf.writer().print("file://{s}", .{path}); try writer.print("file://{s}", .{path});
} else { } else {
try buf.writer().print("file://{s}{c}{s}", .{ self.name, std.fs.path.sep, path }); try writer.print("file://{s}{c}{s}", .{ self.name, std.fs.path.sep, path });
} }
} else try buf.writer().print("file://{s}", .{self.name}); } else try writer.print("file://{s}", .{self.name});
return buf.toOwnedSlice(); return buf.toOwnedSlice();
} }
@ -389,12 +392,12 @@ pub fn query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []co
score: i32, score: i32,
matches: []const usize, matches: []const usize,
}; };
var matches = std.ArrayList(Match).init(self.allocator); var matches: std.ArrayList(Match) = .empty;
for (self.files.items) |file| { for (self.files.items) |file| {
const match = searcher.scoreMatches(file.path, query); const match = searcher.scoreMatches(file.path, query);
if (match.score) |score| { if (match.score) |score| {
(try matches.addOne()).* = .{ (try matches.addOne(self.allocator)).* = .{
.path = file.path, .path = file.path,
.type = file.type, .type = file.type,
.icon = file.icon, .icon = file.icon,
@ -551,12 +554,13 @@ pub fn get_mru_position(self: *Self, from: tp.pid_ref, file_path: []const u8) Cl
} }
pub fn request_tasks(self: *Self, from: tp.pid_ref) ClientError!void { pub fn request_tasks(self: *Self, from: tp.pid_ref) ClientError!void {
var message = std.ArrayList(u8).init(self.allocator); var message: std.Io.Writer.Allocating = .init(self.allocator);
const writer = message.writer(); defer message.deinit();
const writer = &message.writer;
try cbor.writeArrayHeader(writer, self.tasks.items.len); try cbor.writeArrayHeader(writer, self.tasks.items.len);
for (self.tasks.items) |task| for (self.tasks.items) |task|
try cbor.writeValue(writer, task.command); try cbor.writeValue(writer, task.command);
from.send_raw(.{ .buf = message.items }) catch return error.ClientFailed; from.send_raw(.{ .buf = message.written() }) catch return error.ClientFailed;
} }
pub fn add_task(self: *Self, command: []const u8) OutOfMemoryError!void { pub fn add_task(self: *Self, command: []const u8) OutOfMemoryError!void {
@ -569,7 +573,7 @@ pub fn add_task(self: *Self, command: []const u8) OutOfMemoryError!void {
return; return;
}; };
tp.trace(tp.channel.debug, .{ "project", self.name, "add_task", command, mtime }); tp.trace(tp.channel.debug, .{ "project", self.name, "add_task", command, mtime });
(try self.tasks.addOne()).* = .{ (try self.tasks.addOne(self.allocator)).* = .{
.command = try self.allocator.dupe(u8, command), .command = try self.allocator.dupe(u8, command),
.mtime = mtime, .mtime = mtime,
}; };
@ -615,8 +619,8 @@ pub fn did_change(self: *Self, file_path: []const u8, version: usize, text_dst:
} }
var dizzy_edits = std.ArrayListUnmanaged(dizzy.Edit){}; var dizzy_edits = std.ArrayListUnmanaged(dizzy.Edit){};
var edits_cb = std.ArrayList(u8).init(arena); var edits_cb: std.Io.Writer.Allocating = .init(self.allocator);
const writer = edits_cb.writer(); const writer = &edits_cb.writer;
const scratch_len = 4 * (text_dst.len + text_src.len) + 2; const scratch_len = 4 * (text_dst.len + text_src.len) + 2;
const scratch = blk: { const scratch = blk: {
@ -674,16 +678,17 @@ pub fn did_change(self: *Self, file_path: []const u8, version: usize, text_dst:
{ {
const frame = tracy.initZone(@src(), .{ .name = "send" }); const frame = tracy.initZone(@src(), .{ .name = "send" });
defer frame.deinit(); defer frame.deinit();
var msg = std.ArrayList(u8).init(arena); var msg: std.Io.Writer.Allocating = .init(self.allocator);
const msg_writer = msg.writer(); defer msg.deinit();
const msg_writer = &msg.writer;
try cbor.writeMapHeader(msg_writer, 2); try cbor.writeMapHeader(msg_writer, 2);
try cbor.writeValue(msg_writer, "textDocument"); try cbor.writeValue(msg_writer, "textDocument");
try cbor.writeValue(msg_writer, .{ .uri = uri, .version = version }); try cbor.writeValue(msg_writer, .{ .uri = uri, .version = version });
try cbor.writeValue(msg_writer, "contentChanges"); try cbor.writeValue(msg_writer, "contentChanges");
try cbor.writeArrayHeader(msg_writer, edits_count); try cbor.writeArrayHeader(msg_writer, edits_count);
_ = try msg_writer.write(edits_cb.items); _ = try msg_writer.write(edits_cb.written());
lsp.send_notification_raw("textDocument/didChange", msg.items) catch return error.LspFailed; lsp.send_notification_raw("textDocument/didChange", msg.written()) catch return error.LspFailed;
} }
} }
@ -1146,16 +1151,16 @@ pub fn rename_symbol(self: *Self, from: tp.pid_ref, file_path: []const u8, row:
const allocator = std.heap.c_allocator; const allocator = std.heap.c_allocator;
var result: []const u8 = undefined; var result: []const u8 = undefined;
// buffer the renames in order to send as a single, atomic message // buffer the renames in order to send as a single, atomic message
var renames = std.ArrayList(Rename).init(allocator); var renames = std.array_list.Managed(Rename).init(allocator);
defer renames.deinit(); defer renames.deinit();
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.map })) { if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.map })) {
if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&result) })) { if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&result) })) {
try decode_rename_symbol_map(result, &renames); try decode_rename_symbol_map(result, &renames);
// write the renames message manually since there doesn't appear to be an array helper // write the renames message manually since there doesn't appear to be an array helper
var msg_buf = std.ArrayList(u8).init(allocator); var msg_buf: std.Io.Writer.Allocating = .init(allocator);
defer msg_buf.deinit(); defer msg_buf.deinit();
const w = msg_buf.writer(); const w = &msg_buf.writer;
try cbor.writeArrayHeader(w, 3); try cbor.writeArrayHeader(w, 3);
try cbor.writeValue(w, "cmd"); try cbor.writeValue(w, "cmd");
try cbor.writeValue(w, "rename_symbol_item"); try cbor.writeValue(w, "rename_symbol_item");
@ -1181,7 +1186,7 @@ pub fn rename_symbol(self: *Self, from: tp.pid_ref, file_path: []const u8, row:
line, line,
}); });
} }
self_.from.send_raw(.{ .buf = msg_buf.items }) catch return error.ClientFailed; self_.from.send_raw(.{ .buf = msg_buf.written() }) catch return error.ClientFailed;
} }
} }
} }
@ -1199,7 +1204,7 @@ pub fn rename_symbol(self: *Self, from: tp.pid_ref, file_path: []const u8, row:
// decode a WorkspaceEdit record which may have shape {"changes": {}} or {"documentChanges": []} // decode a WorkspaceEdit record which may have shape {"changes": {}} or {"documentChanges": []}
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspaceEdit // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspaceEdit
fn decode_rename_symbol_map(result: []const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void { fn decode_rename_symbol_map(result: []const u8, renames: *std.array_list.Managed(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void {
var iter = result; var iter = result;
var len = cbor.decodeMapHeader(&iter) catch return error.InvalidMessage; var len = cbor.decodeMapHeader(&iter) catch return error.InvalidMessage;
var changes: []const u8 = ""; var changes: []const u8 = "";
@ -1221,7 +1226,7 @@ fn decode_rename_symbol_map(result: []const u8, renames: *std.ArrayList(Rename))
return error.ClientFailed; return error.ClientFailed;
} }
fn decode_rename_symbol_changes(changes: []const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void { fn decode_rename_symbol_changes(changes: []const u8, renames: *std.array_list.Managed(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void {
var iter = changes; var iter = changes;
var files_len = cbor.decodeMapHeader(&iter) catch return error.InvalidMessage; var files_len = cbor.decodeMapHeader(&iter) catch return error.InvalidMessage;
while (files_len > 0) : (files_len -= 1) { while (files_len > 0) : (files_len -= 1) {
@ -1231,7 +1236,7 @@ fn decode_rename_symbol_changes(changes: []const u8, renames: *std.ArrayList(Ren
} }
} }
fn decode_rename_symbol_doc_changes(changes: []const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void { fn decode_rename_symbol_doc_changes(changes: []const u8, renames: *std.array_list.Managed(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void {
var iter = changes; var iter = changes;
var changes_len = cbor.decodeArrayHeader(&iter) catch return error.InvalidMessage; var changes_len = cbor.decodeArrayHeader(&iter) catch return error.InvalidMessage;
while (changes_len > 0) : (changes_len -= 1) { while (changes_len > 0) : (changes_len -= 1) {
@ -1258,7 +1263,7 @@ fn decode_rename_symbol_doc_changes(changes: []const u8, renames: *std.ArrayList
} }
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit
fn decode_rename_symbol_item(file_uri: []const u8, iter: *[]const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void { fn decode_rename_symbol_item(file_uri: []const u8, iter: *[]const u8, renames: *std.array_list.Managed(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void {
var text_edits_len = cbor.decodeArrayHeader(iter) catch return error.InvalidMessage; var text_edits_len = cbor.decodeArrayHeader(iter) catch return error.InvalidMessage;
while (text_edits_len > 0) : (text_edits_len -= 1) { while (text_edits_len > 0) : (text_edits_len -= 1) {
var m_range: ?Range = null; var m_range: ?Range = null;
@ -1366,15 +1371,15 @@ fn send_contents(
}; };
if (is_list) { if (is_list) {
var content = std.ArrayList(u8).init(std.heap.c_allocator); var content: std.Io.Writer.Allocating = .init(std.heap.c_allocator);
defer content.deinit(); defer content.deinit();
while (len > 0) : (len -= 1) { while (len > 0) : (len -= 1) {
if (try cbor.matchValue(&iter, cbor.extract(&value))) { if (try cbor.matchValue(&iter, cbor.extract(&value))) {
try content.appendSlice(value); try content.writer.writeAll(value);
if (len > 1) try content.appendSlice("\n"); if (len > 1) try content.writer.writeAll("\n");
} }
} }
return send_content_msg(to, tag, file_path, row, col, kind, content.items, range); return send_content_msg(to, tag, file_path, row, col, kind, content.written(), range);
} }
while (len > 0) : (len -= 1) { while (len > 0) : (len -= 1) {
@ -1604,7 +1609,7 @@ fn send_lsp_init_request(self: *Self, lsp: *const LSP, project_path: []const u8,
pub fn receive(self_: @This(), _: tp.message) !void { pub fn receive(self_: @This(), _: tp.message) !void {
self_.lsp.send_notification("initialized", .{}) catch return error.LspFailed; self_.lsp.send_notification("initialized", .{}) catch return error.LspFailed;
if (self_.lsp.pid.expired()) return error.LspFailed; if (self_.lsp.pid.expired()) return error.LspFailed;
self_.project.logger_lsp.print("initialized LSP: {s}", .{fmt_lsp_name_func(self_.language_server)}); self_.project.logger_lsp.print("initialized LSP: {f}", .{fmt_lsp_name_func(self_.language_server)});
} }
} = .{ } = .{
.language_server = try std.heap.c_allocator.dupe(u8, language_server), .language_server = try std.heap.c_allocator.dupe(u8, language_server),
@ -1916,18 +1921,14 @@ fn send_lsp_init_request(self: *Self, lsp: *const LSP, project_path: []const u8,
}, handler); }, handler);
} }
fn fmt_lsp_name_func(bytes: []const u8) std.fmt.Formatter(format_lsp_name_func) { fn fmt_lsp_name_func(bytes: []const u8) std.fmt.Formatter([]const u8, format_lsp_name_func) {
return .{ .data = bytes }; return .{ .data = bytes };
} }
fn format_lsp_name_func( fn format_lsp_name_func(
bytes: []const u8, bytes: []const u8,
comptime fmt: []const u8, writer: *std.Io.Writer,
options: std.fmt.FormatOptions, ) std.Io.Writer.Error!void {
writer: anytype,
) !void {
_ = fmt;
_ = options;
var iter: []const u8 = bytes; var iter: []const u8 = bytes;
var len = cbor.decodeArrayHeader(&iter) catch return; var len = cbor.decodeArrayHeader(&iter) catch return;
var first: bool = true; var first: bool = true;
@ -1942,7 +1943,7 @@ fn format_lsp_name_func(
const eol = '\n'; const eol = '\n';
pub const GetLineOfFileError = (OutOfMemoryError || std.fs.File.OpenError || std.fs.File.Reader.Error); pub const GetLineOfFileError = (OutOfMemoryError || std.fs.File.OpenError || std.fs.File.ReadError);
fn get_line_of_file(allocator: std.mem.Allocator, file_path: []const u8, line_: usize) GetLineOfFileError![]const u8 { fn get_line_of_file(allocator: std.mem.Allocator, file_path: []const u8, line_: usize) GetLineOfFileError![]const u8 {
const line = line_ + 1; const line = line_ + 1;
@ -1951,7 +1952,7 @@ fn get_line_of_file(allocator: std.mem.Allocator, file_path: []const u8, line_:
const stat = try file.stat(); const stat = try file.stat();
var buf = try allocator.alloc(u8, @intCast(stat.size)); var buf = try allocator.alloc(u8, @intCast(stat.size));
defer allocator.free(buf); defer allocator.free(buf);
const read_size = try file.reader().readAll(buf); const read_size = try file.readAll(buf);
if (read_size != @as(@TypeOf(read_size), @intCast(stat.size))) if (read_size != @as(@TypeOf(read_size), @intCast(stat.size)))
@panic("get_line_of_file: buffer underrun"); @panic("get_line_of_file: buffer underrun");

View file

@ -224,33 +224,33 @@ pub const Leaf = struct {
} else error.BufferUnderrun; } else error.BufferUnderrun;
} }
inline fn dump(self: *const Leaf, l: *ArrayList(u8), abs_col: usize, metrics: Metrics) !void { inline fn dump(self: *const Leaf, l: *std.Io.Writer, abs_col: usize, metrics: Metrics) !void {
var buf: [16]u8 = undefined; var buf: [16]u8 = undefined;
const wcwidth = try std.fmt.bufPrint(&buf, "{d}", .{self.width(abs_col, metrics)}); const wcwidth = try std.fmt.bufPrint(&buf, "{d}", .{self.width(abs_col, metrics)});
if (self.bol) if (self.bol)
try l.appendSlice("BOL "); try l.writeAll("BOL ");
try l.appendSlice(wcwidth); try l.writeAll(wcwidth);
try l.append('"'); try l.writeAll("\"");
try debug_render_chunk(self.buf, l, metrics); try debug_render_chunk(self.buf, l, metrics);
try l.appendSlice("\" "); try l.writeAll("\" ");
if (self.eol) if (self.eol)
try l.appendSlice("EOL "); try l.writeAll("EOL ");
} }
fn debug_render_chunk(chunk: []const u8, l: *ArrayList(u8), metrics: Metrics) !void { fn debug_render_chunk(chunk: []const u8, l: *std.Io.Writer, metrics: Metrics) !void {
var cols: c_int = 0; var cols: c_int = 0;
var buf = chunk; var buf = chunk;
while (buf.len > 0) { while (buf.len > 0) {
switch (buf[0]) { switch (buf[0]) {
'\x00'...(' ' - 1) => { '\x00'...(' ' - 1) => {
const control = unicode.control_code_to_unicode(buf[0]); const control = unicode.control_code_to_unicode(buf[0]);
try l.appendSlice(control); try l.writeAll(control);
buf = buf[1..]; buf = buf[1..];
}, },
else => { else => {
const bytes = metrics.egc_length(metrics, buf, &cols, 0); const bytes = metrics.egc_length(metrics, buf, &cols, 0);
var buf_: [4096]u8 = undefined; var buf_: [4096]u8 = undefined;
try l.appendSlice(try std.fmt.bufPrint(&buf_, "{s}", .{std.fmt.fmtSliceEscapeLower(buf[0..bytes])})); try l.writeAll(try std.fmt.bufPrint(&buf_, "{f}", .{std.ascii.hexEscape(buf[0..bytes], .lower)}));
buf = buf[bytes..]; buf = buf[bytes..];
}, },
} }
@ -477,21 +477,21 @@ const Node = union(enum) {
} }
} }
fn debug_render_tree(self: *const Node, l: *ArrayList(u8), d: usize) void { fn debug_render_tree(self: *const Node, l: *std.Io.Writer, d: usize) void {
switch (self.*) { switch (self.*) {
.node => |*node| { .node => |*node| {
l.append('(') catch {}; l.writeAll("(") catch {};
node.left.debug_render_tree(l, d + 1); node.left.debug_render_tree(l, d + 1);
l.append(' ') catch {}; l.writeAll(" ") catch {};
node.right.debug_render_tree(l, d + 1); node.right.debug_render_tree(l, d + 1);
l.append(')') catch {}; l.writeAll(")") catch {};
}, },
.leaf => |*leaf| { .leaf => |*leaf| {
l.append('"') catch {}; l.writeAll("\"") catch {};
l.appendSlice(leaf.buf) catch {}; l.writeAll(leaf.buf) catch {};
if (leaf.eol) if (leaf.eol)
l.appendSlice("\\n") catch {}; l.writeAll("\\n") catch {};
l.append('"') catch {}; l.writeAll("\"") catch {};
}, },
} }
} }
@ -554,22 +554,23 @@ const Node = union(enum) {
return pred(egc); return pred(egc);
} }
pub fn get_line_width_map(self: *const Node, line: usize, map: *ArrayList(usize), metrics: Metrics) error{ Stop, NoSpaceLeft }!void { pub fn get_line_width_map(self: *const Node, line: usize, map: *ArrayList(usize), allocator: Allocator, metrics: Metrics) error{ Stop, NoSpaceLeft }!void {
const Ctx = struct { const Ctx = struct {
allocator: Allocator,
map: *ArrayList(usize), map: *ArrayList(usize),
wcwidth: usize = 0, wcwidth: usize = 0,
fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Metrics) Walker { fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Metrics) Walker {
const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_)));
var n = egc.len; var n = egc.len;
while (n > 0) : (n -= 1) { while (n > 0) : (n -= 1) {
const p = ctx.map.addOne() catch |e| return .{ .err = e }; const p = ctx.map.addOne(ctx.allocator) catch |e| return .{ .err = e };
p.* = ctx.wcwidth; p.* = ctx.wcwidth;
} }
ctx.wcwidth += wcwidth; ctx.wcwidth += wcwidth;
return if (egc[0] == '\n') Walker.stop else Walker.keep_walking; return if (egc[0] == '\n') Walker.stop else Walker.keep_walking;
} }
}; };
var ctx: Ctx = .{ .map = map }; var ctx: Ctx = .{ .allocator = allocator, .map = map };
self.walk_egc_forward(line, Ctx.walker, &ctx, metrics) catch |e| return switch (e) { self.walk_egc_forward(line, Ctx.walker, &ctx, metrics) catch |e| return switch (e) {
error.NoSpaceLeft => error.NoSpaceLeft, error.NoSpaceLeft => error.NoSpaceLeft,
else => error.Stop, else => error.Stop,
@ -926,7 +927,7 @@ const Node = union(enum) {
return .{ line, col, self }; return .{ line, col, self };
} }
pub fn store(self: *const Node, writer: anytype, eol_mode: EolMode) !void { pub fn store(self: *const Node, writer: *std.Io.Writer, eol_mode: EolMode) !void {
switch (self.*) { switch (self.*) {
.node => |*node| { .node => |*node| {
try node.left.store(writer, eol_mode); try node.left.store(writer, eol_mode);
@ -943,7 +944,7 @@ const Node = union(enum) {
} }
pub const FindAllCallback = fn (data: *anyopaque, begin_row: usize, begin_col: usize, end_row: usize, end_col: usize) error{Stop}!void; pub const FindAllCallback = fn (data: *anyopaque, begin_row: usize, begin_col: usize, end_row: usize, end_col: usize) error{Stop}!void;
pub fn find_all_ranges(self: *const Node, pattern: []const u8, data: *anyopaque, callback: *const FindAllCallback, allocator: Allocator) !void { pub fn find_all_ranges(self: *const Node, pattern: []const u8, data: *anyopaque, callback: *const FindAllCallback, allocator: Allocator) error{ OutOfMemory, Stop }!void {
const Ctx = struct { const Ctx = struct {
pattern: []const u8, pattern: []const u8,
data: *anyopaque, data: *anyopaque,
@ -952,9 +953,27 @@ const Node = union(enum) {
pos: usize = 0, pos: usize = 0,
buf: []u8, buf: []u8,
rest: []u8 = "", rest: []u8 = "",
writer: std.Io.Writer,
const Ctx = @This(); const Ctx = @This();
const Writer = std.io.Writer(*Ctx, error{Stop}, write); fn drain(w: *std.Io.Writer, data_: []const []const u8, splat: usize) std.Io.Writer.Error!usize {
fn write(ctx: *Ctx, bytes: []const u8) error{Stop}!usize { const ctx: *Ctx = @alignCast(@fieldParentPtr("writer", w));
std.debug.assert(splat == 0);
if (data_.len == 0) return 0;
var written: usize = 0;
for (data_[0 .. data_.len - 1]) |bytes| {
written += try ctx.write(bytes);
}
const pattern_ = data_[data_.len - 1];
switch (pattern_.len) {
0 => return written,
else => for (0..splat) |_| {
written += try ctx.write(pattern_);
},
}
return written;
}
fn write(ctx: *Ctx, bytes: []const u8) std.Io.Writer.Error!usize {
var input = bytes; var input = bytes;
while (true) { while (true) {
const input_consume_size = @min(ctx.buf.len - ctx.rest.len, input.len); const input_consume_size = @min(ctx.buf.len - ctx.rest.len, input.len);
@ -975,7 +994,7 @@ const Node = union(enum) {
ctx.skip(&i, ctx.pattern.len); ctx.skip(&i, ctx.pattern.len);
const end_row = ctx.line + 1; const end_row = ctx.line + 1;
const end_pos = ctx.pos; const end_pos = ctx.pos;
try ctx.callback(ctx.data, begin_row, begin_pos, end_row, end_pos); ctx.callback(ctx.data, begin_row, begin_pos, end_row, end_pos) catch return error.WriteFailed;
} else { } else {
ctx.skip(&i, 1); ctx.skip(&i, 1);
} }
@ -1001,18 +1020,25 @@ const Node = union(enum) {
i.* += 1; i.* += 1;
} }
} }
fn writer(ctx: *Ctx) Writer {
return .{ .context = ctx };
}
}; };
var ctx: Ctx = .{ var ctx: Ctx = .{
.pattern = pattern, .pattern = pattern,
.data = data, .data = data,
.callback = callback, .callback = callback,
.buf = try allocator.alloc(u8, pattern.len * 2), .buf = try allocator.alloc(u8, pattern.len * 2),
.writer = .{
.vtable = &.{
.drain = Ctx.drain,
.flush = std.Io.Writer.noopFlush,
.rebase = std.Io.Writer.failingRebase,
},
.buffer = &.{},
},
}; };
defer allocator.free(ctx.buf); defer allocator.free(ctx.buf);
return self.store(ctx.writer(), .lf); return self.store(&ctx.writer, .lf) catch |e| switch (e) {
error.WriteFailed => error.Stop,
};
} }
pub fn get_byte_pos(self: *const Node, pos_: Cursor, metrics_: Metrics, eol_mode: EolMode) !usize { pub fn get_byte_pos(self: *const Node, pos_: Cursor, metrics_: Metrics, eol_mode: EolMode) !usize {
@ -1065,10 +1091,10 @@ const Node = union(enum) {
} }
pub fn debug_render_chunks(self: *const Node, allocator: std.mem.Allocator, line: usize, metrics_: Metrics) ![]const u8 { pub fn debug_render_chunks(self: *const Node, allocator: std.mem.Allocator, line: usize, metrics_: Metrics) ![]const u8 {
var output = std.ArrayList(u8).init(allocator); var output: std.Io.Writer.Allocating = .init(allocator);
defer output.deinit(); defer output.deinit();
const ctx_ = struct { const ctx_ = struct {
l: *ArrayList(u8), l: *std.Io.Writer,
wcwidth: usize = 0, wcwidth: usize = 0,
fn walker(ctx_: *anyopaque, leaf: *const Leaf, metrics: Metrics) Walker { fn walker(ctx_: *anyopaque, leaf: *const Leaf, metrics: Metrics) Walker {
const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_)));
@ -1077,21 +1103,21 @@ const Node = union(enum) {
return if (!leaf.eol) Walker.keep_walking else Walker.stop; return if (!leaf.eol) Walker.keep_walking else Walker.stop;
} }
}; };
var ctx: ctx_ = .{ .l = &output }; var ctx: ctx_ = .{ .l = &output.writer };
const found = self.walk_from_line_begin_const(line, ctx_.walker, &ctx, metrics_) catch true; const found = self.walk_from_line_begin_const(line, ctx_.walker, &ctx, metrics_) catch true;
if (!found) return error.NotFound; if (!found) return error.NotFound;
var buf: [16]u8 = undefined; var buf: [16]u8 = undefined;
const wcwidth = try std.fmt.bufPrint(&buf, "{d}", .{ctx.wcwidth}); const wcwidth = try std.fmt.bufPrint(&buf, "{d}", .{ctx.wcwidth});
try output.appendSlice(wcwidth); try output.writer.writeAll(wcwidth);
return output.toOwnedSlice(); return output.toOwnedSlice();
} }
pub fn debug_line_render_tree(self: *const Node, allocator: std.mem.Allocator, line: usize) ![]const u8 { pub fn debug_line_render_tree(self: *const Node, allocator: std.mem.Allocator, line: usize) ![]const u8 {
return if (self.find_line_node(line)) |n| blk: { return if (self.find_line_node(line)) |n| blk: {
var l = std.ArrayList(u8).init(allocator); var l: std.Io.Writer.Allocating = .init(allocator);
defer l.deinit(); defer l.deinit();
n.debug_render_tree(&l, 0); n.debug_render_tree(&l.writer, 0);
break :blk l.toOwnedSlice(); break :blk l.toOwnedSlice();
} else error.NotFound; } else error.NotFound;
} }
@ -1152,28 +1178,24 @@ fn new_file(self: *const Self, file_exists: *bool) error{OutOfMemory}!Root {
return Leaf.new(self.allocator, "", true, false); return Leaf.new(self.allocator, "", true, false);
} }
pub fn LoadError(comptime reader_error: anytype) type { pub const LoadError =
return error{ error{
OutOfMemory, OutOfMemory,
BufferUnderrun, BufferUnderrun,
DanglingSurrogateHalf, DanglingSurrogateHalf,
ExpectedSecondSurrogateHalf, ExpectedSecondSurrogateHalf,
UnexpectedSecondSurrogateHalf, UnexpectedSecondSurrogateHalf,
Unexpected, Unexpected,
} || reader_error; } || std.Io.Reader.Error;
}
pub fn load(self: *const Self, reader: anytype, size: usize, eol_mode: *EolMode, utf8_sanitized: *bool) LoadError(@TypeOf(reader).Error)!Root { pub fn load(self: *const Self, reader: *std.Io.Reader, eol_mode: *EolMode, utf8_sanitized: *bool) LoadError!Root {
const lf = '\n'; const lf = '\n';
const cr = '\r'; const cr = '\r';
var buf = try self.external_allocator.alloc(u8, size);
const self_ = @constCast(self); const self_ = @constCast(self);
const read_size = try reader.readAll(buf); var read_buffer: ArrayList(u8) = .empty;
if (read_size != size) defer read_buffer.deinit(self.external_allocator);
return error.BufferUnderrun; try reader.appendRemainingUnlimited(self.external_allocator, &read_buffer);
const final_read = try reader.read(buf); var buf = try read_buffer.toOwnedSlice(self.external_allocator);
if (final_read != 0)
@panic("unexpected data in final read");
if (!std.unicode.utf8ValidateSlice(buf)) { if (!std.unicode.utf8ValidateSlice(buf)) {
const converted = try unicode.utf8_sanitize(self.external_allocator, buf); const converted = try unicode.utf8_sanitize(self.external_allocator, buf);
@ -1213,14 +1235,12 @@ pub fn load(self: *const Self, reader: anytype, size: usize, eol_mode: *EolMode,
return Node.merge_in_place(leaves, self.allocator); return Node.merge_in_place(leaves, self.allocator);
} }
pub const LoadFromStringError = LoadError(error{}); pub fn load_from_string(self: *const Self, s: []const u8, eol_mode: *EolMode, utf8_sanitized: *bool) LoadError!Root {
var reader = std.Io.Reader.fixed(s);
pub fn load_from_string(self: *const Self, s: []const u8, eol_mode: *EolMode, utf8_sanitized: *bool) LoadFromStringError!Root { return self.load(&reader, eol_mode, utf8_sanitized);
var stream = std.io.fixedBufferStream(s);
return self.load(stream.reader(), s.len, eol_mode, utf8_sanitized);
} }
pub fn load_from_string_and_update(self: *Self, file_path: []const u8, s: []const u8) LoadFromStringError!void { pub fn load_from_string_and_update(self: *Self, file_path: []const u8, s: []const u8) LoadError!void {
self.root = try self.load_from_string(s, &self.file_eol_mode, &self.file_utf8_sanitized); self.root = try self.load_from_string(s, &self.file_eol_mode, &self.file_utf8_sanitized);
self.set_file_path(file_path); self.set_file_path(file_path);
self.last_save = self.root; self.last_save = self.root;
@ -1229,7 +1249,7 @@ pub fn load_from_string_and_update(self: *Self, file_path: []const u8, s: []cons
self.mtime = std.time.milliTimestamp(); self.mtime = std.time.milliTimestamp();
} }
pub fn reset_from_string_and_update(self: *Self, s: []const u8) LoadFromStringError!void { pub fn reset_from_string_and_update(self: *Self, s: []const u8) LoadError!void {
self.root = try self.load_from_string(s, &self.file_eol_mode, &self.file_utf8_sanitized); self.root = try self.load_from_string(s, &self.file_eol_mode, &self.file_utf8_sanitized);
self.last_save = self.root; self.last_save = self.root;
self.last_save_eol_mode = self.file_eol_mode; self.last_save_eol_mode = self.file_eol_mode;
@ -1278,7 +1298,7 @@ pub const LoadFromFileError = error{
ProcessNotFound, ProcessNotFound,
Canceled, Canceled,
PermissionDenied, PermissionDenied,
}; } || LoadError;
pub fn load_from_file( pub fn load_from_file(
self: *const Self, self: *const Self,
@ -1294,8 +1314,9 @@ pub fn load_from_file(
file_exists.* = true; file_exists.* = true;
defer file.close(); defer file.close();
const stat = try file.stat(); var read_buf: [4096]u8 = undefined;
return self.load(file.reader(), @intCast(stat.size), eol_mode, utf8_sanitized); var file_reader = file.reader(&read_buf);
return self.load(&file_reader.interface, eol_mode, utf8_sanitized);
} }
pub fn load_from_file_and_update(self: *Self, file_path: []const u8) LoadFromFileError!void { pub fn load_from_file_and_update(self: *Self, file_path: []const u8) LoadFromFileError!void {
@ -1332,16 +1353,9 @@ pub fn store_to_string(self: *const Self, allocator: Allocator, eol_mode: EolMod
return s.toOwnedSlice(); return s.toOwnedSlice();
} }
fn store_to_file_const(self: *const Self, file: anytype) StoreToFileError!void { fn store_to_file_const(self: *const Self, writer: *std.Io.Writer) StoreToFileError!void {
const buffer_size = 4096 * 16; // 64KB try self.root.store(writer, self.file_eol_mode);
const BufferedWriter = std.io.BufferedWriter(buffer_size, std.fs.File.Writer); try writer.flush();
const Writer = std.io.Writer(*BufferedWriter, BufferedWriter.Error, BufferedWriter.write);
const file_writer: std.fs.File.Writer = file.writer();
var buffered_writer: BufferedWriter = .{ .unbuffered_writer = file_writer };
try self.root.store(Writer{ .context = &buffered_writer }, self.file_eol_mode);
try buffered_writer.flush();
} }
pub const StoreToFileError = error{ pub const StoreToFileError = error{
@ -1385,13 +1399,15 @@ pub const StoreToFileError = error{
WouldBlock, WouldBlock,
PermissionDenied, PermissionDenied,
MessageTooBig, MessageTooBig,
WriteFailed,
}; };
pub fn store_to_existing_file_const(self: *const Self, file_path: []const u8) StoreToFileError!void { pub fn store_to_existing_file_const(self: *const Self, file_path: []const u8) StoreToFileError!void {
const stat = try cwd().statFile(file_path); const stat = try cwd().statFile(file_path);
var atomic = try cwd().atomicFile(file_path, .{ .mode = stat.mode }); var write_buffer: [4096]u8 = undefined;
var atomic = try cwd().atomicFile(file_path, .{ .mode = stat.mode, .write_buffer = &write_buffer });
defer atomic.deinit(); defer atomic.deinit();
try self.store_to_file_const(atomic.file); try self.store_to_file_const(&atomic.file_writer.interface);
try atomic.finish(); try atomic.finish();
} }
@ -1400,7 +1416,9 @@ pub fn store_to_new_file_const(self: *const Self, file_path: []const u8) StoreTo
try cwd().makePath(dir_name); try cwd().makePath(dir_name);
const file = try cwd().createFile(file_path, .{ .read = true, .truncate = true }); const file = try cwd().createFile(file_path, .{ .read = true, .truncate = true });
defer file.close(); defer file.close();
try self.store_to_file_const(file); var write_buffer: [4096]u8 = undefined;
var writer = file.writer(&write_buffer);
try self.store_to_file_const(&writer.interface);
} }
pub fn store_to_file_and_clean(self: *Self, file_path: []const u8) StoreToFileError!void { pub fn store_to_file_and_clean(self: *Self, file_path: []const u8) StoreToFileError!void {

View file

@ -37,7 +37,7 @@ pub fn open_file(self: *Self, file_path: []const u8) Buffer.LoadFromFileError!*B
return buffer; return buffer;
} }
pub fn open_scratch(self: *Self, file_path: []const u8, content: []const u8) Buffer.LoadFromStringError!*Buffer { pub fn open_scratch(self: *Self, file_path: []const u8, content: []const u8) Buffer.LoadError!*Buffer {
const buffer = if (self.buffers.get(file_path)) |buffer| buffer else blk: { const buffer = if (self.buffers.get(file_path)) |buffer| buffer else blk: {
var buffer = try Buffer.create(self.allocator); var buffer = try Buffer.create(self.allocator);
errdefer buffer.deinit(); errdefer buffer.deinit();

View file

@ -14,13 +14,13 @@ pub const Context = struct {
pub fn fmt(value: anytype) Context { pub fn fmt(value: anytype) Context {
context_buffer.clearRetainingCapacity(); context_buffer.clearRetainingCapacity();
cbor.writeValue(context_buffer.writer(), value) catch @panic("command.Context.fmt failed"); cbor.writeValue(&context_buffer.writer, value) catch @panic("command.Context.fmt failed");
return .{ .args = .{ .buf = context_buffer.items } }; return .{ .args = .{ .buf = context_buffer.written() } };
} }
}; };
const context_buffer_allocator = std.heap.c_allocator; const context_buffer_allocator = std.heap.c_allocator;
threadlocal var context_buffer: std.ArrayList(u8) = std.ArrayList(u8).init(context_buffer_allocator); threadlocal var context_buffer: std.Io.Writer.Allocating = .init(context_buffer_allocator);
pub const fmt = Context.fmt; pub const fmt = Context.fmt;
const Vtable = struct { const Vtable = struct {
@ -96,12 +96,12 @@ pub fn Closure(comptime T: type) type {
} }
const CommandTable = std.ArrayList(?*Vtable); const CommandTable = std.ArrayList(?*Vtable);
pub var commands: CommandTable = CommandTable.init(command_table_allocator); pub var commands: CommandTable = .empty;
var command_names: std.StringHashMap(ID) = std.StringHashMap(ID).init(command_table_allocator); var command_names: std.StringHashMap(ID) = std.StringHashMap(ID).init(command_table_allocator);
const command_table_allocator = std.heap.c_allocator; const command_table_allocator = std.heap.c_allocator;
fn addCommand(cmd: *Vtable) !ID { fn addCommand(cmd: *Vtable) !ID {
try commands.append(cmd); try commands.append(command_table_allocator, cmd);
return commands.items.len - 1; return commands.items.len - 1;
} }
@ -121,9 +121,9 @@ pub fn execute(id: ID, ctx: Context) tp.result {
if (len < 1) { if (len < 1) {
tp.trace(tp.channel.debug, .{ "command", "execute", id, get_name(id) }); tp.trace(tp.channel.debug, .{ "command", "execute", id, get_name(id) });
} else { } else {
var msg_cb = std.ArrayList(u8).init(command_table_allocator); var msg_cb: std.Io.Writer.Allocating = .init(command_table_allocator);
defer msg_cb.deinit(); defer msg_cb.deinit();
const writer = msg_cb.writer(); const writer = &msg_cb.writer;
cbor.writeArrayHeader(writer, 4 + len) catch break :trace; cbor.writeArrayHeader(writer, 4 + len) catch break :trace;
cbor.writeValue(writer, "command") catch break :trace; cbor.writeValue(writer, "command") catch break :trace;
cbor.writeValue(writer, "execute") catch break :trace; cbor.writeValue(writer, "execute") catch break :trace;
@ -132,9 +132,9 @@ pub fn execute(id: ID, ctx: Context) tp.result {
while (len > 0) : (len -= 1) { while (len > 0) : (len -= 1) {
var arg: []const u8 = undefined; var arg: []const u8 = undefined;
if (cbor.matchValue(&iter, cbor.extract_cbor(&arg)) catch break :trace) if (cbor.matchValue(&iter, cbor.extract_cbor(&arg)) catch break :trace)
msg_cb.appendSlice(arg) catch break :trace; writer.writeAll(arg) catch break :trace;
} }
const msg: tp.message = .{ .buf = msg_cb.items }; const msg: tp.message = .{ .buf = msg_cb.written() };
tp.trace(tp.channel.debug, msg); tp.trace(tp.channel.debug, msg);
} }
} }

View file

@ -122,7 +122,8 @@ pub fn diff(allocator: std.mem.Allocator, dst: []const u8, src: []const u8) ![]D
var dizzy_edits = std.ArrayListUnmanaged(dizzy.Edit){}; var dizzy_edits = std.ArrayListUnmanaged(dizzy.Edit){};
var scratch = std.ArrayListUnmanaged(u32){}; var scratch = std.ArrayListUnmanaged(u32){};
var diffs = std.ArrayList(Diff).init(allocator); var diffs: std.ArrayList(Diff) = .empty;
errdefer diffs.deinit(allocator);
const scratch_len = 4 * (dst.len + src.len) + 2; const scratch_len = 4 * (dst.len + src.len) + 2;
try scratch.ensureTotalCapacity(arena, scratch_len); try scratch.ensureTotalCapacity(arena, scratch_len);
@ -131,7 +132,7 @@ pub fn diff(allocator: std.mem.Allocator, dst: []const u8, src: []const u8) ![]D
try dizzy.PrimitiveSliceDiffer(u8).diff(arena, &dizzy_edits, src, dst, scratch.items); try dizzy.PrimitiveSliceDiffer(u8).diff(arena, &dizzy_edits, src, dst, scratch.items);
if (dizzy_edits.items.len > 2) if (dizzy_edits.items.len > 2)
try diffs.ensureTotalCapacity((dizzy_edits.items.len - 1) / 2); try diffs.ensureTotalCapacity(allocator, (dizzy_edits.items.len - 1) / 2);
var lines_dst: usize = 0; var lines_dst: usize = 0;
var pos_src: usize = 0; var pos_src: usize = 0;
@ -152,7 +153,7 @@ pub fn diff(allocator: std.mem.Allocator, dst: []const u8, src: []const u8) ![]D
pos_dst += dist; pos_dst += dist;
const line_start_dst: usize = lines_dst; const line_start_dst: usize = lines_dst;
scan_char(dst[dizzy_edit.range.start..dizzy_edit.range.end], &lines_dst, '\n', null); scan_char(dst[dizzy_edit.range.start..dizzy_edit.range.end], &lines_dst, '\n', null);
(try diffs.addOne()).* = .{ (try diffs.addOne(allocator)).* = .{
.kind = .insert, .kind = .insert,
.line = line_start_dst, .line = line_start_dst,
.offset = last_offset, .offset = last_offset,
@ -165,7 +166,7 @@ pub fn diff(allocator: std.mem.Allocator, dst: []const u8, src: []const u8) ![]D
const dist = dizzy_edit.range.end - dizzy_edit.range.start; const dist = dizzy_edit.range.end - dizzy_edit.range.start;
pos_src += dist; pos_src += dist;
pos_dst += 0; pos_dst += 0;
(try diffs.addOne()).* = .{ (try diffs.addOne(allocator)).* = .{
.kind = .delete, .kind = .delete,
.line = lines_dst, .line = lines_dst,
.offset = last_offset, .offset = last_offset,
@ -176,7 +177,7 @@ pub fn diff(allocator: std.mem.Allocator, dst: []const u8, src: []const u8) ![]D
}, },
} }
} }
return diffs.toOwnedSlice(); return diffs.toOwnedSlice(allocator);
} }
pub fn get_edits(allocator: std.mem.Allocator, dst: []const u8, src: []const u8) ![]Edit { pub fn get_edits(allocator: std.mem.Allocator, dst: []const u8, src: []const u8) ![]Edit {

View file

@ -42,10 +42,10 @@ fn from_file_type(file_type: syntax.FileType) @This() {
pub fn get_default(allocator: std.mem.Allocator, file_type_name: []const u8) ![]const u8 { pub fn get_default(allocator: std.mem.Allocator, file_type_name: []const u8) ![]const u8 {
const file_type = syntax.FileType.get_by_name_static(file_type_name) orelse return error.UnknownFileType; const file_type = syntax.FileType.get_by_name_static(file_type_name) orelse return error.UnknownFileType;
const config = from_file_type(file_type); const config = from_file_type(file_type);
var content = std.ArrayListUnmanaged(u8).empty; var content: std.Io.Writer.Allocating = .init(allocator);
defer content.deinit(allocator); defer content.deinit();
root.write_config_to_writer(@This(), config, content.writer(allocator)) catch {}; root.write_config_to_writer(@This(), config, &content.writer) catch {};
return content.toOwnedSlice(allocator); return content.toOwnedSlice();
} }
pub fn get_all_names() []const []const u8 { pub fn get_all_names() []const []const u8 {
@ -93,21 +93,23 @@ pub fn get(file_type_name: []const u8) !?@This() {
} }
pub fn get_config_file_path(allocator: std.mem.Allocator, file_type: []const u8) ![]u8 { pub fn get_config_file_path(allocator: std.mem.Allocator, file_type: []const u8) ![]u8 {
var stream = std.ArrayList(u8).fromOwnedSlice(allocator, try get_config_dir_path(allocator)); var stream: std.Io.Writer.Allocating = .initOwnedSlice(allocator, try get_config_dir_path(allocator));
const writer = stream.writer(); defer stream.deinit();
const writer = &stream.writer;
_ = try writer.writeAll(file_type); _ = try writer.writeAll(file_type);
_ = try writer.writeAll(".conf"); _ = try writer.writeAll(".conf");
return stream.toOwnedSlice(); return stream.toOwnedSlice();
} }
fn get_config_dir_path(allocator: std.mem.Allocator) ![]u8 { fn get_config_dir_path(allocator: std.mem.Allocator) ![]u8 {
var stream = std.ArrayList(u8).init(allocator); var stream: std.Io.Writer.Allocating = .init(allocator);
const writer = stream.writer(); defer stream.deinit();
const writer = &stream.writer;
_ = try writer.writeAll(try root.get_config_dir()); _ = try writer.writeAll(try root.get_config_dir());
_ = try writer.writeByte(std.fs.path.sep); _ = try writer.writeByte(std.fs.path.sep);
_ = try writer.writeAll("file_type"); _ = try writer.writeAll("file_type");
_ = try writer.writeByte(std.fs.path.sep); _ = try writer.writeByte(std.fs.path.sep);
std.fs.makeDirAbsolute(stream.items) catch |e| switch (e) { std.fs.makeDirAbsolute(stream.written()) catch |e| switch (e) {
error.PathAlreadyExists => {}, error.PathAlreadyExists => {},
else => return e, else => return e,
}; };

View file

@ -3,7 +3,7 @@ const tp = @import("thespian");
const shell = @import("shell"); const shell = @import("shell");
const bin_path = @import("bin_path"); const bin_path = @import("bin_path");
pub const Error = error{ OutOfMemory, GitNotFound, GitCallFailed }; pub const Error = error{ OutOfMemory, GitNotFound, GitCallFailed, WriteFailed };
const log_execute = false; const log_execute = false;
@ -208,15 +208,16 @@ fn git_err(
) Error!void { ) Error!void {
const cbor = @import("cbor"); const cbor = @import("cbor");
const git_binary = get_git() orelse return error.GitNotFound; const git_binary = get_git() orelse return error.GitNotFound;
var buf: std.ArrayListUnmanaged(u8) = .empty; var buf: std.Io.Writer.Allocating = .init(allocator);
const writer = buf.writer(allocator); defer buf.deinit();
const writer = &buf.writer;
switch (@typeInfo(@TypeOf(cmd))) { switch (@typeInfo(@TypeOf(cmd))) {
.@"struct" => |info| if (info.is_tuple) { .@"struct" => |info| if (info.is_tuple) {
try cbor.writeArrayHeader(writer, info.fields.len + 1); try cbor.writeArrayHeader(writer, info.fields.len + 1);
try cbor.writeValue(writer, git_binary); try cbor.writeValue(writer, git_binary);
inline for (info.fields) |f| inline for (info.fields) |f|
try cbor.writeValue(writer, @field(cmd, f.name)); try cbor.writeValue(writer, @field(cmd, f.name));
return shell.execute(allocator, .{ .buf = buf.items }, .{ return shell.execute(allocator, .{ .buf = buf.written() }, .{
.context = context, .context = context,
.out = to_shell_output_handler(out), .out = to_shell_output_handler(out),
.err = to_shell_output_handler(err), .err = to_shell_output_handler(err),

View file

@ -137,11 +137,11 @@ pub fn get_namespaces(allocator: std.mem.Allocator) ![]const []const u8 {
for (namespaces) |namespace| allocator.free(namespace); for (namespaces) |namespace| allocator.free(namespace);
allocator.free(namespaces); allocator.free(namespaces);
} }
var result = std.ArrayList([]const u8).init(allocator); var result: std.ArrayList([]const u8) = .empty;
try result.append(try allocator.dupe(u8, "flow")); try result.append(allocator, try allocator.dupe(u8, "flow"));
try result.append(try allocator.dupe(u8, "emacs")); try result.append(allocator, try allocator.dupe(u8, "emacs"));
try result.append(try allocator.dupe(u8, "vim")); try result.append(allocator, try allocator.dupe(u8, "vim"));
try result.append(try allocator.dupe(u8, "helix")); try result.append(allocator, try allocator.dupe(u8, "helix"));
for (namespaces) |namespace| { for (namespaces) |namespace| {
var exists = false; var exists = false;
for (result.items) |existing| for (result.items) |existing|
@ -150,9 +150,9 @@ pub fn get_namespaces(allocator: std.mem.Allocator) ![]const []const u8 {
break; break;
}; };
if (!exists) if (!exists)
try result.append(try allocator.dupe(u8, namespace)); try result.append(allocator, try allocator.dupe(u8, namespace));
} }
return result.toOwnedSlice(); return result.toOwnedSlice(allocator);
} }
pub fn get_namespace() []const u8 { pub fn get_namespace() []const u8 {
@ -198,7 +198,7 @@ fn get_mode_binding_set(mode_name: []const u8, insert_command: []const u8) LoadE
return binding_set; return binding_set;
} }
pub const LoadError = (error{ NotFound, NotAnObject } || std.json.ParseError(std.json.Scanner) || parse_flow.ParseError || parse_vim.ParseError || std.json.ParseFromValueError); pub const LoadError = (error{ NotFound, NotAnObject, WriteFailed } || std.json.ParseError(std.json.Scanner) || parse_flow.ParseError || parse_vim.ParseError || std.json.ParseFromValueError);
///A collection of modes that represent a switchable editor emulation ///A collection of modes that represent a switchable editor emulation
const Namespace = struct { const Namespace = struct {
@ -320,7 +320,7 @@ const Command = struct {
return args.len == 1 and args[0] == .integer; return args.len == 1 and args[0] == .integer;
} }
fn load(allocator: std.mem.Allocator, tokens: []const std.json.Value) (parse_flow.ParseError || parse_vim.ParseError)!Command { fn load(allocator: std.mem.Allocator, tokens: []const std.json.Value) (error{WriteFailed} || parse_flow.ParseError || parse_vim.ParseError)!Command {
if (tokens.len == 0) return error.InvalidFormat; if (tokens.len == 0) return error.InvalidFormat;
var state: enum { command, args } = .command; var state: enum { command, args } = .command;
var args = std.ArrayListUnmanaged(std.json.Value){}; var args = std.ArrayListUnmanaged(std.json.Value){};
@ -343,11 +343,10 @@ const Command = struct {
switch (token) { switch (token) {
.string, .integer, .float, .bool => {}, .string, .integer, .float, .bool => {},
else => { else => {
var json = std.ArrayList(u8).init(allocator); const json = try std.json.Stringify.valueAlloc(allocator, token, .{});
defer json.deinit(); defer allocator.free(json);
std.json.stringify(token, .{}, json.writer()) catch {};
const logger = log.logger("keybind"); const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: invalid command argument '{s}'", .{json.items}); logger.print_err("keybind.load", "ERROR: invalid command argument '{s}'", .{json});
logger.deinit(); logger.deinit();
return error.InvalidFormat; return error.InvalidFormat;
}, },
@ -357,14 +356,14 @@ const Command = struct {
} }
} }
var args_cbor = std.ArrayListUnmanaged(u8){}; var args_cbor: std.Io.Writer.Allocating = .init(allocator);
defer args_cbor.deinit(allocator); defer args_cbor.deinit();
const writer = args_cbor.writer(allocator); const writer = &args_cbor.writer;
try cbor.writeArrayHeader(writer, args.items.len); try cbor.writeArrayHeader(writer, args.items.len);
for (args.items) |arg| try cbor.writeJsonValue(writer, arg); for (args.items) |arg| try cbor.writeJsonValue(writer, arg);
return .{ return .{
.command = command_, .command = command_,
.args = try args_cbor.toOwnedSlice(allocator), .args = try args_cbor.toOwnedSlice(),
}; };
} }
}; };
@ -426,7 +425,7 @@ const BindingSet = struct {
const KeySyntax = enum { flow, vim }; const KeySyntax = enum { flow, vim };
const OnMatchFailure = enum { insert, ignore }; const OnMatchFailure = enum { insert, ignore };
fn load(allocator: std.mem.Allocator, namespace_name: []const u8, mode_bindings: std.json.Value, fallback: ?*const BindingSet, namespace: *Namespace) (error{OutOfMemory} || parse_flow.ParseError || parse_vim.ParseError || std.json.ParseFromValueError)!@This() { fn load(allocator: std.mem.Allocator, namespace_name: []const u8, mode_bindings: std.json.Value, fallback: ?*const BindingSet, namespace: *Namespace) (error{ OutOfMemory, WriteFailed } || parse_flow.ParseError || parse_vim.ParseError || std.json.ParseFromValueError)!@This() {
var self: @This() = .{ .name = undefined, .selection_style = undefined }; var self: @This() = .{ .name = undefined, .selection_style = undefined };
const JsonConfig = struct { const JsonConfig = struct {
@ -475,7 +474,7 @@ const BindingSet = struct {
return self; return self;
} }
fn load_event(self: *BindingSet, allocator: std.mem.Allocator, dest: *std.ArrayListUnmanaged(Binding), event: input.Event, bindings: []const []const std.json.Value) (parse_flow.ParseError || parse_vim.ParseError)!void { fn load_event(self: *BindingSet, allocator: std.mem.Allocator, dest: *std.ArrayListUnmanaged(Binding), event: input.Event, bindings: []const []const std.json.Value) (error{WriteFailed} || parse_flow.ParseError || parse_vim.ParseError)!void {
_ = event; _ = event;
bindings: for (bindings) |entry| { bindings: for (bindings) |entry| {
if (entry.len < 2) { if (entry.len < 2) {
@ -509,27 +508,26 @@ const BindingSet = struct {
errdefer allocator.free(key_events); errdefer allocator.free(key_events);
const cmd = entry[1]; const cmd = entry[1];
var cmds = std.ArrayList(Command).init(allocator); var cmds: std.ArrayList(Command) = .empty;
defer cmds.deinit(); defer cmds.deinit(allocator);
if (cmd == .string) { if (cmd == .string) {
try cmds.append(try Command.load(allocator, entry[1..])); try cmds.append(allocator, try Command.load(allocator, entry[1..]));
} else { } else {
for (entry[1..]) |cmd_entry| { for (entry[1..]) |cmd_entry| {
if (cmd_entry != .array) { if (cmd_entry != .array) {
var json = std.ArrayList(u8).init(allocator); const json = try std.json.Stringify.valueAlloc(allocator, cmd_entry, .{});
defer json.deinit(); defer allocator.free(json);
std.json.stringify(cmd_entry, .{}, json.writer()) catch {};
const logger = log.logger("keybind"); const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: invalid command definition {s}", .{json.items}); logger.print_err("keybind.load", "ERROR: invalid command definition {s}", .{json});
logger.deinit(); logger.deinit();
continue :bindings; continue :bindings;
} }
try cmds.append(try Command.load(allocator, cmd_entry.array.items)); try cmds.append(allocator, try Command.load(allocator, cmd_entry.array.items));
} }
} }
try dest.append(allocator, .{ try dest.append(allocator, .{
.key_events = key_events, .key_events = key_events,
.commands = try cmds.toOwnedSlice(), .commands = try cmds.toOwnedSlice(allocator),
}); });
} }
} }
@ -564,13 +562,13 @@ const BindingSet = struct {
for (self.press.items) |binding| { for (self.press.items) |binding| {
const cmd = binding.commands[0].command; const cmd = binding.commands[0].command;
var hint = if (hints_map.get(cmd)) |previous| var hint: std.Io.Writer.Allocating = if (hints_map.get(cmd)) |previous|
std.ArrayList(u8).fromOwnedSlice(allocator, previous) .initOwnedSlice(allocator, previous)
else else
std.ArrayList(u8).init(allocator); .init(allocator);
defer hint.deinit(); defer hint.deinit();
const writer = hint.writer(); const writer = &hint.writer;
if (hint.items.len > 0) try writer.writeAll(", "); if (hint.written().len > 0) try writer.writeAll(", ");
const count = binding.key_events.len; const count = binding.key_events.len;
for (binding.key_events, 0..) |key_, n| { for (binding.key_events, 0..) |key_, n| {
var key = key_; var key = key_;
@ -578,7 +576,7 @@ const BindingSet = struct {
switch (self.syntax) { switch (self.syntax) {
// .flow => { // .flow => {
else => { else => {
try writer.print("{}", .{key}); try writer.print("{f}", .{key});
if (n < count - 1) if (n < count - 1)
try writer.writeAll(" "); try writer.writeAll(" ");
}, },

View file

@ -21,7 +21,7 @@ fn parse_error(comptime format: anytype, args: anytype) ParseError {
pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseError![]input.KeyEvent { pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseError![]input.KeyEvent {
parse_error_reset(); parse_error_reset();
if (str.len == 0) return parse_error("empty", .{}); if (str.len == 0) return parse_error("empty", .{});
var result_events = std.ArrayList(input.KeyEvent).init(allocator); var result_events: std.ArrayList(input.KeyEvent) = .empty;
var iter_sequence = std.mem.tokenizeScalar(u8, str, ' '); var iter_sequence = std.mem.tokenizeScalar(u8, str, ' ');
while (iter_sequence.next()) |item| { while (iter_sequence.next()) |item| {
var key: ?input.Key = null; var key: ?input.Key = null;
@ -65,11 +65,11 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
if (key == null) return parse_error("unknown key '{s}' in '{s}'", .{ part, str }); if (key == null) return parse_error("unknown key '{s}' in '{s}'", .{ part, str });
} }
if (key) |k| if (key) |k|
try result_events.append(input.KeyEvent.from_key_modset(k, mods)) try result_events.append(allocator, input.KeyEvent.from_key_modset(k, mods))
else else
return parse_error("no key defined in '{s}'", .{str}); return parse_error("no key defined in '{s}'", .{str});
} }
return result_events.toOwnedSlice(); return result_events.toOwnedSlice(allocator);
} }
pub const name_map = blk: { pub const name_map = blk: {

View file

@ -81,8 +81,8 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
var state: State = .base; var state: State = .base;
var function_key_number: u8 = 0; var function_key_number: u8 = 0;
var modifiers: input.Mods = 0; var modifiers: input.Mods = 0;
var result = std.ArrayList(input.KeyEvent).init(allocator); var result: std.ArrayList(input.KeyEvent) = .empty;
defer result.deinit(); defer result.deinit(allocator);
var i: usize = 0; var i: usize = 0;
while (i < str.len) { while (i < str.len) {
@ -100,7 +100,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
'0'...'9', '0'...'9',
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
'`', '~', '-', '_', '=', '+', '[', ']', '{', '}', '\\', '|', ':', ';', '\'', '"', ',', '.', '/', '?', => { '`', '~', '-', '_', '=', '+', '[', ']', '{', '}', '\\', '|', ':', ';', '\'', '"', ',', '.', '/', '?', => {
try result.append(from_key(str[i])); try result.append(allocator, from_key(str[i]));
i += 1; i += 1;
}, },
else => return parse_error(error.InvalidInitialCharacter, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), else => return parse_error(error.InvalidInitialCharacter, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }),
@ -216,7 +216,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.insert => { .insert => {
if (std.mem.indexOf(u8, str[i..], "Insert") == 0) { if (std.mem.indexOf(u8, str[i..], "Insert") == 0) {
try result.append(from_key_mods(input.key.insert, modifiers)); try result.append(allocator, from_key_mods(input.key.insert, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 4; i += 4;
@ -224,7 +224,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.end => { .end => {
if (std.mem.indexOf(u8, str[i..], "End") == 0) { if (std.mem.indexOf(u8, str[i..], "End") == 0) {
try result.append(from_key_mods(input.key.end, modifiers)); try result.append(allocator, from_key_mods(input.key.end, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 3; i += 3;
@ -232,7 +232,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.home => { .home => {
if (std.mem.indexOf(u8, str[i..], "Home") == 0) { if (std.mem.indexOf(u8, str[i..], "Home") == 0) {
try result.append(from_key_mods(input.key.home, modifiers)); try result.append(allocator, from_key_mods(input.key.home, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 4; i += 4;
@ -240,7 +240,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.bs => { .bs => {
if (std.mem.indexOf(u8, str[i..], "BS") == 0) { if (std.mem.indexOf(u8, str[i..], "BS") == 0) {
try result.append(from_key_mods(input.key.backspace, modifiers)); try result.append(allocator, from_key_mods(input.key.backspace, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 2; i += 2;
@ -248,7 +248,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.cr => { .cr => {
if (std.mem.indexOf(u8, str[i..], "CR") == 0) { if (std.mem.indexOf(u8, str[i..], "CR") == 0) {
try result.append(from_key_mods(input.key.enter, modifiers)); try result.append(allocator, from_key_mods(input.key.enter, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 2; i += 2;
@ -256,7 +256,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.space => { .space => {
if (std.mem.indexOf(u8, str[i..], "Space") == 0) { if (std.mem.indexOf(u8, str[i..], "Space") == 0) {
try result.append(from_key_mods(input.key.space, modifiers)); try result.append(allocator, from_key_mods(input.key.space, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 5; i += 5;
@ -264,7 +264,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.del => { .del => {
if (std.mem.indexOf(u8, str[i..], "Del") == 0) { if (std.mem.indexOf(u8, str[i..], "Del") == 0) {
try result.append(from_key_mods(input.key.delete, modifiers)); try result.append(allocator, from_key_mods(input.key.delete, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 3; i += 3;
@ -272,7 +272,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.tab => { .tab => {
if (std.mem.indexOf(u8, str[i..], "Tab") == 0) { if (std.mem.indexOf(u8, str[i..], "Tab") == 0) {
try result.append(from_key_mods(input.key.tab, modifiers)); try result.append(allocator, from_key_mods(input.key.tab, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 3; i += 3;
@ -280,7 +280,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.up => { .up => {
if (std.mem.indexOf(u8, str[i..], "Up") == 0) { if (std.mem.indexOf(u8, str[i..], "Up") == 0) {
try result.append(from_key_mods(input.key.up, modifiers)); try result.append(allocator, from_key_mods(input.key.up, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 2; i += 2;
@ -288,7 +288,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.esc => { .esc => {
if (std.mem.indexOf(u8, str[i..], "Esc") == 0) { if (std.mem.indexOf(u8, str[i..], "Esc") == 0) {
try result.append(from_key_mods(input.key.escape, modifiers)); try result.append(allocator, from_key_mods(input.key.escape, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 3; i += 3;
@ -296,7 +296,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.down => { .down => {
if (std.mem.indexOf(u8, str[i..], "Down") == 0) { if (std.mem.indexOf(u8, str[i..], "Down") == 0) {
try result.append(from_key_mods(input.key.down, modifiers)); try result.append(allocator, from_key_mods(input.key.down, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 4; i += 4;
@ -304,7 +304,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.left => { .left => {
if (std.mem.indexOf(u8, str[i..], "Left") == 0) { if (std.mem.indexOf(u8, str[i..], "Left") == 0) {
try result.append(from_key_mods(input.key.left, modifiers)); try result.append(allocator, from_key_mods(input.key.left, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 4; i += 4;
@ -312,7 +312,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.right => { .right => {
if (std.mem.indexOf(u8, str[i..], "Right") == 0) { if (std.mem.indexOf(u8, str[i..], "Right") == 0) {
try result.append(from_key_mods(input.key.right, modifiers)); try result.append(allocator, from_key_mods(input.key.right, modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 5; i += 5;
@ -320,7 +320,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.less_than => { .less_than => {
if (std.mem.indexOf(u8, str[i..], "LT") == 0) { if (std.mem.indexOf(u8, str[i..], "LT") == 0) {
try result.append(from_key_mods('<', modifiers)); try result.append(allocator, from_key_mods('<', modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 2; i += 2;
@ -328,7 +328,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
.greater_than => { .greater_than => {
if (std.mem.indexOf(u8, str[i..], "GT") == 0) { if (std.mem.indexOf(u8, str[i..], "GT") == 0) {
try result.append(from_key_mods('>', modifiers)); try result.append(allocator, from_key_mods('>', modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 2; i += 2;
@ -345,7 +345,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
'>' => { '>' => {
const function_key = input.key.f1 - 1 + function_key_number; const function_key = input.key.f1 - 1 + function_key_number;
try result.append(from_key_mods(function_key, modifiers)); try result.append(allocator, from_key_mods(function_key, modifiers));
modifiers = 0; modifiers = 0;
function_key_number = 0; function_key_number = 0;
state = .base; state = .base;
@ -371,7 +371,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
'0'...'9', '0'...'9',
'`', '-', '=', '[', ']', '\\', ':', ';', '\'', ',', '.', '/', '`', '-', '=', '[', ']', '\\', ':', ';', '\'', ',', '.', '/',
=> { => {
try result.append(from_key_mods(str[i], modifiers)); try result.append(allocator, from_key_mods(str[i], modifiers));
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 1; i += 1;
@ -405,5 +405,5 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
}, },
} }
} }
return result.toOwnedSlice(); return result.toOwnedSlice(allocator);
} }

View file

@ -50,8 +50,8 @@ const Process = struct {
self.* = .{ self.* = .{
.arena = std.heap.ArenaAllocator.init(outer_a), .arena = std.heap.ArenaAllocator.init(outer_a),
.allocator = self.arena.allocator(), .allocator = self.arena.allocator(),
.backwards = std.ArrayList(Entry).init(self.allocator), .backwards = .empty,
.forwards = std.ArrayList(Entry).init(self.allocator), .forwards = .empty,
.receiver = Receiver.init(Process.receive, self), .receiver = Receiver.init(Process.receive, self),
}; };
return tp.spawn_link(self.allocator, self, Process.start, module_name); return tp.spawn_link(self.allocator, self, Process.start, module_name);
@ -65,8 +65,8 @@ const Process = struct {
fn deinit(self: *Process) void { fn deinit(self: *Process) void {
self.clear_backwards(); self.clear_backwards();
self.clear_forwards(); self.clear_forwards();
self.backwards.deinit(); self.backwards.deinit(self.allocator);
self.forwards.deinit(); self.forwards.deinit(self.allocator);
if (self.current) |entry| self.allocator.free(entry.file_path); if (self.current) |entry| self.allocator.free(entry.file_path);
self.arena.deinit(); self.arena.deinit();
outer_a.destroy(self); outer_a.destroy(self);
@ -82,7 +82,7 @@ const Process = struct {
fn clear_table(self: *Process, table: *std.ArrayList(Entry)) void { fn clear_table(self: *Process, table: *std.ArrayList(Entry)) void {
for (table.items) |entry| self.allocator.free(entry.file_path); for (table.items) |entry| self.allocator.free(entry.file_path);
table.clearAndFree(); table.clearAndFree(self.allocator);
} }
fn receive(self: *Process, from: tp.pid_ref, m: tp.message) tp.result { fn receive(self: *Process, from: tp.pid_ref, m: tp.message) tp.result {
@ -122,17 +122,17 @@ const Process = struct {
return self.allocator.free(self.current.?.file_path); return self.allocator.free(self.current.?.file_path);
if (isdupe(self.backwards.getLastOrNull(), entry)) { if (isdupe(self.backwards.getLastOrNull(), entry)) {
if (self.current) |current| self.forwards.append(current) catch {}; if (self.current) |current| self.forwards.append(self.allocator, current) catch {};
if (self.backwards.pop()) |top| if (self.backwards.pop()) |top|
self.allocator.free(top.file_path); self.allocator.free(top.file_path);
tp.trace(tp.channel.all, tp.message.fmt(.{ "location", "back", entry.file_path, entry.cursor.row, entry.cursor.col, self.backwards.items.len, self.forwards.items.len })); tp.trace(tp.channel.all, tp.message.fmt(.{ "location", "back", entry.file_path, entry.cursor.row, entry.cursor.col, self.backwards.items.len, self.forwards.items.len }));
} else if (isdupe(self.forwards.getLastOrNull(), entry)) { } else if (isdupe(self.forwards.getLastOrNull(), entry)) {
if (self.current) |current| self.backwards.append(current) catch {}; if (self.current) |current| self.backwards.append(self.allocator, current) catch {};
if (self.forwards.pop()) |top| if (self.forwards.pop()) |top|
self.allocator.free(top.file_path); self.allocator.free(top.file_path);
tp.trace(tp.channel.all, tp.message.fmt(.{ "location", "forward", entry.file_path, entry.cursor.row, entry.cursor.col, self.backwards.items.len, self.forwards.items.len })); tp.trace(tp.channel.all, tp.message.fmt(.{ "location", "forward", entry.file_path, entry.cursor.row, entry.cursor.col, self.backwards.items.len, self.forwards.items.len }));
} else if (self.current) |current| { } else if (self.current) |current| {
try self.backwards.append(current); try self.backwards.append(self.allocator, current);
tp.trace(tp.channel.all, tp.message.fmt(.{ "location", "new", current.file_path, current.cursor.row, current.cursor.col, self.backwards.items.len, self.forwards.items.len })); tp.trace(tp.channel.all, tp.message.fmt(.{ "location", "new", current.file_path, current.cursor.row, current.cursor.col, self.backwards.items.len, self.forwards.items.len }));
self.clear_forwards(); self.clear_forwards();
} }

View file

@ -94,16 +94,24 @@ fn receive(self: *Self, from: tp.pid_ref, m: tp.message) tp.result {
} else { } else {
self.store(m); self.store(m);
} }
if (!self.no_stderr) if (!self.no_stderr) {
std.io.getStdErr().writer().print("{s}\n", .{output}) catch {}; var stderr_buffer: [1024]u8 = undefined;
var stderr_writer = std.fs.File.stderr().writer(&stderr_buffer);
stderr_writer.interface.print("{s}\n", .{output}) catch {};
stderr_writer.interface.flush() catch {};
}
} else if (try m.match(.{ "log", tp.string, tp.extract(&output) })) { } else if (try m.match(.{ "log", tp.string, tp.extract(&output) })) {
if (self.subscriber) |subscriber| { if (self.subscriber) |subscriber| {
subscriber.send_raw(m) catch {}; subscriber.send_raw(m) catch {};
} else { } else {
self.store(m); self.store(m);
} }
if (!self.no_stdout) if (!self.no_stdout) {
std.io.getStdOut().writer().print("{s}\n", .{output}) catch {}; var stdout_buffer: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
stdout_writer.interface.print("{s}\n", .{output}) catch {};
stdout_writer.interface.flush() catch {};
}
} else if (try m.match(.{"subscribe"})) { } else if (try m.match(.{"subscribe"})) {
// log("subscribed"); // log("subscribed");
if (self.subscriber) |*s| s.deinit(); if (self.subscriber) |*s| s.deinit();
@ -152,8 +160,8 @@ pub const Logger = struct {
} }
pub fn err(self: Logger, context: []const u8, e: anyerror) void { pub fn err(self: Logger, context: []const u8, e: anyerror) void {
var msg_fmt = std.ArrayList(u8).init(std.heap.c_allocator); var msg_fmt: std.ArrayList(u8) = .empty;
defer msg_fmt.deinit(); defer msg_fmt.deinit(std.heap.c_allocator);
defer tp.reset_error(); defer tp.reset_error();
var buf: [max_log_message]u8 = undefined; var buf: [max_log_message]u8 = undefined;
var msg: []const u8 = "UNKNOWN"; var msg: []const u8 = "UNKNOWN";
@ -168,12 +176,12 @@ pub const Logger = struct {
// //
} else { } else {
var failed = false; var failed = false;
msg_fmt.writer().print("{}", .{msg_}) catch { msg_fmt.writer(std.heap.c_allocator).print("{f}", .{msg_}) catch {
failed = true; failed = true;
}; };
if (failed) { if (failed) {
msg_fmt.clearRetainingCapacity(); msg_fmt.clearRetainingCapacity();
msg_fmt.writer().print("{s}", .{std.fmt.fmtSliceEscapeLower(msg_.buf)}) catch {}; msg_fmt.writer(std.heap.c_allocator).print("{f}", .{std.ascii.hexEscape(msg_.buf, .lower)}) catch {};
} }
msg__ = msg_fmt.items; msg__ = msg_fmt.items;
tp.trace(tp.channel.debug, .{ "log_err_fmt", msg__.len, msg__[0..@min(msg__.len, 128)] }); tp.trace(tp.channel.debug, .{ "log_err_fmt", msg__.len, msg__[0..@min(msg__.len, 128)] });

View file

@ -251,12 +251,12 @@ pub fn main() anyerror!void {
const tui_proc = try tui.spawn(a, &ctx, &eh, &env); const tui_proc = try tui.spawn(a, &ctx, &eh, &env);
defer tui_proc.deinit(); defer tui_proc.deinit();
var links = std.ArrayList(file_link.Dest).init(a); var links: std.ArrayList(file_link.Dest) = .empty;
defer links.deinit(); defer links.deinit(a);
var prev: ?*file_link.Dest = null; var prev: ?*file_link.Dest = null;
var line_next: ?usize = null; var line_next: ?usize = null;
var offset_next: ?usize = null; var offset_next: ?usize = null;
for (args.positional.trailing.items) |arg| { for (args.positional.trailing) |arg| {
if (arg.len == 0) continue; if (arg.len == 0) continue;
if (!args.literal and arg[0] == '+') { if (!args.literal and arg[0] == '+') {
@ -286,7 +286,7 @@ pub fn main() anyerror!void {
continue; continue;
} }
const curr = try links.addOne(); const curr = try links.addOne(a);
curr.* = if (!args.literal) try file_link.parse(arg) else .{ .file = .{ .path = arg } }; curr.* = if (!args.literal) try file_link.parse(arg) else .{ .file = .{ .path = arg } };
prev = curr; prev = curr;
@ -354,9 +354,9 @@ pub fn main() anyerror!void {
while (count_args_.next()) |_| count += 1; while (count_args_.next()) |_| count += 1;
if (count == 0) break; if (count == 0) break;
var msg = std.ArrayList(u8).init(a); var msg: std.Io.Writer.Allocating = .init(a);
defer msg.deinit(); defer msg.deinit();
const writer = msg.writer(); const writer = &msg.writer;
var cmd_args = std.mem.splitScalar(u8, cmd, ':'); var cmd_args = std.mem.splitScalar(u8, cmd, ':');
const cmd_ = cmd_args.next(); const cmd_ = cmd_args.next();
@ -372,7 +372,7 @@ pub fn main() anyerror!void {
try cbor.writeValue(writer, arg); try cbor.writeValue(writer, arg);
} }
try tui_proc.send_raw(.{ .buf = msg.items }); try tui_proc.send_raw(.{ .buf = msg.written() });
} }
} }
@ -391,7 +391,10 @@ pub fn print_exit_status(_: void, msg: []const u8) void {
} else if (std.mem.eql(u8, msg, "restart")) { } else if (std.mem.eql(u8, msg, "restart")) {
want_restart = true; want_restart = true;
} else { } else {
std.io.getStdErr().writer().print("\n" ++ application_name ++ " ERROR: {s}\n", .{msg}) catch {}; var stderr_buffer: [1024]u8 = undefined;
var stderr_writer = std.fs.File.stderr().writer(&stderr_buffer);
stderr_writer.interface.print("\n" ++ application_name ++ " ERROR: {s}\n", .{msg}) catch {};
stderr_writer.interface.flush() catch {};
final_exit_status = 1; final_exit_status = 1;
} }
} }
@ -434,31 +437,31 @@ fn trace_to_file(m: thespian.message.c_buffer_type) callconv(.c) void {
} }
} }
}; };
const a = std.heap.c_allocator;
var state: *State = &(State.state orelse init: { var state: *State = &(State.state orelse init: {
const a = std.heap.c_allocator; var path: std.Io.Writer.Allocating = .init(a);
var path = std.ArrayList(u8).init(a);
defer path.deinit(); defer path.deinit();
path.writer().print("{s}{c}trace.log", .{ get_state_dir() catch return, sep }) catch return; path.writer.print("{s}{c}trace.log", .{ get_state_dir() catch return, sep }) catch return;
const file = std.fs.createFileAbsolute(path.items, .{ .truncate = true }) catch return; const file = std.fs.createFileAbsolute(path.written(), .{ .truncate = true }) catch return;
State.state = .{ State.state = .{
.file = file, .file = file,
.last_time = std.time.microTimestamp(), .last_time = std.time.microTimestamp(),
}; };
break :init State.state.?; break :init State.state.?;
}); });
const file_writer = state.file.writer(); var buffer: [4096]u8 = undefined;
var buffer = std.io.bufferedWriter(file_writer); var file_writer = state.file.writer(&buffer);
const writer = buffer.writer(); const writer = &file_writer.interface;
const ts = std.time.microTimestamp(); const ts = std.time.microTimestamp();
State.write_tdiff(writer, ts - state.last_time) catch {}; State.write_tdiff(writer, ts - state.last_time) catch {};
state.last_time = ts; state.last_time = ts;
var stream = std.json.writeStream(writer, .{}); var stream: std.json.Stringify = .{ .writer = writer };
var iter: []const u8 = m.base[0..m.len]; var iter: []const u8 = m.base[0..m.len];
cbor.JsonStream(@TypeOf(buffer)).jsonWriteValue(&stream, &iter) catch {}; cbor.JsonWriter.jsonWriteValue(&stream, &iter) catch {};
_ = writer.write("\n") catch {}; _ = writer.write("\n") catch {};
buffer.flush() catch {}; writer.flush() catch {};
} }
pub fn exit(status: u8) noreturn { pub fn exit(status: u8) noreturn {
@ -533,9 +536,9 @@ fn read_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_:
} }
pub fn parse_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8, content: []const u8) !void { pub fn parse_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8, content: []const u8) !void {
var cbor_buf = std.ArrayList(u8).init(allocator); var cbor_buf: std.Io.Writer.Allocating = .init(allocator);
defer cbor_buf.deinit(); defer cbor_buf.deinit();
const writer = cbor_buf.writer(); const writer = &cbor_buf.writer;
var it = std.mem.splitScalar(u8, content, '\n'); var it = std.mem.splitScalar(u8, content, '\n');
var lineno: u32 = 0; var lineno: u32 = 0;
while (it.next()) |line| { while (it.next()) |line| {
@ -554,7 +557,7 @@ pub fn parse_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, b
}; };
defer allocator.free(cb); defer allocator.free(cb);
try cbor.writeValue(writer, name); try cbor.writeValue(writer, name);
try cbor_buf.appendSlice(cb); try writer.writeAll(cb);
} }
const cb = try cbor_buf.toOwnedSlice(); const cb = try cbor_buf.toOwnedSlice();
var bufs = std.ArrayListUnmanaged([]const u8).fromOwnedSlice(bufs_.*); var bufs = std.ArrayListUnmanaged([]const u8).fromOwnedSlice(bufs_.*);
@ -650,7 +653,7 @@ fn read_nested_include_files(T: type, allocator: std.mem.Allocator, conf: *T, bu
}; };
} }
pub const ConfigWriteError = error{ CreateConfigFileFailed, WriteConfigFileFailed }; pub const ConfigWriteError = error{ CreateConfigFileFailed, WriteConfigFileFailed, WriteFailed };
pub fn write_config(conf: anytype, allocator: std.mem.Allocator) (ConfigDirError || ConfigWriteError)!void { pub fn write_config(conf: anytype, allocator: std.mem.Allocator) (ConfigDirError || ConfigWriteError)!void {
config_mutex.lock(); config_mutex.lock();
@ -667,14 +670,16 @@ fn write_text_config_file(comptime T: type, data: T, file_name: []const u8) Conf
return error.CreateConfigFileFailed; return error.CreateConfigFileFailed;
}; };
defer file.close(); defer file.close();
const writer = file.writer(); var buf: [4096]u8 = undefined;
write_config_to_writer(T, data, writer) catch |e| { var writer = file.writer(&buf);
write_config_to_writer(T, data, &writer.interface) catch |e| {
std.log.err("write file failed with {any} for: {s}", .{ e, file_name }); std.log.err("write file failed with {any} for: {s}", .{ e, file_name });
return error.WriteConfigFileFailed; return error.WriteConfigFileFailed;
}; };
try writer.interface.flush();
} }
pub fn write_config_to_writer(comptime T: type, data: T, writer: anytype) @TypeOf(writer).Error!void { pub fn write_config_to_writer(comptime T: type, data: T, writer: *std.Io.Writer) std.Io.Writer.Error!void {
const default: T = .{}; const default: T = .{};
inline for (@typeInfo(T).@"struct".fields) |field_info| { inline for (@typeInfo(T).@"struct".fields) |field_info| {
if (config_eql( if (config_eql(
@ -693,7 +698,7 @@ pub fn write_config_to_writer(comptime T: type, data: T, writer: anytype) @TypeO
else else
try writer.writeAll("null"), try writer.writeAll("null"),
else => { else => {
var s = std.json.writeStream(writer, .{ .whitespace = .minified }); var s: std.json.Stringify = .{ .writer = writer, .options = .{ .whitespace = .minified } };
try s.write(@field(data, field_info.name)); try s.write(@field(data, field_info.name));
}, },
} }
@ -701,7 +706,7 @@ pub fn write_config_to_writer(comptime T: type, data: T, writer: anytype) @TypeO
} }
} }
fn write_color_value(value: u24, writer: anytype) @TypeOf(writer).Error!void { fn write_color_value(value: u24, writer: *std.Io.Writer) std.Io.Writer.Error!void {
var hex: [7]u8 = undefined; var hex: [7]u8 = undefined;
try writer.writeByte('"'); try writer.writeByte('"');
try writer.writeAll(color.RGB.to_string(color.RGB.from_u24(value), &hex)); try writer.writeAll(color.RGB.to_string(color.RGB.from_u24(value), &hex));
@ -770,15 +775,15 @@ pub fn write_keybind_namespace(namespace_name: []const u8, content: []const u8)
pub fn list_keybind_namespaces(allocator: std.mem.Allocator) ![]const []const u8 { pub fn list_keybind_namespaces(allocator: std.mem.Allocator) ![]const []const u8 {
var dir = try std.fs.openDirAbsolute(try get_keybind_namespaces_directory(), .{ .iterate = true }); var dir = try std.fs.openDirAbsolute(try get_keybind_namespaces_directory(), .{ .iterate = true });
defer dir.close(); defer dir.close();
var result = std.ArrayList([]const u8).init(allocator); var result: std.ArrayList([]const u8) = .empty;
var iter = dir.iterateAssumeFirstIteration(); var iter = dir.iterateAssumeFirstIteration();
while (try iter.next()) |entry| { while (try iter.next()) |entry| {
switch (entry.kind) { switch (entry.kind) {
.file, .sym_link => try result.append(try allocator.dupe(u8, std.fs.path.stem(entry.name))), .file, .sym_link => try result.append(allocator, try allocator.dupe(u8, std.fs.path.stem(entry.name))),
else => continue, else => continue,
} }
} }
return result.toOwnedSlice(); return result.toOwnedSlice(allocator);
} }
pub fn read_theme(allocator: std.mem.Allocator, theme_name: []const u8) ?[]const u8 { pub fn read_theme(allocator: std.mem.Allocator, theme_name: []const u8) ?[]const u8 {
@ -1052,7 +1057,10 @@ fn restart() noreturn {
null, null,
}; };
const ret = std.c.execve(executable, @ptrCast(&argv), @ptrCast(std.os.environ)); const ret = std.c.execve(executable, @ptrCast(&argv), @ptrCast(std.os.environ));
std.io.getStdErr().writer().print("\nrestart failed: {d}", .{ret}) catch {}; var stderr_buffer: [1024]u8 = undefined;
var stderr_writer = std.fs.File.stderr().writer(&stderr_buffer);
stderr_writer.interface.print("\nrestart failed: {d}", .{ret}) catch {};
stderr_writer.interface.flush() catch {};
exit(234); exit(234);
} }

View file

@ -463,12 +463,13 @@ const Process = struct {
} }
fn request_recent_projects(self: *Process, from: tp.pid_ref, project_directory: []const u8) (ProjectError || Project.ClientError)!void { fn request_recent_projects(self: *Process, from: tp.pid_ref, project_directory: []const u8) (ProjectError || Project.ClientError)!void {
var recent_projects = std.ArrayList(RecentProject).init(self.allocator); var recent_projects: std.ArrayList(RecentProject) = .empty;
defer recent_projects.deinit(); defer recent_projects.deinit(self.allocator);
self.load_recent_projects(&recent_projects, project_directory) catch {}; self.load_recent_projects(&recent_projects, project_directory) catch {};
self.sort_projects_by_last_used(&recent_projects); self.sort_projects_by_last_used(&recent_projects);
var message = std.ArrayList(u8).init(self.allocator); var message: std.Io.Writer.Allocating = .init(self.allocator);
const writer = message.writer(); defer message.deinit();
const writer = &message.writer;
try cbor.writeArrayHeader(writer, 3); try cbor.writeArrayHeader(writer, 3);
try cbor.writeValue(writer, "PRJ"); try cbor.writeValue(writer, "PRJ");
try cbor.writeValue(writer, "recent_projects"); try cbor.writeValue(writer, "recent_projects");
@ -478,7 +479,7 @@ const Process = struct {
try cbor.writeValue(writer, project.name); try cbor.writeValue(writer, project.name);
try cbor.writeValue(writer, if (self.projects.get(project.name)) |_| true else false); try cbor.writeValue(writer, if (self.projects.get(project.name)) |_| true else false);
} }
from.send_raw(.{ .buf = message.items }) catch return error.ClientFailed; from.send_raw(.{ .buf = message.written() }) catch return error.ClientFailed;
self.logger.print("{d} projects found", .{recent_projects.items.len}); self.logger.print("{d} projects found", .{recent_projects.items.len});
} }
@ -493,9 +494,9 @@ const Process = struct {
fn request_path_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize, path: []const u8) (ProjectError || SpawnError || std.fs.Dir.OpenError)!void { fn request_path_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize, path: []const u8) (ProjectError || SpawnError || std.fs.Dir.OpenError)!void {
const project = self.projects.get(project_directory) orelse return error.NoProject; const project = self.projects.get(project_directory) orelse return error.NoProject;
var buf = std.ArrayList(u8).init(self.allocator); var buf: std.ArrayList(u8) = .empty;
defer buf.deinit(); defer buf.deinit(self.allocator);
try request_path_files_async(self.allocator, from, project, max, expand_home(&buf, path)); try request_path_files_async(self.allocator, from, project, max, expand_home(self.allocator, &buf, path));
} }
fn request_tasks(self: *Process, from: tp.pid_ref, project_directory: []const u8) (ProjectError || Project.ClientError)!void { fn request_tasks(self: *Process, from: tp.pid_ref, project_directory: []const u8) (ProjectError || Project.ClientError)!void {
@ -651,9 +652,10 @@ const Process = struct {
defer self.allocator.free(file_name); defer self.allocator.free(file_name);
var file = try std.fs.createFileAbsolute(file_name, .{ .truncate = true }); var file = try std.fs.createFileAbsolute(file_name, .{ .truncate = true });
defer file.close(); defer file.close();
var buffer = std.io.bufferedWriter(file.writer()); var buffer: [4096]u8 = undefined;
defer buffer.flush() catch {}; var writer = file.writer(&buffer);
try project.write_state(buffer.writer()); defer writer.interface.flush() catch {};
try project.write_state(&writer.interface);
} }
fn restore_project(self: *Process, project: *Project) !void { fn restore_project(self: *Process, project: *Project) !void {
@ -674,13 +676,14 @@ const Process = struct {
fn get_project_state_file_path(allocator: std.mem.Allocator, project: *Project) ![]const u8 { fn get_project_state_file_path(allocator: std.mem.Allocator, project: *Project) ![]const u8 {
const path = project.name; const path = project.name;
var stream = std.ArrayList(u8).init(allocator); var stream: std.Io.Writer.Allocating = .init(allocator);
const writer = stream.writer(); defer stream.deinit();
const writer = &stream.writer;
_ = try writer.write(try root.get_state_dir()); _ = try writer.write(try root.get_state_dir());
_ = try writer.writeByte(std.fs.path.sep); _ = try writer.writeByte(std.fs.path.sep);
_ = try writer.write("projects"); _ = try writer.write("projects");
_ = try writer.writeByte(std.fs.path.sep); _ = try writer.writeByte(std.fs.path.sep);
std.fs.makeDirAbsolute(stream.items) catch |e| switch (e) { std.fs.makeDirAbsolute(stream.written()) catch |e| switch (e) {
error.PathAlreadyExists => {}, error.PathAlreadyExists => {},
else => return e, else => return e,
}; };
@ -696,19 +699,19 @@ const Process = struct {
} }
fn load_recent_projects(self: *Process, recent_projects: *std.ArrayList(RecentProject), project_directory: []const u8) !void { fn load_recent_projects(self: *Process, recent_projects: *std.ArrayList(RecentProject), project_directory: []const u8) !void {
var path = std.ArrayList(u8).init(self.allocator); var path: std.Io.Writer.Allocating = .init(self.allocator);
defer path.deinit(); defer path.deinit();
const writer = path.writer(); const writer = &path.writer;
_ = try writer.write(try root.get_state_dir()); _ = try writer.write(try root.get_state_dir());
_ = try writer.writeByte(std.fs.path.sep); _ = try writer.writeByte(std.fs.path.sep);
_ = try writer.write("projects"); _ = try writer.write("projects");
var dir = try std.fs.cwd().openDir(path.items, .{ .iterate = true }); var dir = try std.fs.cwd().openDir(path.written(), .{ .iterate = true });
defer dir.close(); defer dir.close();
var iter = dir.iterate(); var iter = dir.iterate();
while (try iter.next()) |entry| { while (try iter.next()) |entry| {
if (entry.kind != .file) continue; if (entry.kind != .file) continue;
try self.read_project_name(path.items, entry.name, recent_projects, project_directory); try self.read_project_name(path.written(), entry.name, recent_projects, project_directory);
} }
} }
@ -719,14 +722,14 @@ const Process = struct {
recent_projects: *std.ArrayList(RecentProject), recent_projects: *std.ArrayList(RecentProject),
project_directory: []const u8, project_directory: []const u8,
) !void { ) !void {
var path = std.ArrayList(u8).init(self.allocator); var path: std.Io.Writer.Allocating = .init(self.allocator);
defer path.deinit(); defer path.deinit();
const writer = path.writer(); const writer = &path.writer;
_ = try writer.write(state_dir); _ = try writer.write(state_dir);
_ = try writer.writeByte(std.fs.path.sep); _ = try writer.writeByte(std.fs.path.sep);
_ = try writer.write(file_path); _ = try writer.write(file_path);
var file = try std.fs.openFileAbsolute(path.items, .{ .mode = .read_only }); var file = try std.fs.openFileAbsolute(path.written(), .{ .mode = .read_only });
defer file.close(); defer file.close();
const stat = try file.stat(); const stat = try file.stat();
const buffer = try self.allocator.alloc(u8, @intCast(stat.size)); const buffer = try self.allocator.alloc(u8, @intCast(stat.size));
@ -737,7 +740,7 @@ const Process = struct {
var name: []const u8 = undefined; var name: []const u8 = undefined;
if (cbor.matchValue(&iter, tp.extract(&name)) catch return) { if (cbor.matchValue(&iter, tp.extract(&name)) catch return) {
const last_used = if (std.mem.eql(u8, project_directory, name)) std.math.maxInt(@TypeOf(stat.mtime)) else stat.mtime; const last_used = if (std.mem.eql(u8, project_directory, name)) std.math.maxInt(@TypeOf(stat.mtime)) else stat.mtime;
(try recent_projects.addOne()).* = .{ .name = try self.allocator.dupe(u8, name), .last_used = last_used }; (try recent_projects.addOne(self.allocator)).* = .{ .name = try self.allocator.dupe(u8, name), .last_used = last_used };
} }
} }
@ -851,14 +854,14 @@ pub fn abbreviate_home(buf: []u8, path: []const u8) []const u8 {
} }
} }
pub fn expand_home(buf: *std.ArrayList(u8), file_path: []const u8) []const u8 { pub fn expand_home(allocator: std.mem.Allocator, buf: *std.ArrayList(u8), file_path: []const u8) []const u8 {
if (builtin.os.tag == .windows) return file_path; if (builtin.os.tag == .windows) return file_path;
if (file_path.len > 0 and file_path[0] == '~') { if (file_path.len > 0 and file_path[0] == '~') {
if (file_path.len > 1 and file_path[1] != std.fs.path.sep) return file_path; if (file_path.len > 1 and file_path[1] != std.fs.path.sep) return file_path;
const homedir = std.posix.getenv("HOME") orelse return file_path; const homedir = std.posix.getenv("HOME") orelse return file_path;
buf.appendSlice(homedir) catch return file_path; buf.appendSlice(allocator, homedir) catch return file_path;
buf.append(std.fs.path.sep) catch return file_path; buf.append(allocator, std.fs.path.sep) catch return file_path;
buf.appendSlice(file_path[2..]) catch return file_path; buf.appendSlice(allocator, file_path[2..]) catch return file_path;
return buf.items; return buf.items;
} else return file_path; } else return file_path;
} }

View file

@ -181,7 +181,7 @@ pub fn putstr(self: *Plane, text: []const u8) !usize {
var result: usize = 0; var result: usize = 0;
const height = self.window.height; const height = self.window.height;
const width = self.window.width; const width = self.window.width;
var iter = self.window.screen.unicode.graphemeIterator(text); var iter = self.window.unicode.graphemeIterator(text);
while (iter.next()) |grapheme| { while (iter.next()) |grapheme| {
const s = grapheme.bytes(text); const s = grapheme.bytes(text);
if (std.mem.eql(u8, s, "\n")) { if (std.mem.eql(u8, s, "\n")) {
@ -443,7 +443,7 @@ pub fn egc_length(self: *const Plane, egcs: []const u8, colcount: *c_int, abs_co
colcount.* = @intCast(tab_width - (abs_col % tab_width)); colcount.* = @intCast(tab_width - (abs_col % tab_width));
return 1; return 1;
} }
var iter = self.window.screen.unicode.graphemeIterator(egcs); var iter = self.window.unicode.graphemeIterator(egcs);
const grapheme = iter.next() orelse { const grapheme = iter.next() orelse {
colcount.* = 1; colcount.* = 1;
return 1; return 1;
@ -470,7 +470,7 @@ pub fn egc_chunk_width(self: *const Plane, chunk_: []const u8, abs_col_: usize,
} }
pub fn egc_last(self: *const Plane, egcs: []const u8) []const u8 { pub fn egc_last(self: *const Plane, egcs: []const u8) []const u8 {
var iter = self.window.screen.unicode.graphemeIterator(egcs); var iter = self.window.unicode.graphemeIterator(egcs);
var last: []const u8 = egcs[0..0]; var last: []const u8 = egcs[0..0];
while (iter.next()) |grapheme| last = grapheme.bytes(egcs); while (iter.next()) |grapheme| last = grapheme.bytes(egcs);
return last; return last;

View file

@ -1,5 +1,6 @@
const vaxis = @import("vaxis"); const vaxis = @import("vaxis");
const Io = @import("std").Io;
const meta = @import("std").meta; const meta = @import("std").meta;
const unicode = @import("std").unicode; const unicode = @import("std").unicode;
const FormatOptions = @import("std").fmt.FormatOptions; const FormatOptions = @import("std").fmt.FormatOptions;
@ -88,12 +89,12 @@ pub const KeyEvent = struct {
return self.modifiers & ~mod.caps_lock; return self.modifiers & ~mod.caps_lock;
} }
pub fn format(self: @This(), comptime _: []const u8, _: FormatOptions, writer: anytype) !void { pub fn format(self: @This(), writer: anytype) !void {
const mods = self.mods_no_shifts(); const mods = self.mods_no_shifts();
return if (self.event > 0) return if (self.event > 0)
writer.print("{}:{}{}", .{ event_fmt(self.event), mod_fmt(mods), key_fmt(self.key) }) writer.print("{f}:{f}{f}", .{ event_fmt(self.event), mod_fmt(mods), key_fmt(self.key) })
else else
writer.print("{}{}", .{ mod_fmt(mods), key_fmt(self.key) }); writer.print("{f}{f}", .{ mod_fmt(mods), key_fmt(self.key) });
} }
pub fn from_key(keypress: Key) @This() { pub fn from_key(keypress: Key) @This() {
@ -335,8 +336,8 @@ pub const utils = struct {
pub fn key_event_short_fmt(ke: KeyEvent) struct { pub fn key_event_short_fmt(ke: KeyEvent) struct {
ke: KeyEvent, ke: KeyEvent,
pub fn format(self: @This(), comptime _: []const u8, _: FormatOptions, writer: anytype) !void { pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
return writer.print("{}{}", .{ mod_short_fmt(self.ke.modifiers), key_short_fmt(self.ke.key) }); return writer.print("{f}{f}", .{ mod_short_fmt(self.ke.modifiers), key_short_fmt(self.ke.key) });
} }
} { } {
return .{ .ke = ke }; return .{ .ke = ke };
@ -344,7 +345,7 @@ pub fn key_event_short_fmt(ke: KeyEvent) struct {
pub fn event_fmt(evt: Event) struct { pub fn event_fmt(evt: Event) struct {
event: Event, event: Event,
pub fn format(self: @This(), comptime _: []const u8, _: FormatOptions, writer: anytype) !void { pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
return switch (self.event) { return switch (self.event) {
event.press => writer.writeAll("press"), event.press => writer.writeAll("press"),
event.repeat => writer.writeAll("repeat"), event.repeat => writer.writeAll("repeat"),
@ -358,7 +359,7 @@ pub fn event_fmt(evt: Event) struct {
pub fn event_short_fmt(evt: Event) struct { pub fn event_short_fmt(evt: Event) struct {
event: Event, event: Event,
pub fn format(self: @This(), comptime _: []const u8, _: FormatOptions, writer: anytype) !void { pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
return switch (self.event) { return switch (self.event) {
event.press => writer.writeAll("P"), event.press => writer.writeAll("P"),
event.repeat => writer.writeAll("RP"), event.repeat => writer.writeAll("RP"),
@ -372,11 +373,11 @@ pub fn event_short_fmt(evt: Event) struct {
pub fn key_fmt(key_: Key) struct { pub fn key_fmt(key_: Key) struct {
key: Key, key: Key,
pub fn format(self: @This(), comptime _: []const u8, _: FormatOptions, writer: anytype) !void { pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
var key_string = utils.key_id_string(self.key); var key_string = utils.key_id_string(self.key);
var buf: [6]u8 = undefined; var buf: [6]u8 = undefined;
if (key_string.len == 0) { if (key_string.len == 0) {
const bytes = try ucs32_to_utf8(&[_]u32{self.key}, &buf); const bytes = ucs32_to_utf8(&[_]u32{self.key}, &buf) catch return error.WriteFailed;
key_string = buf[0..bytes]; key_string = buf[0..bytes];
} }
try writer.writeAll(key_string); try writer.writeAll(key_string);
@ -387,7 +388,7 @@ pub fn key_fmt(key_: Key) struct {
pub fn key_short_fmt(key_: Key) struct { pub fn key_short_fmt(key_: Key) struct {
key: Key, key: Key,
pub fn format(self: @This(), comptime _: []const u8, _: FormatOptions, writer: anytype) !void { pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
var key_string = utils.key_id_string_short(self.key); var key_string = utils.key_id_string_short(self.key);
var buf: [6]u8 = undefined; var buf: [6]u8 = undefined;
if (key_string.len == 0) { if (key_string.len == 0) {
@ -402,7 +403,7 @@ pub fn key_short_fmt(key_: Key) struct {
pub fn mod_fmt(mods: Mods) struct { pub fn mod_fmt(mods: Mods) struct {
modifiers: Mods, modifiers: Mods,
pub fn format(self: @This(), comptime _: []const u8, _: FormatOptions, writer: anytype) !void { pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
const modset: ModSet = @bitCast(self.modifiers); const modset: ModSet = @bitCast(self.modifiers);
if (modset.super) try writer.writeAll("super+"); if (modset.super) try writer.writeAll("super+");
if (modset.ctrl) try writer.writeAll("ctrl+"); if (modset.ctrl) try writer.writeAll("ctrl+");
@ -415,7 +416,7 @@ pub fn mod_fmt(mods: Mods) struct {
pub fn mod_short_fmt(mods: Mods) struct { pub fn mod_short_fmt(mods: Mods) struct {
modifiers: Mods, modifiers: Mods,
pub fn format(self: @This(), comptime _: []const u8, _: FormatOptions, writer: anytype) !void { pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
const modset: ModSet = @bitCast(self.modifiers); const modset: ModSet = @bitCast(self.modifiers);
if (modset.super) try writer.writeAll("Super-"); if (modset.super) try writer.writeAll("Super-");
if (modset.ctrl) try writer.writeAll("C-"); if (modset.ctrl) try writer.writeAll("C-");

View file

@ -22,15 +22,16 @@ allocator: std.mem.Allocator,
tty: vaxis.Tty, tty: vaxis.Tty,
vx: vaxis.Vaxis, vx: vaxis.Vaxis,
tty_buffer: []u8,
no_alternate: bool, no_alternate: bool,
event_buffer: std.ArrayList(u8), event_buffer: std.Io.Writer.Allocating,
input_buffer: std.ArrayList(u8), input_buffer: std.Io.Writer.Allocating,
mods: vaxis.Key.Modifiers = .{}, mods: vaxis.Key.Modifiers = .{},
queries_done: bool, queries_done: bool,
bracketed_paste: bool = false, bracketed_paste: bool = false,
bracketed_paste_buffer: std.ArrayList(u8), bracketed_paste_buffer: std.Io.Writer.Allocating,
handler_ctx: *anyopaque, handler_ctx: *anyopaque,
dispatch_input: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null, dispatch_input: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null,
@ -61,6 +62,7 @@ pub const Error = error{
BadArrayAllocExtract, BadArrayAllocExtract,
InvalidMapType, InvalidMapType,
InvalidUnion, InvalidUnion,
WriteFailed,
} || std.Thread.SpawnError; } || std.Thread.SpawnError;
pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool, _: *const fn (ctx: *anyopaque) void) Error!Self { pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool, _: *const fn (ctx: *anyopaque) void) Error!Self {
@ -74,13 +76,15 @@ pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate:
}, },
.system_clipboard_allocator = allocator, .system_clipboard_allocator = allocator,
}; };
const tty_buffer = try allocator.alloc(u8, 4096);
return .{ return .{
.allocator = allocator, .allocator = allocator,
.tty = vaxis.Tty.init() catch return error.TtyInitError, .tty = vaxis.Tty.init(tty_buffer) catch return error.TtyInitError,
.tty_buffer = tty_buffer,
.vx = try vaxis.init(allocator, opts), .vx = try vaxis.init(allocator, opts),
.no_alternate = no_alternate, .no_alternate = no_alternate,
.event_buffer = std.ArrayList(u8).init(allocator), .event_buffer = .init(allocator),
.input_buffer = std.ArrayList(u8).init(allocator), .input_buffer = .init(allocator),
.bracketed_paste_buffer = std.ArrayList(u8).init(allocator), .bracketed_paste_buffer = std.ArrayList(u8).init(allocator),
.handler_ctx = handler_ctx, .handler_ctx = handler_ctx,
.logger = log.logger(log_name), .logger = log.logger(log_name),
@ -94,6 +98,7 @@ pub fn deinit(self: *Self) void {
self.loop.stop(); self.loop.stop();
self.vx.deinit(self.allocator, self.tty.anyWriter()); self.vx.deinit(self.allocator, self.tty.anyWriter());
self.tty.deinit(); self.tty.deinit();
self.allocator.free(self.tty_buffer);
self.bracketed_paste_buffer.deinit(); self.bracketed_paste_buffer.deinit();
self.input_buffer.deinit(); self.input_buffer.deinit();
self.event_buffer.deinit(); self.event_buffer.deinit();
@ -204,9 +209,8 @@ pub fn run(self: *Self) Error!void {
pub fn render(self: *Self) !void { pub fn render(self: *Self) !void {
if (in_panic.load(.acquire)) return; if (in_panic.load(.acquire)) return;
var bufferedWriter = self.tty.bufferedWriter(); try self.vx.render(&self.tty.writer.interface);
try self.vx.render(bufferedWriter.writer().any()); try self.tty.writer.interface.flush();
try bufferedWriter.flush();
} }
pub fn sigwinch(self: *Self) !void { pub fn sigwinch(self: *Self) !void {
@ -214,7 +218,7 @@ pub fn sigwinch(self: *Self) !void {
try self.resize(try vaxis.Tty.getWinsize(self.input_fd_blocking())); try self.resize(try vaxis.Tty.getWinsize(self.input_fd_blocking()));
} }
fn resize(self: *Self, ws: vaxis.Winsize) error{ TtyWriteError, OutOfMemory }!void { fn resize(self: *Self, ws: vaxis.Winsize) error{ TtyWriteError, OutOfMemory, WriteFailed }!void {
self.vx.resize(self.allocator, self.tty.anyWriter(), ws) catch return error.TtyWriteError; self.vx.resize(self.allocator, self.tty.anyWriter(), ws) catch return error.TtyWriteError;
self.vx.queueRefresh(); self.vx.queueRefresh();
if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{"resize"})); if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{"resize"}));
@ -394,25 +398,26 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void {
} }
} }
fn fmtmsg(self: *Self, value: anytype) std.ArrayList(u8).Writer.Error![]const u8 { fn fmtmsg(self: *Self, value: anytype) std.Io.Writer.Error![]const u8 {
self.event_buffer.clearRetainingCapacity(); self.event_buffer.clearRetainingCapacity();
try cbor.writeValue(self.event_buffer.writer(), value); try cbor.writeValue(&self.event_buffer.writer, value);
return self.event_buffer.items; return self.event_buffer.written();
} }
fn handle_bracketed_paste_input(self: *Self, cbor_msg: []const u8) !bool { fn handle_bracketed_paste_input(self: *Self, cbor_msg: []const u8) !bool {
var keypress: input.Key = undefined; var keypress: input.Key = undefined;
var egc_: input.Key = undefined; var egc_: input.Key = undefined;
var mods: usize = undefined; var mods: usize = undefined;
const writer = &self.bracketed_paste_buffer.writer;
if (try cbor.match(cbor_msg, .{ "I", cbor.number, cbor.extract(&keypress), cbor.extract(&egc_), cbor.string, cbor.extract(&mods) })) { if (try cbor.match(cbor_msg, .{ "I", cbor.number, cbor.extract(&keypress), cbor.extract(&egc_), cbor.string, cbor.extract(&mods) })) {
switch (keypress) { switch (keypress) {
106 => if (mods == 4) try self.bracketed_paste_buffer.appendSlice("\n") else try self.bracketed_paste_buffer.appendSlice("j"), 106 => if (mods == 4) try writer.writeAll("\n") else try writer.writeAll("j"),
input.key.enter => try self.bracketed_paste_buffer.appendSlice("\n"), input.key.enter => try writer.writeAll("\n"),
input.key.tab => try self.bracketed_paste_buffer.appendSlice("\t"), input.key.tab => try writer.writeAll("\t"),
else => if (!input.is_non_input_key(keypress)) { else => if (!input.is_non_input_key(keypress)) {
var buf: [6]u8 = undefined; var buf: [6]u8 = undefined;
const bytes = try input.ucs32_to_utf8(&[_]u32{egc_}, &buf); const bytes = try input.ucs32_to_utf8(&[_]u32{egc_}, &buf);
try self.bracketed_paste_buffer.appendSlice(buf[0..bytes]); try writer.writeAll(buf[0..bytes]);
} else { } else {
var buf: [6]u8 = undefined; var buf: [6]u8 = undefined;
const bytes = try input.ucs32_to_utf8(&[_]u32{egc_}, &buf); const bytes = try input.ucs32_to_utf8(&[_]u32{egc_}, &buf);
@ -431,16 +436,16 @@ fn handle_bracketed_paste_start(self: *Self) !void {
fn handle_bracketed_paste_end(self: *Self) !void { fn handle_bracketed_paste_end(self: *Self) !void {
defer { defer {
self.bracketed_paste_buffer.clearAndFree(); self.bracketed_paste_buffer.clearRetainingCapacity();
self.bracketed_paste = false; self.bracketed_paste = false;
} }
if (!self.bracketed_paste) return; if (!self.bracketed_paste) return;
if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{ "system_clipboard", self.bracketed_paste_buffer.items })); if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{ "system_clipboard", self.bracketed_paste_buffer.written() }));
} }
fn handle_bracketed_paste_error(self: *Self, e: Error) !void { fn handle_bracketed_paste_error(self: *Self, e: Error) !void {
self.logger.err("bracketed paste", e); self.logger.err("bracketed paste", e);
self.bracketed_paste_buffer.clearAndFree(); self.bracketed_paste_buffer.clearRetainingCapacity();
self.bracketed_paste = false; self.bracketed_paste = false;
return e; return e;
} }