From 22a863134b5c8a4115088ea20afbaf274292535b Mon Sep 17 00:00:00 2001 From: Vexu Date: Sat, 16 May 2020 19:04:07 +0300 Subject: [PATCH 1/2] implement global error set completion --- src/analysis.zig | 16 +++++++++++- src/document_store.zig | 55 +++++++++++++++++++++++++++++++++++++++--- src/main.zig | 9 +++++++ 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index d14dc0d..4eefae9 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -405,6 +405,16 @@ 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) { + const tag = decl.cast(ast.Node.ErrorTag).?; + // TODO handle errors better? + analysis_ctx.error_completions.add(analysis_ctx.tree(), tag) catch {}; + } + return node; + }, .SuffixOp => { const suffix_op = node.cast(ast.Node.SuffixOp).?; switch (suffix_op.op) { @@ -499,7 +509,7 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. analysis_ctx.onContainer(node.cast(ast.Node.ContainerDecl).?) catch return null; 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; @@ -916,6 +926,7 @@ pub const PositionContext = union(enum) { string_literal: SourceRange, field_access: SourceRange, var_access: SourceRange, + global_error_set, enum_literal, other, empty, @@ -930,6 +941,7 @@ pub const PositionContext = union(enum) { .enum_literal => null, .other => null, .empty => null, + .global_error_set => null, }; } }; @@ -1002,6 +1014,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), }, @@ -1024,6 +1037,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, } diff --git a/src/document_store.zig b/src/document_store.zig index ed74dee..54f3183 100644 --- a/src/document_store.zig +++ b/src/document_store.zig @@ -30,12 +30,56 @@ pub const Handle = struct { } }; +pub const ErrorCompletion = struct { + values: std.StringHashMap(void), + completions: std.ArrayList(types.CompletionItem), + + pub fn init(allocator: *std.mem.Allocator) ErrorCompletion { + return .{ + .values = std.StringHashMap(void).init(allocator), + .completions = std.ArrayList(types.CompletionItem).init(allocator), + }; + } + + pub fn deinit(self: *ErrorCompletion) 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(); + } + + pub fn add(self: *ErrorCompletion, tree: *std.zig.ast.Tree, tag: *std.zig.ast.Node.ErrorTag) !void { + const name = tree.tokenSlice(tag.name_token); + 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.base)) |docs| + .{ + .kind = .Markdown, + .value = docs, + } + else + null, + }; + + try self.values.putNoClobber(item.label, {}); + try self.completions.append(item); + } +}; + allocator: *std.mem.Allocator, handles: std.StringHashMap(*Handle), has_zig: bool, build_files: std.ArrayListUnmanaged(*BuildFile), build_runner_path: []const u8, +error_completions: ErrorCompletion, + pub fn init( self: *DocumentStore, allocator: *std.mem.Allocator, @@ -47,6 +91,7 @@ pub fn init( self.has_zig = has_zig; self.build_files = .{}; self.build_runner_path = build_runner_path; + self.error_completions = ErrorCompletion.init(allocator); } const LoadPackagesContext = struct { @@ -70,7 +115,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 +224,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 +470,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 +523,7 @@ pub const AnalysisContext = struct { scope_nodes: []*std.zig.ast.Node, in_container: *std.zig.ast.Node, std_uri: ?[]const u8, + error_completions: *ErrorCompletion, pub fn tree(self: AnalysisContext) *std.zig.ast.Tree { return self.handle.tree; @@ -584,6 +630,7 @@ 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, }; } }; @@ -623,6 +670,7 @@ pub fn analysisContext( .scope_nodes = scope_nodes.items, .in_container = in_container, .std_uri = std_uri, + .error_completions = &self.error_completions, }; } @@ -652,4 +700,5 @@ pub fn deinit(self: *DocumentStore) void { } self.build_files.deinit(self.allocator); + self.error_completions.deinit(); } diff --git a/src/main.zig b/src/main.zig index 1d4d45e..edfbf4c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -760,6 +760,15 @@ 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, + }, + }, + }), else => try respondGeneric(id, no_completions_response), } } else { From c1ba26e0a206d9685020355c87700acaf1965a31 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 28 May 2020 18:18:48 +0300 Subject: [PATCH 2/2] implement completion for enum literals --- src/analysis.zig | 28 ++++++++++++++++++++-------- src/document_store.zig | 32 +++++++++++++++++++------------- src/main.zig | 9 +++++++++ 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 4eefae9..c282b6c 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -408,10 +408,9 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. .ErrorSetDecl => { const set = node.cast(ast.Node.ErrorSetDecl).?; var i: usize = 0; - while (set.iterate(i)) |decl| : (i+=1) { - const tag = decl.cast(ast.Node.ErrorTag).?; + while (set.iterate(i)) |decl| : (i += 1) { // TODO handle errors better? - analysis_ctx.error_completions.add(analysis_ctx.tree(), tag) catch {}; + analysis_ctx.error_completions.add(analysis_ctx.tree(), decl) catch {}; } return node; }, @@ -507,6 +506,20 @@ 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, .FnProto => return node, @@ -1080,7 +1093,7 @@ fn addOutlineNodes(allocator: *std.mem.Allocator, children: *std.ArrayList(types // _ = try children.append(try getDocumentSymbolsInternal(allocator, tree, cchild)); return; }, - else => {} + else => {}, } std.debug.warn("{}\n", .{child.id}); _ = try children.append(try getDocumentSymbolsInternal(allocator, tree, child)); @@ -1088,7 +1101,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{ @@ -1099,14 +1111,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 "", @@ -1115,7 +1127,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 54f3183..e9f867e 100644 --- a/src/document_store.zig +++ b/src/document_store.zig @@ -30,35 +30,35 @@ pub const Handle = struct { } }; -pub const ErrorCompletion = struct { +pub const TagStore = struct { values: std.StringHashMap(void), - completions: std.ArrayList(types.CompletionItem), + completions: std.ArrayListUnmanaged(types.CompletionItem), - pub fn init(allocator: *std.mem.Allocator) ErrorCompletion { + pub fn init(allocator: *std.mem.Allocator) TagStore { return .{ .values = std.StringHashMap(void).init(allocator), - .completions = std.ArrayList(types.CompletionItem).init(allocator), + .completions = .{}, }; } - pub fn deinit(self: *ErrorCompletion) void { + 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.completions.deinit(self.values.allocator); } - pub fn add(self: *ErrorCompletion, tree: *std.zig.ast.Tree, tag: *std.zig.ast.Node.ErrorTag) !void { - const name = tree.tokenSlice(tag.name_token); + 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.base)) |docs| + .documentation = if (try analysis.getDocComments(alloc, tree, tag)) |docs| .{ .kind = .Markdown, .value = docs, @@ -68,7 +68,7 @@ pub const ErrorCompletion = struct { }; try self.values.putNoClobber(item.label, {}); - try self.completions.append(item); + try self.completions.append(self.values.allocator, item); } }; @@ -78,7 +78,8 @@ has_zig: bool, build_files: std.ArrayListUnmanaged(*BuildFile), build_runner_path: []const u8, -error_completions: ErrorCompletion, +error_completions: TagStore, +enum_completions: TagStore, pub fn init( self: *DocumentStore, @@ -91,7 +92,8 @@ pub fn init( self.has_zig = has_zig; self.build_files = .{}; self.build_runner_path = build_runner_path; - self.error_completions = ErrorCompletion.init(allocator); + self.error_completions = TagStore.init(allocator); + self.enum_completions = TagStore.init(allocator); } const LoadPackagesContext = struct { @@ -523,7 +525,8 @@ pub const AnalysisContext = struct { scope_nodes: []*std.zig.ast.Node, in_container: *std.zig.ast.Node, std_uri: ?[]const u8, - error_completions: *ErrorCompletion, + error_completions: *TagStore, + enum_completions: *TagStore, pub fn tree(self: AnalysisContext) *std.zig.ast.Tree { return self.handle.tree; @@ -631,6 +634,7 @@ pub const AnalysisContext = struct { .in_container = self.in_container, .std_uri = self.std_uri, .error_completions = self.error_completions, + .enum_completions = self.enum_completions, }; } }; @@ -671,6 +675,7 @@ pub fn analysisContext( .in_container = in_container, .std_uri = std_uri, .error_completions = &self.error_completions, + .enum_completions = &self.enum_completions, }; } @@ -701,4 +706,5 @@ 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 edfbf4c..6502f3c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -769,6 +769,15 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v }, }, }), + .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 {