improve completion on error and enums (#887)

This commit is contained in:
Techatrix 2023-01-06 18:59:20 +00:00 committed by GitHub
parent b163be51d3
commit 3f2700eaa5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 79 deletions

View File

@ -731,16 +731,14 @@ fn nodeToCompletion(
.container_field_init,
=> {
const field = ast.containerField(tree, node).?;
if (!field.ast.tuple_like) {
try list.append(allocator, .{
.label = handle.tree.tokenSlice(field.ast.main_token),
.kind = .Field,
.kind = if (field.ast.tuple_like) .Enum else .Field,
.documentation = doc,
.detail = analysis.getContainerFieldSignature(handle.tree, field),
.insertText = tree.tokenSlice(field.ast.main_token),
.insertTextFormat = .PlainText,
});
}
},
.array_type,
.array_type_sentinel,
@ -919,7 +917,13 @@ fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{OutO
const end = offsets.tokenToLoc(tree, last_token).end;
break :def tree.source[start..end];
},
.pointer_payload, .array_payload, .array_index, .switch_payload, .label_decl => tree.tokenSlice(decl_handle.nameToken()),
.pointer_payload,
.array_payload,
.array_index,
.switch_payload,
.label_decl,
.error_token,
=> tree.tokenSlice(decl_handle.nameToken()),
};
var bound_type_params = analysis.BoundTypeParams{};
@ -1223,6 +1227,17 @@ fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.Decl
.insertTextFormat = .PlainText,
});
},
.error_token => {
const name = tree.tokenSlice(decl_handle.decl.error_token);
try context.completions.append(allocator, .{
.label = name,
.kind = .Constant,
.detail = try std.fmt.allocPrint(allocator, "error.{s}", .{name}),
.insertText = name,
.insertTextFormat = .PlainText,
});
},
}
}

View File

