feat: add LSP client support for error responses to requests

This commit is contained in:
CJ van den Berg 2025-07-20 18:41:12 +02:00
parent 5d256413da
commit 996ec70e42
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9

View file

@ -67,6 +67,45 @@ pub fn send_notification_raw(self: *const Self, method: []const u8, cb: []const
self.pid.send(.{ "NTFY", method, cb }) catch return error.SendFailed;
}
pub const ErrorCode = enum(i32) {
// Defined by JSON-RPC
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
// Defined by LSP
RequestFailed = -32803,
ServerCancelled = -32802,
ContentModified = -32801,
RequestCancelled = -32800,
};
pub fn send_response(allocator: std.mem.Allocator, to: tp.pid_ref, cbor_id: []const u8, result: anytype) (SendError || OutOfMemoryError)!void {
var cb = std.ArrayList(u8).init(allocator);
defer cb.deinit();
const writer = cb.writer();
try cbor.writeArrayHeader(writer, 3);
try cbor.writeValue(writer, "RSP");
try writer.writeAll(cbor_id);
try cbor.writeValue(cb.writer(), result);
to.send_raw(.{ .buf = cb.items }) catch return error.SendFailed;
}
pub fn send_error_response(allocator: std.mem.Allocator, to: tp.pid_ref, cbor_id: []const u8, code: ErrorCode, message: []const u8) (SendError || OutOfMemoryError)!void {
var cb = std.ArrayList(u8).init(allocator);
defer cb.deinit();
const writer = cb.writer();
try cbor.writeArrayHeader(writer, 4);
try cbor.writeValue(writer, "ERR");
try writer.writeAll(cbor_id);
try cbor.writeValue(cb.writer(), code);
try cbor.writeValue(cb.writer(), message);
to.send_raw(.{ .buf = cb.items }) catch return error.SendFailed;
}
pub fn close(self: *Self) void {
self.deinit();
}
@ -252,6 +291,8 @@ const Process = struct {
var err: []const u8 = "";
var code: u32 = 0;
var cbor_id: []const u8 = "";
var error_code: ErrorCode = undefined;
var message: []const u8 = "";
if (try cbor.match(m.buf, .{ "REQ", "initialize", tp.extract(&bytes) })) {
try self.send_request(from, "initialize", bytes);
@ -262,6 +303,8 @@ const Process = struct {
}
} else if (try cbor.match(m.buf, .{ "RSP", tp.extract_cbor(&cbor_id), tp.extract_cbor(&bytes) })) {
try self.send_response(cbor_id, bytes);
} else if (try cbor.match(m.buf, .{ "ERR", tp.extract_cbor(&cbor_id), tp.extract(&error_code), tp.extract(&message) })) {
try self.send_error_response(cbor_id, error_code, message);
} else if (try cbor.match(m.buf, .{ "NTFY", "initialized", tp.extract(&bytes) })) {
self.state = .running;
try self.send_notification("initialized", bytes);
@ -482,6 +525,39 @@ const Process = struct {
self.write_log("### SEND response:\n{s}\n###\n", .{output.items});
}
fn send_error_response(self: *Process, cbor_id: []const u8, error_code: ErrorCode, message: []const u8) (error{Closed} || SendError || cbor.Error || cbor.JsonEncodeError)!void {
const sp = if (self.sp) |*sp| sp else return error.Closed;
var msg = std.ArrayList(u8).init(self.allocator);
defer msg.deinit();
const msg_writer = msg.writer();
try cbor.writeMapHeader(msg_writer, 3);
try cbor.writeValue(msg_writer, "jsonrpc");
try cbor.writeValue(msg_writer, "2.0");
try cbor.writeValue(msg_writer, "id");
try msg_writer.writeAll(cbor_id);
try cbor.writeValue(msg_writer, "error");
try cbor.writeMapHeader(msg_writer, 2);
try cbor.writeValue(msg_writer, "code");
try cbor.writeValue(msg_writer, @intFromEnum(error_code));
try cbor.writeValue(msg_writer, "message");
try cbor.writeValue(msg_writer, message);
const json = try cbor.toJsonAlloc(self.allocator, msg.items);
defer self.allocator.free(json);
var output = std.ArrayList(u8).init(self.allocator);
defer output.deinit();
const writer = output.writer();
const terminator = "\r\n";
const content_length = json.len + terminator.len;
try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length});
_ = try writer.write(json);
_ = try writer.write(terminator);
sp.send(output.items) catch return error.SendFailed;
self.write_log("### SEND error response:\n{s}\n###\n", .{output.items});
}
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;