From af746be77023f6aec78aa8acfab640290826b689 Mon Sep 17 00:00:00 2001 From: Vexu Date: Fri, 22 May 2020 18:51:57 +0300 Subject: [PATCH] provide goto definition for import strings --- src/analysis.zig | 50 ++++++++++++++++++++++++++++++++++-------- src/document_store.zig | 4 ++-- src/main.zig | 31 ++++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 13 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index d83f2fc..ddb2abf 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -261,14 +261,15 @@ fn resolveReturnType(analysis_ctx: *AnalysisContext, fn_decl: *ast.Node.FnProto) if (isTypeFunction(analysis_ctx.tree, fn_decl) and fn_decl.body_node != null) { // If this is a type function and it only contains a single return statement that returns // a container declaration, we will return that declaration. - const ret = findReturnStatement(fn_decl.body_node.?) orelse return null; - if (ret.rhs) |rhs| if (resolveTypeOfNode(analysis_ctx, rhs)) |res_rhs| switch(res_rhs.id) { - .ContainerDecl => { - analysis_ctx.onContainer(res_rhs.cast(ast.Node.ContainerDecl).?) catch return null; - return res_rhs; - }, - else => return null, - }; + const ret = findReturnStatement(fn_decl.body_node.?) orelse return null; + if (ret.rhs) |rhs| + if (resolveTypeOfNode(analysis_ctx, rhs)) |res_rhs| switch (res_rhs.id) { + .ContainerDecl => { + analysis_ctx.onContainer(res_rhs.cast(ast.Node.ContainerDecl).?) catch return null; + return res_rhs; + }, + else => return null, + }; return null; } @@ -366,7 +367,7 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. if (builtin_call.params.len != 0) return null; return analysis_ctx.last_this_node; } - + if (!std.mem.eql(u8, call_name, "@import")) return null; if (builtin_call.params.len > 1) return null; @@ -557,3 +558,34 @@ pub fn declsFromIndex(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, index: } } } + +fn nodeContainsSourceIndex(tree: *ast.Tree, node: *ast.Node, source_index: usize) bool { + const first_token = tree.tokens.at(node.firstToken()); + const last_token = tree.tokens.at(node.lastToken()); + return source_index >= first_token.start and source_index <= last_token.end; +} + +pub fn getImportStr(tree: *ast.Tree, source_index: usize) ?[]const u8 { + var node = &tree.root_node.base; + var index: usize = 0; + while (node.iterate(index)) |child| { + if (!nodeContainsSourceIndex(tree, child, source_index)) { + index += 1; + continue; + } + if (child.cast(ast.Node.BuiltinCall)) |builtin_call| blk: { + const call_name = tree.tokenSlice(builtin_call.builtin_token); + + if (!std.mem.eql(u8, call_name, "@import")) break :blk; + if (builtin_call.params.len != 1) break :blk; + + const import_param = builtin_call.params.at(0).*; + const import_str_node = import_param.cast(ast.Node.StringLiteral) orelse break :blk; + const import_str = tree.tokenSlice(import_str_node.token); + return import_str[1 .. import_str.len - 1]; + } + node = child; + index = 0; + } + return null; +} diff --git a/src/document_store.zig b/src/document_store.zig index 3d07cee..50916ce 100644 --- a/src/document_store.zig +++ b/src/document_store.zig @@ -216,7 +216,7 @@ pub fn applyChanges( try self.removeOldImports(handle, zig_lib_path); } -fn uriFromImportStr( +pub fn uriFromImportStr( store: *DocumentStore, allocator: *std.mem.Allocator, handle: Handle, @@ -371,7 +371,7 @@ pub const AnalysisContext = struct { } }; -fn stdUriFromLibPath(allocator: *std.mem.Allocator, zig_lib_path: ?[]const u8) !?[]const u8 { +pub fn stdUriFromLibPath(allocator: *std.mem.Allocator, zig_lib_path: ?[]const u8) !?[]const u8 { if (zig_lib_path) |zpath| { const std_path = std.fs.path.resolve(allocator, &[_][]const u8{ zpath, "./std/std.zig", diff --git a/src/main.zig b/src/main.zig index ec21821..643e7cd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -365,10 +365,36 @@ fn gotoDefinitionFieldAccess( try respondGeneric(id, null_result_response); } -fn completeGlobal(id: i64, pos_index: usize, handle: *DocumentStore.Handle, config: Config) !void { +fn gotoDefinitionString(id: i64, pos_index: usize, handle: *DocumentStore.Handle, config: Config) !void { var tree = try handle.tree(allocator); defer tree.deinit(); + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + + const import_str = analysis.getImportStr(tree, pos_index) orelse return try respondGeneric(id, null_result_response); + const uri = (try document_store.uriFromImportStr( + &arena.allocator, + handle.*, + import_str, + try DocumentStore.stdUriFromLibPath(&arena.allocator, config.zig_lib_path), + )) orelse return try respondGeneric(id, null_result_response); + + try send(types.Response{ + .id = .{ .Integer = id }, + .result = .{ + .Location = .{ + .uri = uri, + .range = .{ + .start = .{ .line = 0, .character = 0 }, + .end = .{ .line = 0, .character = 0 }, + }, + }, + }, + }); +} + +fn completeGlobal(id: i64, pos_index: usize, handle: *DocumentStore.Handle, config: Config) !void { // We use a local arena allocator to deallocate all temporary data without iterating var arena = std.heap.ArenaAllocator.init(allocator); var completions = std.ArrayList(types.CompletionItem).init(&arena.allocator); @@ -382,7 +408,7 @@ fn completeGlobal(id: i64, pos_index: usize, handle: *DocumentStore.Handle, conf defer analysis_ctx.deinit(); var decl_nodes = std.ArrayList(*std.zig.ast.Node).init(&arena.allocator); - try analysis.declsFromIndex(&decl_nodes, tree, pos_index); + try analysis.declsFromIndex(&decl_nodes, analysis_ctx.tree, pos_index); for (decl_nodes.items) |decl_ptr| { var decl = decl_ptr.*; try nodeToCompletion(&completions, &analysis_ctx, decl_ptr, config); @@ -805,6 +831,7 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v start_idx, configFromUriOr(uri, config), ), + .string_literal => try gotoDefinitionString(id, pos_index, handle, config), else => try respondGeneric(id, null_result_response), } }