@ -292,9 +292,12 @@ pub fn getDeclNameToken(tree: Ast, node: Ast.Node.Index) ?Ast.TokenIndex {
},
// containers
.container_field, .container_field_init, .container_field_align => {
.container_field,
.container_field_init,
.container_field_align,
=> {
const field = ast.containerField(tree, node).?.ast;
return if (field.tuple_like) null else field.main_token;
return field.main_token;
},
.identifier => main_token,
@ -1937,6 +1940,8 @@ pub const Declaration = union(enum) {
label: Ast.TokenIndex,
block: Ast.Node.Index,
},
/// always an identifier
error_token: Ast.Node.Index,
};
pub const DeclWithHandle = struct {
@ -1953,6 +1958,7 @@ pub const DeclWithHandle = struct {
.array_index => |ai| ai,
.switch_payload => |sp| sp.node,
.label_decl => |ld| ld.label,
.error_token => |et| et,
};
}
@ -2052,6 +2058,7 @@ pub const DeclWithHandle = struct {
}
return null;
},
.error_token => return null,
};
}
};
@ -2395,6 +2402,7 @@ pub const DocumentScope = struct {
}
self.scopes.deinit(allocator);
for (self.error_completions.entries.items(.key)) |item| {
if (item.detail) |detail| allocator.free(detail);
switch (item.documentation orelse continue) {
.string => |str| allocator.free(str),
.MarkupContent => |content| allocator.free(content.value),
@ -2402,6 +2410,7 @@ pub const DocumentScope = struct {
}
self.error_completions.deinit(allocator);
for (self.enum_completions.entries.items(.key)) |item| {
if (item.detail) |detail| allocator.free(detail);
switch (item.documentation orelse continue) {
.string => |str| allocator.free(str),
.MarkupContent => |content| allocator.free(content.value),
@ -2475,9 +2484,6 @@ fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx:
const main_tokens = tree.nodes.items(.main_token);
const node_tag = tags[node_idx];
var buf: [2]Ast.Node.Index = undefined;
const ast_decls = ast.declMembers(tree, node_idx, &buf);
var scope = try scopes.addOne(allocator);
scope.* = .{
.loc = offsets.nodeToLoc(tree, node_idx),
@ -2490,26 +2496,26 @@ fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx:
var i = main_tokens[node_idx];
while (i < data[node_idx].rhs) : (i += 1) {
if (token_tags[i] == .identifier) {
try context.errors.put(allocator, .{
.label = tree.tokenSlice(i),
const name = offsets.tokenToSlice(tree, i);
if (try scopes.items[scope_idx].decls.fetchPut(allocator, name, .{ .error_token = i })) |_| {
// TODO Record a redefinition error.
}
const gop = try context.errors.getOrPut(allocator, .{
.label = name,
.kind = .Constant,
.insertText = tree.tokenSlice(i),
//.detail =
.insertText = name,
.insertTextFormat = .PlainText,
}, {});
});
if (!gop.found_existing) {
gop.key_ptr.detail = try std.fmt.allocPrint(allocator, "error.{s}", .{name});
}
}
}
}
var buffer: [2]Ast.Node.Index = undefined;
const container_decl = ast.containerDecl(tree, node_idx, &buffer);
// Only tagged unions and enums should pass this
const can_have_enum_completions = if (container_decl) |container| blk: {
const kind = token_tags[container.ast.main_token];
break :blk kind != .keyword_struct and
(kind != .keyword_union or container.ast.enum_token != null or container.ast.arg != 0);
} else false;
var buf: [2]Ast.Node.Index = undefined;
const ast_decls = ast.declMembers(tree, node_idx, &buf);
for (ast_decls) |decl| {
if (tags[decl] == .@"usingnamespace") {
try scopes.items[scope_idx].uses.append(allocator, decl);
@ -2528,18 +2534,11 @@ fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx:
// TODO Record a redefinition error.
}
if (!can_have_enum_completions)
continue;
var buffer: [2]Ast.Node.Index = undefined;
const container_decl = ast.containerDecl(tree, node_idx, &buffer) orelse continue;
const container_field = switch (tags[decl]) {
.container_field => tree.containerField(decl),
.container_field_align => tree.containerFieldAlign(decl),
.container_field_init => tree.containerFieldInit(decl),
else => null,
};
if (container_field) |_| {
if (!std.mem.eql(u8, name, "_")) {
if (container_decl.ast.enum_token != null) {
if (std.mem.eql(u8, name, "_")) return;
const Documentation = @TypeOf(@as(types.CompletionItem, undefined).documentation);
var doc: Documentation = if (try getDocComments(allocator, tree, decl, .markdown)) |docs| .{ .MarkupContent = types.MarkupContent{ .kind = .markdown, .value = docs } } else null;
@ -2556,7 +2555,6 @@ fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx:
}
}
}
}
// Whether we have already visited the root node.
var had_root = true;

View File

@ -534,6 +534,7 @@ pub fn symbolReferences(
log.warn("Could not find param decl's function", .{});
},
.label_decl => unreachable, // handled separately by labelReferences
.error_token => {},
}
return builder.locations;

View File

@ -268,19 +268,16 @@ test "completion - union" {
}
test "completion - enum" {
// TODO: Fix
return error.SkipZigTest;
// try testCompletion(
// \\const E = enum {
// \\ alpha,
// \\ beta,
// \\};
// \\const foo = E.<cursor>
// , &.{
// // TODO kind should be Enum
// .{ .label = "alpha", .kind = .Field },
// .{ .label = "beta", .kind = .Field },
// });
try testCompletion(
\\const E = enum {
\\ alpha,
\\ beta,
\\};
\\const foo = E.<cursor>
, &.{
.{ .label = "alpha", .kind = .Enum },
.{ .label = "beta", .kind = .Enum },
});
}
test "completion - error union" {
@ -291,21 +288,20 @@ test "completion - error union" {
\\};
\\const baz = error.<cursor>
, &.{
.{ .label = "Foo", .kind = .Constant },
.{ .label = "Bar", .kind = .Constant },
.{ .label = "Foo", .kind = .Constant, .detail = "error.Foo" },
.{ .label = "Bar", .kind = .Constant, .detail = "error.Bar" },
});
// TODO implement completion for error unions
// try testCompletion(
// \\const E = error {
// \\ foo,
// \\ bar,
// \\};
// \\const baz = E.<cursor>
// , &.{
// .{ .label = "foo", .kind = .Constant },
// .{ .label = "bar", .kind = .Constant },
// });
try testCompletion(
\\const E = error {
\\ foo,
\\ bar,
\\};
\\const baz = E.<cursor>
, &.{
.{ .label = "foo", .kind = .Constant, .detail = "error.foo" },
.{ .label = "bar", .kind = .Constant, .detail = "error.bar" },
});
try testCompletion(
\\const S = struct { alpha: u32 };