From 4e753338afd35f55b773af24c264164dabc18c9e Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Mon, 29 Mar 2021 14:02:58 +0300 Subject: [PATCH] Added error set and enum completion deduplication --- src/analysis.zig | 65 ++++++++++++++++++++++++++++++------------ src/document_store.zig | 50 ++++++++++++++++++++------------ 2 files changed, 78 insertions(+), 37 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index cbdf1e3..6b0f853 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -2443,10 +2443,29 @@ pub fn lookupSymbolContainer( return try lookupSymbolContainerInternal(store, arena, container_handle, symbol, instance_access, &use_trail); } +fn eqlCompletionItem(a: types.CompletionItem, b: types.CompletionItem) bool { + return std.mem.eql(u8, a.label, b.label); +} + +fn hashCompletionItem(completion_item: types.CompletionItem) u32 { + return @truncate(u32, std.hash.Wyhash.hash(0, completion_item.label)); +} + +pub const CompletionSet = std.ArrayHashMapUnmanaged( + types.CompletionItem, + void, + hashCompletionItem, + eqlCompletionItem, + false, +); +comptime { + std.debug.assert(@sizeOf(types.CompletionItem) == @sizeOf(CompletionSet.Entry)); +} + pub const DocumentScope = struct { scopes: []Scope, - error_completions: []types.CompletionItem, - enum_completions: []types.CompletionItem, + error_completions: CompletionSet, + enum_completions: CompletionSet, pub fn debugPrint(self: DocumentScope) void { for (self.scopes) |scope| { @@ -2472,17 +2491,21 @@ pub const DocumentScope = struct { } } - pub fn deinit(self: DocumentScope, allocator: *std.mem.Allocator) void { + pub fn deinit(self: *DocumentScope, allocator: *std.mem.Allocator) void { for (self.scopes) |*scope| { scope.decls.deinit(); allocator.free(scope.uses); allocator.free(scope.tests); } allocator.free(self.scopes); - for (self.error_completions) |item| if (item.documentation) |doc| allocator.free(doc.value); - allocator.free(self.error_completions); - for (self.enum_completions) |item| if (item.documentation) |doc| allocator.free(doc.value); - allocator.free(self.enum_completions); + for (self.error_completions.entries.items) |entry| { + if (entry.key.documentation) |doc| allocator.free(doc.value); + } + self.error_completions.deinit(allocator); + for (self.enum_completions.entries.items) |entry| { + if (entry.key.documentation) |doc| allocator.free(doc.value); + } + self.enum_completions.deinit(allocator); } }; @@ -2504,22 +2527,26 @@ pub const Scope = struct { pub fn makeDocumentScope(allocator: *std.mem.Allocator, tree: ast.Tree) !DocumentScope { var scopes = std.ArrayListUnmanaged(Scope){}; - var error_completions = std.ArrayListUnmanaged(types.CompletionItem){}; - var enum_completions = std.ArrayListUnmanaged(types.CompletionItem){}; + var error_completions = CompletionSet{}; + var enum_completions = CompletionSet{}; errdefer { scopes.deinit(allocator); - for (error_completions.items) |item| if (item.documentation) |doc| allocator.free(doc.value); + for (error_completions.entries.items) |entry| { + if (entry.key.documentation) |doc| allocator.free(doc.value); + } error_completions.deinit(allocator); - for (enum_completions.items) |item| if (item.documentation) |doc| allocator.free(doc.value); + for (enum_completions.entries.items) |entry| { + if (entry.key.documentation) |doc| allocator.free(doc.value); + } enum_completions.deinit(allocator); } // pass root node index ('0') try makeScopeInternal(allocator, &scopes, &error_completions, &enum_completions, tree, 0); return DocumentScope{ .scopes = scopes.toOwnedSlice(allocator), - .error_completions = error_completions.toOwnedSlice(allocator), - .enum_completions = enum_completions.toOwnedSlice(allocator), + .error_completions = error_completions, + .enum_completions = enum_completions, }; } @@ -2588,8 +2615,8 @@ pub fn varDecl(tree: ast.Tree, node_idx: ast.Node.Index) ?ast.full.VarDecl { fn makeScopeInternal( allocator: *std.mem.Allocator, scopes: *std.ArrayListUnmanaged(Scope), - error_completions: *std.ArrayListUnmanaged(types.CompletionItem), - enum_completions: *std.ArrayListUnmanaged(types.CompletionItem), + error_completions: *CompletionSet, + enum_completions: *CompletionSet, tree: ast.Tree, node_idx: ast.Node.Index, ) error{OutOfMemory}!void { @@ -2625,12 +2652,12 @@ fn makeScopeInternal( var i = main_tokens[node_idx]; while (i < data[node_idx].rhs) : (i += 1) { if (token_tags[i] == .identifier) { - (try error_completions.addOne(allocator)).* = .{ + try error_completions.put(allocator, .{ .label = tree.tokenSlice(i), .kind = .Constant, .insertTextFormat = .PlainText, .insertText = tree.tokenSlice(i), - }; + }, {}); } } } @@ -2694,14 +2721,14 @@ fn makeScopeInternal( } if (!std.mem.eql(u8, name, "_")) { - (try enum_completions.addOne(allocator)).* = .{ + try enum_completions.put(allocator, .{ .label = name, .kind = .Constant, .documentation = if (try getDocComments(allocator, tree, decl, .Markdown)) |docs| .{ .kind = .Markdown, .value = docs, } else null, - }; + }, {}); } } } diff --git a/src/document_store.zig b/src/document_store.zig index 4ad5f47..f563bb1 100644 --- a/src/document_store.zig +++ b/src/document_store.zig @@ -146,7 +146,7 @@ fn newDocument(self: *DocumentStore, uri: []const u8, text: []u8) anyerror!*Hand var tree = try std.zig.parse(self.allocator, text); errdefer tree.deinit(self.allocator); - const document_scope = try analysis.makeDocumentScope(self.allocator, tree); + var document_scope = try analysis.makeDocumentScope(self.allocator, tree); errdefer document_scope.deinit(self.allocator); handle.* = Handle{ @@ -381,7 +381,7 @@ fn refreshDocument(self: *DocumentStore, handle: *Handle, zig_lib_path: ?[]const const std_uri = try stdUriFromLibPath(&arena.allocator, zig_lib_path); for (import_strs.items) |str| { const uri = (try self.uriFromImportStr(&arena.allocator, handle.*, str)) orelse continue; - + exists_loop: for (still_exist) |*does_still_exist, i| { if (does_still_exist.*) continue; @@ -643,31 +643,45 @@ pub fn deinit(self: *DocumentStore) void { self.build_files.deinit(self.allocator); } -fn tagStoreCompletionItems(self: DocumentStore, arena: *std.heap.ArenaAllocator, base: *DocumentStore.Handle, comptime name: []const u8) ![]types.CompletionItem { +fn tagStoreCompletionItems( + self: DocumentStore, + arena: *std.heap.ArenaAllocator, + base: *DocumentStore.Handle, + comptime name: []const u8, +) ![]types.CompletionItem { // TODO Better solution for deciding what tags to include - var handle_arr = try arena.allocator.alloc(*DocumentStore.Handle, base.import_uris.items.len + 1); - handle_arr[0] = base; - var len: usize = @field(base.document_scope, name).len; - for (base.import_uris.items) |uri, idx| { - handle_arr[idx + 1] = self.handles.get(uri).?; - len += @field(handle_arr[idx + 1].document_scope, name).len; + var max_len: usize = @field(base.document_scope, name).count(); + for (base.import_uris.items) |uri| { + max_len += @field(self.handles.get(uri).?.document_scope, name).count(); } - var result = try arena.allocator.alloc(types.CompletionItem, len); - var res_idx: usize = 0; - for (handle_arr) |handle| { - for (@field(handle.document_scope, name)) |item| { - result[res_idx] = item; - res_idx += 1; + var result_set = analysis.CompletionSet{}; + try result_set.ensureCapacity(&arena.allocator, max_len); + result_set.entries.appendSliceAssumeCapacity(@field(base.document_scope, name).entries.items); + try result_set.reIndex(&arena.allocator); + + for (base.import_uris.items) |uri| { + const curr_set = &@field(self.handles.get(uri).?.document_scope, name); + for (curr_set.entries.items) |entry| { + result_set.putAssumeCapacity(entry.key, {}); } } - return result; + // This is safe to do because CompletionSet.Entry == struct { value: types.CompletionItem } + return std.mem.bytesAsSlice(types.CompletionItem, std.mem.sliceAsBytes(result_set.entries.items)); } -pub fn errorCompletionItems(self: DocumentStore, arena: *std.heap.ArenaAllocator, base: *DocumentStore.Handle) ![]types.CompletionItem { +pub fn errorCompletionItems( + self: DocumentStore, + arena: *std.heap.ArenaAllocator, + base: *DocumentStore.Handle, +) ![]types.CompletionItem { return try self.tagStoreCompletionItems(arena, base, "error_completions"); } -pub fn enumCompletionItems(self: DocumentStore, arena: *std.heap.ArenaAllocator, base: *DocumentStore.Handle) ![]types.CompletionItem { +pub fn enumCompletionItems( + self: DocumentStore, + arena: *std.heap.ArenaAllocator, + base: *DocumentStore.Handle, +) ![]types.CompletionItem { return try self.tagStoreCompletionItems(arena, base, "enum_completions"); }