diff --git a/src/analysis.zig b/src/analysis.zig index 868e80e..fb49028 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -413,6 +413,15 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. else => decl, }; }, + .ErrorSetDecl => { + const set = node.cast(ast.Node.ErrorSetDecl).?; + var i: usize = 0; + while (set.iterate(i)) |decl| : (i += 1) { + // TODO handle errors better? + analysis_ctx.error_completions.add(analysis_ctx.tree(), decl) catch {}; + } + return node; + }, .SuffixOp => { const suffix_op = node.cast(ast.Node.SuffixOp).?; switch (suffix_op.op) { @@ -505,9 +514,23 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. }, .ContainerDecl => { analysis_ctx.onContainer(node.cast(ast.Node.ContainerDecl).?) catch return null; + + const container = node.cast(ast.Node.ContainerDecl).?; + const kind = analysis_ctx.tree().token_ids[container.kind_token]; + + if (kind == .Keyword_struct or (kind == .Keyword_union and container.init_arg_expr == .None)) { + return node; + } + + var i: usize = 0; + while (container.iterate(i)) |decl| : (i += 1) { + if (decl.id != .ContainerField) continue; + // TODO handle errors better? + analysis_ctx.enum_completions.add(analysis_ctx.tree(), decl) catch {}; + } return node; }, - .MultilineStringLiteral, .StringLiteral, .ErrorSetDecl, .FnProto => return node, + .MultilineStringLiteral, .StringLiteral, .FnProto => return node, else => std.debug.warn("Type resolution case not implemented; {}\n", .{node.id}), } return null; @@ -924,6 +947,7 @@ pub const PositionContext = union(enum) { string_literal: SourceRange, field_access: SourceRange, var_access: SourceRange, + global_error_set, enum_literal, other, empty, @@ -938,6 +962,7 @@ pub const PositionContext = union(enum) { .enum_literal => null, .other => null, .empty => null, + .global_error_set => null, }; } }; @@ -1010,6 +1035,7 @@ pub fn documentPositionContext(allocator: *std.mem.Allocator, document: types.Te .enum_literal => curr_ctx.ctx = .empty, .field_access => {}, .other => {}, + .global_error_set => {}, else => curr_ctx.ctx = .{ .field_access = tokenRangeAppend(curr_ctx.ctx.range().?, tok), }, @@ -1032,6 +1058,7 @@ pub fn documentPositionContext(allocator: *std.mem.Allocator, document: types.Te (try peek(&stack)).ctx = .empty; } }, + .Keyword_error => curr_ctx.ctx = .global_error_set, else => curr_ctx.ctx = .empty, } @@ -1065,7 +1092,7 @@ fn addOutlineNodes(allocator: *std.mem.Allocator, children: *std.ArrayList(types try addOutlineNodes(allocator, children, tree, cchild); return; }, - else => {} + else => {}, } std.debug.warn("{}\n", .{child.id}); _ = try children.append(try getDocumentSymbolsInternal(allocator, tree, child)); @@ -1073,7 +1100,6 @@ fn addOutlineNodes(allocator: *std.mem.Allocator, children: *std.ArrayList(types fn getDocumentSymbolsInternal(allocator: *std.mem.Allocator, tree: *ast.Tree, node: *ast.Node) anyerror!types.DocumentSymbol { // const symbols = std.ArrayList(types.DocumentSymbol).init(allocator); - const start_loc = tree.tokenLocation(0, node.firstToken()); const end_loc = tree.tokenLocation(0, node.lastToken()); const range = types.Range{ @@ -1084,14 +1110,14 @@ fn getDocumentSymbolsInternal(allocator: *std.mem.Allocator, tree: *ast.Tree, no .end = .{ .line = @intCast(i64, end_loc.line), .character = @intCast(i64, end_loc.column), - } + }, }; if (getDeclName(tree, node) == null) { std.debug.warn("NULL NAME: {}\n", .{node.id}); } - // TODO: Get my lazy bum to fix detail newlines + // TODO: Get my lazy bum to fix detail newlines return types.DocumentSymbol{ .name = getDeclName(tree, node) orelse "no_name", // .detail = (try getDocComments(allocator, tree, node)) orelse "", @@ -1100,7 +1126,7 @@ fn getDocumentSymbolsInternal(allocator: *std.mem.Allocator, tree: *ast.Tree, no .FnProto => .Function, .VarDecl => .Variable, .ContainerField => .Field, - else => .Variable + else => .Variable, }, .range = range, .selectionRange = range, diff --git a/src/document_store.zig b/src/document_store.zig index ed74dee..e9f867e 100644 --- a/src/document_store.zig +++ b/src/document_store.zig @@ -30,12 +30,57 @@ pub const Handle = struct { } }; +pub const TagStore = struct { + values: std.StringHashMap(void), + completions: std.ArrayListUnmanaged(types.CompletionItem), + + pub fn init(allocator: *std.mem.Allocator) TagStore { + return .{ + .values = std.StringHashMap(void).init(allocator), + .completions = .{}, + }; + } + + pub fn deinit(self: *TagStore) void { + const alloc = self.values.allocator; + for (self.completions.items) |item| { + alloc.free(item.label); + if (item.documentation) |some| alloc.free(some.value); + } + self.values.deinit(); + self.completions.deinit(self.values.allocator); + } + + pub fn add(self: *TagStore, tree: *std.zig.ast.Tree, tag: *std.zig.ast.Node) !void { + const name = analysis.nodeToString(tree, tag).?; + if (self.values.contains(name)) return; + const alloc = self.values.allocator; + const item = types.CompletionItem{ + .label = try std.mem.dupe(alloc, u8, name), + .kind = .Constant, + .documentation = if (try analysis.getDocComments(alloc, tree, tag)) |docs| + .{ + .kind = .Markdown, + .value = docs, + } + else + null, + }; + + try self.values.putNoClobber(item.label, {}); + try self.completions.append(self.values.allocator, item); + } +}; + allocator: *std.mem.Allocator, handles: std.StringHashMap(*Handle), has_zig: bool, build_files: std.ArrayListUnmanaged(*BuildFile), build_runner_path: []const u8, +error_completions: TagStore, +enum_completions: TagStore, + pub fn init( self: *DocumentStore, allocator: *std.mem.Allocator, @@ -47,6 +92,8 @@ pub fn init( self.has_zig = has_zig; self.build_files = .{}; self.build_runner_path = build_runner_path; + self.error_completions = TagStore.init(allocator); + self.enum_completions = TagStore.init(allocator); } const LoadPackagesContext = struct { @@ -70,7 +117,7 @@ fn loadPackages(context: LoadPackagesContext) !void { // open it and handle the error for file not found. var file_exists = true; check_file_exists: { - var fhandle = std.fs.cwd().openFile(target_path, .{.read = true, .write = false }) catch |err| switch (err) { + var fhandle = std.fs.cwd().openFile(target_path, .{ .read = true, .write = false }) catch |err| switch (err) { error.FileNotFound => { file_exists = false; break :check_file_exists; @@ -179,7 +226,7 @@ fn newDocument(self: *DocumentStore, uri: []const u8, text: []u8) anyerror!*Hand .allocator = self.allocator, .build_runner_path = self.build_runner_path, }) catch |err| { - std.debug.warn("Failed to load packages of build file {} (error: {})\n", .{build_file.uri, err}); + std.debug.warn("Failed to load packages of build file {} (error: {})\n", .{ build_file.uri, err }); }; } else if (self.has_zig and !in_std) associate_build_file: { // Look into build files to see if we already have one that fits @@ -425,7 +472,7 @@ pub fn applyChanges( .allocator = self.allocator, .build_runner_path = self.build_runner_path, }) catch |err| { - std.debug.warn("Failed to load packages of build file {} (error: {})\n", .{build_file.uri, err}); + std.debug.warn("Failed to load packages of build file {} (error: {})\n", .{ build_file.uri, err }); }; } } @@ -478,6 +525,8 @@ pub const AnalysisContext = struct { scope_nodes: []*std.zig.ast.Node, in_container: *std.zig.ast.Node, std_uri: ?[]const u8, + error_completions: *TagStore, + enum_completions: *TagStore, pub fn tree(self: AnalysisContext) *std.zig.ast.Tree { return self.handle.tree; @@ -584,6 +633,8 @@ pub const AnalysisContext = struct { .scope_nodes = try std.mem.dupe(&self.arena.allocator, *std.zig.ast.Node, self.scope_nodes), .in_container = self.in_container, .std_uri = self.std_uri, + .error_completions = self.error_completions, + .enum_completions = self.enum_completions, }; } }; @@ -623,6 +674,8 @@ pub fn analysisContext( .scope_nodes = scope_nodes.items, .in_container = in_container, .std_uri = std_uri, + .error_completions = &self.error_completions, + .enum_completions = &self.enum_completions, }; } @@ -652,4 +705,6 @@ pub fn deinit(self: *DocumentStore) void { } self.build_files.deinit(self.allocator); + self.error_completions.deinit(); + self.enum_completions.deinit(); } diff --git a/src/main.zig b/src/main.zig index 1d4d45e..6502f3c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -760,6 +760,24 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v }), .var_access, .empty => try completeGlobal(id, pos_index, handle, this_config), .field_access => |range| try completeFieldAccess(id, handle, pos, range, this_config), + .global_error_set => try send(types.Response{ + .id = .{ .Integer = id }, + .result = .{ + .CompletionList = .{ + .isIncomplete = false, + .items = document_store.error_completions.completions.items, + }, + }, + }), + .enum_literal => try send(types.Response{ + .id = .{ .Integer = id }, + .result = .{ + .CompletionList = .{ + .isIncomplete = false, + .items = document_store.enum_completions.completions.items, + }, + }, + }), else => try respondGeneric(id, no_completions_response), } } else {