ea05916e69
Before, the code lexed only a prefix of the line up to cursor position. Now, we lex the entire line, and do look at the token just after the cursor. This subtly changes sematncih of `getPostionContext`: now it is becomes oblivious of the _exact_ position of the cursor and returns the whole token at cursor's position. I believe this is semantically right approach -- _most_ of the callsite should not worry at all about such details. Something like completion _might_ want to know more, but it's better to make that call site's problem. It might be the case that some existing code relies on the past behavior. It's hard to tell though -- we don't have a lot of tests for _features_, and changes to unit-tests don't explain if the changes are meaningful for user-observable behavior or not. In general, for LSP-shaped thing, I feel that the bulk of testing should be focused on end-to-end behaviors....
72 lines
2.4 KiB
Zig
72 lines
2.4 KiB
Zig
const std = @import("std");
|
|
const mem = std.mem;
|
|
const zls = @import("zls");
|
|
const builtin = @import("builtin");
|
|
|
|
const helper = @import("../helper.zig");
|
|
const Context = @import("../context.zig").Context;
|
|
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;
|
|
|
|
test "definition - smoke" {
|
|
try testDefinition(
|
|
\\fn main() void { f<>oo(); }
|
|
\\fn <def>foo</def>() void {}
|
|
);
|
|
}
|
|
|
|
test "definition - cursor is at the end of an identifier" {
|
|
try testDefinition(
|
|
\\fn main() void { foo<>(); }
|
|
\\fn <def>foo</def>() void {}
|
|
);
|
|
}
|
|
|
|
test "definition - cursor is at the start of an identifier" {
|
|
try testDefinition(
|
|
\\fn main() void { <>foo(); }
|
|
\\fn <def>foo</def>() void {}
|
|
);
|
|
}
|
|
|
|
fn testDefinition(source: []const u8) !void {
|
|
var phr = try helper.collectClearPlaceholders(allocator, source);
|
|
defer phr.deinit(allocator);
|
|
|
|
var cursor: offsets.Loc = .{ .start = 0, .end = 0 };
|
|
var def_start: offsets.Loc = .{ .start = 0, .end = 0 };
|
|
var def_end: offsets.Loc = .{ .start = 0, .end = 0 };
|
|
for (phr.locations.items(.old)) |loc, i| {
|
|
if (mem.eql(u8, source[loc.start..loc.end], "<>")) cursor = phr.locations.items(.new)[i];
|
|
if (mem.eql(u8, source[loc.start..loc.end], "<def>")) def_start = phr.locations.items(.new)[i];
|
|
if (mem.eql(u8, source[loc.start..loc.end], "</def>")) def_end = phr.locations.items(.new)[i];
|
|
}
|
|
|
|
const cursor_lsp = offsets.locToRange(phr.new_source, cursor, .@"utf-16").start;
|
|
const def_range_lsp = offsets.locToRange(phr.new_source, .{ .start = def_start.end, .end = def_end.start }, .@"utf-16");
|
|
|
|
var ctx = try Context.init();
|
|
defer ctx.deinit();
|
|
|
|
const test_uri: []const u8 = switch (builtin.os.tag) {
|
|
.windows => "file:///C:\\test.zig",
|
|
else => "file:///test.zig",
|
|
};
|
|
|
|
try ctx.requestDidOpen(test_uri, phr.new_source);
|
|
|
|
const params = types.TextDocumentPositionParams{
|
|
.textDocument = .{ .uri = test_uri },
|
|
.position = cursor_lsp,
|
|
};
|
|
|
|
const response = try ctx.requestGetResponse(?types.Location, "textDocument/definition", params);
|
|
const result = response.result orelse return error.UnresolvedDefinition;
|
|
try std.testing.expectEqual(def_range_lsp, result.range);
|
|
}
|