diff --git a/src/Server.zig b/src/Server.zig index b8d125203..840b9fc68 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -308,6 +308,7 @@ pub fn getSymbolGlobal( return try server.analyser.lookupSymbolGlobalAdvanced(handle, name, pos_index, .{ .skip_container_fields = false, + .skip_labels = false, }); } @@ -1079,7 +1080,7 @@ pub fn generalReferencesHandler(server: *Server, request: GeneralReferencesReque else => true, }; - const locations = if (pos_context == .label) + const locations = if (decl.decl.* == .label_decl) try references.labelReferences(allocator, decl, server.offset_encoding, include_decl) else try references.symbolReferences( diff --git a/src/analysis.zig b/src/analysis.zig index 24829bd2e..1a2ec637a 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -258,10 +258,45 @@ pub fn hasSelfParam(analyser: *Analyser, handle: *const DocumentStore.Handle, fu return false; } -pub fn getVariableSignature(tree: Ast, var_decl: Ast.full.VarDecl) []const u8 { - const start = offsets.tokenToIndex(tree, var_decl.ast.mut_token); - const end = offsets.tokenToLoc(tree, ast.lastToken(tree, var_decl.ast.init_node)).end; - return tree.source[start..end]; +pub fn getVariableSignature(allocator: std.mem.Allocator, tree: Ast, var_decl: Ast.full.VarDecl) error{OutOfMemory}![]const u8 { + const node_tags = tree.nodes.items(.tag); + + const start_token = var_decl.ast.mut_token; + const end_token = blk: { + const init_node = var_decl.ast.init_node; + if (init_node == 0) + break :blk start_token + 1; + + if (node_tags[init_node] == .merge_error_sets) + return try std.fmt.allocPrint(allocator, "{s} error", .{ + offsets.tokensToSlice(tree, start_token, tree.firstToken(init_node) - 1), + }); + + if (node_tags[init_node] == .error_set_decl) + break :blk tree.firstToken(init_node); + + var buf: [2]Ast.Node.Index = undefined; + const container_decl = tree.fullContainerDecl(&buf, init_node) orelse + break :blk ast.lastToken(tree, init_node); + + var token = container_decl.ast.main_token; + var offset: Ast.TokenIndex = 0; + + // Tagged union: union(enum) + if (container_decl.ast.enum_token) |enum_token| { + token = enum_token; + offset += 1; + } + + // Backing integer: struct(u32), union(enum(u32)) + if (container_decl.ast.arg != 0) { + token = ast.lastToken(tree, container_decl.ast.arg); + offset += 1; + } + + break :blk token + offset; + }; + return offsets.tokensToSlice(tree, start_token, end_token); } pub fn getContainerFieldSignature(tree: Ast, field: Ast.full.ContainerField) []const u8 { @@ -650,14 +685,15 @@ fn allDigits(str: []const u8) bool { return true; } +const primitive_types = std.ComptimeStringMap([]const u8, .{ + .{ "true", "bool" }, + .{ "false", "bool" }, + .{ "null", "@TypeOf(null)" }, + .{ "undefined", "@TypeOf(undefined)" }, +}); + pub fn isValueIdent(text: []const u8) bool { - const PrimitiveTypes = std.ComptimeStringMap(void, .{ - .{"true"}, - .{"false"}, - .{"null"}, - .{"undefined"}, - }); - return PrimitiveTypes.has(text); + return primitive_types.has(text); } pub fn isTypeIdent(text: []const u8) bool { @@ -755,6 +791,13 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e }; } + if (isValueIdent(name)) { + return TypeWithHandle{ + .type = .{ .data = .{ .other = node }, .is_type_val = false }, + .handle = handle, + }; + } + if (try analyser.lookupSymbolGlobal( handle, name, @@ -1101,6 +1144,8 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e .string_literal, .char_literal, .number_literal, + .enum_literal, + .error_value, => return TypeWithHandle{ .type = .{ .data = .{ .other = node }, .is_type_val = false }, .handle = handle, @@ -2543,12 +2588,17 @@ pub fn lookupSymbolGlobal(analyser: *Analyser, handle: *const DocumentStore.Hand return analyser.lookupSymbolGlobalAdvanced(handle, symbol, source_index, .{}); } +pub const LookupSymbolGlobalOptions = struct { + skip_container_fields: bool = true, + skip_labels: bool = true, +}; + pub fn lookupSymbolGlobalAdvanced( analyser: *Analyser, handle: *const DocumentStore.Handle, symbol: []const u8, source_index: usize, - comptime options: struct { skip_container_fields: bool = true }, + comptime options: LookupSymbolGlobalOptions, ) error{OutOfMemory}!?DeclWithHandle { const scope_parents = handle.document_scope.scopes.items(.parent); const scope_decls = handle.document_scope.scopes.items(.decls); @@ -2566,7 +2616,7 @@ pub fn lookupSymbolGlobalAdvanced( .ast_node => |node| { if (options.skip_container_fields and node_tags[node].isContainerField()) continue; }, - .label_decl => continue, + .label_decl => if (options.skip_labels) continue, else => {}, } return DeclWithHandle{ .decl = candidate, .handle = handle }; @@ -2979,6 +3029,20 @@ const ScopeContext = struct { try context.doc_scope.scopes.items(.decls)[@intFromEnum(scope)].put(context.allocator, name, decl_index); } + + fn putDeclLoopLabel( + context: ScopeContext, + tree: Ast, + label: Ast.TokenIndex, + node_idx: Ast.Node.Index, + ) error{OutOfMemory}![]const u8 { + const label_scope = try context.pushScope(offsets.tokenToLoc(tree, label), .other); + context.popScope(); + + const name = tree.tokenSlice(label); + try context.putDecl(label_scope, name, .{ .label_decl = .{ .label = label, .block = node_idx } }); + return name; + } }; fn makeInnerScope( @@ -3320,7 +3384,7 @@ fn makeScopeAt( if (while_node.label_token) |label| { std.debug.assert(token_tags[label] == .identifier); - const name = tree.tokenSlice(label); + const name = try context.putDeclLoopLabel(tree, label, node_idx); try context.putDecl(then_scope, name, .{ .label_decl = .{ .label = label, .block = while_node.ast.then_expr } }); if (else_scope) |index| { try context.putDecl(index, name, .{ .label_decl = .{ .label = label, .block = while_node.ast.else_expr } }); @@ -3380,7 +3444,7 @@ fn makeScopeAt( if (for_node.label_token) |label| { std.debug.assert(token_tags[label] == .identifier); - const name = tree.tokenSlice(label); + const name = try context.putDeclLoopLabel(tree, label, node_idx); try context.putDecl( then_scope, name, @@ -3601,6 +3665,7 @@ fn addReferencedTypes( .container_decl_two, .container_decl_two_trailing, .error_set_decl, + .merge_error_sets, .tagged_union, .tagged_union_trailing, .tagged_union_two, @@ -3768,6 +3833,18 @@ fn addReferencedTypes( .number_literal, .char_literal => return "comptime_int", + .enum_literal => return "@TypeOf(.enum_literal)", + + .error_value => { + const identifier = tree.tokenSlice(datas[p].rhs); + return try std.fmt.allocPrint(allocator, "error{{{s}}}", .{identifier}); + }, + + .identifier => { + const name = offsets.nodeToSlice(tree, p); + return primitive_types.get(name); + }, + else => {}, // TODO: Implement more "other" type expressions; better safe than sorry }, diff --git a/src/features/completions.zig b/src/features/completions.zig index f1f46b07b..ee8abd571 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -221,7 +221,7 @@ fn nodeToCompletion( .label = handle.tree.tokenSlice(var_decl.ast.mut_token + 1), .kind = if (is_const) .Constant else .Variable, .documentation = doc, - .detail = Analyser.getVariableSignature(tree, var_decl), + .detail = try Analyser.getVariableSignature(allocator, tree, var_decl), .insertText = tree.tokenSlice(var_decl.ast.mut_token + 1), .insertTextFormat = .PlainText, }); diff --git a/src/features/hover.zig b/src/features/hover.zig index 1faf1724f..601961c1c 100644 --- a/src/features/hover.zig +++ b/src/features/hover.zig @@ -51,7 +51,7 @@ pub fn hoverSymbol(server: *Server, decl_handle: Analyser.DeclWithHandle, markup &reference_collector, ); - break :def Analyser.getVariableSignature(tree, var_decl); + break :def try Analyser.getVariableSignature(server.arena.allocator(), tree, var_decl); } else if (tree.fullFnProto(&buf, node)) |fn_proto| { break :def Analyser.getFunctionSignature(tree, fn_proto); } else if (tree.fullContainerField(node)) |field| { @@ -111,12 +111,12 @@ pub fn hoverSymbol(server: *Server, decl_handle: Analyser.DeclWithHandle, markup if (doc_str) |doc| try writer.print("\n{s}", .{doc}); if (referenced_types.len > 0) - try writer.print("\n\n", .{}); + try writer.print("\n\n" ++ "Go to ", .{}); for (referenced_types, 0..) |ref, index| { if (index > 0) try writer.print(" | ", .{}); const loc = offsets.tokenToPosition(ref.handle.tree, ref.token, server.offset_encoding); - try writer.print("Go to [{s}]({s}#L{d})", .{ ref.str, ref.handle.uri, loc.line + 1 }); + try writer.print("[{s}]({s}#L{d})", .{ ref.str, ref.handle.uri, loc.line + 1 }); } } else { try writer.print("{s} ({s})", .{ def_str, resolved_type_str }); diff --git a/src/offsets.zig b/src/offsets.zig index 161759d89..981577d61 100644 --- a/src/offsets.zig +++ b/src/offsets.zig @@ -152,6 +152,10 @@ pub fn tokenToSlice(tree: Ast, token_index: Ast.TokenIndex) []const u8 { return locToSlice(tree.source, tokenToLoc(tree, token_index)); } +pub fn tokensToSlice(tree: Ast, first_token: Ast.TokenIndex, last_token: Ast.TokenIndex) []const u8 { + return locToSlice(tree.source, tokensToLoc(tree, first_token, last_token)); +} + pub fn tokenToPosition(tree: Ast, token_index: Ast.TokenIndex, encoding: Encoding) types.Position { const start = tokenToIndex(tree, token_index); return indexToPosition(tree.source, start, encoding);