From ac8a00342e9d43be2db06049e55e9527b80bbd82 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 6 Mar 2021 20:55:59 +0100 Subject: [PATCH] All functionalities implemented. Also implemented ability to skip searching for references through std --- src/analysis.zig | 474 ++++++++++++++++++++++++++++++++++++--------- src/config.zig | 4 + src/main.zig | 58 +++++- src/references.zig | 78 ++++++-- src/rename.zig | 2 +- 5 files changed, 489 insertions(+), 127 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index f2e6fbb..a061294 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -35,15 +35,7 @@ pub fn getDocCommentTokenIndex(tree: ast.Tree, node: ast.Node.Index) ?ast.TokenI if (tokens[idx] == .keyword_pub and idx > 0) idx -= 1; }, - .error_value => idx -= 1, - .container_field, - .container_field_init, - .container_field_align, - => idx -= 1, - .test_decl => idx -= 1, - else => { - log.debug("Doc comment check for tag: {s}", .{tags[node]}); - }, + else => idx -= 1, } // Find first doc comment token @@ -103,9 +95,12 @@ pub fn collectDocComments( /// Gets a function signature (keywords, name, return value) pub fn getFunctionSignature(tree: ast.Tree, func: ast.full.FnProto) []const u8 { - const start = offsets.tokenLocation(tree, func.ast.fn_token).start; - const end = offsets.tokenLocation(tree, tree.nodes.items(.main_token)[func.ast.return_type]).end; - return tree.source[start..end]; + const start = offsets.tokenLocation(tree, func.ast.fn_token); + // return type can be 0 when user wrote incorrect fn signature + // to ensure we don't break, just end the signature at end of fn token + if (func.ast.return_type == 0) return tree.source[start.start..start.end]; + const end = offsets.tokenLocation(tree, tree.lastToken(func.ast.return_type)).end; + return tree.source[start.start..end]; } /// Gets a function snippet insert text @@ -240,12 +235,7 @@ pub fn getDeclNameToken(tree: ast.Tree, node: ast.Node.Index) ?ast.TokenIndex { .container_field_align => tree.containerFieldAlign(node).ast.name_token, .identifier => main_token, - - // @TODO: Errors - // .error_=> { - // const tag = node.castTag(.ErrorTag).?; - // return tag.name_token; - // }, + .error_value => main_token + 2, // 'error'. // lhs of main token is name token, so use `node` - 1 .test_decl => if (tree.tokens.items(.tag)[main_token + 1] == .string_literal) @@ -328,9 +318,7 @@ fn resolveVarDeclAliasInternal( break :block NodeWithHandle{ .node = resolved_node, .handle = resolved.handle }; } else return null; - if (try lookupSymbolContainer(store, arena, container_node, tree.tokenSlice(datas[node_handle.node].rhs), false)) |inner_decl| { - return inner_decl; - } + return try lookupSymbolContainer(store, arena, container_node, tree.tokenSlice(datas[node_handle.node].rhs), false); } return null; } @@ -366,22 +354,70 @@ pub fn resolveVarDeclAlias(store: *DocumentStore, arena: *std.heap.ArenaAllocato return null; } +/// Returns `true` when the given `node` is one of the block tags +fn isBlock(tree: ast.Tree, node: ast.Node.Index) bool { + return switch (tree.nodes.items(.tag)[node]) { + .block, + .block_semicolon, + .block_two, + .block_two_semicolon, + => true, + else => false, + }; +} + +/// Returns `true` when the given `node` is one of the call tags +fn isCall(tree: ast.Tree, node: ast.Node.Index) bool { + return switch (tree.nodes.items(.tag)[node]) { + .call, + .call_comma, + .call_one, + .call_one_comma, + .async_call, + .async_call_comma, + .async_call_one, + .async_call_one_comma, + => true, + else => false, + }; +} + fn findReturnStatementInternal( tree: ast.Tree, - fn_decl: *ast.Node.FnProto, - base_node: *ast.Node, + fn_decl: ast.full.FnProto, + body: ast.Node.Index, already_found: *bool, -) ?*ast.Node.ControlFlowExpression { - var result: ?*ast.Node.ControlFlowExpression = null; - var child_idx: usize = 0; +) ?ast.Node.Index { + var result: ?ast.Node.Index = null; - while (base_node.iterate(child_idx)) |child_node| : (child_idx += 1) { - if (child_node.castTag(.Return)) |cfe| { - // If we are calling ourselves recursively, ignore this return. - if (cfe.getRHS()) |rhs| { - if (rhs.castTag(.Call)) |call_node| { - if (call_node.lhs.tag == .Identifier) { - if (std.mem.eql(u8, getDeclName(tree, call_node.lhs).?, getDeclName(tree, &fn_decl.base).?)) { + const node_tags = tree.nodes.items(.tag); + const datas = tree.nodes.items(.data); + + if (!isBlock(tree, body)) return null; + + const statements: []const ast.Node.Index = switch (node_tags[body]) { + .block, .block_semicolon => tree.extra_data[datas[body].lhs..datas[body].rhs], + .block_two, .block_two_semicolon => blk: { + const statements = &[_]ast.Node.Index{ datas[body].lhs, datas[body].rhs }; + const len: usize = if (datas[body].lhs == 0) + @as(usize, 0) + else if (datas[body].rhs == 0) + @as(usize, 1) + else + @as(usize, 2); + break :blk statements[0..len]; + }, + else => unreachable, + }; + + for (statements) |child_idx| { + if (node_tags[child_idx] == .@"return") { + if (datas[child_idx].lhs != 0) { + const lhs = datas[child_idx].lhs; + if (isCall(tree, lhs)) { + const call_name = getDeclName(tree, datas[lhs].lhs); + if (call_name) |name| { + if (std.mem.eql(u8, name, tree.tokenSlice(fn_decl.name_token.?))) { continue; } } @@ -390,18 +426,19 @@ fn findReturnStatementInternal( if (already_found.*) return null; already_found.* = true; - result = cfe; + result = child_idx; continue; } - result = findReturnStatementInternal(tree, fn_decl, child_node, already_found); + result = findReturnStatementInternal(tree, fn_decl, child_idx, already_found); } + return result; } -fn findReturnStatement(tree: ast.Tree, fn_decl: *ast.Node.FnProto) ?*ast.Node.ControlFlowExpression { +fn findReturnStatement(tree: ast.Tree, fn_decl: ast.full.FnProto, body: ast.Node.Index) ?ast.Node.Index { var already_found = false; - return findReturnStatementInternal(tree, fn_decl, fn_decl.getBodyNode().?, &already_found); + return findReturnStatementInternal(tree, fn_decl, body, &already_found); } /// Resolves the return type of a function @@ -415,14 +452,25 @@ pub fn resolveReturnType( ) !?TypeWithHandle { const tree = handle.tree; if (isTypeFunction(tree, fn_decl) and fn_body != null) { - // @TODO: find return statement inside fn body of `type` (generic) functions + // 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(tree, fn_decl, fn_body.?) orelse return null; + const data = tree.nodes.items(.data)[ret]; + if (data.lhs != 0) { + return try resolveTypeOfNodeInternal(store, arena, .{ + .node = data.lhs, + .handle = handle, + }, bound_type_params); + } + + return null; } if (fn_decl.ast.return_type == 0) return null; - return resolveTypeOfNodeInternal(store, arena, .{ + return ((try resolveTypeOfNodeInternal(store, arena, .{ .node = fn_decl.ast.return_type, .handle = handle, - }, bound_type_params); + }, bound_type_params)) orelse return null).instanceTypeVal(); } /// Resolves the child type of an optional type @@ -647,7 +695,7 @@ pub fn resolveTypeOfNodeInternal( switch (child.decl.*) { .ast_node => |n| { if (n == node) return null; - if (varDecl(tree, n)) |var_decl| { + if (varDecl(child.handle.tree, n)) |var_decl| { if (var_decl.ast.init_node != 0 and var_decl.ast.init_node == node) return null; } }, @@ -721,7 +769,7 @@ pub fn resolveTypeOfNodeInternal( const call_param_type = (try resolveTypeOfNodeInternal(store, arena, .{ .node = call.ast.params[it.param_i - 1 - @boolToInt(has_self_param)], - .handle = decl.handle, + .handle = handle, }, bound_type_params)) orelse continue; if (!call_param_type.type.is_type_val) continue; @@ -810,7 +858,7 @@ pub fn resolveTypeOfNodeInternal( arena, .{ .node = left_type_node, .handle = left_type.handle }, rhs_str, - left_type.type.is_type_val, + !left_type.type.is_type_val, )) |child| { return try child.resolveType(store, arena, bound_type_params); } else return null; @@ -829,13 +877,13 @@ pub fn resolveTypeOfNodeInternal( }, bound_type_params)) orelse return null; return try resolveUnwrapErrorType(store, arena, left_type, bound_type_params); }, - .error_union => return TypeWithHandle.typeVal(node_handle), .array_type, .array_type_sentinel, .optional_type, .ptr_type_aligned, .ptr_type, .ptr_type_bit_range, + .error_union, => return TypeWithHandle.typeVal(node_handle), .@"try" => { const rhs_type = (try resolveTypeOfNodeInternal(store, arena, .{ @@ -860,7 +908,11 @@ pub fn resolveTypeOfNodeInternal( .handle = rhs_type.handle, }; }, - .builtin_call, .builtin_call_comma, .builtin_call_two, .builtin_call_two_comma => { + .builtin_call, + .builtin_call_comma, + .builtin_call_two, + .builtin_call_two_comma, + => { const data = datas[node]; const params = switch (node_tags[node]) { .builtin_call, .builtin_call_comma => tree.extra_data[data.lhs..data.rhs], @@ -935,15 +987,24 @@ pub fn resolveTypeOfNodeInternal( .container_decl_trailing, .container_decl_two, .container_decl_two_trailing, + .tagged_union, + .tagged_union_trailing, + .tagged_union_two, + .tagged_union_two_trailing, + .tagged_union_enum_tag, + .tagged_union_enum_tag_trailing, => { return TypeWithHandle.typeVal(node_handle); }, - .fn_proto, .fn_proto_multi, .fn_proto_one, .fn_proto_simple, .fn_decl => { + .fn_proto, + .fn_proto_multi, + .fn_proto_one, + .fn_proto_simple, + .fn_decl, + => { var buf: [1]ast.Node.Index = undefined; - const fn_proto = fnProto(tree, node, &buf).?; - // This is a function type - if (fn_proto.name_token == null) { + if (fnProto(tree, node, &buf).?.name_token == null) { return TypeWithHandle.typeVal(node_handle); } @@ -952,7 +1013,9 @@ pub fn resolveTypeOfNodeInternal( .handle = handle, }; }, - .multiline_string_literal, .string_literal => return TypeWithHandle{ + .multiline_string_literal, + .string_literal, + => return TypeWithHandle{ .type = .{ .data = .{ .other = node }, .is_type_val = false }, .handle = handle, }, @@ -1317,11 +1380,7 @@ pub fn nodeToString(tree: ast.Tree, node: ast.Node.Index) ?[]const u8 { .container_field => return tree.tokenSlice(tree.containerField(node).ast.name_token), .container_field_init => return tree.tokenSlice(tree.containerFieldInit(node).ast.name_token), .container_field_align => return tree.tokenSlice(tree.containerFieldAlign(node).ast.name_token), - // @TODO: Error tag name - // .ErrorTag => { - // const tag = node.castTag(.ErrorTag).?; - // return tree.tokenSlice(tag.name_token); - // }, + .error_value => return tree.tokenSlice(data[node].rhs), .identifier => return tree.tokenSlice(main_token), .fn_proto, .fn_proto_multi, @@ -2005,6 +2064,7 @@ fn iterateSymbolsContainerInternal( .label_decl => continue, else => {}, } + const decl = DeclWithHandle{ .decl = &entry.value, .handle = handle }; if (handle != orig_handle and !decl.isPublic()) continue; try callback(context, decl); @@ -2136,12 +2196,24 @@ fn resolveUse( if (std.mem.indexOfScalar(ast.Node.Index, use_trail.items, use) != null) continue; try use_trail.append(use); - const use_expr = (try resolveTypeOfNode(store, arena, .{ .node = handle.tree.nodes.items(.data)[use].lhs, .handle = handle })) orelse continue; + const use_expr = (try resolveTypeOfNode( + store, + arena, + .{ .node = handle.tree.nodes.items(.data)[use].lhs, .handle = handle }, + )) orelse continue; + const use_expr_node = switch (use_expr.type.data) { .other => |n| n, else => continue, }; - if (try lookupSymbolContainerInternal(store, arena, .{ .node = use_expr_node, .handle = use_expr.handle }, symbol, false, use_trail)) |candidate| { + if (try lookupSymbolContainerInternal( + store, + arena, + .{ .node = use_expr_node, .handle = use_expr.handle }, + symbol, + false, + use_trail, + )) |candidate| { if (candidate.handle != handle and !candidate.isPublic()) { continue; } @@ -2183,24 +2255,24 @@ fn lookupSymbolGlobalInternal( use_trail: *std.ArrayList(ast.Node.Index), ) error{OutOfMemory}!?DeclWithHandle { for (handle.document_scope.scopes) |scope| { - if (source_index >= scope.range.start and source_index < scope.range.end) { - if (scope.decls.getEntry(symbol)) |candidate| { - switch (candidate.value) { - .ast_node => |node| { - if (handle.tree.nodes.items(.tag)[node].isContainerField()) continue; - }, - .label_decl => continue, - else => {}, - } - return DeclWithHandle{ - .decl = &candidate.value, - .handle = handle, - }; + // if (source_index >= scope.range.start and source_index < scope.range.end) { + if (scope.decls.getEntry(symbol)) |candidate| { + switch (candidate.value) { + .ast_node => |node| { + if (handle.tree.nodes.items(.tag)[node].isContainerField()) continue; + }, + .label_decl => continue, + else => {}, } - - if (try resolveUse(store, arena, scope.uses, symbol, handle, use_trail)) |result| return result; + return DeclWithHandle{ + .decl = &candidate.value, + .handle = handle, + }; } + if (try resolveUse(store, arena, scope.uses, symbol, handle, use_trail)) |result| return result; + // } + if (scope.range.start > source_index) return null; } @@ -2235,7 +2307,7 @@ fn lookupSymbolContainerInternal( const token_tags = tree.tokens.items(.tag); const main_token = tree.nodes.items(.main_token)[container]; - const is_enum = if (isContainer(node_tags[container]) and node_tags[container] != .root) + const is_enum = if (container != 0 and isContainer(node_tags[container])) token_tags[main_token] == .keyword_enum else false; @@ -2397,7 +2469,6 @@ pub fn declMembers(tree: ast.Tree, tag: ast.Node.Tag, node_idx: ast.Node.Index, .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => tree.taggedUnionEnumTag(node_idx).ast.members, .tagged_union_two, .tagged_union_two_trailing => tree.taggedUnionTwo(buffer, node_idx).ast.members, .root => tree.rootDecls(), - // @TODO: Fix error set declarations .error_set_decl => &[_]ast.Node.Index{}, else => unreachable, }; @@ -2425,6 +2496,7 @@ fn makeScopeInternal( tree: ast.Tree, node_idx: ast.Node.Index, ) error{OutOfMemory}!void { + // if (node_idx > tree.nodes.len) return; const tags = tree.nodes.items(.tag); const token_tags = tree.tokens.items(.tag); const data = tree.nodes.items(.data); @@ -2460,13 +2532,12 @@ fn makeScopeInternal( try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, decl); const name = getDeclName(tree, decl) orelse continue; - // @TODO: implement tests - // if (decl.tag == .TestDecl) { - // try tests.append(decl); - // continue; - // } + if (tags[decl] == .test_decl) { + try tests.append(decl); + continue; + } - if (tags[decl] == .error_set_decl) { + if (node == .error_set_decl) { (try error_completions.addOne(allocator)).* = .{ .label = name, .kind = .Constant, @@ -2490,7 +2561,6 @@ fn makeScopeInternal( continue; } - // @TODO: We can probably just use node_idx directly instead of first transforming to container const container_decl: ?ast.full.ContainerDecl = switch (node) { .container_decl, .container_decl_trailing => tree.containerDecl(node_idx), .container_decl_arg, .container_decl_arg_trailing => tree.containerDeclArg(node_idx), @@ -2537,7 +2607,12 @@ fn makeScopeInternal( } switch (node) { - .fn_proto, .fn_proto_one, .fn_proto_simple, .fn_proto_multi, .fn_decl => |fn_tag| { + .fn_proto, + .fn_proto_one, + .fn_proto_simple, + .fn_proto_multi, + .fn_decl, + => |fn_tag| { var buf: [1]ast.Node.Index = undefined; const func = fnProto(tree, node_idx, &buf).?; @@ -2569,7 +2644,11 @@ fn makeScopeInternal( .test_decl => { return try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, data[node_idx].rhs); }, - .block, .block_semicolon, .block_two, .block_two_semicolon => { + .block, + .block_semicolon, + .block_two, + .block_two_semicolon, + => { const first_token = tree.firstToken(node_idx); const last_token = tree.lastToken(node_idx); @@ -2638,10 +2717,9 @@ fn makeScopeInternal( scopes.items[scope_idx].uses = uses.toOwnedSlice(); return; }, - .@"comptime", .@"nosuspend" => { - return try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, data[node_idx].lhs); - }, - .@"if", .if_simple => { + .@"if", + .if_simple, + => { const if_node: ast.full.If = if (node == .@"if") tree.ifFull(node_idx) else @@ -2697,7 +2775,12 @@ fn makeScopeInternal( try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, if_node.ast.else_expr); } }, - .@"while", .while_simple, .while_cont, .@"for", .for_simple => { + .@"while", + .while_simple, + .while_cont, + .@"for", + .for_simple, + => { const while_node: ast.full.While = switch (node) { .@"while" => tree.whileFull(node_idx), .while_simple => tree.whileSimple(node_idx), @@ -2773,7 +2856,9 @@ fn makeScopeInternal( try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, while_node.ast.else_expr); } }, - .switch_case, .switch_case_one => { + .switch_case, + .switch_case_one, + => { const switch_case: ast.full.SwitchCase = switch (node) { .switch_case => tree.switchCase(node_idx), .switch_case_one => tree.switchCaseOne(node_idx), @@ -2809,7 +2894,11 @@ fn makeScopeInternal( try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, switch_case.ast.target_expr); }, - .global_var_decl, .local_var_decl, .aligned_var_decl, .simple_var_decl => { + .global_var_decl, + .local_var_decl, + .aligned_var_decl, + .simple_var_decl, + => { const var_decl = varDecl(tree, node_idx).?; if (var_decl.ast.type_node != 0) { try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, var_decl.ast.type_node); @@ -2819,13 +2908,206 @@ fn makeScopeInternal( try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, var_decl.ast.init_node); } }, + .call, + .call_comma, + .call_one, + .call_one_comma, + .async_call, + .async_call_comma, + .async_call_one, + .async_call_one_comma, + => { + var buf: [1]ast.Node.Index = undefined; + const call: ast.full.Call = switch (node) { + .async_call, + .async_call_comma, + .call, + .call_comma, + => tree.callFull(node_idx), + .async_call_one, + .async_call_one_comma, + .call_one, + .call_one_comma, + => tree.callOne(&buf, node_idx), + else => unreachable, + }; + + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, call.ast.fn_expr); + for (call.ast.params) |param| + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, param); + }, + .struct_init, + .struct_init_comma, + .struct_init_dot, + .struct_init_dot_comma, + .struct_init_dot_two, + .struct_init_dot_two_comma, + .struct_init_one, + .struct_init_one_comma, + => { + var buf: [2]ast.Node.Index = undefined; + const struct_init: ast.full.StructInit = switch (node) { + .struct_init, .struct_init_comma => tree.structInit(node_idx), + .struct_init_dot, .struct_init_dot_comma => tree.structInitDot(node_idx), + .struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(&buf, node_idx), + .struct_init_one, .struct_init_one_comma => tree.structInitOne(buf[0..1], node_idx), + else => unreachable, + }; + + if (struct_init.ast.type_expr != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, struct_init.ast.type_expr); + + for (struct_init.ast.fields) |field| { + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, field); + } + }, + .array_init, + .array_init_comma, + .array_init_dot, + .array_init_dot_comma, + .array_init_dot_two, + .array_init_dot_two_comma, + .array_init_one, + .array_init_one_comma, + => { + var buf: [2]ast.Node.Index = undefined; + const array_init: ast.full.ArrayInit = switch (node) { + .array_init, .array_init_comma => tree.arrayInit(node_idx), + .array_init_dot, .array_init_dot_comma => tree.arrayInitDot(node_idx), + .array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(&buf, node_idx), + .array_init_one, .array_init_one_comma => tree.arrayInitOne(buf[0..1], node_idx), + else => unreachable, + }; + + if (array_init.ast.type_expr != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, array_init.ast.type_expr); + for (array_init.ast.elements) |elem| { + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, elem); + } + }, + .container_field, + .container_field_align, + .container_field_init, + => { + const field = containerField(tree, node_idx).?; + + if (field.ast.type_expr != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, field.ast.type_expr); + if (field.ast.align_expr != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, field.ast.align_expr); + if (field.ast.value_expr != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, field.ast.value_expr); + }, + .builtin_call, + .builtin_call_comma, + .builtin_call_two, + .builtin_call_two_comma, + => { + const b_data = data[node_idx]; + const params = switch (node) { + .builtin_call, .builtin_call_comma => tree.extra_data[b_data.lhs..b_data.rhs], + .builtin_call_two, .builtin_call_two_comma => if (b_data.lhs == 0) + &[_]ast.Node.Index{} + else if (b_data.rhs == 0) + &[_]ast.Node.Index{b_data.lhs} + else + &[_]ast.Node.Index{ b_data.lhs, b_data.rhs }, + else => unreachable, + }; + + for (params) |param| { + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, param); + } + }, + .ptr_type, + .ptr_type_aligned, + .ptr_type_bit_range, + .ptr_type_sentinel, + => { + const ptr_type: ast.full.PtrType = ptrType(tree, node_idx).?; + if (ptr_type.ast.sentinel != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, ptr_type.ast.sentinel); + if (ptr_type.ast.align_node != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, ptr_type.ast.align_node); + if (ptr_type.ast.child_type != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, ptr_type.ast.child_type); + }, + .slice, + .slice_open, + .slice_sentinel, + => { + const slice: ast.full.Slice = switch (node) { + .slice => tree.slice(node_idx), + .slice_open => tree.sliceOpen(node_idx), + .slice_sentinel => tree.sliceSentinel(node_idx), + else => unreachable, + }; + + if (slice.ast.sliced != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, slice.ast.sliced); + if (slice.ast.start != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, slice.ast.start); + if (slice.ast.end != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, slice.ast.end); + if (slice.ast.sentinel != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, slice.ast.sentinel); + }, + + // no scope + .@"asm", + .asm_simple, + .asm_output, + .asm_input, + .error_value, + .@"anytype", + .multiline_string_literal, + .string_literal, + .enum_literal, + .identifier, + .anyframe_type, + .anyframe_literal, + .char_literal, + .integer_literal, + .float_literal, + .false_literal, + .true_literal, + .null_literal, + .undefined_literal, + .unreachable_literal, + .@"continue", + => {}, + .@"break", .@"defer" => { + if (data[node_idx].rhs != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, data[node_idx].rhs); + }, + // all lhs kind of nodes + .@"return", + .@"resume", + .field_access, + .@"suspend", + .deref, + .@"try", + .@"await", + .optional_type, + .@"comptime", + .@"nosuspend", + .bool_not, + .negation, + .bit_not, + .negation_wrap, + .address_of, + .grouped_expression, + .unwrap_optional, + => { + if (data[node_idx].lhs != 0) { + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, data[node_idx].lhs); + } + }, else => { - // log.debug("Implement makeScopeInternal for node type: '{s}'", .{node}); - // @TODO: Could we just do node_idx + 1 here? - // var child_idx: usize = 0; - // while (node.iterate(child_idx)) |child_node| : (child_idx += 1) { - // try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, child_node); - // } + if (data[node_idx].lhs != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, data[node_idx].lhs); + if (data[node_idx].rhs != 0) + try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, data[node_idx].rhs); }, } } diff --git a/src/config.zig b/src/config.zig index 9039e59..346aa7d 100644 --- a/src/config.zig +++ b/src/config.zig @@ -25,3 +25,7 @@ enable_semantic_tokens: bool = true, /// Whether to enable `*` and `?` operators in completion lists operator_completions: bool = true, + +/// Skips references to std. This will improve lookup speeds. +/// Going to definition however will continue to work +skip_std_references: bool = true, diff --git a/src/main.zig b/src/main.zig index 10d85bb..11c12b5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -366,6 +366,7 @@ fn nodeToCompletion( .arena = arena, .orig_handle = orig_handle, }; + logger.debug("eklafgaef", .{}); try analysis.iterateSymbolsContainer(&document_store, arena, node_handle, orig_handle, declToCompletion, context, !is_type_val); } @@ -427,7 +428,11 @@ fn nodeToCompletion( }); } }, - .global_var_decl, .local_var_decl, .aligned_var_decl, .simple_var_decl => { + .global_var_decl, + .local_var_decl, + .aligned_var_decl, + .simple_var_decl, + => { const var_decl = analysis.varDecl(tree, node).?; const is_const = token_tags[var_decl.ast.mut_token] == .keyword_const; @@ -448,7 +453,10 @@ fn nodeToCompletion( .detail = analysis.getVariableSignature(tree, var_decl), }); }, - .container_field, .container_field_align, .container_field_init => { + .container_field, + .container_field_align, + .container_field_init, + => { const field = analysis.containerField(tree, node).?; try list.append(.{ .label = handle.tree.tokenSlice(field.ast.name_token), @@ -457,13 +465,19 @@ fn nodeToCompletion( .detail = analysis.getContainerFieldSignature(handle.tree, field), }); }, - .array_type, .array_type_sentinel => { + .array_type, + .array_type_sentinel, + => { try list.append(.{ .label = "len", .kind = .Field, }); }, - .ptr_type, .ptr_type_aligned, .ptr_type_bit_range, .ptr_type_sentinel => { + .ptr_type, + .ptr_type_aligned, + .ptr_type_bit_range, + .ptr_type_sentinel, + => { const ptr_type = analysis.ptrType(tree, node).?; switch (ptr_type.size) { @@ -496,7 +510,7 @@ fn nodeToCompletion( .kind = .Field, }); }, - else => if (analysis.nodeToString(handle.tree, node)) |string| { + else => if (analysis.nodeToString(tree, node)) |string| { try list.append(.{ .label = string, .kind = .Field, @@ -828,10 +842,26 @@ fn renameDefinitionLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, h }); } -fn referencesDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, pos_index: usize, include_decl: bool) !void { +fn referencesDefinitionGlobal( + arena: *std.heap.ArenaAllocator, + id: types.RequestId, + handle: *DocumentStore.Handle, + pos_index: usize, + include_decl: bool, + skip_std_references: bool, +) !void { const decl = (try getSymbolGlobal(arena, pos_index, handle)) orelse return try respondGeneric(id, null_result_response); var locs = std.ArrayList(types.Location).init(&arena.allocator); - try references.symbolReferences(arena, &document_store, decl, offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append); + try references.symbolReferences( + arena, + &document_store, + decl, + offset_encoding, + include_decl, + &locs, + std.ArrayList(types.Location).append, + skip_std_references, + ); try send(arena, types.Response{ .id = id, .result = .{ .Locations = locs.items }, @@ -849,7 +879,7 @@ fn referencesDefinitionFieldAccess( ) !void { const decl = (try getSymbolFieldAccess(handle, arena, position, range, config)) orelse return try respondGeneric(id, null_result_response); var locs = std.ArrayList(types.Location).init(&arena.allocator); - try references.symbolReferences(arena, &document_store, decl, offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append); + try references.symbolReferences(arena, &document_store, decl, offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append, config.skip_std_references); try send(arena, types.Response{ .id = id, .result = .{ .Locations = locs.items }, @@ -885,7 +915,15 @@ fn hasComment(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenI fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) !void { const tree = decl_handle.handle.tree; switch (decl_handle.decl.*) { - .ast_node => |node| try nodeToCompletion(context.arena, context.completions, .{ .node = node, .handle = decl_handle.handle }, null, context.orig_handle, false, context.config.*), + .ast_node => |node| try nodeToCompletion( + context.arena, + context.completions, + .{ .node = node, .handle = decl_handle.handle }, + null, + context.orig_handle, + false, + context.config.*, + ), .param_decl => |param| { const doc_kind: types.MarkupContent.Kind = if (client_capabilities.completion_doc_supports_md) .Markdown else .PlainText; const doc = if (param.first_doc_comment) |doc_comments| @@ -1419,7 +1457,7 @@ fn referencesHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: const include_decl = req.params.context.includeDeclaration; switch (pos_context) { - .var_access => try referencesDefinitionGlobal(arena, id, handle, doc_position.absolute_index, include_decl), + .var_access => try referencesDefinitionGlobal(arena, id, handle, doc_position.absolute_index, include_decl, config.skip_std_references), .field_access => |range| try referencesDefinitionFieldAccess(arena, id, handle, doc_position, range, include_decl, config), .label => try referencesDefinitionLabel(arena, id, handle, doc_position.absolute_index, include_decl), else => try respondGeneric(id, null_result_response), diff --git a/src/references.zig b/src/references.zig index 951476b..b77749f 100644 --- a/src/references.zig +++ b/src/references.zig @@ -122,7 +122,11 @@ fn symbolReferencesInternal( for (analysis.declMembers(tree, node_tags[node], node, &buf)) |member| try symbolReferencesInternal(arena, store, .{ .node = member, .handle = handle }, decl, encoding, context, handler); }, - .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => { + .global_var_decl, + .local_var_decl, + .simple_var_decl, + .aligned_var_decl, + => { const var_decl = analysis.varDecl(tree, node).?; if (var_decl.ast.type_node != 0) { try symbolReferencesInternal(arena, store, .{ .node = var_decl.ast.type_node, .handle = handle }, decl, encoding, context, handler); @@ -131,12 +135,13 @@ fn symbolReferencesInternal( try symbolReferencesInternal(arena, store, .{ .node = var_decl.ast.init_node, .handle = handle }, decl, encoding, context, handler); } }, - // @TODO: Usingnamespace - // .Use => { - // const use = node.cast(ast.Node.Use).?; - // try symbolReferencesInternal(arena, store, .{ .node = use.expr, .handle = handle }, decl, encoding, context, handler); - // }, - .container_field, .container_field_align, .container_field_init => { + .@"usingnamespace" => { + try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler); + }, + .container_field, + .container_field_align, + .container_field_init, + => { const field = analysis.containerField(tree, node).?; if (field.ast.type_expr != 0) { try symbolReferencesInternal(arena, store, .{ .node = field.ast.type_expr, .handle = handle }, decl, encoding, context, handler); @@ -152,7 +157,12 @@ fn symbolReferencesInternal( } } }, - .fn_proto, .fn_proto_multi, .fn_proto_one, .fn_proto_simple, .fn_decl => { + .fn_proto, + .fn_proto_multi, + .fn_proto_one, + .fn_proto_simple, + .fn_decl, + => { var buf: [1]ast.Node.Index = undefined; const fn_proto = analysis.fnProto(tree, node, &buf).?; var it = fn_proto.iterate(tree); @@ -189,7 +199,9 @@ fn symbolReferencesInternal( .@"nosuspend" => { try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler); }, - .@"switch", .switch_comma => { + .@"switch", + .switch_comma, + => { // TODO When renaming a union(enum) field, also rename switch items that refer to it. try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler); const extra = tree.extraData(datas[node].rhs, ast.Node.SubRange); @@ -212,7 +224,12 @@ fn symbolReferencesInternal( for (case.ast.values) |val| try symbolReferencesInternal(arena, store, .{ .node = val, .handle = handle }, decl, encoding, context, handler); }, - .@"while", .while_simple, .while_cont, .for_simple, .@"for" => { + .@"while", + .while_simple, + .while_cont, + .for_simple, + .@"for", + => { const loop: ast.full.While = switch (node_tags[node]) { .@"while" => tree.whileFull(node), .while_simple => tree.whileSimple(node), @@ -230,7 +247,9 @@ fn symbolReferencesInternal( try symbolReferencesInternal(arena, store, .{ .node = loop.ast.else_expr, .handle = handle }, decl, encoding, context, handler); } }, - .@"if", .if_simple => { + .@"if", + .if_simple, + => { const if_node: ast.full.If = if (node_tags[node] == .@"if") tree.ifFull(node) else tree.ifSimple(node); try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.cond_expr, .handle = handle }, decl, encoding, context, handler); @@ -239,11 +258,17 @@ fn symbolReferencesInternal( try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.else_expr, .handle = handle }, decl, encoding, context, handler); } }, - .array_type, .array_type_sentinel => { + .array_type, + .array_type_sentinel, + => { try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler); }, - .ptr_type, .ptr_type_aligned, .ptr_type_bit_range, .ptr_type_sentinel => { + .ptr_type, + .ptr_type_aligned, + .ptr_type_bit_range, + .ptr_type_sentinel, + => { const ptr_type = analysis.ptrType(tree, node).?; if (ptr_type.ast.align_node != 0) { @@ -334,7 +359,10 @@ fn symbolReferencesInternal( try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler); } }, - .slice, .slice_sentinel, .slice_open => |s| { + .slice, + .slice_sentinel, + .slice_open, + => |s| { const slice: ast.full.Slice = switch (s) { .slice => tree.slice(node), .slice_open => tree.sliceOpen(node), @@ -353,13 +381,18 @@ fn symbolReferencesInternal( try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler); }, - .deref, .unwrap_optional => { + .deref, + .unwrap_optional, + => { try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler); }, .grouped_expression => { try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler); }, - .@"return", .@"break", .@"continue" => { + .@"return", + .@"break", + .@"continue", + => { if (datas[node].lhs != 0) { try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler); } @@ -389,7 +422,9 @@ fn symbolReferencesInternal( for (params) |param| try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler); }, - .@"asm", .asm_simple => |a| { + .@"asm", + .asm_simple, + => |a| { const _asm: ast.full.Asm = if (a == .@"asm") tree.asmFull(node) else tree.asmSimple(node); if (_asm.ast.items.len == 0) try symbolReferencesInternal(arena, store, .{ .node = _asm.ast.template, .handle = handle }, decl, encoding, context, handler); @@ -491,6 +526,7 @@ pub fn symbolReferences( include_decl: bool, context: anytype, comptime handler: anytype, + skip_std_references: bool, ) !void { std.debug.assert(decl_handle.decl.* != .label_decl); const curr_handle = decl_handle.handle; @@ -500,16 +536,18 @@ pub fn symbolReferences( var handles = std.ArrayList(*DocumentStore.Handle).init(&arena.allocator); var handle_it = store.handles.iterator(); while (handle_it.next()) |entry| { + if (skip_std_references and std.mem.indexOf(u8, entry.key, "std") != null) { + if (!include_decl or entry.value != curr_handle) + continue; + } try handles.append(entry.value); } for (handles.items) |handle| { if (include_decl and handle == curr_handle) { try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler); - try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = handle }, decl_handle, encoding, context, handler); } - // @TODO: make references across files working - // try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = handle }, decl_handle, encoding, context, handler); + try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = handle }, decl_handle, encoding, context, handler); } }, .param_decl => |param| { diff --git a/src/rename.zig b/src/rename.zig index 803b424..5d87cec 100644 --- a/src/rename.zig +++ b/src/rename.zig @@ -40,7 +40,7 @@ pub fn renameSymbol( .edits = edits, .allocator = &arena.allocator, .new_name = new_name, - }, refHandler); + }, refHandler, true); } pub fn renameLabel(