From 220d6689290312c910f18df195d0b626273dc1ac Mon Sep 17 00:00:00 2001 From: SuperAuguste Date: Sat, 16 May 2020 14:06:48 -0400 Subject: [PATCH] local var completion --- src/analysis.zig | 122 +++++++++++++++++++++++++++++++++++++++-- src/document_store.zig | 7 ++- src/main.zig | 24 ++++++-- 3 files changed, 140 insertions(+), 13 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 4cf5324..1a21e72 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -149,9 +149,13 @@ pub fn getFunctionSnippet(allocator: *std.mem.Allocator, tree: *ast.Tree, func: pub fn getVariableSignature(tree: *ast.Tree, var_decl: *ast.Node.VarDecl) []const u8 { const start = tree.tokens.at(var_decl.firstToken()).start; const end = tree.tokens.at(var_decl.semicolon_token).start; - // var end = - // if (var_decl.init_n) |body| tree.tokens.at(body.firstToken()).start - // else tree.tokens.at(var_decl.name_token).end; + return tree.source[start..end]; +} + +/// Gets a param signature +pub fn getParamSignature(tree: *ast.Tree, param: *ast.Node.ParamDecl) []const u8 { + const start = tree.tokens.at(param.firstToken()).start; + const end = tree.tokens.at(param.lastToken()).end; return tree.source[start..end]; } @@ -191,13 +195,51 @@ pub fn getChild(tree: *ast.Tree, node: *ast.Node, name: []const u8) ?*ast.Node { return null; } +/// 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 => { + const vari = child.cast(ast.Node.VarDecl).?; + if (std.mem.eql(u8, tree.tokenSlice(vari.name_token), name)) return child; + }, + .ParamDecl => { + const decl = child.cast(ast.Node.ParamDecl).?; + if (decl.name_token != null and std.mem.eql(u8, tree.tokenSlice(decl.name_token.?), name)) return child; + }, + .FnProto => { + const func = child.cast(ast.Node.FnProto).?; + if (func.name_token != null and std.mem.eql(u8, tree.tokenSlice(func.name_token.?), name)) return child; + }, + .ContainerField => { + const field = child.cast(ast.Node.ContainerField).?; + if (std.mem.eql(u8, tree.tokenSlice(field.name_token), name)) return child; + }, + 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).?; return resolveTypeOfNode(analysis_ctx, vari.type_node orelse vari.init_node.?) orelse null; }, + .ParamDecl => { + const decl = node.cast(ast.Node.ParamDecl).?; + switch (decl.param_type) { + .var_type, .type_expr => |var_type| { + return resolveTypeOfNode(analysis_ctx, var_type) orelse null; + }, + else => {} + } + }, .FnProto => { const func = node.cast(ast.Node.FnProto).?; switch (func.return_type) { @@ -206,7 +248,9 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. } }, .Identifier => { - if (getChild(analysis_ctx.tree, &analysis_ctx.tree.root_node.base, analysis_ctx.tree.getNodeSource(node))) |child| { + // 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; }, @@ -331,7 +375,7 @@ pub fn getFieldAccessTypeNode(analysis_ctx: *AnalysisContext, tokenizer: *std.zi .Identifier => { // var root = current_node.cast(ast.Node.Root).?; // current_node. - if (getChild(analysis_ctx.tree, current_node, tokenizer.buffer[next.start..next.end])) |child| { + 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; } else return null; @@ -406,3 +450,71 @@ 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 { + 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); + + if (func.body_node) |body_node| + try declsFromIndexInternal(allocator, tree, body_node, nodes); + }, + .Block => { + var index: usize = 0; + + while (node.iterate(index)) |inode| { + try declsFromIndexInternal(allocator, tree, inode, nodes); + index += 1; + } + }, + .VarDecl => { + try nodes.append(node); + }, + .ParamDecl => { + try nodes.append(node); + }, + else => { + try nodes.appendSlice(try getCompletionsFromNode(allocator, 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); + + var index: usize = 0; + while (node.iterate(index)) |child_node| { + try nodes.append(child_node); + + index += 1; + } + + return nodes.items; +} + +pub fn declsFromIndex(allocator: *std.mem.Allocator, tree: *ast.Tree, index: usize) ![]*ast.Node { + var iindex: usize = 0; + + 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); + } + + 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 99178f1..80f0dfc 100644 --- a/src/document_store.zig +++ b/src/document_store.zig @@ -260,6 +260,7 @@ pub const AnalysisContext = struct { // not for the tree allocations. arena: *std.heap.ArenaAllocator, tree: *std.zig.ast.Tree, + scope_nodes: []*std.zig.ast.Node, pub fn onImport(self: *AnalysisContext, import_str: []const u8) !?*std.zig.ast.Node { const allocator = self.store.allocator; @@ -335,12 +336,14 @@ pub const AnalysisContext = struct { } }; -pub fn analysisContext(self: *DocumentStore, handle: *Handle, arena: *std.heap.ArenaAllocator) !AnalysisContext { +pub fn analysisContext(self: *DocumentStore, handle: *Handle, arena: *std.heap.ArenaAllocator, position: types.Position) !AnalysisContext { + const tree = try handle.tree(self.allocator); return AnalysisContext{ .store = self, .handle = handle, .arena = arena, - .tree = try handle.tree(self.allocator), + .tree = tree, + .scope_nodes = try analysis.declsFromIndex(&arena.allocator, tree, try handle.document.positionToIndex(position)) }; } diff --git a/src/main.zig b/src/main.zig index 6db997a..46f1c7d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -223,6 +223,16 @@ fn nodeToCompletion(alloc: *std.mem.Allocator, tree: *std.zig.ast.Tree, decl: *s .detail = analysis.getVariableSignature(tree, var_decl), }; }, + .ParamDecl => { + const param = decl.cast(std.zig.ast.Node.ParamDecl).?; + if (param.name_token) |name_token| + return types.CompletionItem{ + .label = tree.tokenSlice(name_token), + .kind = .Variable, + .documentation = doc, + .detail = analysis.getParamSignature(tree, param), + }; + }, else => if (analysis.nodeToString(tree, decl)) |string| { return types.CompletionItem{ .label = string, @@ -235,7 +245,7 @@ fn nodeToCompletion(alloc: *std.mem.Allocator, tree: *std.zig.ast.Tree, decl: *s return null; } -fn completeGlobal(id: i64, handle: DocumentStore.Handle, config: Config) !void { +fn completeGlobal(id: i64, pos_index: usize, handle: DocumentStore.Handle, config: Config) !void { var tree = try handle.tree(allocator); defer tree.deinit(); @@ -245,10 +255,11 @@ fn completeGlobal(id: i64, handle: DocumentStore.Handle, config: Config) !void { // Deallocate all temporary data. defer arena.deinit(); - var decls = tree.root_node.decls.iterator(0); - while (decls.next()) |decl_ptr| { + // var decls = tree.root_node.decls.iterator(0); + var decls = try analysis.declsFromIndex(&arena.allocator, tree, pos_index); + for (decls) |decl_ptr| { var decl = decl_ptr.*; - if (try nodeToCompletion(&arena.allocator, tree, decl, config)) |completion| { + if (try nodeToCompletion(&arena.allocator, tree, decl_ptr, config)) |completion| { try completions.append(completion); } } @@ -268,7 +279,7 @@ fn completeFieldAccess(id: i64, handle: *DocumentStore.Handle, position: types.P var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); - var analysis_ctx = try document_store.analysisContext(handle, &arena); + var analysis_ctx = try document_store.analysisContext(handle, &arena, position); defer analysis_ctx.deinit(); var completions = std.ArrayList(types.CompletionItem).init(&arena.allocator); @@ -276,6 +287,7 @@ fn completeFieldAccess(id: i64, handle: *DocumentStore.Handle, position: types.P var 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| { var index: usize = 0; while (node.iterate(index)) |child_node| { @@ -537,7 +549,7 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v }, }, }), - .var_access, .empty => try completeGlobal(id, handle.*, config), + .var_access, .empty => try completeGlobal(id, pos_index, handle.*, config), .field_access => |start_idx| try completeFieldAccess(id, handle, pos, start_idx, config), else => try respondGeneric(id, no_completions_response), }