* Fix lsp weird behaviour on block cursors (#891) Adds lookahead option to getPositionContext.
This commit is contained in:
parent
8e98bd439b
commit
903f85ab94
@ -837,12 +837,12 @@ pub fn identifierFromPosition(pos_index: usize, handle: DocumentStore.Handle) []
|
|||||||
if (pos_index + 1 >= handle.text.len) return "";
|
if (pos_index + 1 >= handle.text.len) return "";
|
||||||
var start_idx = pos_index;
|
var start_idx = pos_index;
|
||||||
|
|
||||||
while (start_idx > 0 and isSymbolChar(handle.text[start_idx - 1])) {
|
while (start_idx > 0 and analysis.isSymbolChar(handle.text[start_idx - 1])) {
|
||||||
start_idx -= 1;
|
start_idx -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var end_idx = pos_index;
|
var end_idx = pos_index;
|
||||||
while (end_idx < handle.text.len and isSymbolChar(handle.text[end_idx])) {
|
while (end_idx < handle.text.len and analysis.isSymbolChar(handle.text[end_idx])) {
|
||||||
end_idx += 1;
|
end_idx += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -850,10 +850,6 @@ pub fn identifierFromPosition(pos_index: usize, handle: DocumentStore.Handle) []
|
|||||||
return handle.text[start_idx..end_idx];
|
return handle.text[start_idx..end_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isSymbolChar(char: u8) bool {
|
|
||||||
return std.ascii.isAlphanumeric(char) or char == '_';
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gotoDefinitionSymbol(
|
fn gotoDefinitionSymbol(
|
||||||
server: *Server,
|
server: *Server,
|
||||||
decl_handle: analysis.DeclWithHandle,
|
decl_handle: analysis.DeclWithHandle,
|
||||||
@ -2124,7 +2120,7 @@ fn completionHandler(server: *Server, request: types.CompletionParams) Error!?ty
|
|||||||
}
|
}
|
||||||
|
|
||||||
const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding);
|
const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding);
|
||||||
const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index);
|
const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index, false);
|
||||||
|
|
||||||
const maybe_completions = switch (pos_context) {
|
const maybe_completions = switch (pos_context) {
|
||||||
.builtin => try server.completeBuiltin(),
|
.builtin => try server.completeBuiltin(),
|
||||||
@ -2205,7 +2201,7 @@ fn gotoHandler(server: *Server, request: types.TextDocumentPositionParams, resol
|
|||||||
if (request.position.character == 0) return null;
|
if (request.position.character == 0) return null;
|
||||||
|
|
||||||
const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding);
|
const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding);
|
||||||
const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index);
|
const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index, true);
|
||||||
|
|
||||||
return switch (pos_context) {
|
return switch (pos_context) {
|
||||||
.var_access => try server.gotoDefinitionGlobal(source_index, handle, resolve_alias),
|
.var_access => try server.gotoDefinitionGlobal(source_index, handle, resolve_alias),
|
||||||
@ -2245,7 +2241,7 @@ fn hoverHandler(server: *Server, request: types.HoverParams) Error!?types.Hover
|
|||||||
if (request.position.character == 0) return null;
|
if (request.position.character == 0) return null;
|
||||||
|
|
||||||
const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding);
|
const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding);
|
||||||
const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index);
|
const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index, true);
|
||||||
|
|
||||||
const response = switch (pos_context) {
|
const response = switch (pos_context) {
|
||||||
.builtin => try server.hoverDefinitionBuiltin(source_index, handle),
|
.builtin => try server.hoverDefinitionBuiltin(source_index, handle),
|
||||||
@ -2391,7 +2387,7 @@ fn generalReferencesHandler(server: *Server, request: GeneralReferencesRequest)
|
|||||||
if (request.position().character <= 0) return null;
|
if (request.position().character <= 0) return null;
|
||||||
|
|
||||||
const source_index = offsets.positionToIndex(handle.text, request.position(), server.offset_encoding);
|
const source_index = offsets.positionToIndex(handle.text, request.position(), server.offset_encoding);
|
||||||
const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index);
|
const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index, true);
|
||||||
|
|
||||||
const decl = switch (pos_context) {
|
const decl = switch (pos_context) {
|
||||||
.var_access => try server.getSymbolGlobal(source_index, handle),
|
.var_access => try server.getSymbolGlobal(source_index, handle),
|
||||||
|
@ -1553,15 +1553,32 @@ fn tokenLocAppend(prev: offsets.Loc, token: std.zig.Token) offsets.Loc {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn isSymbolChar(char: u8) bool {
|
||||||
|
return std.ascii.isAlphanumeric(char) or char == '_';
|
||||||
|
}
|
||||||
|
|
||||||
/// Given a byte index in a document (typically cursor offset), classify what kind of entity is at that index.
|
/// Given a byte index in a document (typically cursor offset), classify what kind of entity is at that index.
|
||||||
///
|
///
|
||||||
/// Classification is based on the lexical structure -- we fetch the line containin index, tokenize it,
|
/// Classification is based on the lexical structure -- we fetch the line containin index, tokenize it,
|
||||||
/// and look at the sequence of tokens just before the cursor. Due to the nice way zig is designed (only line
|
/// and look at the sequence of tokens just before the cursor. Due to the nice way zig is designed (only line
|
||||||
/// comments, etc) lexing just a single line is always correct.
|
/// comments, etc) lexing just a single line is always correct.
|
||||||
pub fn getPositionContext(allocator: std.mem.Allocator, text: []const u8, doc_index: usize) !PositionContext {
|
pub fn getPositionContext(
|
||||||
const line_loc = offsets.lineLocAtIndex(text, doc_index);
|
allocator: std.mem.Allocator,
|
||||||
|
text: []const u8,
|
||||||
|
doc_index: usize,
|
||||||
|
/// Should we look to the end of the current context? Yes for goto def, no for completions
|
||||||
|
lookahead: bool,
|
||||||
|
) !PositionContext {
|
||||||
|
var new_index = doc_index;
|
||||||
|
if (lookahead and new_index < text.len and isSymbolChar(text[new_index])) {
|
||||||
|
new_index += 1;
|
||||||
|
} else if (lookahead and new_index + 1 < text.len and text[new_index] == '@') {
|
||||||
|
new_index += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const line_loc = if (!lookahead) offsets.lineLocAtIndex(text, new_index) else offsets.lineLocUntilIndex(text, new_index);
|
||||||
const line = offsets.locToSlice(text, line_loc);
|
const line = offsets.locToSlice(text, line_loc);
|
||||||
const prev_char = if (doc_index > 0) text[doc_index - 1] else 0;
|
const prev_char = if (new_index > 0) text[new_index - 1] else 0;
|
||||||
|
|
||||||
const is_comment = std.mem.startsWith(u8, std.mem.trimLeft(u8, line, " \t"), "//");
|
const is_comment = std.mem.startsWith(u8, std.mem.trimLeft(u8, line, " \t"), "//");
|
||||||
if (is_comment) return .comment;
|
if (is_comment) return .comment;
|
||||||
@ -1582,8 +1599,8 @@ pub fn getPositionContext(allocator: std.mem.Allocator, text: []const u8, doc_in
|
|||||||
while (true) {
|
while (true) {
|
||||||
const tok = tokenizer.next();
|
const tok = tokenizer.next();
|
||||||
// Early exits.
|
// Early exits.
|
||||||
if (tok.loc.start > doc_index) break;
|
if (tok.loc.start > new_index) break;
|
||||||
if (tok.loc.start == doc_index) {
|
if (tok.loc.start == new_index) {
|
||||||
// Tie-breaking, the curosr is exactly between two tokens, and
|
// Tie-breaking, the curosr is exactly between two tokens, and
|
||||||
// `tok` is the latter of the two.
|
// `tok` is the latter of the two.
|
||||||
if (tok.tag != .identifier) break;
|
if (tok.tag != .identifier) break;
|
||||||
|
@ -9,16 +9,47 @@ const allocator = std.testing.allocator;
|
|||||||
|
|
||||||
test "position context - var access" {
|
test "position context - var access" {
|
||||||
try testContext(
|
try testContext(
|
||||||
\\const this_var = id<cursor>entifier;
|
\\const a_var =<cursor> identifier;
|
||||||
|
,
|
||||||
|
.empty,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\const a_var = <cursor>identifier;
|
||||||
|
,
|
||||||
|
.var_access,
|
||||||
|
"i",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\const a_var = iden<cursor>tifier;
|
||||||
|
,
|
||||||
|
.var_access,
|
||||||
|
"ident",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\const a_var = identifier<cursor>;
|
||||||
,
|
,
|
||||||
.var_access,
|
.var_access,
|
||||||
"identifier",
|
"identifier",
|
||||||
);
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\const this_var = identifier<cursor>;
|
\\const a_var = identifier;<cursor>
|
||||||
|
,
|
||||||
|
.empty,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
try testContext(
|
||||||
|
\\ fn foo() !<cursor>Str {
|
||||||
,
|
,
|
||||||
.var_access,
|
.var_access,
|
||||||
"identifier",
|
"S",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\ fn foo() !St<cursor>r {
|
||||||
|
,
|
||||||
|
.var_access,
|
||||||
|
"Str",
|
||||||
);
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\ fn foo() !Str<cursor> {
|
\\ fn foo() !Str<cursor> {
|
||||||
@ -26,74 +57,259 @@ test "position context - var access" {
|
|||||||
.var_access,
|
.var_access,
|
||||||
"Str",
|
"Str",
|
||||||
);
|
);
|
||||||
// TODO fix failing test!
|
try testContext(
|
||||||
|
\\ fn foo() !Str <cursor>{
|
||||||
|
,
|
||||||
|
.var_access,
|
||||||
|
"Str",
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO fix failing tests
|
||||||
|
// try testContext(
|
||||||
|
// \\ fn foo() <cursor>Err!void {
|
||||||
|
// ,
|
||||||
|
// .var_access,
|
||||||
|
// "E",
|
||||||
|
// );
|
||||||
|
// try testContext(
|
||||||
|
// \\ fn foo() Er<cursor>r!void {
|
||||||
|
// ,
|
||||||
|
// .var_access,
|
||||||
|
// "Err",
|
||||||
|
// );
|
||||||
// try testContext(
|
// try testContext(
|
||||||
// \\ fn foo() Err<cursor>!void {
|
// \\ fn foo() Err<cursor>!void {
|
||||||
// ,
|
// ,
|
||||||
// .var_access,
|
// .var_access,
|
||||||
// "Err",
|
// "Err",
|
||||||
// );
|
// );
|
||||||
|
// try testContext(
|
||||||
|
// \\ fn foo() Err!<cursor>void {
|
||||||
|
// ,
|
||||||
|
// .var_access,
|
||||||
|
// "v",
|
||||||
|
// );
|
||||||
|
|
||||||
|
try testContext(
|
||||||
|
\\if (<cursor>bar.field == foo) {
|
||||||
|
,
|
||||||
|
.var_access,
|
||||||
|
"b",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\if (ba<cursor>r.field == foo) {
|
||||||
|
,
|
||||||
|
.var_access,
|
||||||
|
"bar",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\if (bar<cursor>.field == foo) {
|
||||||
|
,
|
||||||
|
.var_access,
|
||||||
|
"bar",
|
||||||
|
);
|
||||||
|
|
||||||
|
try testContext(
|
||||||
|
\\if (bar[0]<cursor>.field == foo) {
|
||||||
|
,
|
||||||
|
.var_access,
|
||||||
|
"bar",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "position context - field access" {
|
test "position context - field access" {
|
||||||
try testContext(
|
try testContext(
|
||||||
\\if (foo.<cursor>field == foo) {
|
\\if (bar.<cursor>field == foo) {
|
||||||
,
|
,
|
||||||
.field_access,
|
.field_access,
|
||||||
"foo.field",
|
"bar.f",
|
||||||
);
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\if (foo.member.<cursor>field == foo) {
|
\\if (bar.fie<cursor>ld == foo) {
|
||||||
,
|
,
|
||||||
.field_access,
|
.field_access,
|
||||||
"foo.member.field",
|
"bar.fiel",
|
||||||
);
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\if (foo.*.?.<cursor>field == foo) {
|
\\if (bar.field<cursor> == foo) {
|
||||||
,
|
,
|
||||||
.field_access,
|
.field_access,
|
||||||
"foo.*.?.field",
|
"bar.field",
|
||||||
|
);
|
||||||
|
|
||||||
|
try testContext(
|
||||||
|
\\if (bar.member<cursor>.field == foo) {
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"bar.member",
|
||||||
);
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\if (foo[0].<cursor>field == foo) {
|
\\if (bar.member.<cursor>field == foo) {
|
||||||
,
|
,
|
||||||
.field_access,
|
.field_access,
|
||||||
"foo[0].field",
|
"bar.member.f",
|
||||||
);
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\if (foo.<cursor>@"field" == foo) {
|
\\if (bar.member.fie<cursor>ld == foo) {
|
||||||
,
|
,
|
||||||
.field_access,
|
.field_access,
|
||||||
"foo.@\"field\"",
|
"bar.member.fiel",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\if (bar.member.field<cursor> == foo) {
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"bar.member.field",
|
||||||
|
);
|
||||||
|
|
||||||
|
try testContext(
|
||||||
|
\\if (bar.*.?<cursor>.field == foo) {
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"bar.*.?",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\if (bar.*.?.<cursor>field == foo) {
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"bar.*.?.f",
|
||||||
|
);
|
||||||
|
|
||||||
|
try testContext(
|
||||||
|
\\if (bar[0].<cursor>field == foo) {
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"bar[0].f",
|
||||||
|
);
|
||||||
|
|
||||||
|
try testContext(
|
||||||
|
\\if (bar.<cursor>@"field" == foo) {
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"bar.@\"",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\if (bar.@"fie<cursor>ld" == foo) {
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"bar.@\"fiel",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\if (bar.@"field"<cursor> == foo) {
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"bar.@\"field\"",
|
||||||
|
);
|
||||||
|
|
||||||
|
try testContext(
|
||||||
|
\\const arr = std.ArrayList(SomeStruct(a, b, c, d)).<cursor>init(allocator);
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"std.ArrayList(SomeStruct(a, b, c, d)).i",
|
||||||
);
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\const arr = std.ArrayList(SomeStruct(a, b, c, d)).in<cursor>it(allocator);
|
\\const arr = std.ArrayList(SomeStruct(a, b, c, d)).in<cursor>it(allocator);
|
||||||
,
|
,
|
||||||
.field_access,
|
.field_access,
|
||||||
"std.ArrayList(SomeStruct(a, b, c, d)).init",
|
"std.ArrayList(SomeStruct(a, b, c, d)).ini",
|
||||||
);
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\fn foo() !Foo.b<cursor> {
|
\\const arr = std.ArrayList(SomeStruct(a, b, c, d)).init<cursor>(allocator);
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"std.ArrayList(SomeStruct(a, b, c, d)).init",
|
||||||
|
);
|
||||||
|
|
||||||
|
try testContext(
|
||||||
|
\\fn foo() !Foo.<cursor>bar {
|
||||||
,
|
,
|
||||||
.field_access,
|
.field_access,
|
||||||
"Foo.b",
|
"Foo.b",
|
||||||
);
|
);
|
||||||
// TODO fix failing test!
|
try testContext(
|
||||||
|
\\fn foo() !Foo.ba<cursor>r {
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"Foo.bar",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\fn foo() !Foo.bar<cursor> {
|
||||||
|
,
|
||||||
|
.field_access,
|
||||||
|
"Foo.bar",
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO fix failing tests
|
||||||
// try testContext(
|
// try testContext(
|
||||||
// \\fn foo() Foo.b<cursor>!void {
|
// \\fn foo() Foo.<cursor>bar!void {
|
||||||
// ,
|
// ,
|
||||||
// .field_access,
|
// .field_access,
|
||||||
// "Foo.b",
|
// "Foo.b",
|
||||||
// );
|
// );
|
||||||
|
// try testContext(
|
||||||
|
// \\fn foo() Foo.ba<cursor>r!void {
|
||||||
|
// ,
|
||||||
|
// .field_access,
|
||||||
|
// "Foo.bar",
|
||||||
|
// );
|
||||||
|
// try testContext(
|
||||||
|
// \\fn foo() Foo.bar<cursor>!void {
|
||||||
|
// ,
|
||||||
|
// .field_access,
|
||||||
|
// "Foo.bar",
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
test "position context - builtin" {
|
test "position context - builtin" {
|
||||||
|
try testContext(
|
||||||
|
\\var foo = <cursor>@
|
||||||
|
,
|
||||||
|
.empty,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\var foo = <cursor>@intC(u32, 5);
|
||||||
|
,
|
||||||
|
.builtin,
|
||||||
|
"@i",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\var foo = @<cursor>intC(u32, 5);
|
||||||
|
,
|
||||||
|
.builtin,
|
||||||
|
"@i",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\var foo = @int<cursor>C(u32, 5);
|
||||||
|
,
|
||||||
|
.builtin,
|
||||||
|
"@intC",
|
||||||
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\var foo = @intC<cursor>(u32, 5);
|
\\var foo = @intC<cursor>(u32, 5);
|
||||||
,
|
,
|
||||||
.builtin,
|
.builtin,
|
||||||
"@intC",
|
"@intC",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try testContext(
|
||||||
|
\\fn foo() void { <cursor>@setRuntime(false); };
|
||||||
|
,
|
||||||
|
.builtin,
|
||||||
|
"@s",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\fn foo() void { @<cursor>setRuntime(false); };
|
||||||
|
,
|
||||||
|
.builtin,
|
||||||
|
"@s",
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\fn foo() void { @set<cursor>Runtime(false); };
|
||||||
|
,
|
||||||
|
.builtin,
|
||||||
|
"@setR",
|
||||||
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\fn foo() void { @setRuntime<cursor>(false); };
|
\\fn foo() void { @setRuntime<cursor>(false); };
|
||||||
,
|
,
|
||||||
@ -118,17 +334,29 @@ test "position context - comment" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "position context - import/embedfile string literal" {
|
test "position context - import/embedfile string literal" {
|
||||||
|
try testContext(
|
||||||
|
\\const std = @import("s<cursor>t");
|
||||||
|
,
|
||||||
|
.import_string_literal,
|
||||||
|
"\"st", // maybe report just "st"
|
||||||
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\const std = @import("st<cursor>");
|
\\const std = @import("st<cursor>");
|
||||||
,
|
,
|
||||||
.import_string_literal,
|
.import_string_literal,
|
||||||
"\"st\"", // maybe report just "st"
|
"\"st", // maybe report just "st"
|
||||||
);
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\const std = @embedFile("file.<cursor>");
|
\\const std = @embedFile("file.<cursor>");
|
||||||
,
|
,
|
||||||
.embedfile_string_literal,
|
.embedfile_string_literal,
|
||||||
"\"file.\"", // maybe report just "file."
|
"\"file.", // maybe report just "file."
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\const std = @embedFile("file<cursor>.");
|
||||||
|
,
|
||||||
|
.embedfile_string_literal,
|
||||||
|
"\"file", // maybe report just "file."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,29 +365,49 @@ test "position context - string literal" {
|
|||||||
\\var foo = "he<cursor>llo world!";
|
\\var foo = "he<cursor>llo world!";
|
||||||
,
|
,
|
||||||
.string_literal,
|
.string_literal,
|
||||||
"\"hello world!\"", // maybe report just "hello world!"
|
"\"hel", // maybe report just "he"
|
||||||
);
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\var foo = \\hello<cursor>;
|
\\var foo = \\hell<cursor>o;
|
||||||
,
|
,
|
||||||
.string_literal,
|
.string_literal,
|
||||||
"\\\\hello;", // maybe report just "hello;"
|
"\\\\hello", // maybe report just "hello;"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "position context - global error set" {
|
test "position context - global error set" {
|
||||||
|
// TODO why is this a .var_access instead of a .global_error_set?
|
||||||
|
// try testContext(
|
||||||
|
// \\fn foo() <cursor>error!void {
|
||||||
|
// ,
|
||||||
|
// .global_error_set,
|
||||||
|
// null,
|
||||||
|
// );
|
||||||
|
try testContext(
|
||||||
|
\\fn foo() erro<cursor>r!void {
|
||||||
|
,
|
||||||
|
.global_error_set,
|
||||||
|
null,
|
||||||
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\fn foo() error<cursor>!void {
|
\\fn foo() error<cursor>!void {
|
||||||
,
|
,
|
||||||
.global_error_set,
|
.global_error_set,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
try testContext(
|
||||||
|
\\fn foo() error<cursor>.!void {
|
||||||
|
,
|
||||||
|
.global_error_set,
|
||||||
|
null,
|
||||||
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\fn foo() error.<cursor>!void {
|
\\fn foo() error.<cursor>!void {
|
||||||
,
|
,
|
||||||
.global_error_set,
|
.global_error_set,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO this should probably also be .global_error_set
|
// TODO this should probably also be .global_error_set
|
||||||
// try testContext(
|
// try testContext(
|
||||||
// \\fn foo() error{<cursor>}!void {
|
// \\fn foo() error{<cursor>}!void {
|
||||||
@ -176,12 +424,30 @@ test "position context - global error set" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "position context - enum literal" {
|
test "position context - enum literal" {
|
||||||
|
try testContext(
|
||||||
|
\\var foo = .<cursor>tag;
|
||||||
|
,
|
||||||
|
.enum_literal,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\var foo = .ta<cursor>g;
|
||||||
|
,
|
||||||
|
.enum_literal,
|
||||||
|
null,
|
||||||
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\var foo = .tag<cursor>;
|
\\var foo = .tag<cursor>;
|
||||||
,
|
,
|
||||||
.enum_literal,
|
.enum_literal,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
try testContext(
|
||||||
|
\\var foo = <cursor>.;
|
||||||
|
,
|
||||||
|
.empty,
|
||||||
|
null,
|
||||||
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\var foo = .<cursor>;
|
\\var foo = .<cursor>;
|
||||||
,
|
,
|
||||||
@ -191,6 +457,24 @@ test "position context - enum literal" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "position context - label" {
|
test "position context - label" {
|
||||||
|
try testContext(
|
||||||
|
\\var foo = blk: { break <cursor>:blk null };
|
||||||
|
,
|
||||||
|
.pre_label,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\var foo = blk: { break :<cursor>blk null };
|
||||||
|
,
|
||||||
|
.label,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
try testContext(
|
||||||
|
\\var foo = blk: { break :bl<cursor>k null };
|
||||||
|
,
|
||||||
|
.label,
|
||||||
|
null,
|
||||||
|
);
|
||||||
try testContext(
|
try testContext(
|
||||||
\\var foo = blk: { break :blk<cursor> null };
|
\\var foo = blk: { break :blk<cursor> null };
|
||||||
,
|
,
|
||||||
@ -206,12 +490,6 @@ test "position context - empty" {
|
|||||||
.empty,
|
.empty,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
try testContext(
|
|
||||||
\\<cursor>const foo = struct {};
|
|
||||||
,
|
|
||||||
.empty,
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
try testContext(
|
try testContext(
|
||||||
\\try foo(arg, slice[<cursor>]);
|
\\try foo(arg, slice[<cursor>]);
|
||||||
,
|
,
|
||||||
@ -237,7 +515,7 @@ fn testContext(line: []const u8, tag: std.meta.Tag(analysis.PositionContext), ma
|
|||||||
const final_line = try std.mem.concat(allocator, u8, &.{ line[0..cursor_idx], line[cursor_idx + "<cursor>".len ..] });
|
const final_line = try std.mem.concat(allocator, u8, &.{ line[0..cursor_idx], line[cursor_idx + "<cursor>".len ..] });
|
||||||
defer allocator.free(final_line);
|
defer allocator.free(final_line);
|
||||||
|
|
||||||
const ctx = try analysis.getPositionContext(allocator, final_line, cursor_idx);
|
const ctx = try analysis.getPositionContext(allocator, final_line, cursor_idx, true);
|
||||||
|
|
||||||
if (std.meta.activeTag(ctx) != tag) {
|
if (std.meta.activeTag(ctx) != tag) {
|
||||||
std.debug.print("Expected tag `{s}`, got `{s}`\n", .{ @tagName(tag), @tagName(std.meta.activeTag(ctx)) });
|
std.debug.print("Expected tag `{s}`, got `{s}`\n", .{ @tagName(tag), @tagName(std.meta.activeTag(ctx)) });
|
||||||
|
Loading…
Reference in New Issue
Block a user