Skip to content
Open
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
21 changes: 21 additions & 0 deletions src/jsonrpc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,24 @@ pub const JsonPreformatted = struct {
try jw.print("{s}", .{self.raw});
}
};

pub const Notification = struct {
jsonrpc: []const u8 = "2.0",
method: []const u8,
params: JsonPreformatted,

pub fn jsonStringify(self: @This(), jw: anytype) !void {
try jw.beginObject();

try jw.objectField("jsonrpc");
try jw.write(self.jsonrpc);

try jw.objectField("method");
try jw.write(self.method);

try jw.objectField("params");
try jw.write(self.params);

try jw.endObject();
}
};
41 changes: 41 additions & 0 deletions src/lsp.zig
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,44 @@ pub const Location = struct {
uri: []const u8,
range: Range,
};

pub const Diagnostic = struct {
range: Range,
severity: ?DiagnosticSeverity = null,
code: ?[]const u8 = null,
source: ?[]const u8 = "glsl_analyzer",
message: []const u8,
tags: ?[]DiagnosticTag = null,
relatedInformation: ?[]DiagnosticRelatedInformation = null,
};

pub const DiagnosticSeverity = enum(u8) {
@"error" = 1,
warning = 2,
information = 3,
hint = 4,

pub fn jsonStringify(self: @This(), jw: anytype) !void {
try jw.write(@intFromEnum(self));
}
};

pub const DiagnosticTag = enum(u8) {
unnecessary = 1,
deprecated = 2,

pub fn jsonStringify(self: @This(), jw: anytype) !void {
try jw.write(@intFromEnum(self));
}
};

pub const DiagnosticRelatedInformation = struct {
location: Location,
message: []const u8,
};

pub const PublishDiagnosticsParams = struct {
uri: []const u8,
version: ?i64 = null,
diagnostics: []const Diagnostic,
};
73 changes: 73 additions & 0 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const lsp = @import("lsp.zig");
const rpc = @import("jsonrpc.zig");
const Response = rpc.Response;
const Request = rpc.Request;
const Notification = rpc.Notification;

const Workspace = @import("Workspace.zig");
const cli = @import("cli.zig");
Expand Down Expand Up @@ -385,6 +386,64 @@ const State = struct {
try self.channel.flush();
}

pub fn sendNotification(self: *State, method: []const u8, params: anytype) !void {
const params_bytes = try std.json.stringifyAlloc(self.allocator, params, .{});
defer self.allocator.free(params_bytes);

const notification = Notification{
.method = method,
.params = .{ .raw = params_bytes },
};

const format_options = std.json.StringifyOptions{
.emit_null_optional_fields = false,
};

var counting = std.io.countingWriter(std.io.null_writer);
try std.json.stringify(notification, format_options, counting.writer());
const content_length = counting.bytes_written;

const writer = self.channel.writer();
try writer.print("Content-Length: {}\r\n\r\n", .{content_length});
try std.json.stringify(notification, format_options, writer);
try self.channel.flush();
}

pub fn publishDiagnostics(self: *State, document: *Workspace.Document) !void {
var parse_diagnostics = std.ArrayList(parse.Diagnostic).init(self.allocator);
defer parse_diagnostics.deinit();

const parsed: *const Workspace.Document.CompleteParseTree = try document.parseTree();
try parse_diagnostics.appendSlice(parsed.diagnostics);

// Convert parser diagnostics to LSP diagnostics
const lsp_diagnostics = try self.allocator.alloc(lsp.Diagnostic, parse_diagnostics.items.len);
defer self.allocator.free(lsp_diagnostics);

for (parse_diagnostics.items, lsp_diagnostics) |parse_diag, *lsp_diag| {
const start_pos = parse_diag.position(document.source());
const end_pos = util.positionFromUtf8(document.source(), parse_diag.span.end);

lsp_diag.* = .{
.range = .{
.start = start_pos,
.end = end_pos,
},
.severity = .@"error",
.source = "glsl_analyzer",
.message = parse_diag.message,
};
}

const params = lsp.PublishDiagnosticsParams{
.uri = document.uri,
.version = document.version,
.diagnostics = lsp_diagnostics,
};

try self.sendNotification("textDocument/publishDiagnostics", params);
}

pub fn fail(
self: *State,
id: Request.Id,
Expand Down Expand Up @@ -519,6 +578,10 @@ pub const Dispatch = struct {
const file = try state.workspace.getOrCreateDocument(document.versioned());
try file.replaceAll(document.text);

state.publishDiagnostics(file) catch |err| {
std.log.warn("failed to publish diagnostics: {}", .{err});
};

return;
}

Expand Down Expand Up @@ -547,6 +610,12 @@ pub const Dispatch = struct {
if (params.value.text) |text| text.len else null,
});

if (state.workspace.getDocument(params.value.textDocument) catch null) |file| {
state.publishDiagnostics(file) catch |err| {
std.log.warn("failed to publish diagnostics: {}", .{err});
};
}

return;
}

Expand All @@ -571,6 +640,10 @@ pub const Dispatch = struct {
}

file.version = params.value.textDocument.version;

state.publishDiagnostics(file) catch |err| {
std.log.warn("failed to publish diagnostics: {}", .{err});
};
}

pub const CompletionParams = struct {
Expand Down