diff --git a/build.zig.zon b/build.zig.zon index 52aa86d..84116ea 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -18,8 +18,8 @@ .hash = "1220220dbc7fe91c1c54438193ca765cebbcb7d58f35cdcaee404a9d2245a42a4362", }, .thespian = .{ - .url = "https://github.com/neurocyte/thespian/archive/06ff2a148c1e33095d4838579461f31141565958.tar.gz", - .hash = "122064ef6d9da7aa0352da12804ab46dbecdabb033e9d6c1b4208844f9dd3694e373", + .url = "https://github.com/neurocyte/thespian/archive/6e65fa623a45a4925875955aeb45e5cb0b5f7a68.tar.gz", + .hash = "12206feb723c41340acfe574b16f98a90e3af1f2662850ee2904ec0facb9dc5f0eef", }, .themes = .{ .url = "https://github.com/neurocyte/flow-themes/releases/download/master-803da089c5a0fc3b4513a7c34afe9bdaff83efdc/flow-themes.tar.gz", diff --git a/src/LSP.zig b/src/LSP.zig index f65922f..6150d0a 100644 --- a/src/LSP.zig +++ b/src/LSP.zig @@ -14,7 +14,11 @@ const sp_tag = "child"; const debug_lsp = true; const lsp_request_timeout = std.time.ns_per_s * 30; -pub fn open(allocator: std.mem.Allocator, project: []const u8, cmd: tp.message) !Self { +const OutOfMemoryError = error{OutOfMemory}; +const SendError = error{SendFailed}; +const CallError = tp.CallError; + +pub fn open(allocator: std.mem.Allocator, project: []const u8, cmd: tp.message) (error{ ThespianSpawnFailed, InvalidArgument } || cbor.Error)!Self { return .{ .allocator = allocator, .pid = try Process.create(allocator, project, cmd) }; } @@ -28,29 +32,29 @@ pub fn term(self: *Self) void { self.pid.deinit(); } -pub fn send_request(self: Self, allocator: std.mem.Allocator, method: []const u8, m: anytype) !tp.message { +pub fn send_request(self: Self, allocator: std.mem.Allocator, method: []const u8, m: anytype) CallError!tp.message { var cb = std.ArrayList(u8).init(self.allocator); defer cb.deinit(); try cbor.writeValue(cb.writer(), m); return self.pid.call(allocator, lsp_request_timeout, .{ "REQ", method, cb.items }); } -pub fn send_response(self: Self, id: i32, m: anytype) !tp.message { +pub fn send_response(self: Self, id: i32, m: anytype) SendError!tp.message { var cb = std.ArrayList(u8).init(self.allocator); defer cb.deinit(); try cbor.writeValue(cb.writer(), m); return self.pid.send(.{ "RSP", id, cb.items }); } -pub fn send_notification(self: Self, method: []const u8, m: anytype) !void { +pub fn send_notification(self: Self, method: []const u8, m: anytype) (OutOfMemoryError || SendError)!void { var cb = std.ArrayList(u8).init(self.allocator); defer cb.deinit(); try cbor.writeValue(cb.writer(), m); return self.send_notification_raw(method, cb.items); } -pub fn send_notification_raw(self: Self, method: []const u8, cb: []const u8) !void { - return self.pid.send(.{ "NTFY", method, cb }); +pub fn send_notification_raw(self: Self, method: []const u8, cb: []const u8) SendError!void { + self.pid.send(.{ "NTFY", method, cb }) catch return error.SendFailed; } pub fn close(self: *Self) void { @@ -73,14 +77,14 @@ const Process = struct { const Receiver = tp.Receiver(*Process); - pub fn create(allocator: std.mem.Allocator, project: []const u8, cmd: tp.message) !tp.pid { + pub fn create(allocator: std.mem.Allocator, project: []const u8, cmd: tp.message) (error{ ThespianSpawnFailed, InvalidArgument } || OutOfMemoryError || cbor.Error)!tp.pid { var tag: []const u8 = undefined; - if (try cmd.match(.{tp.extract(&tag)})) { + if (try cbor.match(cmd.buf, .{tp.extract(&tag)})) { // - } else if (try cmd.match(.{ tp.extract(&tag), tp.more })) { + } else if (try cbor.match(cmd.buf, .{ tp.extract(&tag), tp.more })) { // } else { - return tp.exit("no LSP command"); + return error.InvalidArgument; } const self = try allocator.create(Process); var sp_tag_ = std.ArrayList(u8).init(allocator); @@ -112,18 +116,18 @@ const Process = struct { if (self.log_file) |file| file.close(); } - fn close(self: *Process) tp.result { + fn close(self: *Process) error{CloseFailed}!void { if (self.sp) |*sp| { defer self.sp = null; - try sp.close(); + sp.close() catch return error.CloseFailed; self.write_log("### closed ###\n", .{}); } } - fn term(self: *Process) tp.result { + fn term(self: *Process) error{TerminateFailed}!void { if (self.sp) |*sp| { defer self.sp = null; - try sp.term(); + sp.term() catch return error.TerminateFailed; self.write_log("### terminated ###\n", .{}); } } @@ -143,29 +147,27 @@ const Process = struct { } fn receive(self: *Process, from: tp.pid_ref, m: tp.message) tp.result { - return self.receive_safe(from, m) catch |e| return tp.exit_error(e, @errorReturnTrace()); + return self.receive_safe(from, m) catch |e| switch (e) { + error.ExitNormal => tp.exit_normal(), + error.ExitUnexpected => error.Exit, + else => tp.exit_error(e, @errorReturnTrace()), + }; } - const ReceiveError = error{ - OutOfMemory, - NoSpaceLeft, - BufferUnderrun, - CborIntegerTooLarge, - CborIntegerTooSmall, - CborInvalidType, - CborTooShort, - CborUnsupportedType, - SyntaxError, - UnexpectedEndOfInput, + const Error = (cbor.Error || cbor.JsonDecodeError || OutOfMemoryError || SendError || error{ InvalidSyntax, InvalidMessageField, InvalidMessage, InvalidContentLength, Closed, - Exit, - }; + CloseFailed, + TerminateFailed, + UnsupportedType, + ExitNormal, + ExitUnexpected, + }); - fn receive_safe(self: *Process, from: tp.pid_ref, m: tp.message) ReceiveError!void { + fn receive_safe(self: *Process, from: tp.pid_ref, m: tp.message) Error!void { const frame = tracy.initZone(@src(), .{ .name = module_name }); defer frame.deinit(); errdefer self.deinit(); @@ -175,34 +177,34 @@ const Process = struct { var code: u32 = 0; var id: i32 = 0; - if (try m.match(.{ "REQ", tp.extract(&method), tp.extract(&bytes) })) { + if (try cbor.match(m.buf, .{ "REQ", tp.extract(&method), tp.extract(&bytes) })) { try self.send_request(from, method, bytes); - } else if (try m.match(.{ "RSP", tp.extract(&id), tp.extract(&bytes) })) { + } else if (try cbor.match(m.buf, .{ "RSP", tp.extract(&id), tp.extract(&bytes) })) { try self.send_response(id, bytes); - } else if (try m.match(.{ "NTFY", tp.extract(&method), tp.extract(&bytes) })) { + } else if (try cbor.match(m.buf, .{ "NTFY", tp.extract(&method), tp.extract(&bytes) })) { try self.send_notification(method, bytes); - } else if (try m.match(.{"close"})) { + } else if (try cbor.match(m.buf, .{"close"})) { self.write_log("### LSP close ###\n", .{}); try self.close(); - } else if (try m.match(.{"term"})) { + } else if (try cbor.match(m.buf, .{"term"})) { self.write_log("### LSP terminated ###\n", .{}); try self.term(); - } else if (try m.match(.{ self.sp_tag, "stdout", tp.extract(&bytes) })) { + } else if (try cbor.match(m.buf, .{ self.sp_tag, "stdout", tp.extract(&bytes) })) { try self.handle_output(bytes); - } else if (try m.match(.{ self.sp_tag, "term", tp.extract(&err), tp.extract(&code) })) { + } else if (try cbor.match(m.buf, .{ self.sp_tag, "term", tp.extract(&err), tp.extract(&code) })) { try self.handle_terminated(err, code); - } else if (try m.match(.{ self.sp_tag, "stderr", tp.extract(&bytes) })) { + } else if (try cbor.match(m.buf, .{ self.sp_tag, "stderr", tp.extract(&bytes) })) { self.write_log("{s}\n", .{bytes}); - } else if (try m.match(.{ "exit", "normal" })) { + } else if (try cbor.match(m.buf, .{ "exit", "normal" })) { // self.write_log("### exit normal ###\n", .{}); } else { - const e = tp.unexpected(m); + tp.unexpected(m) catch {}; self.write_log("{s}\n", .{tp.error_text()}); - return e; + return error.ExitUnexpected; } } - fn receive_lsp_message(self: *Process, cb: []const u8) !void { + fn receive_lsp_message(self: *Process, cb: []const u8) Error!void { var iter = cb; const MsgMembers = struct { @@ -252,7 +254,7 @@ const Process = struct { } } - fn handle_output(self: *Process, bytes: []u8) ReceiveError!void { + fn handle_output(self: *Process, bytes: []u8) Error!void { try self.recv_buf.appendSlice(bytes); self.write_log("### RECV:\n{s}\n###\n", .{bytes}); self.frame_message_recv() catch |e| { @@ -261,15 +263,15 @@ const Process = struct { }; } - fn handle_terminated(self: *Process, err: []const u8, code: u32) tp.result { + fn handle_terminated(self: *Process, err: []const u8, code: u32) error{ExitNormal}!void { const logger = log.logger("LSP"); logger.print("terminated: {s} {d}", .{ err, code }); self.write_log("### subprocess terminated {s} {d} ###\n", .{ err, code }); - try self.parent.send(.{ sp_tag, self.tag, "done" }); - return tp.exit_normal(); + self.parent.send(.{ sp_tag, self.tag, "done" }) catch {}; + return error.ExitNormal; } - fn send_request(self: *Process, from: tp.pid_ref, method: []const u8, params_cb: []const u8) ReceiveError!void { + fn send_request(self: *Process, from: tp.pid_ref, method: []const u8, params_cb: []const u8) Error!void { const sp = if (self.sp) |*sp| sp else return error.Closed; const id = self.next_id; @@ -299,12 +301,12 @@ const Process = struct { _ = try writer.write(json); _ = try writer.write(terminator); - try sp.send(output.items); + sp.send(output.items) catch return error.SendFailed; self.write_log("### SEND request:\n{s}\n###\n", .{output.items}); try self.requests.put(id, from.clone()); } - fn send_response(self: *Process, id: i32, result_cb: []const u8) !void { + fn send_response(self: *Process, id: i32, result_cb: []const u8) (error{Closed} || SendError || cbor.Error || cbor.JsonEncodeError)!void { const sp = if (self.sp) |*sp| sp else return error.Closed; var msg = std.ArrayList(u8).init(self.allocator); @@ -329,11 +331,11 @@ const Process = struct { _ = try writer.write(json); _ = try writer.write(terminator); - try sp.send(output.items); + sp.send(output.items) catch return error.SendFailed; self.write_log("### SEND response:\n{s}\n###\n", .{output.items}); } - fn send_notification(self: *Process, method: []const u8, params_cb: []const u8) !void { + fn send_notification(self: *Process, method: []const u8, params_cb: []const u8) Error!void { const sp = if (self.sp) |*sp| sp else return error.Closed; const have_params = !(cbor.match(params_cb, cbor.null_) catch false); @@ -364,11 +366,11 @@ const Process = struct { _ = try writer.write(json); _ = try writer.write(terminator); - try sp.send(output.items); + sp.send(output.items) catch return error.SendFailed; self.write_log("### SEND notification:\n{s}\n###\n", .{output.items}); } - fn frame_message_recv(self: *Process) ReceiveError!void { + fn frame_message_recv(self: *Process) Error!void { const sep = "\r\n\r\n"; const headers_end = std.mem.indexOf(u8, self.recv_buf.items, sep) orelse return; const headers_data = self.recv_buf.items[0..headers_end]; @@ -386,7 +388,7 @@ const Process = struct { if (rest.len > 0) return self.frame_message_recv(); } - fn receive_lsp_request(self: *Process, id: i32, method: []const u8, params: ?[]const u8) !void { + fn receive_lsp_request(self: *Process, id: i32, method: []const u8, params: ?[]const u8) Error!void { const json = if (params) |p| try cbor.toJsonPrettyAlloc(self.allocator, p) else null; defer if (json) |p| self.allocator.free(p); self.write_log("### RECV req: {d}\nmethod: {s}\n{s}\n###\n", .{ id, method, json orelse "no params" }); @@ -401,10 +403,10 @@ const Process = struct { try cbor.writeValue(writer, method); try cbor.writeValue(writer, id); if (params) |p| _ = try writer.write(p) else try cbor.writeValue(writer, null); - try self.parent.send_raw(.{ .buf = msg.items }); + self.parent.send_raw(.{ .buf = msg.items }) catch return error.SendFailed; } - fn receive_lsp_response(self: *Process, id: i32, result: ?[]const u8, err: ?[]const u8) !void { + fn receive_lsp_response(self: *Process, id: i32, result: ?[]const u8, err: ?[]const u8) Error!void { const json = if (result) |p| try cbor.toJsonPrettyAlloc(self.allocator, p) else null; defer if (json) |p| self.allocator.free(p); const json_err = if (err) |p| try cbor.toJsonPrettyAlloc(self.allocator, p) else null; @@ -424,10 +426,10 @@ const Process = struct { try cbor.writeValue(writer, "result"); _ = try writer.write(result_); } - try from.send_raw(.{ .buf = msg.items }); + from.send_raw(.{ .buf = msg.items }) catch return error.SendFailed; } - fn receive_lsp_notification(self: *Process, method: []const u8, params: ?[]const u8) !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; defer if (json) |p| self.allocator.free(p); self.write_log("### RECV notify:\nmethod: {s}\n{s}\n###\n", .{ method, json orelse "no params" }); @@ -441,7 +443,7 @@ const Process = struct { try cbor.writeValue(writer, "notify"); try cbor.writeValue(writer, method); if (params) |p| _ = try writer.write(p) else try cbor.writeValue(writer, null); - try self.parent.send_raw(.{ .buf = msg.items }); + self.parent.send_raw(.{ .buf = msg.items }) catch return error.SendFailed; } fn write_log(self: *Process, comptime format: []const u8, args: anytype) void { @@ -455,7 +457,7 @@ const Headers = struct { content_length: usize = 0, content_type: ?[]const u8 = null, - fn parse(buf_: []const u8) Process.ReceiveError!Headers { + fn parse(buf_: []const u8) Process.Error!Headers { var buf = buf_; var ret: Headers = .{}; while (true) { @@ -475,7 +477,7 @@ const Headers = struct { } } - fn parse_one(self: *Headers, name: []const u8, value: []const u8) Process.ReceiveError!void { + fn parse_one(self: *Headers, name: []const u8, value: []const u8) Process.Error!void { if (std.mem.eql(u8, "Content-Length", name)) { self.content_length = std.fmt.parseInt(@TypeOf(self.content_length), value, 10) catch |e| switch (e) { error.Overflow => return error.InvalidContentLength, diff --git a/src/Project.zig b/src/Project.zig index aa2a8fb..73076e4 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -29,7 +29,7 @@ const File = struct { visited: bool = false, }; -pub fn init(allocator: std.mem.Allocator, name: []const u8) error{OutOfMemory}!Self { +pub fn init(allocator: std.mem.Allocator, name: []const u8) OutOfMemoryError!Self { return .{ .allocator = allocator, .name = try allocator.dupe(u8, name), @@ -83,7 +83,7 @@ pub fn restore_state(self: *Self, data: []const u8) !void { tp.extract(&row), tp.extract(&col), }) catch |e| switch (e) { - error.CborTooShort => return, + error.TooShort => return, else => return e, }) { self.longest_file_path = @max(self.longest_file_path, path.len); @@ -96,7 +96,9 @@ pub fn restore_state(self: *Self, data: []const u8) !void { } } -fn get_lsp(self: *Self, language_server: []const u8) !LSP { +pub const GetLspError = (error{ ThespianSpawnFailed, Timeout, InvalidArgument } || OutOfMemoryError || SendError || cbor.Error); + +fn get_lsp(self: *Self, language_server: []const u8) GetLspError!LSP { if (self.language_servers.get(language_server)) |lsp| return lsp; const logger = log.logger("lsp"); errdefer |e| logger.print_err("get_lsp", "failed to initialize LSP: {s} -> {any}", .{ fmt_lsp_name_func(language_server), e }); @@ -113,14 +115,16 @@ fn get_lsp(self: *Self, language_server: []const u8) !LSP { return lsp; } -fn get_file_lsp(self: *Self, file_path: []const u8) !LSP { +pub const GetFileLspError = (GetLspError || error{NoLsp}); + +fn get_file_lsp(self: *Self, file_path: []const u8) GetFileLspError!LSP { const logger = log.logger("lsp"); errdefer logger.print_err("get_file_lsp", "no LSP found for file: {s} ({s})", .{ std.fmt.fmtSliceEscapeLower(file_path), self.name, }); - const lsp = self.file_language_server.get(file_path) orelse return tp.exit("no language server"); - if (lsp.pid.expired()) return tp.exit("no language server"); + const lsp = self.file_language_server.get(file_path) orelse return error.NoLsp; + if (lsp.pid.expired()) return error.NoLsp; return lsp; } @@ -145,20 +149,20 @@ pub fn sort_files_by_mtime(self: *Self) void { std.mem.sort(File, self.files.items, {}, less_fn); } -pub fn request_most_recent_file(self: *Self, from: tp.pid_ref) error{ OutOfMemory, Exit }!void { +pub fn request_most_recent_file(self: *Self, from: tp.pid_ref) SendError!void { const file_path = if (self.files.items.len > 0) self.files.items[0].path else null; - try from.send(.{file_path}); + from.send(.{file_path}) catch return error.SendFailed; } -pub fn request_recent_files(self: *Self, from: tp.pid_ref, max: usize) error{ OutOfMemory, Exit }!void { +pub fn request_recent_files(self: *Self, from: tp.pid_ref, max: usize) SendError!void { defer from.send(.{ "PRJ", "recent_done", self.longest_file_path, "" }) catch {}; for (self.files.items, 0..) |file, i| { - try from.send(.{ "PRJ", "recent", self.longest_file_path, file.path }); + from.send(.{ "PRJ", "recent", self.longest_file_path, file.path }) catch return error.SendFailed; if (i >= max) return; } } -fn simple_query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) error{ OutOfMemory, Exit }!usize { +fn simple_query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) (OutOfMemoryError || SendError)!usize { var i: usize = 0; defer from.send(.{ "PRJ", "recent_done", self.longest_file_path, query }) catch {}; for (self.files.items) |file| { @@ -168,7 +172,7 @@ fn simple_query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: [ defer self.allocator.free(matches); var n: usize = 0; while (n < query.len) : (n += 1) matches[n] = idx + n; - try from.send(.{ "PRJ", "recent", self.longest_file_path, file.path, matches }); + from.send(.{ "PRJ", "recent", self.longest_file_path, file.path, matches }) catch return error.SendFailed; i += 1; if (i >= max) return i; } @@ -176,7 +180,7 @@ fn simple_query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: [ return i; } -pub fn query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) error{ OutOfMemory, Exit }!usize { +pub fn query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) (OutOfMemoryError || SendError)!usize { if (query.len < 3) return self.simple_query_recent_files(from, max, query); defer from.send(.{ "PRJ", "recent_done", self.longest_file_path, query }) catch {}; @@ -216,16 +220,16 @@ pub fn query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []co std.mem.sort(Match, matches.items, {}, less_fn); for (matches.items[0..@min(max, matches.items.len)]) |match| - try from.send(.{ "PRJ", "recent", self.longest_file_path, match.path, match.matches }); + from.send(.{ "PRJ", "recent", self.longest_file_path, match.path, match.matches }) catch return error.SendFailed; return @min(max, matches.items.len); } -pub fn add_pending_file(self: *Self, file_path: []const u8, mtime: i128) error{OutOfMemory}!void { +pub fn add_pending_file(self: *Self, file_path: []const u8, mtime: i128) OutOfMemoryError!void { self.longest_file_path = @max(self.longest_file_path, file_path.len); (try self.pending.addOne()).* = .{ .path = try self.allocator.dupe(u8, file_path), .mtime = mtime }; } -pub fn merge_pending_files(self: *Self) error{OutOfMemory}!void { +pub fn merge_pending_files(self: *Self) OutOfMemoryError!void { defer self.sort_files_by_mtime(); const existing = try self.files.toOwnedSlice(); self.files = self.pending; @@ -237,12 +241,12 @@ pub fn merge_pending_files(self: *Self) error{OutOfMemory}!void { self.allocator.free(existing); } -pub fn update_mru(self: *Self, file_path: []const u8, row: usize, col: usize) !void { +pub fn update_mru(self: *Self, file_path: []const u8, row: usize, col: usize) OutOfMemoryError!void { defer self.sort_files_by_mtime(); try self.update_mru_internal(file_path, std.time.nanoTimestamp(), row, col); } -fn update_mru_internal(self: *Self, file_path: []const u8, mtime: i128, row: usize, col: usize) !void { +fn update_mru_internal(self: *Self, file_path: []const u8, mtime: i128, row: usize, col: usize) OutOfMemoryError!void { for (self.files.items) |*file| { if (!std.mem.eql(u8, file.path, file_path)) continue; file.mtime = mtime; @@ -269,16 +273,16 @@ fn update_mru_internal(self: *Self, file_path: []const u8, mtime: i128, row: usi } } -pub fn get_mru_position(self: *Self, from: tp.pid_ref, file_path: []const u8) !void { +pub fn get_mru_position(self: *Self, from: tp.pid_ref, file_path: []const u8) SendError!void { for (self.files.items) |*file| { if (!std.mem.eql(u8, file.path, file_path)) continue; if (file.row != 0) - try from.send(.{ "cmd", "goto_line_and_column", .{ file.row + 1, file.col + 1 } }); + from.send(.{ "cmd", "goto_line_and_column", .{ file.row + 1, file.col + 1 } }) catch return error.SendFailed; return; } } -pub fn did_open(self: *Self, file_path: []const u8, file_type: []const u8, language_server: []const u8, version: usize, text: []const u8) !void { +pub fn did_open(self: *Self, file_path: []const u8, file_type: []const u8, language_server: []const u8, version: usize, text: []const u8) GetLspError!void { self.update_mru(file_path, 0, 0) catch {}; const lsp = try self.get_lsp(language_server); if (!self.file_language_server.contains(file_path)) { @@ -292,7 +296,7 @@ pub fn did_open(self: *Self, file_path: []const u8, file_type: []const u8, langu }); } -pub fn did_change(self: *Self, file_path: []const u8, version: usize, root_dst_addr: usize, root_src_addr: usize) !void { +pub fn did_change(self: *Self, file_path: []const u8, version: usize, root_dst_addr: usize, root_src_addr: usize) GetFileLspError!void { const lsp = try self.get_file_lsp(file_path); const uri = try self.make_URI(file_path); defer self.allocator.free(uri); @@ -404,23 +408,25 @@ pub fn did_close(self: *Self, file_path: []const u8) !void { }); } -pub fn goto_definition(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) !void { +pub fn goto_definition(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) SendGotoRequestError!void { return self.send_goto_request(from, file_path, row, col, "textDocument/definition"); } -pub fn goto_declaration(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) !void { +pub fn goto_declaration(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) SendGotoRequestError!void { return self.send_goto_request(from, file_path, row, col, "textDocument/declaration"); } -pub fn goto_implementation(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) !void { +pub fn goto_implementation(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) SendGotoRequestError!void { return self.send_goto_request(from, file_path, row, col, "textDocument/implementation"); } -pub fn goto_type_definition(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) !void { +pub fn goto_type_definition(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) SendGotoRequestError!void { return self.send_goto_request(from, file_path, row, col, "textDocument/typeDefinition"); } -fn send_goto_request(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize, method: []const u8) !void { +pub const SendGotoRequestError = (SendError || InvalidMessageError || GetFileLspError || GetLineOfFileError || cbor.Error); + +fn send_goto_request(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize, method: []const u8) SendGotoRequestError!void { const lsp = try self.get_file_lsp(file_path); const uri = try self.make_URI(file_path); defer self.allocator.free(uri); @@ -431,20 +437,24 @@ fn send_goto_request(self: *Self, from: tp.pid_ref, file_path: []const u8, row: defer self.allocator.free(response.buf); var link: []const u8 = undefined; var locations: []const u8 = undefined; - if (try response.match(.{ "child", tp.string, "result", tp.array })) { - if (try response.match(.{ tp.any, tp.any, tp.any, .{tp.extract_cbor(&link)} })) { + if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.array })) { + if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, .{tp.extract_cbor(&link)} })) { try self.navigate_to_location_link(from, link); - } else if (try response.match(.{ tp.any, tp.any, tp.any, tp.extract_cbor(&locations) })) { + } else if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&locations) })) { try self.send_reference_list(from, locations); } - } else if (try response.match(.{ "child", tp.string, "result", tp.null_ })) { + } else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) { return; - } else if (try response.match(.{ "child", tp.string, "result", tp.extract_cbor(&link) })) { + } else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&link) })) { try self.navigate_to_location_link(from, link); } } -fn navigate_to_location_link(_: *Self, from: tp.pid_ref, location_link: []const u8) !void { +const OutOfMemoryError = error{OutOfMemory}; +pub const SendError = error{SendFailed}; +pub const InvalidMessageError = error{ InvalidMessage, InvalidMessageField, InvalidTargetURI }; + +fn navigate_to_location_link(_: *Self, from: tp.pid_ref, location_link: []const u8) (SendError || InvalidMessageError || cbor.Error)!void { var iter = location_link; var targetUri: ?[]const u8 = null; var targetRange: ?Range = null; @@ -480,7 +490,7 @@ fn navigate_to_location_link(_: *Self, from: tp.pid_ref, location_link: []const }; } if (targetSelectionRange) |sel| { - try from.send(.{ "cmd", "navigate", .{ + from.send(.{ "cmd", "navigate", .{ .file = file_path, .goto = .{ targetRange.?.start.line + 1, @@ -490,19 +500,19 @@ fn navigate_to_location_link(_: *Self, from: tp.pid_ref, location_link: []const sel.end.line, sel.end.character, }, - } }); + } }) catch return error.SendFailed; } else { - try from.send(.{ "cmd", "navigate", .{ + from.send(.{ "cmd", "navigate", .{ .file = file_path, .goto = .{ targetRange.?.start.line + 1, targetRange.?.start.character + 1, }, - } }); + } }) catch return error.SendFailed; } } -pub fn references(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) !void { +pub fn references(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) SendGotoRequestError!void { const lsp = try self.get_file_lsp(file_path); const uri = try self.make_URI(file_path); defer self.allocator.free(uri); @@ -515,14 +525,14 @@ pub fn references(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usi }); defer self.allocator.free(response.buf); var locations: []const u8 = undefined; - if (try response.match(.{ "child", tp.string, "result", tp.null_ })) { + if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) { return; - } else if (try response.match(.{ "child", tp.string, "result", tp.extract_cbor(&locations) })) { + } else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&locations) })) { try self.send_reference_list(from, locations); } } -fn send_reference_list(self: *Self, to: tp.pid_ref, locations: []const u8) !void { +fn send_reference_list(self: *Self, to: tp.pid_ref, locations: []const u8) (InvalidMessageError || SendError || GetLineOfFileError || cbor.Error)!void { defer to.send(.{ "REF", "done" }) catch {}; var iter = locations; var len = try cbor.decodeArrayHeader(&iter); @@ -536,7 +546,7 @@ fn send_reference_list(self: *Self, to: tp.pid_ref, locations: []const u8) !void log.logger("lsp").print("found {d} references", .{count}); } -fn send_reference(self: *Self, to: tp.pid_ref, location: []const u8) !void { +fn send_reference(self: *Self, to: tp.pid_ref, location: []const u8) (InvalidMessageError || SendError || GetLineOfFileError || cbor.Error)!void { var iter = location; var targetUri: ?[]const u8 = null; var targetRange: ?Range = null; @@ -577,7 +587,7 @@ fn send_reference(self: *Self, to: tp.pid_ref, location: []const u8) !void { file_path[self.name.len + 1 ..] else file_path; - try to.send(.{ + to.send(.{ "REF", file_path_, targetRange.?.start.line + 1, @@ -585,10 +595,10 @@ fn send_reference(self: *Self, to: tp.pid_ref, location: []const u8) !void { targetRange.?.end.line + 1, targetRange.?.end.character, line, - }); + }) catch return error.SendFailed; } -pub fn completion(self: *Self, _: tp.pid_ref, file_path: []const u8, row: usize, col: usize) !void { +pub fn completion(self: *Self, _: tp.pid_ref, file_path: []const u8, row: usize, col: usize) (SendError || GetFileLspError)!void { const lsp = try self.get_file_lsp(file_path); const uri = try self.make_URI(file_path); defer self.allocator.free(uri); @@ -599,7 +609,7 @@ pub fn completion(self: *Self, _: tp.pid_ref, file_path: []const u8, row: usize, defer self.allocator.free(response.buf); } -pub fn hover(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) !void { +pub fn hover(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) (SendError || InvalidMessageError || GetFileLspError || cbor.Error)!void { const lsp = try self.get_file_lsp(file_path); const uri = try self.make_URI(file_path); defer self.allocator.free(uri); @@ -611,14 +621,14 @@ pub fn hover(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, c }); defer self.allocator.free(response.buf); var result: []const u8 = undefined; - if (try response.match(.{ "child", tp.string, "result", tp.null_ })) { + if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) { try send_content_msg_empty(from, "hover", file_path, row, col); - } else if (try response.match(.{ "child", tp.string, "result", tp.extract_cbor(&result) })) { + } else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&result) })) { try self.send_hover(from, file_path, row, col, result); } } -fn send_hover(self: *Self, to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, result: []const u8) !void { +fn send_hover(self: *Self, to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, result: []const u8) (SendError || InvalidMessageError || cbor.Error)!void { var iter = result; var len = cbor.decodeMapHeader(&iter) catch return; var contents: []const u8 = ""; @@ -698,19 +708,19 @@ fn send_content_msg( kind: []const u8, content: []const u8, range: ?Range, -) !void { +) SendError!void { const r = range orelse Range{ .start = .{ .line = row, .character = col }, .end = .{ .line = row, .character = col }, }; - return try to.send(.{ tag, file_path, kind, content, r.start.line, r.start.character, r.end.line, r.end.character }); + to.send(.{ tag, file_path, kind, content, r.start.line, r.start.character, r.end.line, r.end.character }) catch return error.SendFailed; } -fn send_content_msg_empty(to: tp.pid_ref, tag: []const u8, file_path: []const u8, row: usize, col: usize) !void { +fn send_content_msg_empty(to: tp.pid_ref, tag: []const u8, file_path: []const u8, row: usize, col: usize) SendError!void { return send_content_msg(to, tag, file_path, row, col, "plaintext", "", null); } -pub fn publish_diagnostics(self: *Self, to: tp.pid_ref, params_cb: []const u8) !void { +pub fn publish_diagnostics(self: *Self, to: tp.pid_ref, params_cb: []const u8) (InvalidMessageError || SendError || cbor.Error)!void { var uri: ?[]const u8 = null; var diagnostics: []const u8 = &.{}; var iter = params_cb; @@ -728,7 +738,7 @@ pub fn publish_diagnostics(self: *Self, to: tp.pid_ref, params_cb: []const u8) ! } if (uri == null) return error.InvalidMessageField; - if (!std.mem.eql(u8, uri.?[0..7], "file://")) return error.InvalidURI; + if (!std.mem.eql(u8, uri.?[0..7], "file://")) return error.InvalidTargetURI; var file_path_buf: [std.fs.max_path_bytes]u8 = undefined; const file_path = std.Uri.percentDecodeBackwards(&file_path_buf, uri.?[7..]); @@ -744,7 +754,7 @@ pub fn publish_diagnostics(self: *Self, to: tp.pid_ref, params_cb: []const u8) ! } } -fn send_diagnostic(_: *Self, to: tp.pid_ref, file_path: []const u8, diagnostic: []const u8) !void { +fn send_diagnostic(_: *Self, to: tp.pid_ref, file_path: []const u8, diagnostic: []const u8) (InvalidMessageError || SendError || cbor.Error)!void { var source: []const u8 = "unknown"; var code: []const u8 = "none"; var message: []const u8 = "empty"; @@ -772,7 +782,7 @@ fn send_diagnostic(_: *Self, to: tp.pid_ref, file_path: []const u8, diagnostic: } } if (range == null) return error.InvalidMessageField; - try to.send(.{ "cmd", "add_diagnostic", .{ + to.send(.{ "cmd", "add_diagnostic", .{ file_path, source, code, @@ -782,11 +792,11 @@ fn send_diagnostic(_: *Self, to: tp.pid_ref, file_path: []const u8, diagnostic: range.?.start.character, range.?.end.line, range.?.end.character, - } }); + } }) catch return error.SendFailed; } -fn send_clear_diagnostics(_: *Self, to: tp.pid_ref, file_path: []const u8) !void { - try to.send(.{ "cmd", "clear_diagnostics", .{file_path} }); +fn send_clear_diagnostics(_: *Self, to: tp.pid_ref, file_path: []const u8) SendError!void { + to.send(.{ "cmd", "clear_diagnostics", .{file_path} }) catch return error.SendFailed; } const Range = struct { start: Position, end: Position }; @@ -860,19 +870,19 @@ pub fn show_message(_: *Self, _: tp.pid_ref, params_cb: []const u8) !void { logger.print("{s}", .{msg}); } -pub fn register_capability(self: *Self, from: tp.pid_ref, id: i32, params_cb: []const u8) !void { +pub fn register_capability(self: *Self, from: tp.pid_ref, id: i32, params_cb: []const u8) (OutOfMemoryError || SendError)!void { _ = params_cb; return self.send_lsp_response(from, id, null); } -pub fn send_lsp_response(self: *Self, from: tp.pid_ref, id: i32, result: anytype) !void { +pub fn send_lsp_response(self: *Self, from: tp.pid_ref, id: i32, result: anytype) (OutOfMemoryError || SendError)!void { var cb = std.ArrayList(u8).init(self.allocator); defer cb.deinit(); try cbor.writeValue(cb.writer(), result); - return from.send(.{ "RSP", id, cb.items }); + from.send(.{ "RSP", id, cb.items }) catch return error.SendFailed; } -fn send_lsp_init_request(self: *Self, lsp: LSP, project_path: []const u8, project_basename: []const u8, project_uri: []const u8) !tp.message { +fn send_lsp_init_request(self: *Self, lsp: LSP, project_path: []const u8, project_basename: []const u8, project_uri: []const u8) (OutOfMemoryError || SendError)!tp.message { return lsp.send_request(self.allocator, "initialize", .{ .processId = if (builtin.os.tag == .linux) std.os.linux.getpid() else null, .rootPath = project_path, @@ -1198,7 +1208,9 @@ fn format_lsp_name_func( const eol = '\n'; -fn get_line_of_file(self: *Self, allocator: std.mem.Allocator, file_path: []const u8, line_: usize) ![]const u8 { +const GetLineOfFileError = (OutOfMemoryError || std.fs.File.OpenError || std.fs.File.Reader.Error); + +fn get_line_of_file(self: *Self, allocator: std.mem.Allocator, file_path: []const u8, line_: usize) GetLineOfFileError![]const u8 { const line = line_ + 1; const file = try std.fs.cwd().openFile(file_path, .{ .mode = .read_only }); defer file.close(); diff --git a/src/project_manager.zig b/src/project_manager.zig index ede4734..c576a68 100644 --- a/src/project_manager.zig +++ b/src/project_manager.zig @@ -5,6 +5,7 @@ const log = @import("log"); const tracy = @import("tracy"); const FileType = @import("syntax").FileType; const root = @import("root"); +const builtin = @import("builtin"); const Project = @import("Project.zig"); @@ -14,12 +15,25 @@ const Self = @This(); const module_name = @typeName(Self); const request_timeout = std.time.ns_per_s * 5; -pub fn get() !Self { +pub const ProjectError = error{NoProject}; + +const SpawnError = (OutOfMemoryError || error{ThespianSpawnFailed}); +const OutOfMemoryError = error{OutOfMemory}; +const SendError = (SpawnError || error{SendFailed}); +const FileSystemError = error{FileSystem}; +const SetCwdError = if (builtin.os.tag == .windows) error{UnrecognizedVolume} else error{}; +const CallError = tp.CallError; + +pub fn get() SpawnError!Self { const pid = tp.env.get().proc(module_name); return if (pid.expired()) create() else .{ .pid = pid }; } -fn create() !Self { +fn send(message: anytype) SendError!void { + return (try get()).pid.send(message) catch error.SendFailed; +} + +fn create() SpawnError!Self { const pid = try Process.create(); defer pid.deinit(); tp.env.get().proc_set(module_name, pid.ref()); @@ -35,142 +49,142 @@ pub fn shutdown() void { pid.send(.{"shutdown"}) catch {}; } -pub fn open(rel_project_directory: []const u8) !void { +pub fn open(rel_project_directory: []const u8) (SpawnError || FileSystemError || SendError || std.fs.File.OpenError || SetCwdError)!void { var path_buf: [std.fs.max_path_bytes]u8 = undefined; const project_directory = std.fs.cwd().realpath(rel_project_directory, &path_buf) catch "(none)"; var dir = try std.fs.openDirAbsolute(project_directory, .{}); try dir.setAsCwd(); dir.close(); tp.env.get().str_set("project", project_directory); - return (try get()).pid.send(.{ "open", project_directory }); + return send(.{ "open", project_directory }); } -pub fn request_most_recent_file(allocator: std.mem.Allocator) !?[]const u8 { +pub fn request_most_recent_file(allocator: std.mem.Allocator) (CallError || ProjectError || cbor.Error)!?[]const u8 { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); + return error.NoProject; const rsp = try (try get()).pid.call(allocator, request_timeout, .{ "request_most_recent_file", project }); defer allocator.free(rsp.buf); var file_path: []const u8 = undefined; - return if (try rsp.match(.{tp.extract(&file_path)})) try allocator.dupe(u8, file_path) else null; + return if (try cbor.match(rsp.buf, .{tp.extract(&file_path)})) try allocator.dupe(u8, file_path) else null; } -pub fn request_recent_files(max: usize) !void { +pub fn request_recent_files(max: usize) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "request_recent_files", project, max }); + return error.NoProject; + return send(.{ "request_recent_files", project, max }); } -pub fn request_recent_projects(allocator: std.mem.Allocator) !tp.message { +pub fn request_recent_projects(allocator: std.mem.Allocator) (ProjectError || CallError)!tp.message { const project = tp.env.get().str("project"); return (try get()).pid.call(allocator, request_timeout, .{ "request_recent_projects", project }); } -pub fn query_recent_files(max: usize, query: []const u8) !void { +pub fn query_recent_files(max: usize, query: []const u8) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "query_recent_files", project, max, query }); + return error.NoProject; + return send(.{ "query_recent_files", project, max, query }); } -pub fn request_path_files(max: usize, path: []const u8) !void { +pub fn request_path_files(max: usize, path: []const u8) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "request_path_files", project, max, path }); + return error.NoProject; + return send(.{ "request_path_files", project, max, path }); } -pub fn did_open(file_path: []const u8, file_type: *const FileType, version: usize, text: []const u8) !void { +pub fn did_open(file_path: []const u8, file_type: *const FileType, version: usize, text: []const u8) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); + return error.NoProject; const text_ptr: usize = if (text.len > 0) @intFromPtr(text.ptr) else 0; - return (try get()).pid.send(.{ "did_open", project, file_path, file_type.name, file_type.language_server, version, text_ptr, text.len }); + return send(.{ "did_open", project, file_path, file_type.name, file_type.language_server, version, text_ptr, text.len }); } -pub fn did_change(file_path: []const u8, version: usize, root_dst: usize, root_src: usize) !void { +pub fn did_change(file_path: []const u8, version: usize, root_dst: usize, root_src: usize) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "did_change", project, file_path, version, root_dst, root_src }); + return error.NoProject; + return send(.{ "did_change", project, file_path, version, root_dst, root_src }); } -pub fn did_save(file_path: []const u8) !void { +pub fn did_save(file_path: []const u8) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "did_save", project, file_path }); + return error.NoProject; + return send(.{ "did_save", project, file_path }); } -pub fn did_close(file_path: []const u8) !void { +pub fn did_close(file_path: []const u8) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "did_close", project, file_path }); + return error.NoProject; + return send(.{ "did_close", project, file_path }); } -pub fn goto_definition(file_path: []const u8, row: usize, col: usize) !void { +pub fn goto_definition(file_path: []const u8, row: usize, col: usize) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "goto_definition", project, file_path, row, col }); + return error.NoProject; + return send(.{ "goto_definition", project, file_path, row, col }); } -pub fn goto_declaration(file_path: []const u8, row: usize, col: usize) !void { +pub fn goto_declaration(file_path: []const u8, row: usize, col: usize) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "goto_declaration", project, file_path, row, col }); + return error.NoProject; + return send(.{ "goto_declaration", project, file_path, row, col }); } -pub fn goto_implementation(file_path: []const u8, row: usize, col: usize) !void { +pub fn goto_implementation(file_path: []const u8, row: usize, col: usize) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "goto_implementation", project, file_path, row, col }); + return error.NoProject; + return send(.{ "goto_implementation", project, file_path, row, col }); } -pub fn goto_type_definition(file_path: []const u8, row: usize, col: usize) !void { +pub fn goto_type_definition(file_path: []const u8, row: usize, col: usize) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "goto_type_definition", project, file_path, row, col }); + return error.NoProject; + return send(.{ "goto_type_definition", project, file_path, row, col }); } -pub fn references(file_path: []const u8, row: usize, col: usize) !void { +pub fn references(file_path: []const u8, row: usize, col: usize) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "references", project, file_path, row, col }); + return error.NoProject; + return send(.{ "references", project, file_path, row, col }); } -pub fn completion(file_path: []const u8, row: usize, col: usize) !void { +pub fn completion(file_path: []const u8, row: usize, col: usize) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "completion", project, file_path, row, col }); + return error.NoProject; + return send(.{ "completion", project, file_path, row, col }); } -pub fn hover(file_path: []const u8, row: usize, col: usize) !void { +pub fn hover(file_path: []const u8, row: usize, col: usize) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "hover", project, file_path, row, col }); + return error.NoProject; + return send(.{ "hover", project, file_path, row, col }); } -pub fn update_mru(file_path: []const u8, row: usize, col: usize) !void { +pub fn update_mru(file_path: []const u8, row: usize, col: usize) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "update_mru", project, file_path, row, col }); + return error.NoProject; + return send(.{ "update_mru", project, file_path, row, col }); } -pub fn get_mru_position(file_path: []const u8) !void { +pub fn get_mru_position(file_path: []const u8) (ProjectError || SendError)!void { const project = tp.env.get().str("project"); if (project.len == 0) - return tp.exit("No project"); - return (try get()).pid.send(.{ "get_mru_position", project, file_path }); + return error.NoProject; + return send(.{ "get_mru_position", project, file_path }); } const Process = struct { @@ -181,6 +195,9 @@ const Process = struct { projects: ProjectsMap, walker: ?tp.pid = null, + const InvalidArgumentError = error{InvalidArgument}; + const UnsupportedError = error{Unsupported}; + const Receiver = tp.Receiver(*Process); const ProjectsMap = std.StringHashMap(*Project); const RecentProject = struct { @@ -188,7 +205,7 @@ const Process = struct { last_used: i128, }; - fn create() !tp.pid { + fn create() SpawnError!tp.pid { const allocator = std.heap.c_allocator; const self = try allocator.create(Process); self.* = .{ @@ -221,14 +238,17 @@ const Process = struct { fn receive(self: *Process, from: tp.pid_ref, m: tp.message) tp.result { errdefer self.deinit(); - self.receive_safe(from, m) catch |e| { - if (std.mem.eql(u8, "normal", tp.error_text())) - return e; - self.logger.err("receive", tp.exit_error(e, @errorReturnTrace())); + return self.receive_safe(from, m) catch |e| switch (e) { + error.ExitNormal => tp.exit_normal(), + else => blk: { + const err = tp.exit_error(e, @errorReturnTrace()); + self.logger.err("receive", err); + break :blk err; + }, }; } - fn receive_safe(self: *Process, from: tp.pid_ref, m: tp.message) !void { + fn receive_safe(self: *Process, from: tp.pid_ref, m: tp.message) (error{ExitNormal} || SendError || cbor.Error)!void { var project_directory: []const u8 = undefined; var path: []const u8 = undefined; var query: []const u8 = undefined; @@ -249,77 +269,77 @@ const Process = struct { var root_dst: usize = 0; var root_src: usize = 0; - if (try m.match(.{ "walk_tree_entry", tp.extract(&project_directory), tp.extract(&path), tp.extract(&high), tp.extract(&low) })) { + if (try cbor.match(m.buf, .{ "walk_tree_entry", tp.extract(&project_directory), tp.extract(&path), tp.extract(&high), tp.extract(&low) })) { const mtime = (@as(i128, @intCast(high)) << 64) | @as(i128, @intCast(low)); if (self.projects.get(project_directory)) |project| project.add_pending_file( path, mtime, ) catch |e| self.logger.err("walk_tree_entry", e); - } else if (try m.match(.{ "walk_tree_done", tp.extract(&project_directory) })) { + } else if (try cbor.match(m.buf, .{ "walk_tree_done", tp.extract(&project_directory) })) { if (self.walker) |pid| pid.deinit(); self.walker = null; - self.loaded(project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "update_mru", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { - self.update_mru(project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "child", tp.extract(&project_directory), tp.extract(&language_server), "notify", tp.extract(&method), tp.extract_cbor(¶ms_cb) })) { + self.loaded(project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "update_mru", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { + self.update_mru(project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "child", tp.extract(&project_directory), tp.extract(&language_server), "notify", tp.extract(&method), tp.extract_cbor(¶ms_cb) })) { self.dispatch_notify(project_directory, language_server, method, params_cb) catch |e| return self.logger.err("lsp-handling", e); - } else if (try m.match(.{ "child", tp.extract(&project_directory), tp.extract(&language_server), "request", tp.extract(&method), tp.extract(&id), tp.extract_cbor(¶ms_cb) })) { + } else if (try cbor.match(m.buf, .{ "child", tp.extract(&project_directory), tp.extract(&language_server), "request", tp.extract(&method), tp.extract(&id), tp.extract_cbor(¶ms_cb) })) { self.dispatch_request(from, project_directory, language_server, method, id, params_cb) catch |e| return self.logger.err("lsp-handling", e); - } else if (try m.match(.{ "child", tp.extract(&path), "done" })) { + } else if (try cbor.match(m.buf, .{ "child", tp.extract(&path), "done" })) { self.logger.print_err("lsp-handling", "child '{s}' terminated", .{path}); - } else if (try m.match(.{ "open", tp.extract(&project_directory) })) { - self.open(project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "request_most_recent_file", tp.extract(&project_directory) })) { - self.request_most_recent_file(from, project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "request_recent_files", tp.extract(&project_directory), tp.extract(&max) })) { - self.request_recent_files(from, project_directory, max) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "request_recent_projects", tp.extract(&project_directory) })) { - self.request_recent_projects(from, project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "query_recent_files", tp.extract(&project_directory), tp.extract(&max), tp.extract(&query) })) { - self.query_recent_files(from, project_directory, max, query) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "request_path_files", tp.extract(&project_directory), tp.extract(&max), tp.extract(&path) })) { - self.request_path_files(from, project_directory, max, path) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "did_open", tp.extract(&project_directory), tp.extract(&path), tp.extract(&file_type), tp.extract_cbor(&language_server), tp.extract(&version), tp.extract(&text_ptr), tp.extract(&text_len) })) { + } else if (try cbor.match(m.buf, .{ "open", tp.extract(&project_directory) })) { + self.open(project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "request_most_recent_file", tp.extract(&project_directory) })) { + self.request_most_recent_file(from, project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "request_recent_files", tp.extract(&project_directory), tp.extract(&max) })) { + self.request_recent_files(from, project_directory, max) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "request_recent_projects", tp.extract(&project_directory) })) { + self.request_recent_projects(from, project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "query_recent_files", tp.extract(&project_directory), tp.extract(&max), tp.extract(&query) })) { + self.query_recent_files(from, project_directory, max, query) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "request_path_files", tp.extract(&project_directory), tp.extract(&max), tp.extract(&path) })) { + self.request_path_files(from, project_directory, max, path) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "did_open", tp.extract(&project_directory), tp.extract(&path), tp.extract(&file_type), tp.extract_cbor(&language_server), tp.extract(&version), tp.extract(&text_ptr), tp.extract(&text_len) })) { const text = if (text_len > 0) @as([*]const u8, @ptrFromInt(text_ptr))[0..text_len] else ""; - self.did_open(project_directory, path, file_type, language_server, version, text) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "did_change", tp.extract(&project_directory), tp.extract(&path), tp.extract(&version), tp.extract(&root_dst), tp.extract(&root_src) })) { - self.did_change(project_directory, path, version, root_dst, root_src) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "did_save", tp.extract(&project_directory), tp.extract(&path) })) { - self.did_save(project_directory, path) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "did_close", tp.extract(&project_directory), tp.extract(&path) })) { - self.did_close(project_directory, path) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "goto_definition", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { - self.goto_definition(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "goto_declaration", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { - self.goto_declaration(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "goto_implementation", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { - self.goto_implementation(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "goto_type_definition", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { - self.goto_type_definition(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "references", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { - self.references(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "completion", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { - self.completion(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "hover", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { - self.hover(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{ "get_mru_position", tp.extract(&project_directory), tp.extract(&path) })) { - self.get_mru_position(from, project_directory, path) catch |e| return from.forward_error(e, @errorReturnTrace()); - } else if (try m.match(.{"shutdown"})) { + self.did_open(project_directory, path, file_type, language_server, version, text) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "did_change", tp.extract(&project_directory), tp.extract(&path), tp.extract(&version), tp.extract(&root_dst), tp.extract(&root_src) })) { + self.did_change(project_directory, path, version, root_dst, root_src) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "did_save", tp.extract(&project_directory), tp.extract(&path) })) { + self.did_save(project_directory, path) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "did_close", tp.extract(&project_directory), tp.extract(&path) })) { + self.did_close(project_directory, path) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "goto_definition", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { + self.goto_definition(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "goto_declaration", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { + self.goto_declaration(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "goto_implementation", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { + self.goto_implementation(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "goto_type_definition", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { + self.goto_type_definition(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "references", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { + self.references(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "completion", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { + self.completion(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "hover", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { + self.hover(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{ "get_mru_position", tp.extract(&project_directory), tp.extract(&path) })) { + self.get_mru_position(from, project_directory, path) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.SendFailed; + } else if (try cbor.match(m.buf, .{"shutdown"})) { if (self.walker) |pid| pid.send(.{"stop"}) catch {}; self.persist_projects(); - try from.send(.{ "project_manager", "shutdown" }); - return tp.exit_normal(); - } else if (try m.match(.{ "exit", "normal" })) { + from.send(.{ "project_manager", "shutdown" }) catch return error.SendFailed; + return error.ExitNormal; + } else if (try cbor.match(m.buf, .{ "exit", "normal" })) { return; - } else if (try m.match(.{ "exit", "DEADSEND", tp.more })) { + } else if (try cbor.match(m.buf, .{ "exit", "DEADSEND", tp.more })) { return; } else { self.logger.err("receive", tp.unexpected(m)); } } - fn open(self: *Process, project_directory: []const u8) !void { + fn open(self: *Process, project_directory: []const u8) (SpawnError || std.fs.Dir.OpenError)!void { if (self.projects.get(project_directory) == null) { self.logger.print("opening: {s}", .{project_directory}); const project = try self.allocator.create(Project); @@ -333,7 +353,7 @@ const Process = struct { } } - fn loaded(self: *Process, project_directory: []const u8) error{ OutOfMemory, Exit }!void { + fn loaded(self: *Process, project_directory: []const u8) OutOfMemoryError!void { const project = self.projects.get(project_directory) orelse return; try project.merge_pending_files(); self.logger.print("opened: {s} with {d} files in {d} ms", .{ @@ -343,19 +363,19 @@ const Process = struct { }); } - fn request_most_recent_file(self: *Process, from: tp.pid_ref, project_directory: []const u8) error{ OutOfMemory, Exit }!void { - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + fn request_most_recent_file(self: *Process, from: tp.pid_ref, project_directory: []const u8) (ProjectError || SendError)!void { + const project = self.projects.get(project_directory) orelse return error.NoProject; project.sort_files_by_mtime(); return project.request_most_recent_file(from); } - fn request_recent_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize) error{ OutOfMemory, Exit }!void { - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + fn request_recent_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize) (ProjectError || SendError)!void { + const project = self.projects.get(project_directory) orelse return error.NoProject; project.sort_files_by_mtime(); return project.request_recent_files(from, max); } - fn request_recent_projects(self: *Process, from: tp.pid_ref, project_directory: []const u8) error{ OutOfMemory, Exit }!void { + fn request_recent_projects(self: *Process, from: tp.pid_ref, project_directory: []const u8) (ProjectError || SendError)!void { var recent_projects = std.ArrayList(RecentProject).init(self.allocator); defer recent_projects.deinit(); self.load_recent_projects(&recent_projects, project_directory) catch {}; @@ -365,11 +385,11 @@ const Process = struct { try cbor.writeArrayHeader(writer, recent_projects.items.len); for (recent_projects.items) |project| try cbor.writeValue(writer, project.name); - try from.send_raw(.{ .buf = message.items }); + from.send_raw(.{ .buf = message.items }) catch return error.SendFailed; } - fn query_recent_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize, query: []const u8) error{ OutOfMemory, Exit }!void { - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + fn query_recent_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize, query: []const u8) (ProjectError || SendError)!void { + const project = self.projects.get(project_directory) orelse return error.NoProject; const start_time = std.time.milliTimestamp(); const matched = try project.query_recent_files(from, max, query); const query_time = std.time.milliTimestamp() - start_time; @@ -377,103 +397,103 @@ const Process = struct { self.logger.print("query \"{s}\" matched {d}/{d} in {d} ms", .{ query, matched, project.files.items.len, query_time }); } - fn request_path_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize, path: []const u8) error{ OutOfMemory, Exit }!void { - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); - request_path_files_async(self.allocator, from, project, max, path) catch |e| return tp.exit_error(e, @errorReturnTrace()); + 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; + try request_path_files_async(self.allocator, from, project, max, path); } - fn did_open(self: *Process, project_directory: []const u8, file_path: []const u8, file_type: []const u8, language_server: []const u8, version: usize, text: []const u8) !void { + fn did_open(self: *Process, project_directory: []const u8, file_path: []const u8, file_type: []const u8, language_server: []const u8, version: usize, text: []const u8) (ProjectError || InvalidArgumentError || SendError || CallError || cbor.Error)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".did_open" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.did_open(file_path, file_type, language_server, version, text); } - fn did_change(self: *Process, project_directory: []const u8, file_path: []const u8, version: usize, root_dst: usize, root_src: usize) !void { + fn did_change(self: *Process, project_directory: []const u8, file_path: []const u8, version: usize, root_dst: usize, root_src: usize) (ProjectError || Project.GetFileLspError)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".did_change" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.did_change(file_path, version, root_dst, root_src); } - fn did_save(self: *Process, project_directory: []const u8, file_path: []const u8) !void { + fn did_save(self: *Process, project_directory: []const u8, file_path: []const u8) (ProjectError || Project.GetFileLspError)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".did_save" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.did_save(file_path); } - fn did_close(self: *Process, project_directory: []const u8, file_path: []const u8) !void { + fn did_close(self: *Process, project_directory: []const u8, file_path: []const u8) (ProjectError || Project.GetFileLspError)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".did_close" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.did_close(file_path); } - fn goto_definition(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) !void { + fn goto_definition(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) (ProjectError || Project.SendGotoRequestError)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".goto_definition" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.goto_definition(from, file_path, row, col); } - fn goto_declaration(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) !void { + fn goto_declaration(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) (ProjectError || Project.SendGotoRequestError)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".goto_declaration" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.goto_declaration(from, file_path, row, col); } - fn goto_implementation(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) !void { + fn goto_implementation(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) (ProjectError || Project.SendGotoRequestError)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".goto_implementation" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.goto_implementation(from, file_path, row, col); } - fn goto_type_definition(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) !void { + fn goto_type_definition(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) (ProjectError || Project.SendGotoRequestError)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".goto_type_definition" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.goto_type_definition(from, file_path, row, col); } - fn references(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) !void { + fn references(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) (ProjectError || Project.SendGotoRequestError)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".references" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.references(from, file_path, row, col); } - fn completion(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) !void { + fn completion(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) (ProjectError || Project.SendError || Project.GetFileLspError)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".completion" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.completion(from, file_path, row, col); } - fn hover(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) !void { + fn hover(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) (ProjectError || Project.SendError || Project.InvalidMessageError || Project.GetFileLspError || cbor.Error)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".hover" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.hover(from, file_path, row, col); } - fn get_mru_position(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8) !void { + fn get_mru_position(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8) (ProjectError || Project.SendError)!void { const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".get_mru_position" }); defer frame.deinit(); - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.get_mru_position(from, file_path); } - fn update_mru(self: *Process, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) !void { - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + fn update_mru(self: *Process, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) (ProjectError || OutOfMemoryError)!void { + const project = self.projects.get(project_directory) orelse return error.NoProject; return project.update_mru(file_path, row, col); } - fn dispatch_notify(self: *Process, project_directory: []const u8, language_server: []const u8, method: []const u8, params_cb: []const u8) !void { + fn dispatch_notify(self: *Process, project_directory: []const u8, language_server: []const u8, method: []const u8, params_cb: []const u8) (ProjectError || Project.InvalidMessageError || Project.SendError || cbor.Error || cbor.JsonEncodeError)!void { _ = language_server; - const project = self.projects.get(project_directory) orelse return tp.exit("No project"); + const project = self.projects.get(project_directory) orelse return error.NoProject; return if (std.mem.eql(u8, method, "textDocument/publishDiagnostics")) project.publish_diagnostics(self.parent.ref(), params_cb) else if (std.mem.eql(u8, method, "window/showMessage")) @@ -487,15 +507,16 @@ const Process = struct { }; } - fn dispatch_request(self: *Process, from: tp.pid_ref, project_directory: []const u8, language_server: []const u8, method: []const u8, id: i32, params_cb: []const u8) !void { + fn dispatch_request(self: *Process, from: tp.pid_ref, project_directory: []const u8, language_server: []const u8, method: []const u8, id: i32, params_cb: []const u8) (ProjectError || SendError || cbor.Error || cbor.JsonEncodeError || UnsupportedError)!void { _ = language_server; - const project = if (self.projects.get(project_directory)) |p| p else return tp.exit("No project"); + const project = if (self.projects.get(project_directory)) |p| p else return error.NoProject; return if (std.mem.eql(u8, method, "client/registerCapability")) project.register_capability(from, id, params_cb) else blk: { const params = try cbor.toJsonAlloc(self.allocator, params_cb); defer self.allocator.free(params); - break :blk tp.exit_fmt("unsupported LSP request: {s} -> {s}", .{ method, params }); + self.logger.print_err("lsp", "unsupported LSP request: {s} -> {s}", .{ method, params }); + break :blk error.Unsupported; }; } @@ -609,7 +630,7 @@ const Process = struct { } }; -fn request_path_files_async(a_: std.mem.Allocator, parent_: tp.pid_ref, project_: *Project, max_: usize, path_: []const u8) !void { +fn request_path_files_async(a_: std.mem.Allocator, parent_: tp.pid_ref, project_: *Project, max_: usize, path_: []const u8) (SpawnError || std.fs.Dir.OpenError)!void { return struct { allocator: std.mem.Allocator, project_name: []const u8, @@ -621,7 +642,7 @@ fn request_path_files_async(a_: std.mem.Allocator, parent_: tp.pid_ref, project_ const path_files = @This(); const Receiver = tp.Receiver(*path_files); - fn spawn_link(allocator: std.mem.Allocator, parent: tp.pid_ref, project: *Project, max: usize, path: []const u8) !void { + fn spawn_link(allocator: std.mem.Allocator, parent: tp.pid_ref, project: *Project, max: usize, path: []const u8) (SpawnError || std.fs.Dir.OpenError)!void { const self = try allocator.create(path_files); self.* = .{ .allocator = allocator, @@ -673,7 +694,7 @@ fn request_path_files_async(a_: std.mem.Allocator, parent_: tp.pid_ref, project_ }.spawn_link(a_, parent_, project_, max_, path_); } -fn walk_tree_async(a_: std.mem.Allocator, root_path_: []const u8) !tp.pid { +fn walk_tree_async(a_: std.mem.Allocator, root_path_: []const u8) (SpawnError || std.fs.Dir.OpenError)!tp.pid { return struct { allocator: std.mem.Allocator, root_path: []const u8, @@ -685,7 +706,7 @@ fn walk_tree_async(a_: std.mem.Allocator, root_path_: []const u8) !tp.pid { const tree_walker = @This(); const Receiver = tp.Receiver(*tree_walker); - fn spawn_link(allocator: std.mem.Allocator, root_path: []const u8) !tp.pid { + fn spawn_link(allocator: std.mem.Allocator, root_path: []const u8) (SpawnError || std.fs.Dir.OpenError)!tp.pid { const self = try allocator.create(tree_walker); self.* = .{ .allocator = allocator, @@ -775,7 +796,7 @@ const FilteredWalker = struct { dirname_len: usize, }; - pub fn next(self: *FilteredWalker) error{OutOfMemory}!?Path { + pub fn next(self: *FilteredWalker) OutOfMemoryError!?Path { while (self.stack.items.len != 0) { var top = &self.stack.items[self.stack.items.len - 1]; var containing = top; diff --git a/src/tui/MessageFilter.zig b/src/tui/MessageFilter.zig index 3f9ba16..a6b0811 100644 --- a/src/tui/MessageFilter.zig +++ b/src/tui/MessageFilter.zig @@ -1,5 +1,6 @@ const std = @import("std"); const tp = @import("thespian"); +const cbor = @import("cbor"); const Allocator = std.mem.Allocator; const ArrayList = std.ArrayList; const Self = @This(); @@ -8,9 +9,16 @@ const MessageFilter = Self; ptr: *anyopaque, vtable: *const VTable, +pub const Error = (cbor.Error || cbor.JsonEncodeError || error{ + OutOfMemory, + ThespianSpawnFailed, + NoProject, + SendFailed, +}); + pub const VTable = struct { deinit: *const fn (ctx: *anyopaque) void, - filter: *const fn (ctx: *anyopaque, from: tp.pid_ref, m: tp.message) error{Exit}!bool, + filter: *const fn (ctx: *anyopaque, from: tp.pid_ref, m: tp.message) Error!bool, type_name: []const u8, }; @@ -27,7 +35,7 @@ pub fn to_owned(pimpl: anytype) Self { } }.deinit, .filter = struct { - pub fn filter(ctx: *anyopaque, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { + pub fn filter(ctx: *anyopaque, from_: tp.pid_ref, m: tp.message) Error!bool { return child.filter(@as(*child, @ptrCast(@alignCast(ctx))), from_, m); } }.filter, @@ -46,7 +54,7 @@ pub fn to_unowned(pimpl: anytype) Self { pub fn deinit(_: *anyopaque) void {} }.deinit, .filter = struct { - pub fn filter(ctx: *anyopaque, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { + pub fn filter(ctx: *anyopaque, from_: tp.pid_ref, m: tp.message) Error!bool { return child.filter(@as(*child, @ptrCast(@alignCast(ctx))), from_, m); } }.filter, @@ -54,7 +62,7 @@ pub fn to_unowned(pimpl: anytype) Self { }; } -pub fn bind(pimpl: anytype, comptime f: *const fn (ctx: @TypeOf(pimpl), from: tp.pid_ref, m: tp.message) error{Exit}!bool) Self { +pub fn bind(pimpl: anytype, comptime f: *const fn (ctx: @TypeOf(pimpl), from: tp.pid_ref, m: tp.message) Error!bool) Self { const impl = @typeInfo(@TypeOf(pimpl)); const child: type = impl.Pointer.child; return .{ @@ -65,7 +73,7 @@ pub fn bind(pimpl: anytype, comptime f: *const fn (ctx: @TypeOf(pimpl), from: tp pub fn deinit(_: *anyopaque) void {} }.deinit, .filter = struct { - pub fn filter(ctx: *anyopaque, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { + pub fn filter(ctx: *anyopaque, from_: tp.pid_ref, m: tp.message) Error!bool { return @call(.auto, f, .{ @as(*child, @ptrCast(@alignCast(ctx))), from_, m }); } }.filter, @@ -84,7 +92,7 @@ pub fn dynamic_cast(self: Self, comptime T: type) ?*T { null; } -pub fn filter(self: Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { +pub fn filter(self: Self, from_: tp.pid_ref, m: tp.message) Error!bool { return self.vtable.filter(self.ptr, from_, m); } @@ -120,14 +128,14 @@ pub const List = struct { self.list.orderedRemove(i).deinit(); } - pub fn filter(self: *const List, from: tp.pid_ref, m: tp.message) error{Exit}!bool { + pub fn filter(self: *const List, from: tp.pid_ref, m: tp.message) Error!bool { var sfa = std.heap.stackFallback(4096, self.allocator); const a = sfa.get(); - const buf = a.alloc(u8, m.buf.len) catch |e| return tp.exit_error(e, @errorReturnTrace()); + const buf = try a.alloc(u8, m.buf.len); defer a.free(buf); @memcpy(buf[0..m.buf.len], m.buf); const m_: tp.message = .{ .buf = buf[0..m.buf.len] }; - var e: ?error{Exit} = null; + var e: ?Error = null; for (self.list.items) |*i| { const consume = i.filter(from, m_) catch |e_| ret: { e = e_; diff --git a/src/tui/editor_gutter.zig b/src/tui/editor_gutter.zig index 477d2d3..3eb9fc4 100644 --- a/src/tui/editor_gutter.zig +++ b/src/tui/editor_gutter.zig @@ -333,7 +333,7 @@ fn diff_result_send(from: tp.pid_ref, edits: []diff.Edit) !void { from.send_raw(tp.message{ .buf = stream.getWritten() }) catch return; } -pub fn process_diff(self: *Self, cb: []const u8) !void { +pub fn process_diff(self: *Self, cb: []const u8) MessageFilter.Error!void { var iter = cb; self.diff_symbols_clear(); var count = try cbor.decodeArrayHeader(&iter); @@ -382,10 +382,10 @@ fn process_edit(self: *Self, kind: Kind, line: usize, offset: usize, bytes: []co }; } -pub fn filter_receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { +pub fn filter_receive(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { var cb: []const u8 = undefined; - if (try m.match(.{ "DIFF", tp.extract_cbor(&cb) })) { - self.process_diff(cb) catch |e| return tp.exit_error(e, @errorReturnTrace()); + if (cbor.match(m.buf, .{ "DIFF", tp.extract_cbor(&cb) }) catch false) { + try self.process_diff(cb); return true; } return false; diff --git a/src/tui/logview.zig b/src/tui/logview.zig index 69443ee..6417e58 100644 --- a/src/tui/logview.zig +++ b/src/tui/logview.zig @@ -5,10 +5,12 @@ const Allocator = @import("std").mem.Allocator; const ArrayList = @import("std").ArrayList; const tp = @import("thespian"); +const cbor = @import("cbor"); const Plane = @import("renderer").Plane; const Widget = @import("Widget.zig"); +const MessageFilter = @import("MessageFilter.zig"); const escape = fmt.fmtSliceEscapeLower; @@ -82,21 +84,21 @@ fn output_tdiff(self: *Self, tdiff: i64) !void { } } -pub fn process_log(m: tp.message) !void { +pub fn process_log(m: tp.message) MessageFilter.Error!void { var src: []const u8 = undefined; var context: []const u8 = undefined; var msg: []const u8 = undefined; const buffer = get_buffer(); - if (try m.match(.{ "log", tp.extract(&src), tp.extract(&msg) })) { + if (try cbor.match(m.buf, .{ "log", tp.extract(&src), tp.extract(&msg) })) { try append(buffer, src, msg, .info); - } else if (try m.match(.{ "log", "error", tp.extract(&src), tp.extract(&context), "->", tp.extract(&msg) })) { + } else if (try cbor.match(m.buf, .{ "log", "error", tp.extract(&src), tp.extract(&context), "->", tp.extract(&msg) })) { const err_stop = "error.Stop"; if (eql(u8, msg, err_stop)) return; if (msg.len >= err_stop.len + 1 and eql(u8, msg[0 .. err_stop.len + 1], err_stop ++ "\n")) return; try append_error(buffer, src, context, msg); - } else if (try m.match(.{ "log", tp.extract(&src), tp.more })) { + } else if (try cbor.match(m.buf, .{ "log", tp.extract(&src), tp.more })) { try append_json(buffer, src, m); } } @@ -120,16 +122,23 @@ fn append(buffer: *Buffer, src: []const u8, msg: []const u8, level: Level) !void }; } -fn append_error(buffer: *Buffer, src: []const u8, context: []const u8, msg_: []const u8) !void { - var buf: [4096]u8 = undefined; - const msg = try fmt.bufPrint(&buf, "error in {s}: {s}", .{ context, msg_ }); - try append(buffer, src, msg, .err); +fn append_error(buffer: *Buffer, src: []const u8, context: []const u8, msg_: []const u8) MessageFilter.Error!void { + const std = @import("std"); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + var sfa = std.heap.stackFallback(4096, arena.allocator()); + var msg = std.ArrayList(u8).init(sfa.get()); + try fmt.format(msg.writer(), "error in {s}: {s}", .{ context, msg_ }); + try append(buffer, src, msg.items, .err); } -fn append_json(buffer: *Buffer, src: []const u8, m: tp.message) !void { - var buf: [4096]u8 = undefined; - const json = try m.to_json(&buf); - try append(buffer, src, json, .err); +fn append_json(buffer: *Buffer, src: []const u8, m: tp.message) MessageFilter.Error!void { + const std = @import("std"); + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + var sfa = std.heap.stackFallback(4096, arena.allocator()); + const msg = try cbor.toJsonAlloc(sfa.get(), m.buf); + try append(buffer, src, msg, .err); } fn get_buffer() *Buffer { diff --git a/src/tui/mode/mini/file_browser.zig b/src/tui/mode/mini/file_browser.zig index 2234cc8..fd2dcbe 100644 --- a/src/tui/mode/mini/file_browser.zig +++ b/src/tui/mode/mini/file_browser.zig @@ -1,5 +1,6 @@ const std = @import("std"); const tp = @import("thespian"); +const cbor = @import("cbor"); const log = @import("log"); const root = @import("root"); @@ -216,15 +217,15 @@ pub fn Create(options: type) type { try self.do_complete(); } - fn receive_path_entry(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { - if (try m.match(.{ "PRJ", tp.more })) { - self.process_project_manager(m) catch |e| return tp.exit_error(e, @errorReturnTrace()); + fn receive_path_entry(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { + if (try cbor.match(m.buf, .{ "PRJ", tp.more })) { + try self.process_project_manager(m); return true; } return false; } - fn process_project_manager(self: *Self, m: tp.message) !void { + fn process_project_manager(self: *Self, m: tp.message) MessageFilter.Error!void { defer { if (tui.current().mini_mode) |*mini_mode| { mini_mode.text = self.file_path.items; @@ -232,23 +233,23 @@ pub fn Create(options: type) type { } } var count: usize = undefined; - if (try m.match(.{ "PRJ", "path_entry", tp.more })) { + if (try cbor.match(m.buf, .{ "PRJ", "path_entry", tp.more })) { return self.process_path_entry(m); - } else if (try m.match(.{ "PRJ", "path_done", tp.any, tp.any, tp.extract(&count) })) { + } else if (try cbor.match(m.buf, .{ "PRJ", "path_done", tp.any, tp.any, tp.extract(&count) })) { try self.do_complete(); } else { log.logger("file_browser").err("receive", tp.unexpected(m)); } } - fn process_path_entry(self: *Self, m: tp.message) !void { + fn process_path_entry(self: *Self, m: tp.message) MessageFilter.Error!void { var path: []const u8 = undefined; var file_name: []const u8 = undefined; - if (try m.match(.{ tp.any, tp.any, tp.any, tp.extract(&path), "DIR", tp.extract(&file_name) })) { + if (try cbor.match(m.buf, .{ tp.any, tp.any, tp.any, tp.extract(&path), "DIR", tp.extract(&file_name) })) { (try self.entries.addOne()).* = .{ .name = try self.allocator.dupe(u8, file_name), .type = .dir }; - } else if (try m.match(.{ tp.any, tp.any, tp.any, tp.extract(&path), "LINK", tp.extract(&file_name) })) { + } else if (try cbor.match(m.buf, .{ tp.any, tp.any, tp.any, tp.extract(&path), "LINK", tp.extract(&file_name) })) { (try self.entries.addOne()).* = .{ .name = try self.allocator.dupe(u8, file_name), .type = .link }; - } else if (try m.match(.{ tp.any, tp.any, tp.any, tp.extract(&path), "FILE", tp.extract(&file_name) })) { + } else if (try cbor.match(m.buf, .{ tp.any, tp.any, tp.any, tp.extract(&path), "FILE", tp.extract(&file_name) })) { (try self.entries.addOne()).* = .{ .name = try self.allocator.dupe(u8, file_name), .type = .file }; } else { log.logger("file_browser").err("receive", tp.unexpected(m)); diff --git a/src/tui/mode/overlay/command_palette.zig b/src/tui/mode/overlay/command_palette.zig index 56216be..c2a9aa7 100644 --- a/src/tui/mode/overlay/command_palette.zig +++ b/src/tui/mode/overlay/command_palette.zig @@ -118,7 +118,7 @@ pub fn restore_state(palette: *Type) !void { tp.extract(&name_), tp.extract(&used_time), }) catch |e| switch (e) { - error.CborTooShort => return, + error.TooShort => return, else => return e, }) { const id = command.getId(name_) orelse continue; diff --git a/src/tui/mode/overlay/open_recent.zig b/src/tui/mode/overlay/open_recent.zig index 75ccd21..59da250 100644 --- a/src/tui/mode/overlay/open_recent.zig +++ b/src/tui/mode/overlay/open_recent.zig @@ -148,19 +148,19 @@ fn add_item(self: *Self, file_name: []const u8, matches: ?[]const u8) !void { try self.menu.add_item_with_handler(label.items, menu_action_open_file); } -fn receive_project_manager(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { - if (try m.match(.{ "PRJ", tp.more })) { - self.process_project_manager(m) catch |e| return tp.exit_error(e, @errorReturnTrace()); +fn receive_project_manager(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { + if (cbor.match(m.buf, .{ "PRJ", tp.more }) catch false) { + try self.process_project_manager(m); return true; } return false; } -fn process_project_manager(self: *Self, m: tp.message) !void { +fn process_project_manager(self: *Self, m: tp.message) MessageFilter.Error!void { var file_name: []const u8 = undefined; var matches: []const u8 = undefined; var query: []const u8 = undefined; - if (try m.match(.{ "PRJ", "recent", tp.extract(&self.longest), tp.extract(&file_name), tp.extract_cbor(&matches) })) { + if (try cbor.match(m.buf, .{ "PRJ", "recent", tp.extract(&self.longest), tp.extract(&file_name), tp.extract_cbor(&matches) })) { if (self.need_reset) self.reset_results(); try self.add_item(file_name, matches); self.menu.resize(.{ .y = 0, .x = self.menu_pos_x(), .w = self.menu_width() }); @@ -169,7 +169,7 @@ fn process_project_manager(self: *Self, m: tp.message) !void { self.need_select_first = false; } tui.need_render(); - } else if (try m.match(.{ "PRJ", "recent", tp.extract(&self.longest), tp.extract(&file_name) })) { + } else if (try cbor.match(m.buf, .{ "PRJ", "recent", tp.extract(&self.longest), tp.extract(&file_name) })) { if (self.need_reset) self.reset_results(); try self.add_item(file_name, null); self.menu.resize(.{ .y = 0, .x = self.menu_pos_x(), .w = self.menu_width() }); @@ -178,7 +178,7 @@ fn process_project_manager(self: *Self, m: tp.message) !void { self.need_select_first = false; } tui.need_render(); - } else if (try m.match(.{ "PRJ", "recent_done", tp.extract(&self.longest), tp.extract(&query) })) { + } else if (try cbor.match(m.buf, .{ "PRJ", "recent_done", tp.extract(&self.longest), tp.extract(&query) })) { self.query_pending = false; self.need_reset = true; if (!std.mem.eql(u8, self.inputbox.text.items, query)) @@ -283,7 +283,7 @@ fn reset_results(self: *Self) void { self.need_select_first = true; } -fn start_query(self: *Self) !void { +fn start_query(self: *Self) MessageFilter.Error!void { if (self.query_pending) return; self.query_pending = true; try project_manager.query_recent_files(max_recent_files, self.inputbox.text.items); diff --git a/src/tui/status/clock.zig b/src/tui/status/clock.zig index d2ac2c4..d59e1ad 100644 --- a/src/tui/status/clock.zig +++ b/src/tui/status/clock.zig @@ -1,5 +1,6 @@ const std = @import("std"); const tp = @import("thespian"); +const cbor = @import("cbor"); const zeit = @import("zeit"); const Plane = @import("renderer").Plane; @@ -67,8 +68,8 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { return false; } -fn receive_tick(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { - if (try m.match(.{"CLOCK"})) { +fn receive_tick(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { + if (try cbor.match(m.buf, .{"CLOCK"})) { tui.need_render(); self.update_tick_timer(.ticked); return true; diff --git a/src/tui/status/minilog.zig b/src/tui/status/minilog.zig index a604d01..2fa6e5b 100644 --- a/src/tui/status/minilog.zig +++ b/src/tui/status/minilog.zig @@ -1,5 +1,6 @@ const std = @import("std"); const tp = @import("thespian"); +const cbor = @import("cbor"); const log = @import("log"); const Plane = @import("renderer").Plane; @@ -76,13 +77,13 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { return false; } -fn receive_log(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { +fn receive_log(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { var clear_msg_num: usize = 0; - if (try m.match(.{ "log", tp.more })) { - logview.process_log(m) catch |e| return tp.exit_error(e, @errorReturnTrace()); - self.process_log(m) catch |e| return tp.exit_error(e, @errorReturnTrace()); + if (try cbor.match(m.buf, .{ "log", tp.more })) { + try logview.process_log(m); + try self.process_log(m); return true; - } else if (try m.match(.{ "MINILOG", tp.extract(&clear_msg_num) })) { + } else if (try cbor.match(m.buf, .{ "MINILOG", tp.extract(&clear_msg_num) })) { if (clear_msg_num == self.msg_counter) self.clear(); return true; @@ -90,20 +91,20 @@ fn receive_log(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { return false; } -fn process_log(self: *Self, m: tp.message) !void { +fn process_log(self: *Self, m: tp.message) MessageFilter.Error!void { var src: []const u8 = undefined; var context: []const u8 = undefined; var msg: []const u8 = undefined; - if (try m.match(.{ "log", tp.extract(&src), tp.extract(&msg) })) { + if (try cbor.match(m.buf, .{ "log", tp.extract(&src), tp.extract(&msg) })) { try self.set(msg, .info); - } else if (try m.match(.{ "log", "error", tp.extract(&src), tp.extract(&context), "->", tp.extract(&msg) })) { + } else if (try cbor.match(m.buf, .{ "log", "error", tp.extract(&src), tp.extract(&context), "->", tp.extract(&msg) })) { const err_stop = "error.Stop"; if (std.mem.eql(u8, msg, err_stop)) return; if (msg.len >= err_stop.len + 1 and std.mem.eql(u8, msg[0 .. err_stop.len + 1], err_stop ++ "\n")) return; try self.set(msg, .err); - } else if (try m.match(.{ "log", tp.extract(&src), tp.more })) { + } else if (try cbor.match(m.buf, .{ "log", tp.extract(&src), tp.more })) { self.level = .err; var s = std.json.writeStream(self.msg.writer(), .{}); var iter: []const u8 = m.buf;