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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Provides the necessary building blocks to develop Language Server Protocol imple
# Installation

> [!NOTE]
> The default branch requires Zig `0.15.0-dev.1145+3ae0ba096` or later. Checkout the `0.14.x` branch when using Zig 0.14
> The default branch requires Zig `0.15.0-dev.1160+e43617e68` or later. Checkout the `0.14.x` branch when using Zig 0.14

```bash
# Initialize a `zig build` project if you haven't already
Expand Down
2 changes: 1 addition & 1 deletion build.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const std = @import("std");
const builtin = @import("builtin");

const minimum_zig_version = "0.15.0-dev.1145+3ae0ba096";
const minimum_zig_version = "0.15.0-dev.1160+e43617e68";

pub fn build(b: *std.Build) void {
comptime if (builtin.zig_version.order(std.SemanticVersion.parse(minimum_zig_version) catch unreachable) == .lt) {
Expand Down
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.{
.name = .lsp_kit,
.version = "0.1.0",
.minimum_zig_version = "0.15.0-dev.1145+3ae0ba096",
.minimum_zig_version = "0.15.0-dev.1160+e43617e68",
.dependencies = .{},
.paths = .{
"build.zig",
Expand Down
84 changes: 42 additions & 42 deletions src/lsp.zig
Original file line number Diff line number Diff line change
Expand Up @@ -707,23 +707,23 @@ pub const JsonRPCMessage = union(enum) {
test "emit_null_optional_fields" {
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"exit"}
, "{f}", .{parser.jsonFmt(JsonRPCMessage{ .notification = .{ .method = "exit", .params = null } }, .{ .emit_null_optional_fields = false })});
, "{f}", .{std.json.fmt(JsonRPCMessage{ .notification = .{ .method = "exit", .params = null } }, .{ .emit_null_optional_fields = false })});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"exit","params":null}
, "{f}", .{parser.jsonFmt(JsonRPCMessage{ .notification = .{ .method = "exit", .params = null } }, .{ .emit_null_optional_fields = true })});
, "{f}", .{std.json.fmt(JsonRPCMessage{ .notification = .{ .method = "exit", .params = null } }, .{ .emit_null_optional_fields = true })});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"exit","params":null}
, "{f}", .{parser.jsonFmt(JsonRPCMessage{ .notification = .{ .method = "exit", .params = .null } }, .{ .emit_null_optional_fields = false })});
, "{f}", .{std.json.fmt(JsonRPCMessage{ .notification = .{ .method = "exit", .params = .null } }, .{ .emit_null_optional_fields = false })});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"exit","params":null}
, "{f}", .{parser.jsonFmt(JsonRPCMessage{ .notification = .{ .method = "exit", .params = .null } }, .{ .emit_null_optional_fields = true })});
, "{f}", .{std.json.fmt(JsonRPCMessage{ .notification = .{ .method = "exit", .params = .null } }, .{ .emit_null_optional_fields = true })});

try std.testing.expectFmt(
\\{"jsonrpc":"2.0","result":null}
, "{f}", .{parser.jsonFmt(JsonRPCMessage{ .response = .{ .id = null, .result_or_error = .{ .result = null } } }, .{ .emit_null_optional_fields = false })});
, "{f}", .{std.json.fmt(JsonRPCMessage{ .response = .{ .id = null, .result_or_error = .{ .result = null } } }, .{ .emit_null_optional_fields = false })});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","id":null,"result":null}
, "{f}", .{parser.jsonFmt(JsonRPCMessage{ .response = .{ .id = null, .result_or_error = .{ .result = null } } }, .{ .emit_null_optional_fields = true })});
, "{f}", .{std.json.fmt(JsonRPCMessage{ .response = .{ .id = null, .result_or_error = .{ .result = null } } }, .{ .emit_null_optional_fields = true })});
}

fn testParse(message: []const u8, expected: JsonRPCMessage, parse_options: std.json.ParseOptions) !void {
Expand All @@ -738,10 +738,10 @@ pub const JsonRPCMessage = union(enum) {
const parsed_from_value = try std.json.parseFromValue(JsonRPCMessage, allocator, parsed_value.value, parse_options);
defer parsed_from_value.deinit();

const from_slice_stringified = try std.json.stringifyAlloc(allocator, parsed_from_slice.value, .{ .whitespace = .indent_2 });
const from_slice_stringified = try std.json.Stringify.valueAlloc(allocator, parsed_from_slice.value, .{ .whitespace = .indent_2 });
defer allocator.free(from_slice_stringified);

const from_value_stringified = try std.json.stringifyAlloc(allocator, parsed_from_value.value, .{ .whitespace = .indent_2 });
const from_value_stringified = try std.json.Stringify.valueAlloc(allocator, parsed_from_value.value, .{ .whitespace = .indent_2 });
defer allocator.free(from_value_stringified);

if (!std.mem.eql(u8, from_slice_stringified, from_value_stringified)) {
Expand Down Expand Up @@ -872,13 +872,13 @@ test TypedJsonRPCRequest {

try std.testing.expectFmt(
\\{"jsonrpc":"2.0","id":42,"method":"name","params":null}
, "{f}", .{parser.jsonFmt(Request{ .id = .{ .number = 42 }, .method = "name", .params = null }, .{})});
, "{f}", .{std.json.fmt(Request{ .id = .{ .number = 42 }, .method = "name", .params = null }, .{})});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","id":"42","method":"name"}
, "{f}", .{parser.jsonFmt(Request{ .id = .{ .string = "42" }, .method = "name", .params = null }, .{ .emit_null_optional_fields = false })});
, "{f}", .{std.json.fmt(Request{ .id = .{ .string = "42" }, .method = "name", .params = null }, .{ .emit_null_optional_fields = false })});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","id":42,"method":"name","params":true}
, "{f}", .{parser.jsonFmt(Request{ .id = .{ .number = 42 }, .method = "name", .params = true }, .{})});
, "{f}", .{std.json.fmt(Request{ .id = .{ .number = 42 }, .method = "name", .params = true }, .{})});
}

pub fn TypedJsonRPCNotification(
Expand Down Expand Up @@ -924,13 +924,13 @@ test TypedJsonRPCNotification {

try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"name","params":null}
, "{f}", .{parser.jsonFmt(Notification{ .method = "name", .params = null }, .{})});
, "{f}", .{std.json.fmt(Notification{ .method = "name", .params = null }, .{})});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"name"}
, "{f}", .{parser.jsonFmt(Notification{ .method = "name", .params = null }, .{ .emit_null_optional_fields = false })});
, "{f}", .{std.json.fmt(Notification{ .method = "name", .params = null }, .{ .emit_null_optional_fields = false })});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"name","params":true}
, "{f}", .{parser.jsonFmt(Notification{ .method = "name", .params = true }, .{})});
, "{f}", .{std.json.fmt(Notification{ .method = "name", .params = true }, .{})});
}

pub fn TypedJsonRPCResponse(
Expand Down Expand Up @@ -982,13 +982,13 @@ test TypedJsonRPCResponse {

try std.testing.expectFmt(
\\{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"message","data":null}}
, "{f}", .{parser.jsonFmt(Response{
, "{f}", .{std.json.fmt(Response{
.id = null,
.result_or_error = .{ .@"error" = .{ .code = .invalid_request, .message = "message", .data = .null } },
}, .{})});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","id":5,"result":true}
, "{f}", .{parser.jsonFmt(Response{
, "{f}", .{std.json.fmt(Response{
.id = .{ .number = 5 },
.result_or_error = .{ .result = true },
}, .{})});
Expand Down Expand Up @@ -1205,14 +1205,14 @@ pub const Transport = struct {
method: []const u8,
comptime Params: type,
params: Params,
options: std.json.StringifyOptions,
options: std.json.Stringify.Options,
) (WriteError || std.mem.Allocator.Error)!void {
const request: TypedJsonRPCRequest(Params) = .{
.id = id,
.method = method,
.params = params,
};
const json_message = try std.json.stringifyAlloc(allocator, request, options);
const json_message = try std.json.Stringify.valueAlloc(allocator, request, options);
defer allocator.free(json_message);
try transport.writeJsonMessage(json_message);
}
Expand All @@ -1223,13 +1223,13 @@ pub const Transport = struct {
method: []const u8,
comptime Params: type,
params: Params,
options: std.json.StringifyOptions,
options: std.json.Stringify.Options,
) (WriteError || std.mem.Allocator.Error)!void {
const request: TypedJsonRPCNotification(Params) = .{
.method = method,
.params = params,
};
const json_message = try std.json.stringifyAlloc(allocator, request, options);
const json_message = try std.json.Stringify.valueAlloc(allocator, request, options);
defer allocator.free(json_message);
try transport.writeJsonMessage(json_message);
}
Expand All @@ -1240,13 +1240,13 @@ pub const Transport = struct {
id: ?JsonRPCMessage.ID,
comptime Result: type,
result: Result,
options: std.json.StringifyOptions,
options: std.json.Stringify.Options,
) (WriteError || std.mem.Allocator.Error)!void {
const request: TypedJsonRPCResponse(Result) = .{
.id = id,
.result_or_error = .{ .result = result },
};
const json_message = try std.json.stringifyAlloc(allocator, request, options);
const json_message = try std.json.Stringify.valueAlloc(allocator, request, options);
defer allocator.free(json_message);
try transport.writeJsonMessage(json_message);
}
Expand All @@ -1256,13 +1256,13 @@ pub const Transport = struct {
allocator: std.mem.Allocator,
id: ?JsonRPCMessage.ID,
err: JsonRPCMessage.Response.Error,
options: std.json.StringifyOptions,
options: std.json.Stringify.Options,
) (WriteError || std.mem.Allocator.Error)!void {
const request: TypedJsonRPCResponse(void) = .{
.id = id,
.result_or_error = .{ .@"error" = err },
};
const json_message = try std.json.stringifyAlloc(allocator, request, options);
const json_message = try std.json.Stringify.valueAlloc(allocator, request, options);
defer allocator.free(json_message);
try transport.writeJsonMessage(json_message);
}
Expand Down Expand Up @@ -1375,14 +1375,14 @@ pub fn writeRequest(
method: []const u8,
comptime Params: type,
params: Params,
options: std.json.StringifyOptions,
options: std.json.Stringify.Options,
) (std.io.Writer.Error || std.mem.Allocator.Error)!void {
const request: TypedJsonRPCRequest(Params) = .{
.id = id,
.method = method,
.params = params,
};
const json_message = try std.json.stringifyAlloc(allocator, request, options);
const json_message = try std.json.Stringify.valueAlloc(allocator, request, options);
defer allocator.free(json_message);
try writeJsonMessage(writer, json_message);
}
Expand Down Expand Up @@ -1418,13 +1418,13 @@ pub fn writeNotification(
method: []const u8,
comptime Params: type,
params: Params,
options: std.json.StringifyOptions,
options: std.json.Stringify.Options,
) (std.io.Writer.Error || std.mem.Allocator.Error)!void {
const request: TypedJsonRPCNotification(Params) = .{
.method = method,
.params = params,
};
const json_message = try std.json.stringifyAlloc(allocator, request, options);
const json_message = try std.json.Stringify.valueAlloc(allocator, request, options);
defer allocator.free(json_message);
try writeJsonMessage(writer, json_message);
}
Expand Down Expand Up @@ -1458,13 +1458,13 @@ pub fn writeResponse(
id: ?JsonRPCMessage.ID,
comptime Result: type,
result: Result,
options: std.json.StringifyOptions,
options: std.json.Stringify.Options,
) (std.io.Writer.Error || std.mem.Allocator.Error)!void {
const request: TypedJsonRPCResponse(Result) = .{
.id = id,
.result_or_error = .{ .result = result },
};
const json_message = try std.json.stringifyAlloc(allocator, request, options);
const json_message = try std.json.Stringify.valueAlloc(allocator, request, options);
defer allocator.free(json_message);
try writeJsonMessage(writer, json_message);
}
Expand Down Expand Up @@ -1497,13 +1497,13 @@ pub fn writeErrorResponse(
allocator: std.mem.Allocator,
id: ?JsonRPCMessage.ID,
err: JsonRPCMessage.Response.Error,
options: std.json.StringifyOptions,
options: std.json.Stringify.Options,
) (std.io.Writer.Error || std.mem.Allocator.Error)!void {
const request: TypedJsonRPCResponse(void) = .{
.id = id,
.result_or_error = .{ .@"error" = err },
};
const json_message = try std.json.stringifyAlloc(allocator, request, options);
const json_message = try std.json.Stringify.valueAlloc(allocator, request, options);
defer allocator.free(json_message);
try writeJsonMessage(writer, json_message);
}
Expand Down Expand Up @@ -1571,7 +1571,7 @@ fn bufPrintLogMessageTypeErased(
var writer: std.io.Writer = .fixed(buffer);
writer.print(
\\{{"jsonrpc":"2.0","method":"window/logMessage","params":{{"type":{f},"message":"
, .{parser.jsonFmt(message_type, .{})}) catch unreachable;
, .{std.json.fmt(message_type, .{})}) catch unreachable;

const json_message_suffix = "\"}}".*;
const ellipses = "...".*;
Expand Down Expand Up @@ -2332,9 +2332,9 @@ fn testMessageWithOptions(
const message_from_value = try std.json.parseFromValueLeaky(ExampleMessage, arena.allocator(), value, parse_options);
const message_from_slice = try ExampleMessage.parseFromSliceLeaky(arena.allocator(), json_message, parse_options);

const message_string = try std.json.stringifyAlloc(arena.allocator(), message, .{ .whitespace = .indent_2 });
const message_from_value_string = try std.json.stringifyAlloc(arena.allocator(), message_from_value, .{ .whitespace = .indent_2 });
const message_from_slice_string = try std.json.stringifyAlloc(arena.allocator(), message_from_slice, .{ .whitespace = .indent_2 });
const message_string = try std.json.Stringify.valueAlloc(arena.allocator(), message, .{ .whitespace = .indent_2 });
const message_from_value_string = try std.json.Stringify.valueAlloc(arena.allocator(), message_from_value, .{ .whitespace = .indent_2 });
const message_from_slice_string = try std.json.Stringify.valueAlloc(arena.allocator(), message_from_slice, .{ .whitespace = .indent_2 });

try std.testing.expectEqualStrings(message_string, message_from_value_string);
try std.testing.expectEqualStrings(message_string, message_from_slice_string);
Expand Down Expand Up @@ -2422,23 +2422,23 @@ test "Message - ignore_unknown_fields" {
test "Message - stringify emit_null_optional_fields" {
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"exit"}
, "{f}", .{parser.jsonFmt(ExampleMessage{ .notification = .{ .params = .exit } }, .{ .emit_null_optional_fields = false })});
, "{f}", .{std.json.fmt(ExampleMessage{ .notification = .{ .params = .exit } }, .{ .emit_null_optional_fields = false })});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"exit","params":null}
, "{f}", .{parser.jsonFmt(ExampleMessage{ .notification = .{ .params = .exit } }, .{ .emit_null_optional_fields = true })});
, "{f}", .{std.json.fmt(ExampleMessage{ .notification = .{ .params = .exit } }, .{ .emit_null_optional_fields = true })});

try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"foo"}
, "{f}", .{parser.jsonFmt(ExampleMessage{ .notification = .{ .params = .{ .other = .{ .method = "foo", .params = null } } } }, .{ .emit_null_optional_fields = false })});
, "{f}", .{std.json.fmt(ExampleMessage{ .notification = .{ .params = .{ .other = .{ .method = "foo", .params = null } } } }, .{ .emit_null_optional_fields = false })});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"foo","params":null}
, "{f}", .{parser.jsonFmt(ExampleMessage{ .notification = .{ .params = .{ .other = .{ .method = "foo", .params = null } } } }, .{ .emit_null_optional_fields = true })});
, "{f}", .{std.json.fmt(ExampleMessage{ .notification = .{ .params = .{ .other = .{ .method = "foo", .params = null } } } }, .{ .emit_null_optional_fields = true })});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"foo","params":null}
, "{f}", .{parser.jsonFmt(ExampleMessage{ .notification = .{ .params = .{ .other = .{ .method = "foo", .params = .null } } } }, .{ .emit_null_optional_fields = false })});
, "{f}", .{std.json.fmt(ExampleMessage{ .notification = .{ .params = .{ .other = .{ .method = "foo", .params = .null } } } }, .{ .emit_null_optional_fields = false })});
try std.testing.expectFmt(
\\{"jsonrpc":"2.0","method":"foo","params":null}
, "{f}", .{parser.jsonFmt(ExampleMessage{ .notification = .{ .params = .{ .other = .{ .method = "foo", .params = .null } } } }, .{ .emit_null_optional_fields = true })});
, "{f}", .{std.json.fmt(ExampleMessage{ .notification = .{ .params = .{ .other = .{ .method = "foo", .params = .null } } } }, .{ .emit_null_optional_fields = true })});
}

test "Message.Request" {
Expand Down
28 changes: 4 additions & 24 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -338,14 +338,14 @@ fn writeRequest(writer: *std.io.Writer, meta_model: MetaModel, request: MetaMode
\\
, .{
request.method,
if (request.documentation) |documentation| jsonFmt(documentation, .{}) else null,
if (request.documentation) |documentation| std.json.fmt(documentation, .{}) else null,
messageDirectionName(request.messageDirection),
// NOTE: Multiparams not used here, so we dont have to implement them :)
if (request.params) |params| fmtType(params.Type, &meta_model) else null,
fmtType(request.result, &meta_model),
if (request.partialResult) |ty| fmtType(ty, &meta_model) else null,
if (request.errorData) |ty| fmtType(ty, &meta_model) else null,
if (request.registrationMethod) |method| jsonFmt(method, .{}) else null,
if (request.registrationMethod) |method| std.json.fmt(method, .{}) else null,
if (request.registrationOptions) |ty| fmtType(ty, &meta_model) else null,
});
}
Expand All @@ -364,11 +364,11 @@ fn writeNotification(writer: *std.io.Writer, meta_model: MetaModel, notification
\\
, .{
notification.method,
if (notification.documentation) |documentation| jsonFmt(documentation, .{}) else null,
if (notification.documentation) |documentation| std.json.fmt(documentation, .{}) else null,
messageDirectionName(notification.messageDirection),
// NOTE: Multiparams not used here, so we dont have to implement them :)
if (notification.params) |params| fmtType(params.Type, &meta_model) else null,
if (notification.registrationMethod) |method| jsonFmt(method, .{}) else null,
if (notification.registrationMethod) |method| std.json.fmt(method, .{}) else null,
if (notification.registrationOptions) |ty| fmtType(ty, &meta_model) else null,
});
}
Expand Down Expand Up @@ -479,23 +479,3 @@ fn writeMetaModel(writer: *std.io.Writer, meta_model: MetaModel) std.io.Writer.E
}
try writer.writeAll("};\n");
}

/// Like `std.json.fmt` but supports `std.io.Writer`.
pub fn jsonFmt(value: anytype, options: std.json.StringifyOptions) std.fmt.Alt(FormatJson(@TypeOf(value)), FormatJson(@TypeOf(value)).format) {
return .{ .data = .{ .value = value, .options = options } };
}

fn FormatJson(comptime T: type) type {
return struct {
value: T,
options: std.json.StringifyOptions,

pub fn format(data: @This(), writer: *std.io.Writer) std.io.Writer.Error!void {
const any_writer: std.io.AnyWriter = .{
.context = @ptrCast(writer),
.writeFn = @ptrCast(&std.io.Writer.write),
};
std.json.stringify(data.value, data.options, any_writer) catch |err| return @errorCast(err);
}
};
}
Loading
Loading