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
3 changes: 0 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ jobs:
matrix:
zig-version: [master]
os: [ubuntu-22.04, macos-latest, windows-latest]
include:
- zig-version: "0.14.0"
os: ubuntu-22.04
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
Expand Down
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 minimum supported Zig version is `0.14.0`.
> The default branch requires Zig `0.15.0-dev.1018+1a998886c` 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
10 changes: 5 additions & 5 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
const std = @import("std");
const builtin = @import("builtin");

const minimum_zig_version = std.SemanticVersion.parse("0.14.0") catch unreachable;
const minimum_zig_version = "0.15.0-dev.1018+1a998886c";

pub fn build(b: *std.Build) void {
comptime if (builtin.zig_version.order(minimum_zig_version) == .lt) {
comptime if (builtin.zig_version.order(std.SemanticVersion.parse("0.15.0-dev.1018+1a998886c") catch unreachable) == .lt) {
@compileError(std.fmt.comptimePrint(
\\Your Zig version does not meet the minimum build requirement:
\\ required Zig version: {[minimum_zig_version]}
\\ actual Zig version: {[current_version]}
\\ required Zig version: {[minimum_zig_version]s}
\\ actual Zig version: {[current_version]s}
\\
, .{
.current_version = builtin.zig_version,
.current_version = builtin.zig_version_string,
.minimum_zig_version = minimum_zig_version,
}));
};
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_codegen,
.version = "0.1.0",
.minimum_zig_version = "0.14.0",
.minimum_zig_version = "0.15.0-dev.1023+f551c7c58",
.dependencies = .{},
.paths = .{
"build.zig",
Expand Down
30 changes: 18 additions & 12 deletions examples/hello_client.zig
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@ pub fn main() !void {
// Language servers can support multiple communication channels (e.g. stdio, pipes, sockets).
// See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#implementationConsiderations
//
// The `TransportOverStdio` implements the necessary logic to read and write messages over stdio.
var transport: lsp.TransportOverStdio = .init(child_process.stdout.?, child_process.stdin.?);
// The `lsp.Transport.Stdio` implements the necessary logic to read and write messages over stdio.
var read_buffer: [256]u8 = undefined;
var stdio_transport: lsp.Transport.Stdio = .init(&read_buffer, child_process.stdout.?, child_process.stdin.?);
const transport: *lsp.Transport = &stdio_transport.transport;

// The order of exchanged messages will look similar to this:
//
Expand All @@ -90,7 +92,7 @@ pub fn main() !void {
// 5. send `exit` notification

std.log.debug("sending 'initialize' request to server", .{});
try transport.any().writeRequest(
try transport.writeRequest(
gpa,
.{ .number = 0 },
"initialize", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize
Expand All @@ -102,7 +104,7 @@ pub fn main() !void {
// Wait for the response from the server
// 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.
// A more sophisticated client implementation will need to handle messages asynchronously.
const initialize_response = try readAndIgnoreUntilResponse(gpa, transport.any(), .{ .number = 0 }, "initialize");
const initialize_response = try readAndIgnoreUntilResponse(gpa, transport, .{ .number = 0 }, "initialize");
defer initialize_response.deinit();

const initialize_result: lsp.types.InitializeResult = initialize_response.value;
Expand All @@ -126,7 +128,7 @@ pub fn main() !void {
}

std.log.debug("sending 'initialized' notification to server", .{});
try transport.any().writeNotification(
try transport.writeNotification(
gpa,
"initialized", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialized
lsp.types.InitializedParams,
Expand All @@ -138,7 +140,7 @@ pub fn main() !void {

std.log.info("This document recently came in by the CLI.", .{});
std.log.debug("sending 'textDocument/didOpen' notification to server", .{});
try transport.any().writeNotification(
try transport.writeNotification(
gpa,
"textDocument/didOpen", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_didOpen
lsp.types.DidOpenTextDocumentParams,
Expand All @@ -155,7 +157,7 @@ pub fn main() !void {

std.log.info("Just to double check, could you verify that it is formatted correctly?", .{});
std.log.debug("sending 'textDocument/formatting' request to server", .{});
try transport.any().writeRequest(
try transport.writeRequest(
gpa,
.{ .number = 1 },
"textDocument/formatting", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
Expand All @@ -167,7 +169,7 @@ pub fn main() !void {
.{ .emit_null_optional_fields = false },
);

const formatting_response = try readAndIgnoreUntilResponse(gpa, transport.any(), .{ .number = 1 }, "textDocument/formatting");
const formatting_response = try readAndIgnoreUntilResponse(gpa, transport, .{ .number = 1 }, "textDocument/formatting");
defer formatting_response.deinit();

const text_edits = formatting_response.value orelse &.{};
Expand All @@ -183,7 +185,7 @@ pub fn main() !void {

// Even though this is a request, we do not wait for a response because we are going to close the server anyway.
std.log.debug("sending 'shutdown' request to server", .{});
try transport.any().writeRequest(
try transport.writeRequest(
gpa,
.{ .number = 2 },
"shutdown", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#shutdown
Expand All @@ -193,7 +195,7 @@ pub fn main() !void {
);

std.log.debug("sending 'exit' notification to server", .{});
try transport.any().writeNotification(
try transport.writeNotification(
gpa,
"exit", // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#exit
void,
Expand All @@ -206,7 +208,11 @@ pub fn main() !void {
}

fn fatalWithUsage(comptime format: []const u8, args: anytype) noreturn {
std.io.getStdErr().writeAll(usage) catch {};
{
const stderr = std.debug.lockStderrWriter(&.{});
defer std.debug.unlockStderrWriter();
stderr.writeAll(usage) catch {};
}
std.log.err(format, args);
std.process.exit(1);
}
Expand All @@ -219,7 +225,7 @@ fn fatal(comptime format: []const u8, args: anytype) noreturn {
/// Do not use such a function in an actual implementation.
fn readAndIgnoreUntilResponse(
allocator: std.mem.Allocator,
transport: lsp.AnyTransport,
transport: *lsp.Transport,
id: lsp.JsonRPCMessage.ID,
comptime method: []const u8,
) !std.json.Parsed(lsp.ResultType(method)) {
Expand Down
20 changes: 11 additions & 9 deletions examples/hello_server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ pub fn main() !void {
// Language servers can support multiple communication channels (e.g. stdio, pipes, sockets).
// See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#implementationConsiderations
//
// The `TransportOverStdio` implements the necessary logic to read and write messages over stdio.
var transport: lsp.TransportOverStdio = .init(std.io.getStdIn(), std.io.getStdOut());
// The `lsp.Transport.Stdio` implements the necessary logic to read and write messages over stdio.
var read_buffer: [256]u8 = undefined;
var stdio_transport: lsp.Transport.Stdio = .init(&read_buffer, .stdin(), .stdout());
const transport: *lsp.Transport = &stdio_transport.transport;

// keep track of opened documents
var documents: std.StringArrayHashMapUnmanaged([]const u8) = .empty;
Expand Down Expand Up @@ -95,7 +97,7 @@ pub fn main() !void {
.request => |request| switch (request.params) {
.initialize => |params| {
_ = params.capabilities; // the client capabilities tell the server what "features" the client supports
try transport.any().writeResponse(
try transport.writeResponse(
gpa,
request.id,
lsp.types.InitializeResult,
Expand All @@ -111,11 +113,11 @@ pub fn main() !void {
.{ .emit_null_optional_fields = false },
);
},
.shutdown => try transport.any().writeResponse(gpa, request.id, void, {}, .{}),
.shutdown => try transport.writeResponse(gpa, request.id, void, {}, .{}),
.@"textDocument/formatting" => |params| {
const source = documents.get(params.textDocument.uri) orelse {
// We should read the document from the file system
try transport.any().writeResponse(gpa, request.id, void, {}, .{});
try transport.writeResponse(gpa, request.id, void, {}, .{});
continue;
};
const source_z = try gpa.dupeZ(u8, source);
Expand All @@ -125,15 +127,15 @@ pub fn main() !void {
defer tree.deinit(gpa);

if (tree.errors.len != 0) {
try transport.any().writeResponse(gpa, request.id, void, {}, .{});
try transport.writeResponse(gpa, request.id, void, {}, .{});
continue;
}

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

if (std.mem.eql(u8, source, formatte_source)) {
try transport.any().writeResponse(gpa, request.id, void, {}, .{});
try transport.writeResponse(gpa, request.id, void, {}, .{});
continue;
}

Expand All @@ -145,9 +147,9 @@ pub fn main() !void {
.newText = formatte_source,
}};

try transport.any().writeResponse(gpa, request.id, []const lsp.types.TextEdit, result, .{});
try transport.writeResponse(gpa, request.id, []const lsp.types.TextEdit, result, .{});
},
.other => try transport.any().writeResponse(gpa, request.id, void, {}, .{}),
.other => try transport.writeResponse(gpa, request.id, void, {}, .{}),
},
.notification => |notification| switch (notification.params) {
.initialized => {},
Expand Down
6 changes: 4 additions & 2 deletions examples/my_first_server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ pub fn main() !void {
};

// language server typically communicate over stdio (stdin and stdout)
var transport: lsp.TransportOverStdio = .init(std.io.getStdIn(), std.io.getStdOut());
var read_buffer: [256]u8 = undefined;
var stdio_transport: lsp.Transport.Stdio = .init(&read_buffer, .stdin(), .stdout());
const transport: *lsp.Transport = &stdio_transport.transport;

var handler: Handler = .init(gpa);
defer handler.deinit();

try lsp.basic_server.run(
gpa,
transport.any(),
transport,
&handler,
std.log.err,
);
Expand Down
2 changes: 1 addition & 1 deletion src/basic_server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const types = lsp.types;

pub fn run(
allocator: std.mem.Allocator,
transport: lsp.AnyTransport,
transport: *lsp.Transport,
/// Must be a pointer to a container type (e.g. `struct`) that implements
/// the desired LSP methods.
///
Expand Down
Loading
Loading