Use zig-lsp-codegen (#850)

* add lsp.zig

* change references from types.zig to lsp.zig

* remove types.zig and requests.zig

* add tres as a submodule

* transition codebase from types.zig to lsp.zig

* update lsp.zig

* completely overhaul message handler

* fix memory errors

* partially transition tests to lsp.zig

* update lsp.zig

* more test fixes

* disable failing tests

* fix message handling bugs

* fix remaining tests

* access correct union in diff.applyTextEdits

* more message handler fixes

* run zig fmt

* update tres submodule

* fix memory access to freed memory

* simplify initialize_msg for testing

* check if publishDiagnostics is supported
This commit is contained in:
Techatrix
2022-12-27 06:47:57 +00:00
committed by GitHub
parent 941882371c
commit 61c0981294
28 changed files with 9015 additions and 1881 deletions

View File

@@ -8,13 +8,12 @@ const ErrorBuilder = @import("../ErrorBuilder.zig");
const types = zls.types;
const offsets = zls.offsets;
const requests = zls.requests;
const allocator: std.mem.Allocator = std.testing.allocator;
const Completion = struct {
label: []const u8,
kind: types.CompletionItem.Kind,
kind: types.CompletionItemKind,
detail: ?[]const u8 = null,
};
@@ -412,16 +411,13 @@ fn testCompletion(source: []const u8, expected_completions: []const Completion)
try ctx.requestDidOpen(test_uri, text);
const request = requests.Completion{
.params = .{
.textDocument = .{ .uri = test_uri },
.position = offsets.indexToPosition(source, cursor_idx, ctx.server.offset_encoding),
},
const params = types.CompletionParams{
.textDocument = .{ .uri = test_uri },
.position = offsets.indexToPosition(source, cursor_idx, ctx.server.offset_encoding),
};
@setEvalBranchQuota(2000);
const response = try ctx.requestGetResponse(?types.CompletionList, "textDocument/completion", request);
defer response.deinit();
@setEvalBranchQuota(5000);
const response = try ctx.requestGetResponse(?types.CompletionList, "textDocument/completion", params);
const completion_list: types.CompletionList = response.result orelse {
std.debug.print("Server returned `null` as the result\n", .{});
@@ -462,11 +458,11 @@ fn testCompletion(source: []const u8, expected_completions: []const Completion)
unreachable;
};
if (expected_completion.kind != actual_completion.kind) {
try error_builder.msgAtIndex("label '{s}' should be of kind '{s}' but was '{s}'!", cursor_idx, .err, .{
if (actual_completion.kind == null or expected_completion.kind != actual_completion.kind.?) {
try error_builder.msgAtIndex("label '{s}' should be of kind '{s}' but was '{?s}'!", cursor_idx, .err, .{
label,
@tagName(expected_completion.kind),
@tagName(actual_completion.kind),
if (actual_completion.kind) |kind| @tagName(kind) else null,
});
return error.InvalidCompletionKind;
}
@@ -500,9 +496,15 @@ fn extractCompletionLabels(items: anytype) error{ DuplicateCompletionLabel, OutO
errdefer set.deinit(allocator);
try set.ensureTotalCapacity(allocator, items.len);
for (items) |item| {
switch (item.kind) {
.Keyword, .Snippet => continue,
else => {},
const maybe_kind = switch (@typeInfo(@TypeOf(item.kind))) {
.Optional => item.kind,
else => @as(?@TypeOf(item.kind), item.kind),
};
if (maybe_kind) |kind| {
switch (kind) {
.Keyword, .Snippet => continue,
else => {},
}
}
if (set.fetchPutAssumeCapacity(item.label, {}) != null) return error.DuplicateCompletionLabel;
}

View File

@@ -2,10 +2,11 @@ const std = @import("std");
const zls = @import("zls");
const builtin = @import("builtin");
const tres = @import("tres");
const Context = @import("../context.zig").Context;
const types = zls.types;
const requests = zls.requests;
const allocator: std.mem.Allocator = std.testing.allocator;
@@ -48,15 +49,16 @@ fn testFoldingRange(source: []const u8, expect: []const u8) !void {
try ctx.requestDidOpen(test_uri, source);
const request = requests.FoldingRange{ .params = .{ .textDocument = .{ .uri = test_uri } } };
const params = types.FoldingRangeParams{ .textDocument = .{ .uri = test_uri } };
const response = try ctx.requestGetResponse(?[]types.FoldingRange, "textDocument/foldingRange", request);
defer response.deinit();
const response = try ctx.requestGetResponse(?[]types.FoldingRange, "textDocument/foldingRange", params);
var actual = std.ArrayList(u8).init(allocator);
defer actual.deinit();
try std.json.stringify(response.result, .{}, actual.writer());
try tres.stringify(response.result, .{
.emit_null_optional_fields = false,
}, actual.writer());
try expectEqualJson(expect, actual.items);
}

View File

@@ -8,7 +8,6 @@ const ErrorBuilder = @import("../ErrorBuilder.zig");
const types = zls.types;
const offsets = zls.offsets;
const requests = zls.requests;
const allocator: std.mem.Allocator = std.testing.allocator;
@@ -83,7 +82,7 @@ fn testInlayHints(source: []const u8) !void {
const range = types.Range{
.start = types.Position{ .line = 0, .character = 0 },
.end = offsets.indexToPosition(phr.new_source, phr.new_source.len, .utf16),
.end = offsets.indexToPosition(phr.new_source, phr.new_source.len, .@"utf-16"),
};
const InlayHint = struct {
@@ -92,15 +91,12 @@ fn testInlayHints(source: []const u8) !void {
kind: types.InlayHintKind,
};
const request = requests.InlayHint{
.params = .{
.textDocument = .{ .uri = test_uri },
.range = range,
},
const params = types.InlayHintParams{
.textDocument = .{ .uri = test_uri },
.range = range,
};
const response = try ctx.requestGetResponse(?[]InlayHint, "textDocument/inlayHint", request);
defer response.deinit();
const response = try ctx.requestGetResponse(?[]InlayHint, "textDocument/inlayHint", params);
const hints: []InlayHint = response.result orelse {
std.debug.print("Server returned `null` as the result\n", .{});
@@ -124,7 +120,7 @@ fn testInlayHints(source: []const u8) !void {
for (hints) |hint| {
if (position.line != hint.position.line or position.character != hint.position.character) continue;
const actual_label = hint.label[0 .. hint.label.len - 1]; // exclude :
const actual_label = hint.label[0..hint.label.len];
if (!std.mem.eql(u8, expected_label, actual_label)) {
try error_builder.msgAtLoc("expected label `{s}` here but got `{s}`!", new_loc, .err, .{ expected_label, actual_label });

View File

@@ -7,7 +7,6 @@ const Context = @import("../context.zig").Context;
const ErrorBuilder = @import("../ErrorBuilder.zig");
const types = zls.types;
const requests = zls.requests;
const offsets = zls.offsets;
const allocator: std.mem.Allocator = std.testing.allocator;
@@ -113,16 +112,13 @@ fn testReferences(source: []const u8) !void {
const var_name = offsets.locToSlice(source, var_loc);
const var_loc_middle = var_loc.start + (var_loc.end - var_loc.start) / 2;
const request = requests.References{
.params = .{
.textDocument = .{ .uri = file_uri },
.position = offsets.indexToPosition(source, var_loc_middle, ctx.server.offset_encoding),
.context = .{ .includeDeclaration = true },
},
const params = types.ReferenceParams{
.textDocument = .{ .uri = file_uri },
.position = offsets.indexToPosition(source, var_loc_middle, ctx.server.offset_encoding),
.context = .{ .includeDeclaration = true },
};
const response = try ctx.requestGetResponse(?[]types.Location, "textDocument/references", request);
defer response.deinit();
const response = try ctx.requestGetResponse(?[]types.Location, "textDocument/references", params);
const locations: []types.Location = response.result orelse {
std.debug.print("Server returned `null` as the result\n", .{});

View File

@@ -38,20 +38,19 @@ fn testSelectionRange(source: []const u8, want: []const []const u8) !void {
try ctx.requestDidOpen(test_uri, phr.new_source);
const position = offsets.locToRange(phr.new_source, phr.locations.items(.new)[0], .utf16).start;
const position = offsets.locToRange(phr.new_source, phr.locations.items(.new)[0], .@"utf-16").start;
const SelectionRange = struct {
range: types.Range,
parent: ?*@This(),
parent: ?*@This() = null,
};
const request = requests.SelectionRange{ .params = .{
const params = types.SelectionRangeParams{
.textDocument = .{ .uri = test_uri },
.positions = &[_]types.Position{position},
} };
};
const response = try ctx.requestGetResponse(?[]SelectionRange, "textDocument/selectionRange", request);
defer response.deinit();
const response = try ctx.requestGetResponse(?[]SelectionRange, "textDocument/selectionRange", params);
const selectionRanges: []SelectionRange = response.result orelse {
std.debug.print("Server returned `null` as the result\n", .{});
@@ -63,7 +62,7 @@ fn testSelectionRange(source: []const u8, want: []const []const u8) !void {
var it: ?*SelectionRange = &selectionRanges[0];
while (it) |r| {
const slice = offsets.rangeToSlice(phr.new_source, r.range, .utf16);
const slice = offsets.rangeToSlice(phr.new_source, r.range, .@"utf-16");
(try got.addOne()).* = slice;
it = r.parent;
}

View File

@@ -4,7 +4,7 @@ const builtin = @import("builtin");
const Context = @import("../context.zig").Context;
const requests = zls.requests;
const types = zls.types;
const allocator: std.mem.Allocator = std.testing.allocator;
@@ -41,21 +41,7 @@ fn testSemanticTokens(source: []const u8, expected: []const u32) !void {
var ctx = try Context.init();
defer ctx.deinit();
const open_document = requests.OpenDocument{
.params = .{
.textDocument = .{
.uri = file_uri,
// .languageId = "zig",
// .version = 420,
.text = source,
},
},
};
const did_open_method = try std.json.stringifyAlloc(allocator, open_document.params, .{});
defer allocator.free(did_open_method);
try ctx.request("textDocument/didOpen", did_open_method, null);
try ctx.requestDidOpen(file_uri, source);
const Response = struct {
data: []const u32,