diff --git a/src/Server.zig b/src/Server.zig index c64400406..6cd02b37c 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -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, @@ -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, + }); + }, } } diff --git a/src/analysis.zig b/src/analysis.zig index c7dca19fa..5b3ef867b 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -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,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); } } } diff --git a/src/references.zig b/src/references.zig index bbdd5e31e..e508ea94a 100644 --- a/src/references.zig +++ b/src/references.zig @@ -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; diff --git a/tests/lsp_features/completion.zig b/tests/lsp_features/completion.zig index 9f6ec86a6..968efdfff 100644 --- a/tests/lsp_features/completion.zig +++ b/tests/lsp_features/completion.zig @@ -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. - // , &.{ - // // TODO kind should be Enum - // .{ .label = "alpha", .kind = .Field }, - // .{ .label = "beta", .kind = .Field }, - // }); + try testCompletion( + \\const E = enum { + \\ alpha, + \\ beta, + \\}; + \\const foo = E. + , &.{ + .{ .label = "alpha", .kind = .Enum }, + .{ .label = "beta", .kind = .Enum }, + }); } test "completion - error union" { @@ -291,21 +288,20 @@ test "completion - error union" { \\}; \\const baz = error. , &.{ - .{ .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. - // , &.{ - // .{ .label = "foo", .kind = .Constant }, - // .{ .label = "bar", .kind = .Constant }, - // }); + try testCompletion( + \\const E = error { + \\ foo, + \\ bar, + \\}; + \\const baz = E. + , &.{ + .{ .label = "foo", .kind = .Constant, .detail = "error.foo" }, + .{ .label = "bar", .kind = .Constant, .detail = "error.bar" }, + }); try testCompletion( \\const S = struct { alpha: u32 };