diff --git a/src/analysis.zig b/src/analysis.zig index 91a2f00..04dbcd9 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -206,7 +206,6 @@ pub fn getChild(tree: *ast.Tree, node: *ast.Node, name: []const u8) ?*ast.Node { /// Gets the child of slice pub fn getChildOfSlice(tree: *ast.Tree, nodes: []*ast.Node, name: []const u8) ?*ast.Node { - // var index: usize = 0; for (nodes) |child| { switch (child.id) { .VarDecl => { @@ -227,14 +226,12 @@ pub fn getChildOfSlice(tree: *ast.Tree, nodes: []*ast.Node, name: []const u8) ?* }, else => {}, } - // index += 1; } return null; } /// Resolves the type of a node pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast.Node { - std.debug.warn("NODE {}\n", .{node}); switch (node.id) { .VarDecl => { const vari = node.cast(ast.Node.VarDecl).?; @@ -257,22 +254,14 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. } }, .Identifier => { - // std.debug.warn("IDENTIFIER {}\n", .{analysis_ctx.tree.getNodeSource(node)}); if (getChildOfSlice(analysis_ctx.tree, analysis_ctx.scope_nodes, analysis_ctx.tree.getNodeSource(node))) |child| { - // std.debug.warn("CHILD {}\n", .{child}); return resolveTypeOfNode(analysis_ctx, child); } else return null; }, - .ContainerDecl => { - return node; - }, .ContainerField => { const field = node.cast(ast.Node.ContainerField).?; return resolveTypeOfNode(analysis_ctx, field.type_expr orelse return null); }, - .ErrorSetDecl => { - return node; - }, .SuffixOp => { const suffix_op = node.cast(ast.Node.SuffixOp).?; switch (suffix_op.op) { @@ -326,12 +315,8 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. break :block null; }; }, - .MultilineStringLiteral, .StringLiteral => { - return node; - }, - else => { - std.debug.warn("Type resolution case not implemented; {}\n", .{node.id}); - }, + .MultilineStringLiteral, .StringLiteral, .ContainerDecl, .ErrorSetDecl => return node, + else => std.debug.warn("Type resolution case not implemented; {}\n", .{node.id}), } return null; } @@ -349,10 +334,8 @@ fn maybeCollectImport(tree: *ast.Tree, builtin_call: *ast.Node.BuiltinCall, arr: /// Collects all imports we can find into a slice of import paths (without quotes). /// The import paths are valid as long as the tree is. -pub fn collectImports(allocator: *std.mem.Allocator, tree: *ast.Tree) ![][]const u8 { - // TODO: Currently only detects `const smth = @import("string literal")<.SometThing>;` - var arr = std.ArrayList([]const u8).init(allocator); - +pub fn collectImports(import_arr: *std.ArrayList([]const u8), tree: *ast.Tree) !void { + // TODO: Currently only detects `const smth = @import("string literal")<.SomeThing>;` var idx: usize = 0; while (tree.root_node.iterate(idx)) |decl| : (idx += 1) { if (decl.id != .VarDecl) continue; @@ -362,7 +345,7 @@ pub fn collectImports(allocator: *std.mem.Allocator, tree: *ast.Tree) ![][]const switch (var_decl.init_node.?.id) { .BuiltinCall => { const builtin_call = var_decl.init_node.?.cast(ast.Node.BuiltinCall).?; - try maybeCollectImport(tree, builtin_call, &arr); + try maybeCollectImport(tree, builtin_call, import_arr); }, .InfixOp => { const infix_op = var_decl.init_node.?.cast(ast.Node.InfixOp).?; @@ -372,13 +355,11 @@ pub fn collectImports(allocator: *std.mem.Allocator, tree: *ast.Tree) ![][]const else => continue, } if (infix_op.lhs.id != .BuiltinCall) continue; - try maybeCollectImport(tree, infix_op.lhs.cast(ast.Node.BuiltinCall).?, &arr); + try maybeCollectImport(tree, infix_op.lhs.cast(ast.Node.BuiltinCall).?, import_arr); }, else => {}, } } - - return arr.toOwnedSlice(); } pub fn getFieldAccessTypeNode(analysis_ctx: *AnalysisContext, tokenizer: *std.zig.Tokenizer) ?*ast.Node { @@ -387,12 +368,8 @@ pub fn getFieldAccessTypeNode(analysis_ctx: *AnalysisContext, tokenizer: *std.zi while (true) { var next = tokenizer.next(); switch (next.id) { - .Eof => { - return current_node; - }, + .Eof => return current_node, .Identifier => { - // var root = current_node.cast(ast.Node.Root).?; - // current_node. if (getChildOfSlice(analysis_ctx.tree, analysis_ctx.scope_nodes, tokenizer.buffer[next.start..next.end])) |child| { if (resolveTypeOfNode(analysis_ctx, child)) |node_type| { current_node = node_type; @@ -411,9 +388,7 @@ pub fn getFieldAccessTypeNode(analysis_ctx: *AnalysisContext, tokenizer: *std.zi } else return null; } }, - else => { - std.debug.warn("Not implemented; {}\n", .{next.id}); - }, + else => std.debug.warn("Not implemented; {}\n", .{next.id}), } } @@ -462,70 +437,44 @@ pub fn nodeToString(tree: *ast.Tree, node: *ast.Node) ?[]const u8 { return null; } -pub fn declsFromIndexInternal(allocator: *std.mem.Allocator, tree: *ast.Tree, node: *ast.Node, nodes: *std.ArrayList(*ast.Node)) anyerror!void { +pub fn declsFromIndexInternal(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, node: *ast.Node) anyerror!void { switch (node.id) { .FnProto => { const func = node.cast(ast.Node.FnProto).?; var param_index: usize = 0; while (param_index < func.params.len) : (param_index += 1) - try declsFromIndexInternal(allocator, tree, func.params.at(param_index).*, nodes); + try declsFromIndexInternal(decls, tree, func.params.at(param_index).*); if (func.body_node) |body_node| - try declsFromIndexInternal(allocator, tree, body_node, nodes); + try declsFromIndexInternal(decls, tree, body_node); }, .Block => { var index: usize = 0; - - while (node.iterate(index)) |inode| { - try declsFromIndexInternal(allocator, tree, inode, nodes); - index += 1; + while (node.iterate(index)) |inode| : (index += 1) { + try declsFromIndexInternal(decls, tree, inode); } }, - .VarDecl => { - try nodes.append(node); - }, - .ParamDecl => { - try nodes.append(node); - }, - else => { - try nodes.appendSlice(try getCompletionsFromNode(allocator, tree, node)); - }, + .VarDecl, .ParamDecl => try decls.append(node), + else => try addChildrenNodes(decls, tree, node), } } -pub fn getCompletionsFromNode(allocator: *std.mem.Allocator, tree: *ast.Tree, node: *ast.Node) ![]*ast.Node { - var nodes = std.ArrayList(*ast.Node).init(allocator); - +pub fn addChildrenNodes(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, node: *ast.Node) !void { var index: usize = 0; - while (node.iterate(index)) |child_node| { - try nodes.append(child_node); - - index += 1; + while (node.iterate(index)) |child_node| : (index += 1) { + try decls.append(child_node); } - - return nodes.items; } -pub fn declsFromIndex(allocator: *std.mem.Allocator, tree: *ast.Tree, index: usize) ![]*ast.Node { - var iindex: usize = 0; - +pub fn declsFromIndex(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, index: usize) !void { var node = &tree.root_node.base; - var nodes = std.ArrayList(*ast.Node).init(allocator); - try nodes.appendSlice(try getCompletionsFromNode(allocator, tree, node)); - - while (node.iterate(iindex)) |inode| { - if (tree.tokens.at(inode.firstToken()).start < index and index < tree.tokens.at(inode.lastToken()).start) { - try declsFromIndexInternal(allocator, tree, inode, &nodes); + try addChildrenNodes(decls, tree, node); + var node_index: usize = 0; + while (node.iterate(node_index)) |inode| : (node_index += 1) { + if (tree.tokens.at(inode.firstToken()).start < index and index < tree.tokens.at(inode.lastToken()).end) { + try declsFromIndexInternal(decls, tree, inode); } - - iindex += 1; } - - if (tree.tokens.at(node.firstToken()).start < index and index < tree.tokens.at(node.lastToken()).start) { - return nodes.items; - } - - return nodes.items; } diff --git a/src/document_store.zig b/src/document_store.zig index 478fd08..02dd85f 100644 --- a/src/document_store.zig +++ b/src/document_store.zig @@ -61,7 +61,6 @@ fn newDocument(self: *DocumentStore, uri: []const u8, text: []u8) !*Handle { .mem = text, }, }; - try self.checkSanity(&handle); const kv = try self.handles.getOrPutValue(uri, handle); return &kv.value; } @@ -117,10 +116,7 @@ pub fn getHandle(self: *DocumentStore, uri: []const u8) ?*Handle { } // Check if the document text is now sane, move it to sane_text if so. -fn checkSanity(self: *DocumentStore, handle: *Handle) !void { - const tree = try handle.tree(self.allocator); - defer tree.deinit(); - +fn removeOldImports(self: *DocumentStore, handle: *Handle) !void { std.debug.warn("New text for document {}\n", .{handle.uri()}); // TODO: Better algorithm or data structure? // Removing the imports is costly since they live in an array list @@ -129,19 +125,22 @@ fn checkSanity(self: *DocumentStore, handle: *Handle) !void { // Try to detect removed imports and decrement their counts. if (handle.import_uris.items.len == 0) return; - const import_strs = try analysis.collectImports(self.allocator, tree); - defer self.allocator.free(import_strs); + const tree = try handle.tree(self.allocator); + defer tree.deinit(); - const still_exist = try self.allocator.alloc(bool, handle.import_uris.items.len); - defer self.allocator.free(still_exist); + var arena = std.heap.ArenaAllocator.init(self.allocator); + defer arena.deinit(); + var import_strs = std.ArrayList([]const u8).init(&arena.allocator); + try analysis.collectImports(&import_strs, tree); + + const still_exist = try arena.allocator.alloc(bool, handle.import_uris.items.len); for (still_exist) |*ex| { ex.* = false; } - for (import_strs) |str| { - const uri = (try uriFromImportStr(self, handle.*, str)) orelse continue; - defer self.allocator.free(uri); + for (import_strs.items) |str| { + const uri = (try uriFromImportStr(self, &arena.allocator, handle.*, str)) orelse continue; var idx: usize = 0; exists_loop: while (idx < still_exist.len) : (idx += 1) { @@ -222,29 +221,29 @@ pub fn applyChanges(self: *DocumentStore, handle: *Handle, content_changes: std. } } - try self.checkSanity(handle); + try self.removeOldImports(handle); } -fn uriFromImportStr(store: *DocumentStore, handle: Handle, import_str: []const u8) !?[]const u8 { +fn uriFromImportStr(store: *DocumentStore, allocator: *std.mem.Allocator, handle: Handle, import_str: []const u8) !?[]const u8 { return if (std.mem.eql(u8, import_str, "std")) - if (store.std_uri) |std_root_uri| try std.mem.dupe(store.allocator, u8, std_root_uri) + if (store.std_uri) |std_root_uri| try std.mem.dupe(allocator, u8, std_root_uri) else { std.debug.warn("Cannot resolve std library import, path is null.\n", .{}); return null; } else b: { // Find relative uri - const path = try URI.parse(store.allocator, handle.uri()); - defer store.allocator.free(path); + const path = try URI.parse(allocator, handle.uri()); + defer allocator.free(path); const dir_path = std.fs.path.dirname(path) orelse ""; - const import_path = try std.fs.path.resolve(store.allocator, &[_][]const u8 { + const import_path = try std.fs.path.resolve(allocator, &[_][]const u8 { dir_path, import_str }); - defer store.allocator.free(import_path); + defer allocator.free(import_path); - break :b (try URI.fromPath(store.allocator, import_path)); + break :b (try URI.fromPath(allocator, import_path)); }; } @@ -257,9 +256,15 @@ pub const AnalysisContext = struct { tree: *std.zig.ast.Tree, scope_nodes: []*std.zig.ast.Node, + fn refreshScopeNodes(self: *AnalysisContext) !void { + var scope_nodes = std.ArrayList(*std.zig.ast.Node).init(&self.arena.allocator); + try analysis.addChildrenNodes(&scope_nodes, self.tree, &self.tree.root_node.base); + self.scope_nodes = scope_nodes.items; + } + pub fn onImport(self: *AnalysisContext, import_str: []const u8) !?*std.zig.ast.Node { const allocator = self.store.allocator; - const final_uri = (try uriFromImportStr(self.store, self.handle.*, import_str)) orelse return null; + const final_uri = (try uriFromImportStr(self.store, self.store.allocator, self.handle.*, import_str)) orelse return null; std.debug.warn("Import final URI: {}\n", .{final_uri}); var consumed_final_uri = false; @@ -273,6 +278,7 @@ pub const AnalysisContext = struct { self.tree.deinit(); self.tree = try self.handle.tree(allocator); + try self.refreshScopeNodes(); return &self.tree.root_node.base; } } @@ -286,6 +292,7 @@ pub const AnalysisContext = struct { self.tree.deinit(); self.tree = try self.handle.tree(allocator); + try self.refreshScopeNodes(); return &self.tree.root_node.base; } @@ -325,6 +332,7 @@ pub const AnalysisContext = struct { // If we return null, no one should access the tree. self.tree.deinit(); self.tree = try self.handle.tree(allocator); + try self.refreshScopeNodes(); return &self.tree.root_node.base; } @@ -335,12 +343,16 @@ pub const AnalysisContext = struct { pub fn analysisContext(self: *DocumentStore, handle: *Handle, arena: *std.heap.ArenaAllocator, position: types.Position) !AnalysisContext { const tree = try handle.tree(self.allocator); + + var scope_nodes = std.ArrayList(*std.zig.ast.Node).init(&arena.allocator); + try analysis.declsFromIndex(&scope_nodes, tree, try handle.document.positionToIndex(position)); + return AnalysisContext{ .store = self, .handle = handle, .arena = arena, .tree = tree, - .scope_nodes = try analysis.declsFromIndex(&arena.allocator, tree, try handle.document.positionToIndex(position)) + .scope_nodes = scope_nodes.items, }; } diff --git a/src/main.zig b/src/main.zig index a5d78fe..450a430 100644 --- a/src/main.zig +++ b/src/main.zig @@ -280,9 +280,9 @@ fn completeGlobal(id: i64, pos_index: usize, handle: DocumentStore.Handle, confi // Deallocate all temporary data. defer arena.deinit(); - // var decls = tree.root_node.decls.iterator(0); - var decls = try analysis.declsFromIndex(&arena.allocator, tree, pos_index); - for (decls) |decl_ptr| { + var decl_nodes = std.ArrayList(*std.zig.ast.Node).init(&arena.allocator); + try analysis.declsFromIndex(&decl_nodes, tree, pos_index); + for (decl_nodes.items) |decl_ptr| { var decl = decl_ptr.*; try nodeToCompletion(&completions, tree, decl_ptr, config); } @@ -310,7 +310,6 @@ fn completeFieldAccess(id: i64, handle: *DocumentStore.Handle, position: types.P const line = try handle.document.getLine(@intCast(usize, position.line)); var tokenizer = std.zig.Tokenizer.init(line[line_start_idx..]); - // var decls = try analysis.declsFromIndex(&arena.allocator, analysis_ctx.tree, try handle.document.positionToIndex(position)); if (analysis.getFieldAccessTypeNode(&analysis_ctx, &tokenizer)) |node| { try nodeToCompletion(&completions, analysis_ctx.tree, node, config); }