Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve completion on error and enums #887

Merged
merged 1 commit into from
Jan 6, 2023
Merged
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
37 changes: 26 additions & 11 deletions src/Server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
.documentation = doc,
.detail = analysis.getContainerFieldSignature(handle.tree, field),
.insertText = tree.tokenSlice(field.ast.main_token),
.insertTextFormat = .PlainText,
});
}
try list.append(allocator, .{
.label = handle.tree.tokenSlice(field.ast.main_token),
.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,
Expand Down Expand Up @@ -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{};
Expand Down Expand Up @@ -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,
});
},
}
}

Expand Down
82 changes: 40 additions & 42 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
};
}

Expand Down Expand Up @@ -2052,6 +2058,7 @@ pub const DeclWithHandle = struct {
}
return null;
},
.error_token => return null,
};
}
};
Expand Down Expand Up @@ -2395,13 +2402,15 @@ 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),
}
}
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),
Expand Down Expand Up @@ -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),
Expand All @@ -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);
Expand All @@ -2528,31 +2534,23 @@ fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx:
// TODO Record a redefinition error.
}

if (!can_have_enum_completions)
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,
};
var buffer: [2]Ast.Node.Index = undefined;
const container_decl = ast.containerDecl(tree, node_idx, &buffer) orelse continue;

if (container_field) |_| {
if (!std.mem.eql(u8, name, "_")) {
const Documentation = @TypeOf(@as(types.CompletionItem, undefined).documentation);
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;
var gop_res = try context.enums.getOrPut(allocator, .{
.label = name,
.kind = .Constant,
.insertText = name,
.insertTextFormat = .PlainText,
.documentation = doc,
});
if (gop_res.found_existing) {
if (doc) |d| allocator.free(d.MarkupContent.value);
}
var doc: Documentation = if (try getDocComments(allocator, tree, decl, .markdown)) |docs| .{ .MarkupContent = types.MarkupContent{ .kind = .markdown, .value = docs } } else null;
var gop_res = try context.enums.getOrPut(allocator, .{
.label = name,
.kind = .Constant,
.insertText = name,
.insertTextFormat = .PlainText,
.documentation = doc,
});
if (gop_res.found_existing) {
if (doc) |d| allocator.free(d.MarkupContent.value);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/references.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
48 changes: 22 additions & 26 deletions tests/lsp_features/completion.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand All @@ -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 };
Expand Down