Skip to content

Commit 762a865

Browse files
committed
add some convenience function to AnyTransport for writing typed messages
1 parent 81da5d2 commit 762a865

File tree

3 files changed

+244
-122
lines changed

3 files changed

+244
-122
lines changed

examples/hello_client.zig

Lines changed: 61 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,15 @@ pub fn main() !void {
102102
// 4. send `shutdown` request and receive response
103103
// 5. send `exit` notification
104104

105-
// Send an "initialize" request to the server
106-
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize
107-
try sendRequestToServer(gpa, transport.any(), .{ .number = 0 }, "initialize", lsp.types.InitializeParams{
108-
.capabilities = .{}, // the client capabilities tell the server what "features" the client supports
109-
});
105+
std.log.debug("sending 'initialize' request to server", .{});
106+
try transport.any().writeRequest(
107+
gpa,
108+
.{ .number = 0 },
109+
"initialize", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize
110+
lsp.types.InitializeParams,
111+
.{ .capabilities = .{} }, // the client capabilities tell the server what "features" the client supports
112+
.{ .emit_null_optional_fields = false },
113+
);
110114

111115
// Wait for the response from the server
112116
// For the sake of simplicity, we will block here and read messages until the response to our request has been found. All other messages will be ignored.
@@ -134,27 +138,47 @@ pub fn main() !void {
134138
std.process.exit(1);
135139
}
136140

137-
// Send a "initialized" notification to the server
138-
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialized
139-
try sendNotificationToServer(gpa, transport.any(), "initialized", lsp.types.InitializedParams{});
141+
std.log.debug("sending 'initialized' notification to server", .{});
142+
try transport.any().writeNotification(
143+
gpa,
144+
"initialized", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialized
145+
lsp.types.InitializedParams,
146+
.{},
147+
.{ .emit_null_optional_fields = false },
148+
);
140149

141150
// ----------------
142151

143152
std.log.info("This document recently came in by the CLI.", .{});
144-
try sendNotificationToServer(gpa, transport.any(), "textDocument/didOpen", lsp.types.DidOpenTextDocumentParams{
145-
.textDocument = .{
146-
.uri = "untitled:Document", // Usually a file system uri will be provided like 'file:///path/to/main.zig'
147-
.languageId = "",
148-
.text = input_file,
149-
.version = 0,
153+
std.log.debug("sending 'textDocument/didOpen' notification to server", .{});
154+
try transport.any().writeNotification(
155+
gpa,
156+
"textDocument/didOpen", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_didOpen
157+
lsp.types.DidOpenTextDocumentParams,
158+
.{
159+
.textDocument = .{
160+
.uri = "untitled:Document", // Usually a file system uri will be provided like 'file:///path/to/main.zig'
161+
.languageId = "",
162+
.text = input_file,
163+
.version = 0,
164+
},
150165
},
151-
});
166+
.{ .emit_null_optional_fields = false },
167+
);
152168

153169
std.log.info("Just to double check, could you verify that it is formatted correctly?", .{});
154-
try sendRequestToServer(gpa, transport.any(), .{ .number = 1 }, "textDocument/formatting", lsp.types.DocumentFormattingParams{
155-
.textDocument = .{ .uri = "untitled:Document" },
156-
.options = .{ .tabSize = 4, .insertSpaces = true },
157-
});
170+
std.log.debug("sending 'textDocument/formatting' request to server", .{});
171+
try transport.any().writeRequest(
172+
gpa,
173+
.{ .number = 1 },
174+
"textDocument/formatting", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
175+
lsp.types.DocumentFormattingParams,
176+
.{
177+
.textDocument = .{ .uri = "untitled:Document" },
178+
.options = .{ .tabSize = 4, .insertSpaces = true },
179+
},
180+
.{ .emit_null_optional_fields = false },
181+
);
158182

159183
const formatting_response = try readAndIgnoreUntilResponse(gpa, transport.any(), .{ .number = 1 }, "textDocument/formatting");
160184
defer formatting_response.deinit();
@@ -170,59 +194,30 @@ pub fn main() !void {
170194

171195
std.log.info("Well, thanks for your insight on this. Now get out!", .{});
172196

173-
// Send a "shutdown" request to the server
174-
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/#shutdown
175197
// Even though this is a request, we do not wait for a response because we are going to close the server anyway.
176-
try sendRequestToServer(gpa, transport.any(), .{ .number = 2 }, "shutdown", {});
177-
178-
// Send a "exit" notification to the server
179-
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialized
180-
try sendNotificationToServer(gpa, transport.any(), "exit", {});
198+
std.log.debug("sending 'shutdown' request to server", .{});
199+
try transport.any().writeRequest(
200+
gpa,
201+
.{ .number = 2 },
202+
"shutdown", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#shutdown
203+
void,
204+
{},
205+
.{ .emit_null_optional_fields = false },
206+
);
207+
208+
std.log.debug("sending 'exit' notification to server", .{});
209+
try transport.any().writeNotification(
210+
gpa,
211+
"exit", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#exit
212+
void,
213+
{},
214+
.{ .emit_null_optional_fields = false },
215+
);
181216

182217
// The "exit" notification will ask the server to exit its process. Ideally we should wait with a timeout in case the server is not behaving correctly.
183218
_ = try child_process.wait();
184219
}
185220

186-
fn sendRequestToServer(
187-
allocator: std.mem.Allocator,
188-
transport: lsp.AnyTransport,
189-
id: lsp.JsonRPCMessage.ID,
190-
comptime method: []const u8,
191-
params: lsp.ParamsType(method),
192-
) !void {
193-
std.log.debug("sending '{s}' request to server", .{method});
194-
195-
const request: lsp.TypedJsonRPCRequest(lsp.ParamsType(method)) = .{
196-
.id = id,
197-
.method = method,
198-
.params = params,
199-
};
200-
201-
const request_stringified = try std.json.stringifyAlloc(allocator, request, .{ .emit_null_optional_fields = false });
202-
defer allocator.free(request_stringified);
203-
204-
try transport.writeJsonMessage(request_stringified);
205-
}
206-
207-
fn sendNotificationToServer(
208-
allocator: std.mem.Allocator,
209-
transport: lsp.AnyTransport,
210-
comptime method: []const u8,
211-
params: lsp.ParamsType(method),
212-
) !void {
213-
std.log.debug("sending '{s}' notification to server", .{method});
214-
215-
const notification: lsp.TypedJsonRPCNotification(lsp.ParamsType(method)) = .{
216-
.method = method,
217-
.params = params,
218-
};
219-
220-
const notification_stringified = try std.json.stringifyAlloc(allocator, notification, .{ .emit_null_optional_fields = false });
221-
defer allocator.free(notification_stringified);
222-
223-
try transport.writeJsonMessage(notification_stringified);
224-
}
225-
226221
/// Do not use such a function in an actual implementation.
227222
fn readAndIgnoreUntilResponse(
228223
allocator: std.mem.Allocator,

examples/hello_server.zig

Lines changed: 21 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -84,26 +84,31 @@ pub fn main() !void {
8484
// 5. receive `exit` notification
8585

8686
switch (parsed_message.value) {
87-
// requests must be send a response back to the client
87+
// requests must send a response back to the client
8888
.request => |request| switch (request.params) {
8989
.initialize => |params| {
9090
_ = params.capabilities; // the client capabilities tell the server what "features" the client supports
91-
92-
try sendResponseToClient(gpa, transport.any(), request.id, lsp.types.InitializeResult{
93-
// the server capabilities tell the client what "features" the server supports
94-
.serverInfo = .{
95-
.name = "hello-server",
96-
},
97-
.capabilities = .{
98-
.documentFormattingProvider = .{ .bool = true },
91+
try transport.any().writeResponse(
92+
gpa,
93+
request.id,
94+
lsp.types.InitializeResult,
95+
.{
96+
// the server capabilities tell the client what "features" the server supports
97+
.serverInfo = .{
98+
.name = "hello-server",
99+
},
100+
.capabilities = .{
101+
.documentFormattingProvider = .{ .bool = true },
102+
},
99103
},
100-
});
104+
.{ .emit_null_optional_fields = false },
105+
);
101106
},
102-
.shutdown => try sendResponseToClient(gpa, transport.any(), request.id, {}),
107+
.shutdown => try transport.any().writeResponse(gpa, request.id, void, {}, .{}),
103108
.@"textDocument/formatting" => |params| {
104109
const source = documents.get(params.textDocument.uri) orelse {
105110
// We should read the document from the file system
106-
try sendResponseToClient(gpa, transport.any(), request.id, {});
111+
try transport.any().writeResponse(gpa, request.id, void, {}, .{});
107112
continue;
108113
};
109114
const source_z = try gpa.dupeZ(u8, source);
@@ -113,15 +118,15 @@ pub fn main() !void {
113118
defer tree.deinit(gpa);
114119

115120
if (tree.errors.len != 0) {
116-
try sendResponseToClient(gpa, transport.any(), request.id, {});
121+
try transport.any().writeResponse(gpa, request.id, void, {}, .{});
117122
continue;
118123
}
119124

120125
const formatte_source = try tree.render(gpa);
121126
defer gpa.free(formatte_source);
122127

123128
if (std.mem.eql(u8, source, formatte_source)) {
124-
try sendResponseToClient(gpa, transport.any(), request.id, {});
129+
try transport.any().writeResponse(gpa, request.id, void, {}, .{});
125130
continue;
126131
}
127132

@@ -133,9 +138,9 @@ pub fn main() !void {
133138
.newText = formatte_source,
134139
}};
135140

136-
try sendResponseToClient(gpa, transport.any(), request.id, result);
141+
try transport.any().writeResponse(gpa, request.id, []const lsp.types.TextEdit, result, .{});
137142
},
138-
.other => try sendResponseToClient(gpa, transport.any(), request.id, {}),
143+
.other => try transport.any().writeResponse(gpa, request.id, void, {}, .{}),
139144
},
140145
.notification => |notification| switch (notification.params) {
141146
.initialized => {},
@@ -190,43 +195,3 @@ const NotificationMethods = union(enum) {
190195
@"textDocument/didClose": lsp.types.DidCloseTextDocumentParams,
191196
other: lsp.MethodWithParams,
192197
};
193-
194-
fn sendResponseToClient(allocator: std.mem.Allocator, transport: lsp.AnyTransport, id: lsp.JsonRPCMessage.ID, result: anytype) !void {
195-
const response: lsp.TypedJsonRPCResponse(@TypeOf(result)) = .{
196-
.id = id,
197-
.result_or_error = .{ .result = result },
198-
};
199-
const message_stringified = try std.json.stringifyAlloc(allocator, response, .{ .emit_null_optional_fields = false });
200-
defer allocator.free(message_stringified);
201-
try transport.writeJsonMessage(message_stringified);
202-
}
203-
204-
fn sendRequestToClient(allocator: std.mem.Allocator, transport: lsp.AnyTransport, id: lsp.JsonRPCMessage.ID, method: []const u8, params: anytype) !void {
205-
const request: lsp.TypedJsonRPCRequest(@TypeOf(params)) = .{
206-
.id = id,
207-
.method = method,
208-
.params = params,
209-
};
210-
const message_stringified = try std.json.stringifyAlloc(allocator, request, .{ .emit_null_optional_fields = false });
211-
defer allocator.free(message_stringified);
212-
try transport.writeJsonMessage(message_stringified);
213-
}
214-
215-
fn sendNotificationToClient(allocator: std.mem.Allocator, transport: lsp.AnyTransport, method: []const u8, params: anytype) !void {
216-
const notification: lsp.TypedJsonRPCNotification(@TypeOf(params)) = .{
217-
.method = method,
218-
.params = params,
219-
};
220-
const message_stringified = try std.json.stringifyAlloc(allocator, notification, .{ .emit_null_optional_fields = false });
221-
defer allocator.free(message_stringified);
222-
try transport.writeJsonMessage(message_stringified);
223-
}
224-
225-
fn sendResponseErrorToClient(allocator: std.mem.Allocator, transport: lsp.AnyTransport, id: lsp.JsonRPCMessage.ID, err: lsp.JsonRPCMessage.Response.Error) !void {
226-
const response: lsp.JsonRPCMessage = .{
227-
.response = .{ .id = id, .result_or_error = .{ .@"error" = err } },
228-
};
229-
const message_stringified = try std.json.stringifyAlloc(allocator, response, .{ .emit_null_optional_fields = false });
230-
defer allocator.free(message_stringified);
231-
try transport.writeJsonMessage(message_stringified);
232-
}

0 commit comments

Comments
 (0)